diff --git a/.cursor/agent/产品经理/evolution/2026-03-31.md b/.cursor/agent/产品经理/evolution/2026-03-31.md new file mode 100644 index 00000000..c0ec575f --- /dev/null +++ b/.cursor/agent/产品经理/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 产品经理 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- 超级个体(VIP 曝光)与 @ 人物(内容引用)应视为同一业务的两面;融合优先统一运营心智与验收口径,再决定 UI 是否单页。 +- 一期验收需明确:无 @ 人物的有效 VIP 的提示策略、管理端跳转路径是否满足运营闭环。 diff --git a/.cursor/agent/后端工程师/evolution/2026-03-31.md b/.cursor/agent/后端工程师/evolution/2026-03-31.md new file mode 100644 index 00000000..c9a3563d --- /dev/null +++ b/.cursor/agent/后端工程师/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 后端工程师 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- 列表融合优先在现有聚合接口(如 `/api/db/vip-members`)上增加 Person 关联只读字段,避免新表与重复「谁算超级个体」规则;注意批量查询、路由组边界不变。 +- 字段命名宜与 read-extras / mention 侧对齐,便于管理端一行展示。 diff --git a/.cursor/agent/团队/evolution/2026-03-31.md b/.cursor/agent/团队/evolution/2026-03-31.md new file mode 100644 index 00000000..648d4848 --- /dev/null +++ b/.cursor/agent/团队/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 团队 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- **架构决议**:先 **数据与入口融合(接口字段 + 管理端互链)**,再视需求做单页;不先合并数据库表。 +- **路由约定不变**:小程序仅 `/api/miniprogram/*`,管理端 `/api/db/*` 聚合;详见 `meeting/2026-03-31_超级个体与@列表融合.md`。 diff --git a/.cursor/agent/小程序开发工程师/evolution/2026-03-30.md b/.cursor/agent/小程序开发工程师/evolution/2026-03-30.md new file mode 100644 index 00000000..f0d9d270 --- /dev/null +++ b/.cursor/agent/小程序开发工程师/evolution/2026-03-30.md @@ -0,0 +1,9 @@ +# 小程序开发工程师 经验记录 - 2026-03-30 + +## 阅读页跳转其他小程序附带手机号 + +- **场景**:文章详情正文 `#` 链接标签,`type=miniprogram`,`wx.navigateToMiniProgram`。 +- **实现**:`read.js` 中 `appendQueryToPath` + `getLoggedInUserPhone`,有手机号时在 `path` 追加 `phone=`。 +- **注意**:无手机号时不追加;`path` 已有 query 时用 `&` 拼接。 + +详见会议:`.cursor/meeting/2026-03-30_文章详情跳转MBTI小程序传手机号.md` diff --git a/.cursor/agent/小程序开发工程师/evolution/2026-03-31.md b/.cursor/agent/小程序开发工程师/evolution/2026-03-31.md new file mode 100644 index 00000000..1931be9e --- /dev/null +++ b/.cursor/agent/小程序开发工程师/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 小程序开发工程师 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- 管理端融合默认 **不改动** C 端:`vip/members` 与 read-extras mention 分离合理;若后端扩展展示字段再评估必要性。 +- 联调重点:首页超级个体顺序与阅读页 @ 行为不因后台列表改造而回归。 diff --git a/.cursor/agent/小程序开发工程师/evolution/索引.md b/.cursor/agent/小程序开发工程师/evolution/索引.md index 7d9bc89c..dba0947d 100644 --- a/.cursor/agent/小程序开发工程师/evolution/索引.md +++ b/.cursor/agent/小程序开发工程师/evolution/索引.md @@ -13,3 +13,4 @@ | 2026-03-14 | 我的页设置入口隐藏;资料修改引导场景梳理(登录后、@某人、找伙伴、链接卡若) | [2026-03-14.md](./2026-03-14.md) | | 2026-03-16 | 编辑资料页分享名片:转发/朋友圈特殊处理,Canvas 绘制封面,标题「昵称+为您分享名片」 | [2026-03-16.md](./2026-03-16.md) | | 2026-03-17 | 代付美团式:读页→代付页→分享;详情页双态(发起人/好友);目录 loading、最新新增 5 条折叠 | [2026-03-17.md](./2026-03-17.md) | +| 2026-03-30 | 阅读页 `navigateToMiniProgram` 时 path 追加 `phone`(已登录且已绑定) | [2026-03-30.md](./2026-03-30.md) | diff --git a/.cursor/agent/开发助理/项目索引/产品.md b/.cursor/agent/开发助理/项目索引/产品.md index e479d269..22a47fa0 100644 --- a/.cursor/agent/开发助理/项目索引/产品.md +++ b/.cursor/agent/开发助理/项目索引/产品.md @@ -28,9 +28,10 @@ Soul 创业派对产品定位:面向创业者的社区/工具型小程序。 | 2026-03-18 | 会议:超级个体开通后自动创建@人与支付前资料引导(头像+昵称) | 已完成 | | 2026-03-24 | 会议:开发进度同步;项目索引补齐 2026-03-19/20 及之后记录;文档同步原则确认 | 已完成 | | 2026-03-24 | 会议:需求与进度及三端闭环评审;规则/风控缺口清单化;VIP 支付前资料规则待拍板 | 已完成 | +| 2026-03-31 | 会议:超级个体列表与 @ 列表融合方案(先数据互链再单页);一期验收口径待拍板 | 待续 | > **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置 --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 diff --git a/.cursor/agent/开发助理/项目索引/后端.md b/.cursor/agent/开发助理/项目索引/后端.md index 55d10908..faa47eae 100644 --- a/.cursor/agent/开发助理/项目索引/后端.md +++ b/.cursor/agent/开发助理/项目索引/后端.md @@ -41,9 +41,10 @@ soul-api(Go + Gin + GORM + MySQL)提供三组路由:`/api/miniprogram/*` | 2026-03-20 | 提现:审批逻辑修复、fail_reason/error_message 落库、referral_config withdrawFee/enableAutoWithdraw | 已完成 | | 2026-03-24 | 会议:开发进度同步;项目索引补齐;router/ossConfig 待确认 | 已完成 | | 2026-03-24 | 会议:三端闭环评审;提现/CKB/匹配后端校验与 errorCode 为 P0/P1 | 已完成 | +| 2026-03-31 | 会议:超级个体与 @ 列表融合;优先扩展 vip-members 聚合 Person 关联字段 | 待续 | > **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置 --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 diff --git a/.cursor/agent/开发助理/项目索引/团队.md b/.cursor/agent/开发助理/项目索引/团队.md index 8af82f44..b5a12fcb 100644 --- a/.cursor/agent/开发助理/项目索引/团队.md +++ b/.cursor/agent/开发助理/项目索引/团队.md @@ -32,9 +32,10 @@ Soul 创业派对全项目架构与约定:路由隔离(miniprogram/admin/db | 2026-03-18 | 吸收经验:分享进入链路需兼容朋友圈 singlePage;单页模式不执行支付/自动领取等强动作并引导“前往小程序” | 已完成 | | 2026-03-18 | 会议:超级个体开通后自动创建@人统一走 Person;幂等键绑定 userId;默认资料 flags 后端输出 | 已完成 | | 2026-03-24 | 会议:需求与进度及三端闭环评审;主路径闭环成立、规则闭环待加固;失败响应与 err.response 契约 | 已完成 | +| 2026-03-31 | 会议:超级个体与 @ 列表融合;先数据与入口融合、路由边界不变 | 已完成 | > **格式说明**:每次架构级讨论后在此追加一行,日期格式 YYYY-MM-DD --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 diff --git a/.cursor/agent/开发助理/项目索引/小程序.md b/.cursor/agent/开发助理/项目索引/小程序.md index d63e2650..cb1ad7e9 100644 --- a/.cursor/agent/开发助理/项目索引/小程序.md +++ b/.cursor/agent/开发助理/项目索引/小程序.md @@ -46,9 +46,11 @@ | 2026-03-24 | 会议:开发进度同步;项目索引补齐;富文本 rich-text 技术债待续 | 已完成 | | 2026-03-24 | 会议:三端闭环评审;app.request err.response;提现双入口与后端契约对齐 | 已完成 | | 2026-03-24 | 落地:app.request 挂 err.response;soulBridge 留资扩展;阅读页带码与 referral/my 提现 catch 对齐 | 已完成 | +| 2026-03-30 | 会议:文章详情 # 跳转关联小程序(如 MBTI)时 path 追加 `phone`;read.js 已实现 | 已完成 | +| 2026-03-31 | 会议:超级个体与 @ 列表融合;C 端默认不改接口,仅回归 | 已完成 | > **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置 --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 diff --git a/.cursor/agent/开发助理/项目索引/测试.md b/.cursor/agent/开发助理/项目索引/测试.md index 120cd795..ad50a5c7 100644 --- a/.cursor/agent/开发助理/项目索引/测试.md +++ b/.cursor/agent/开发助理/项目索引/测试.md @@ -34,7 +34,8 @@ | 2026-03-18 | 会议:新增用例(资料默认阻断支付、Person 自动创建幂等、昵称变更同步回归) | 已完成 | | 2026-03-24 | 会议:开发进度同步;项目索引补齐;提现/登录/收益/推广设置用例补充;singlePage、getPhoneNumber 边界 | 已完成 | | 2026-03-24 | 会议:三端闭环评审;API 绕过负例与双入口提现回归待契约落地后执行 | 已完成 | +| 2026-03-31 | 会议:超级个体与 @ 列表融合;回归清单含排序/@/绑定/Webhook 边界 | 待续 | --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 diff --git a/.cursor/agent/开发助理/项目索引/管理端.md b/.cursor/agent/开发助理/项目索引/管理端.md index e0477190..aad92a7d 100644 --- a/.cursor/agent/开发助理/项目索引/管理端.md +++ b/.cursor/agent/开发助理/项目索引/管理端.md @@ -46,11 +46,12 @@ | 2026-03-20 | 提现审核:备注列(fail_reason/error_message)、自动审批开关;推广设置提现手续费 | 已完成 | | 2026-03-24 | 会议:开发进度同步;项目索引补齐;DistributionPage Order.description 待修 | 已完成 | | 2026-03-24 | 会议:三端闭环评审;管理端与小程序配套整体齐全;类型债择机修 | 已完成 | +| 2026-03-31 | 会议:超级个体与 @ 列表融合;UsersPage 表格加 @ 状态与跳转内容页 | 待续 | > **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置 --- -**最后更新**:2026-03-24 +**最后更新**:2026-03-31 > 注:soul-admin 构建仍有 DistributionPage Order.description 类型错误(与本次迁移无关),待修复。 diff --git a/.cursor/agent/管理端开发工程师/evolution/2026-03-31.md b/.cursor/agent/管理端开发工程师/evolution/2026-03-31.md new file mode 100644 index 00000000..f8f09844 --- /dev/null +++ b/.cursor/agent/管理端开发工程师/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 管理端开发工程师 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- UsersPage「超级个体列表」与 ContentPage 人物/@ 区短期以 **数据同源 + 互链** 为主:表格增加 @ 绑定状态列、跳转内容管理;避免两处各写排序逻辑。 +- 二期再评估「超级个体中心」单页,复用现有表格与人物卡片模式。 diff --git a/.cursor/agent/软件测试/evolution/2026-03-31.md b/.cursor/agent/软件测试/evolution/2026-03-31.md new file mode 100644 index 00000000..06ad864b --- /dev/null +++ b/.cursor/agent/软件测试/evolution/2026-03-31.md @@ -0,0 +1,6 @@ +# 软件测试 经验记录 - 2026-03-31 + +## 会议:超级个体列表与 @ 列表融合 + +- 回归清单:VIP 排序↔首页、@ 解析与跳转、Person 与 VIP 绑定/解绑、获客/Webhook。 +- 边界:无 Person 的 VIP、VIP 过期仍有 Person 等交叉场景需与产品确认预期。 diff --git a/.cursor/meeting/2026-03-30_文章详情跳转MBTI小程序传手机号.md b/.cursor/meeting/2026-03-30_文章详情跳转MBTI小程序传手机号.md new file mode 100644 index 00000000..a3529917 --- /dev/null +++ b/.cursor/meeting/2026-03-30_文章详情跳转MBTI小程序传手机号.md @@ -0,0 +1,68 @@ +# 会议纪要 - 2026-03-30 | 文章详情 # 标签跳转 MBTI 小程序并传手机号 + +> 本文件由**助理橙子**在会议结束后整理。 + +--- + +## 基本信息 + +- **时间**:2026-03-30 +- **议题**:阅读页通过正文 `#` 链接标签跳转 MBTI 等关联小程序时,将当前用户手机号通过 URL 查询参数带给目标小程序;目标侧(MBTI)在 `onLoad` 解析 `phone`。 +- **触发方式**:开个会 +- **参与角色**:产品经理、后端开发、管理端开发工程师、小程序开发工程师、测试人员 + +--- + +## 各角色发言 + +### 【产品经理】 + +需求明确:从文章详情点击配置好的 `#` 标签进入 MBTI 小程序时,需要携带本端已绑定手机号,便于 MBTI 侧识别用户或做承接。未登录或未绑定手机号时仍可跳转,但不带 `phone` 参数(由 MBTI 侧引导登录或留资)。验收:已登录且资料有手机号时,目标小程序启动路径含 `phone` 参数且值正确。 + +### 【后端开发】 + +本次无需新增接口。手机号已在登录/资料接口中写入 `userInfo.phone` 与本地缓存;阅读页扩展 `read-extras` 中 `linkedMiniprograms` 与 `linkTags` 配置不变。若未来需服务端校验或脱敏,可再评估专用 token 跳转,当前按产品要求用 URL 传参。 + +### 【管理端开发工程师】 + +链接标签「类型=小程序」、`mpKey` 与 `page_path` 继续在内容管理里配置;无需改管理端字段。运营需确保 MBTI 小程序已配置在 `linked_miniprograms` 且 `app.json` 白名单含目标 AppID。 + +### 【小程序开发工程师】 + +在 `read.js` 的 `onLinkTagTap` 中,`tagType === 'miniprogram'` 且 `wx.navigateToMiniProgram` 时,若本地存在手机号(`globalData.userInfo.phone` 或 `wx.getStorageSync('user_phone')`),则在 `path` 上追加 `phone=encodeURIComponent(手机号)`,兼容已有 `?` 查询串。参数名:`phone`。 + +### 【测试人员】 + +用例:① 已登录有手机号 → 跳转路径含 `phone`;② 未绑定手机号 → 跳转无 `phone` 或仍成功打开;③ `page_path` 已带 `?a=1` → 追加 `&phone=`;④ 真机从阅读页点 `#MBTI`(或配置标签)验证 MBTI 侧 `onLoad(options)` 能收到 `options.phone`。 + +--- + +## 会议决议 + +1. **Soul 小程序阅读页**:跳转其他小程序(`navigateToMiniProgram`)时,若当前用户有手机号,则向 `path` 追加查询参数 `phone`。 +2. **MBTI 小程序**:在目标页面 `onLoad` 中读取 `options.phone`(需自行对接与合规,如仅用于展示/关联,勿明文日志)。 +3. **无需**后端与管理端接口变更;配置与现网「链接标签 + 关联小程序」一致。 + +--- + +## 待办事项 + +| 责任角色 | 任务 | 截止 | +|----------|------|------| +| MBTI 小程序侧 | 目标页解析 `phone` 并完成业务闭环 | 待排期 | +| 测试 | 真机联调验证带参跳转 | 发版前 | + +--- + +## 问题与作答区 + +| 问题 | 责任角色 | 作答 | +|------|----------|------| +| 参数名是否固定为 `phone`? | 小程序 | 已约定为 `phone`,与 MBTI 对齐即可 | +| 是否只对 MBTI 传参? | 产品 | 当前实现为**所有**关联小程序跳转均带手机号(若本地有);若仅 MBTI 需传,可后续加配置开关 | + +--- + +## 各角色经验与业务理解更新 + +- **小程序**:`miniprogram/pages/read/read.js` 已实现 `appendQueryToPath` + `getLoggedInUserPhone`,见代码注释。 diff --git a/.cursor/meeting/2026-03-31_超级个体与@列表融合.md b/.cursor/meeting/2026-03-31_超级个体与@列表融合.md new file mode 100644 index 00000000..d13ddfa8 --- /dev/null +++ b/.cursor/meeting/2026-03-31_超级个体与@列表融合.md @@ -0,0 +1,106 @@ +# 会议纪要 - 2026-03-31 | 超级个体列表与 @ 列表融合方案 + +> 本文件由**助理橙子**在会议结束后生成。 + +--- + +## 基本信息 + +- **时间**:2026-03-31(会议即时) +- **议题**:将「超级个体列表」与「可 @ 的人物列表(@列表)」在管理与认知上融合,减少双入口维护、统一运营视角 +- **触发方式**:开个会 / 方案讨论 +- **参与角色**:产品经理、后端开发、管理端开发工程师、小程序开发工程师、测试人员 + +--- + +## 各角色发言 + +### 【产品经理】 + +当前存在两条线:**用户管理里的「超级个体列表」**(VIP 排序、获客统计、Webhook)与 **内容/人物侧的「可 @ 人物」**(Person、token、阅读页 mention)。业务上应是同一批「派对超级个体」:开通 VIP 且资料合规者既可上首页横滑,又应可被正文 @。融合的价值是:运营只维护一处心智——「谁是超级个体、是否已绑定 @ 人、排序是否生效」。验收上需明确:列表合并后仍能完成「调排序、看点击/获客、配飞书、删/建 @ 人」全流程,且不与「导师绑定用户」等场景混淆。 + +### 【后端开发】 + +数据上 **users(VIP 字段)** 与 **persons(@ 侧)** 已通过 user_id / 绑定关系关联;`/api/db/vip-members` 已聚合 click、lead、webhook。融合不必先造新表,优先 **聚合查询或扩展现有接口字段**(例如在 vip-members 响应中附带 `personId`、`hasMentionToken`、`inPool` 等,字段名与小程序 read-extras 对齐)。新路由若需要,仍挂在 **db 或 admin 组**,避免小程序直接访问 db。性能上列表 limit 已存在上限,批量补 persons 信息时注意 N+1,沿用现有 batch 模式。 + +### 【管理端开发工程师】 + +现状:**用户管理 → 超级个体列表** 与 **内容管理里的人物/@ 相关区块** 分属不同页面。融合路径可分档:**短期**在超级个体表格增加「@ 人物状态」列与跳转内容页锚点(或 Drawer 快捷查看 token);**中期**抽「超级个体中心」单页,左侧 VIP 列表右侧 Person 详情,或保留两页但统一数据源与面包屑。**禁止**在未统一接口前两处各写一套排序逻辑。组件上可复用 UsersPage 已有表格与 ContentPage 已有 person 卡片模式。 + +### 【小程序开发工程师】 + +C 端已分离:**首页** `/api/miniprogram/vip/members`,**阅读页 @** 来自 `read-extras` 的 mentionPersons。管理端融合 **不强制改小程序**;若后端在 vip/members 增加展示用字段,需评估包体与缓存。回归点:首页超级个体横滑、阅读页 @ 高亮与跳转 member-detail **行为不变**。 + +### 【测试人员】 + +回归清单:**VIP 排序**(管理端改 vip_sort → 首页顺序)、**@ 解析**(正文 @ 人物跳转与 inPool)、**绑定/解绑** 超级个体与 Person、**获客/Webhook** 推送。边界:无 Person 的 VIP、有 Person 但 VIP 过期的展示策略需与产品一致。 + +--- + +## 讨论过程 + +- **【管理端开发工程师】回复【产品经理】**:完全合并成一个页面开发量较大,建议先「数据同源 + 界面互链」,再视反馈做单页。 +- **【后端开发】回复【管理端开发工程师】**:扩字段比新开「大一统」接口风险小;若要做统一 API,命名需与现有 `vip-members`、人物接口文档同步,避免管理端调两套。 +- **【小程序开发工程师】回复【后端开发】**:小程序侧保持 miniprogram 路由不变即可,融合主要是管理端与数据层体验。 + +--- + +## 会议决议 + +1. **概念对齐**:以 **VIP 用户(超级个体)为主实体**,Person 为内容侧 @ 扩展;「融合」优先指 **管理端统一展示与互链**,而非强行合并数据库表。 +2. **实施顺序**:**第一阶段** — `vip-members`(或等价 admin/db 接口)增加与 @ 人物相关的 **只读关联字段**,超级个体列表展示绑定状态并提供 **跳转内容/人物管理** 的入口;**第二阶段** — 评估是否整合为单一「超级个体中心」页面或保留双入口强绑定数据源。 +3. **待确认项**:产品拍板「无 @ 人物的有效 VIP」在列表中的文案与是否强提示补全;是否需要在内容页反向高亮「对应 VIP 用户」。 + +--- + +## 待办事项 + +| 责任角色 | 任务 | 优先级 | 截止建议 | +|---------|------|--------|---------| +| 产品经理 | 确认「融合」一期验收:必显字段、跳转路径、无 Person 时的运营提示文案 | 高 | 迭代规划前 | +| 后端开发 | 评估并在 `vip-members`(或文档约定接口)增加 person 关联字段;注意批量查询性能 | 高 | 与前端联调前 | +| 管理端开发工程师 | 超级个体列表增加 @ 状态列与跳转;与 ContentPage 人物区行为对齐 | 高 | 同上 | +| 小程序开发工程师 | 接口不变则仅做回归;若字段下沉到 C 端再评估 | 中 | 联调时 | +| 测试人员 | 按回归清单执行;记录边界用例 | 中 | 提测阶段 | + +--- + +## 问题与作答区 + +| # | 问题 | 责任角色 | 作答 | +|---|------|---------|------| +| 1 | 一期是否要求「仅互链」不合并页面? | 产品经理 | (待补充) | +| 2 | `vip-members` 增加哪些字段名与 read-extras 完全一致? | 后端开发 | (待补充) | +| 3 | 内容页是否增加「对应超级个体」反向入口? | 产品经理、管理端开发工程师 | (待补充) | + +--- + +## 各角色经验与业务理解更新 + +### 产品经理 + +- 超级个体与 @ 人物是同一业务的两面:对外曝光(首页)与内容引用(@),融合先从运营视角统一,再考虑 UI 合并。 + +### 后端开发 + +- 列表融合优先 **扩展现有聚合接口**,避免小程序与管理端各维护一套「谁算超级个体」的规则。 + +### 管理端开发工程师 + +- 用户页超级个体 tab 与内容人物管理应 **共享数据源展示字段**,短期用跳转与列补齐降低重复配置。 + +### 小程序开发工程师 + +- 首页 VIP 列表与阅读 @ 数据源可继续分离;管理端融合对 C 端默认 **零改动**。 + +### 测试人员 + +- 融合类需求重点测 **排序、绑定状态、过期 VIP** 三类交叉场景。 + +### 团队共享 + +- 决议:**先数据与入口融合,再视需要做单页**;架构上保持 `/api/miniprogram/*` 与 `/api/db/*` 边界不变。 + +--- + +*会议纪要由助理橙子生成 | 各角色经验已同步至 `agent/{角色}/evolution/2026-03-31.md`* diff --git a/.cursor/meeting/README.md b/.cursor/meeting/README.md index 751eb581..d6c9039f 100644 --- a/.cursor/meeting/README.md +++ b/.cursor/meeting/README.md @@ -82,3 +82,5 @@ YYYY-MM-DD_会议主题.md | 2026-03-18 | 超级个体开通后自动创建@人与资料引导 | 产品、后端、管理端、小程序、测试 | [2026-03-18_超级个体开通后自动创建@人与资料引导.md](2026-03-18_超级个体开通后自动创建@人与资料引导.md) | | 2026-03-24 | 开发进度同步会议(查看代码与开发文档对齐) | 产品、后端、管理端、小程序、测试 | [2026-03-24_开发进度同步会议.md](2026-03-24_开发进度同步会议.md) | | 2026-03-24 | 需求与进度及三端闭环评审(业务闭环、核心功能、小程序与管理端配套) | 产品、后端、管理端、小程序、测试 | [2026-03-24_需求与进度及三端闭环评审.md](2026-03-24_需求与进度及三端闭环评审.md) | +| 2026-03-30 | 文章详情 # 标签跳转 MBTI 小程序传手机号 | 产品、后端、管理端、小程序、测试 | [2026-03-30_文章详情跳转MBTI小程序传手机号.md](2026-03-30_文章详情跳转MBTI小程序传手机号.md) | +| 2026-03-31 | 超级个体列表与 @ 列表融合方案 | 产品、后端、管理端、小程序、测试、团队 | [2026-03-31_超级个体与@列表融合.md](2026-03-31_超级个体与@列表融合.md) | diff --git a/.cursor/skills/assistant-doc-sync/sync-log.md b/.cursor/skills/assistant-doc-sync/sync-log.md index cdf9517d..1a1ad61c 100644 --- a/.cursor/skills/assistant-doc-sync/sync-log.md +++ b/.cursor/skills/assistant-doc-sync/sync-log.md @@ -6,6 +6,10 @@ --- +## 2026-03-31 + +- 会议收尾:超级个体列表与 @ 列表融合;纪要 `.cursor/meeting/2026-03-31_超级个体与@列表融合.md`;各角色 evolution 与项目索引已更新 + ## 2026-03-24 - Skill 优化:assistant-doc-sync 新增 sync-log 记忆机制、assets 模板、Skill 撰写原则 diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index 677f8bec..d94da466 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -138,6 +138,20 @@ function pickLinkTagField(tag, keys, defaultValue = '') { return defaultValue } +/** 跳转其他小程序时在 path 上追加 query(保留已有 ?…&…) */ +function appendQueryToPath(path, key, value) { + if (value == null || value === '') return path || '' + const base = (path || '').trim() + const sep = base.includes('?') ? '&' : '?' + return `${base}${sep}${encodeURIComponent(key)}=${encodeURIComponent(String(value))}` +} + +/** 当前用户已绑定手机号(与 app 内登录态一致) */ +function getLoggedInUserPhone() { + const u = app.globalData.userInfo || {} + return (u.phone || wx.getStorageSync('user_phone') || '').trim().replace(/\s/g, '') +} + Page({ data: { // 系统信息 @@ -897,9 +911,14 @@ Page({ } const linked = (app.globalData.linkedMiniprograms || []).find(m => m.key === mpKey) if (linked && linked.appId) { + let targetPath = pagePath || linked.path || '' + const phone = getLoggedInUserPhone() + if (phone) { + targetPath = appendQueryToPath(targetPath, 'phone', phone) + } wx.navigateToMiniProgram({ appId: linked.appId, - path: pagePath || linked.path || '', + path: targetPath, envVersion: 'release', success: () => {}, fail: (err) => { diff --git a/miniprogram/pages/read/read.wxml b/miniprogram/pages/read/read.wxml index eadcf8bd..87037a7d 100644 --- a/miniprogram/pages/read/read.wxml +++ b/miniprogram/pages/read/read.wxml @@ -417,7 +417,7 @@ diff --git a/miniprogram/pages/read/read.wxss b/miniprogram/pages/read/read.wxss index fafe0157..e56a2954 100644 --- a/miniprogram/pages/read/read.wxss +++ b/miniprogram/pages/read/read.wxss @@ -1448,12 +1448,13 @@ button.action-share-native { filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.25)); } -/* 朋友圈单页:垂直箭头指向底栏右侧「前往小程序」大致区域 */ +/* 朋友圈单页:垂直向下箭头,贴近底栏「前往小程序」按钮区域 */ .singlepage-launch-pointer { position: fixed; /* 与单页底栏右侧按钮区对齐(略偏左于右缘,对准按钮中心) */ right: 72rpx; - bottom: calc(168rpx + env(safe-area-inset-bottom)); + /* bottom 越小越靠近底栏,与「前往小程序」间距更短 */ + bottom: calc(68rpx + env(safe-area-inset-bottom)); z-index: 99985; pointer-events: none; display: flex; @@ -1461,7 +1462,7 @@ button.action-share-native { align-items: center; } .singlepage-launch-pointer__arrow { - font-size: 56rpx; + font-size: 82rpx; line-height: 1; color: #00CED1; text-shadow: 0 0 20rpx rgba(0, 206, 209, 0.55); @@ -1475,6 +1476,6 @@ button.action-share-native { } 50% { opacity: 1; - transform: translateY(12rpx) scale(1.06); + transform: translateY(10rpx) scale(1.04); } } diff --git a/new-soul/miniprogram/.gitignore b/new-soul/miniprogram/.gitignore deleted file mode 100644 index 14ea590c..00000000 --- a/new-soul/miniprogram/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Windows -[Dd]esktop.ini -Thumbs.db -$RECYCLE.BIN/ - -# macOS -.DS_Store -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes - -# Node.js -node_modules/ diff --git a/new-soul/miniprogram/README.md b/new-soul/miniprogram/README.md deleted file mode 100644 index 2cc11dbf..00000000 --- a/new-soul/miniprogram/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Soul创业实验 - 微信小程序 - -> 一场SOUL的创业实验场 - 来自Soul派对房的真实商业故事 - -## 📱 项目简介 - -本项目是《一场SOUL的创业实验场》的微信小程序版本,完整还原了Web端的所有UI界面和功能。 - -## 🎨 设计特点 - -- **主题色**: Soul青色 (#00CED1) -- **设计风格**: 深色主题 + 毛玻璃效果 -- **1:1还原**: 完全复刻Web端的UI设计 - -## 📂 项目结构 - -``` -miniprogram/ -├── app.js # 应用入口 -├── app.json # 应用配置 -├── app.wxss # 全局样式 -├── custom-tab-bar/ # 自定义TabBar组件 -│ ├── index.js -│ ├── index.json -│ ├── index.wxml -│ └── index.wxss -├── pages/ -│ ├── index/ # 首页 -│ ├── chapters/ # 目录页 -│ ├── match/ # 找伙伴页 -│ ├── my/ # 我的页面 -│ ├── read/ # 阅读页 -│ ├── about/ # 关于作者 -│ ├── referral/ # 推广中心 -│ ├── purchases/ # 订单页 -│ └── settings/ # 设置页 -├── utils/ -│ ├── util.js # 工具函数 -│ └── payment.js # 支付工具 -├── assets/ -│ └── icons/ # 图标资源 -├── project.config.json # 项目配置 -└── sitemap.json # 站点地图 -``` - -## 🚀 功能列表 - -### 核心功能 -- ✅ 首页 - 书籍展示、推荐章节、阅读进度 -- ✅ 目录 - 完整章节列表、篇章折叠展开 -- ✅ 找伙伴 - 匹配动画、匹配类型选择 -- ✅ 我的 - 个人信息、订单、推广中心 -- ✅ 阅读 - 付费墙、章节导航、分享功能 - -### 特色功能 -- ✅ 自定义TabBar(中间突出的找伙伴按钮) -- ✅ 阅读进度条 -- ✅ 匹配动画效果 -- ✅ 付费墙与购买流程 -- ✅ 分享海报功能 -- ✅ 推广佣金系统 - -## 🛠 开发指南 - -### 环境要求 -- 微信开发者工具 >= 1.06.2308310 -- 基础库版本 >= 3.3.4 - -### 快速开始 - -1. **下载微信开发者工具** - - 前往 [微信开发者工具下载页面](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) - -2. **导入项目** - - 打开微信开发者工具 - - 选择"导入项目" - - 项目目录选择 `miniprogram` 文件夹 - - AppID 使用: `wx432c93e275548671` - -3. **编译运行** - - 点击"编译"按钮 - - 在模拟器中预览效果 - -### 真机调试 - -1. 点击工具栏的"预览"按钮 -2. 使用微信扫描二维码 -3. 在真机上测试所有功能 - -## 📝 配置说明 - -### API配置 -在 `app.js` 中修改 `globalData.baseUrl`: - -```javascript -globalData: { - baseUrl: 'https://soul.ckb.fit', // 你的API地址 - // ... -} -``` - -### AppID配置 -在 `project.config.json` 中修改: - -```json -{ - "appid": "你的小程序AppID" -} -``` - -## 🎯 上线发布 - -1. **准备工作** - - 确保所有功能测试通过 - - 检查API接口是否正常 - - 确认支付功能已配置 - -2. **上传代码** - - 在开发者工具中点击"上传" - - 填写版本号和项目备注 - -3. **提交审核** - - 登录[微信公众平台](https://mp.weixin.qq.com) - - 进入"版本管理" - - 提交审核 - -4. **发布上线** - - 审核通过后点击"发布" - -## 🔗 相关链接 - -- **Web版本**: https://soul.ckb.fit -- **作者微信**: 28533368 -- **技术支持**: 存客宝 - -## 📄 版权信息 - -© 2024 卡若. All rights reserved. diff --git a/new-soul/miniprogram/RESTORE-ANALYSIS.md b/new-soul/miniprogram/RESTORE-ANALYSIS.md deleted file mode 100644 index 558a31ff..00000000 --- a/new-soul/miniprogram/RESTORE-ANALYSIS.md +++ /dev/null @@ -1,67 +0,0 @@ -# miniprogram 功能还原分析报告 - -## 一、对比结论 - -| 项目 | miniprogram(甲方) | miniprogram2(你写的) | -|------|-------------------|------------------------| -| 页面分享 | 仅 read、referral 有 | 几乎所有页面都有 | -| scene 解析 | 无 | 有 utils/scene.js | -| 推荐码获取 | 分散(userInfo?.referralCode 等) | 统一 app.getMyReferralCode() | -| 书籍 API | /api/book/all-chapters | /api/miniprogram/book/all-chapters | -| 特有页面 | vip、member-detail | scan、profile-edit | - -## 二、已完成的还原项 - -### 1. 基础能力(app.js + utils/scene.js) - -- **新增** `utils/scene.js`:扫码 scene 参数编解码,支持 `mid`、`id`、`ref` -- **app.js**: - - 引入 `parseScene`,`handleReferralCode` 支持 `options.scene` 解析 - - 新增 `getMyReferralCode()`:统一获取邀请码 - - 新增 `getSectionMid(sectionId)`:根据 id 查 mid - - `loadBookData` 改为 `/api/miniprogram/book/all-chapters` - -### 2. 页面分享(onShareAppMessage) - -已为以下页面补充分享,路径统一带 `ref` 参数: - -- index、chapters、match、my -- read、referral(原有,已统一用 getMyReferralCode) -- search、settings、purchases、privacy -- withdraw-records、addresses、addresses/edit -- agreement、about、vip、member-detail - -### 3. read.js 分享逻辑与 mid 支持 - -- 使用 `app.getMyReferralCode()` 替代 `userInfo?.referralCode || wx.getStorageSync('referralCode')` -- **mid 支持**:onLoad 支持 `options.scene`、`options.mid`;mid 有值无 id 时从 bookData 或 `/api/miniprogram/book/chapter/by-mid/:mid` 解析 id -- 分享 path 优先用 `mid=`(扫码/海报闭环),无则用 `id=`;API 返回 `res.mid` 时写入 `sectionMid` - -### 4. API 路径修正 - -- `app.loadBookData`:`/api/book/all-chapters` → `/api/miniprogram/book/all-chapters` -- `index.loadBookData`、`loadFeaturedFromServer`、`loadLatestChapters`:同上 -- `chapters.loadDailyChapters`:同上 -- **VIP 接口**:`/api/vip/*` → `/api/miniprogram/vip/*`(vip.js、my.js、index.js、member-detail.js) - -## 三、已处理项 - -- **vip 相关接口**:已改为 `/api/miniprogram/vip/*`(members、status、profile),符合项目边界。**后端需在 soul-api 的 miniprogram 组下挂对应路由**,可复用现有 handler。 -- **页面结构**:保留 vip、member-detail,未引入 scan、profile-edit - -## 四、后端已补全(soul-api) - -已在 miniprogram 组下新增以下路由: - -| 路径 | 方法 | 用途 | -|------|------|------| -| `/api/miniprogram/vip/status` | GET | 查询用户 VIP 状态(按 fullbook/vip 订单判断) | -| `/api/miniprogram/vip/profile` | GET/POST | 获取/更新 VIP 资料(映射 users 表 nickname/phone) | -| `/api/miniprogram/vip/members` | GET | VIP 会员列表(无 id)或单个(?id=) | -| `/api/miniprogram/users` | GET | 用户列表(?limit=)或单个(?id=),首页超级个体、会员详情回退 | - -## 五、后续建议 - -1. **soul-api 路由**:确认 `/api/miniprogram/book/all-chapters` 已注册;VIP 接口见「四、后端待办」。 -2. **referral.js**:已统一使用 `app.getMyReferralCode()` 作为回退。 -3. **read.js mid 支持**:已完成。若 soul-api 未提供 `/api/miniprogram/book/chapter/by-mid/:mid`,扫码带 mid 时需依赖 bookData 已加载完成;建议后端补充 by-mid 接口。 diff --git a/new-soul/miniprogram/app.js b/new-soul/miniprogram/app.js deleted file mode 100644 index e6522742..00000000 --- a/new-soul/miniprogram/app.js +++ /dev/null @@ -1,750 +0,0 @@ -/** - * Soul创业派对 - 小程序入口 - * 开发: 卡若 - */ - -const { parseScene } = require('./utils/scene.js') - -const DEFAULT_BASE_URL = 'https://soulapi.quwanzhi.com' -const DEFAULT_APP_ID = 'wxb8bbb2b10dec74aa' -const DEFAULT_MCH_ID = '1318592501' -const DEFAULT_WITHDRAW_TMPL_ID = 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE' - -function getRuntimeBootstrapConfig() { - try { - const extCfg = wx.getExtConfigSync ? (wx.getExtConfigSync() || {}) : {} - return { - baseUrl: extCfg.apiBaseUrl || wx.getStorageSync('apiBaseUrl') || DEFAULT_BASE_URL, - appId: extCfg.appId || DEFAULT_APP_ID, - mchId: extCfg.mchId || DEFAULT_MCH_ID, - withdrawSubscribeTmplId: extCfg.withdrawSubscribeTmplId || DEFAULT_WITHDRAW_TMPL_ID - } - } catch (_) { - return { - baseUrl: DEFAULT_BASE_URL, - appId: DEFAULT_APP_ID, - mchId: DEFAULT_MCH_ID, - withdrawSubscribeTmplId: DEFAULT_WITHDRAW_TMPL_ID - } - } -} - -const bootstrapConfig = getRuntimeBootstrapConfig() - -App({ - globalData: { - // 运行配置:优先外部配置/缓存,其次默认值 - baseUrl: bootstrapConfig.baseUrl, - appId: bootstrapConfig.appId, - - // 订阅消息:用户点击「申请提现」→「立即提现」时会先弹出订阅授权窗 - withdrawSubscribeTmplId: bootstrapConfig.withdrawSubscribeTmplId, - - // 微信支付配置 - mchId: bootstrapConfig.mchId, - - // 用户信息 - userInfo: null, - openId: null, // 微信openId,支付必需 - isLoggedIn: false, - - // 书籍数据 - bookData: null, - totalSections: 0, - supportWechat: '', - - // 购买记录 - purchasedSections: [], - hasFullBook: false, - // VIP 会员(365天,包含增值版免费;与 hasFullBook=9.9 买断不同) - isVip: false, - vipExpireDate: '', - - // 已读章节(仅统计有权限打开过的章节,用于首页「已读/待读」) - readSectionIds: [], - - // 推荐绑定 - pendingReferralCode: null, // 待绑定的推荐码 - - // 主题配置 - theme: { - brandColor: '#00CED1', - brandSecondary: '#20B2AA', - goldColor: '#FFD700', - bgColor: '#000000', - cardBg: '#1c1c1e' - }, - - // 系统信息 - systemInfo: null, - statusBarHeight: 44, - navBarHeight: 88, - - // TabBar相关 - currentTab: 0, - - // 是否处于「单页模式」(如朋友圈文章里的单页预览) - // 用于在受限环境下给出引导文案,提示用户点击底部「前往小程序」进入完整体验 - isSinglePageMode: false, - - // 更新检测:上次检测时间戳,避免频繁请求 - lastUpdateCheck: 0 - }, - - onLaunch(options) { - this.globalData.readSectionIds = wx.getStorageSync('readSectionIds') || [] - // 获取系统信息 - this.getSystemInfo() - - // 场景值兜底:1154 为「朋友圈单页模式」进入 - try { - const launchOpts = wx.getLaunchOptionsSync ? wx.getLaunchOptionsSync() : null - if (launchOpts && launchOpts.scene === 1154) { - this.globalData.isSinglePageMode = true - } - } catch (e) { - console.warn('[App] 读取 LaunchOptions 失败:', e) - } - - // 检查登录状态 - this.checkLoginStatus() - this.loadRuntimeConfig() - - // 加载书籍数据 - this.loadBookData() - - // 检查更新 - this.checkUpdate() - - // 处理分享参数(推荐码绑定) - this.handleReferralCode(options) - }, - - // 小程序显示时:处理分享参数、检测更新(从后台切回时) - onShow(options) { - this.handleReferralCode(options) - this.checkUpdate() - }, - - // 处理推荐码绑定:官方以 options.scene 接收扫码参数(可同时带 mid/id + ref),与 utils/scene 解析闭环 - handleReferralCode(options) { - const query = options?.query || {} - let refCode = query.ref || query.referralCode - const sceneStr = (options && (typeof options.scene === 'string' ? options.scene : '')) || '' - if (sceneStr) { - const parsed = parseScene(sceneStr) - if (parsed.mid) this.globalData.initialSectionMid = parsed.mid - if (parsed.id) this.globalData.initialSectionId = parsed.id - if (parsed.ref) refCode = parsed.ref - } - if (refCode) { - console.log('[App] 检测到推荐码:', refCode) - - // 立即记录访问(不需要登录,用于统计"通过链接进的人数") - this.recordReferralVisit(refCode) - - // 保存待绑定的推荐码(不再在前端做"只能绑定一次"的限制,让后端根据30天规则判断续期/抢夺) - this.globalData.pendingReferralCode = refCode - wx.setStorageSync('pendingReferralCode', refCode) - // 同步写入 referral_code,供章节/找伙伴支付时传给后端,订单会记录 referrer_id 与 referral_code - wx.setStorageSync('referral_code', refCode) - - // 如果已登录,立即尝试绑定,由 /api/miniprogram/referral/bind 按 30 天规则决定 new / renew / takeover - if (this.globalData.isLoggedIn && this.globalData.userInfo) { - this.bindReferralCode(refCode) - } - } - }, - - // 记录推荐访问(不需要登录,用于统计) - async recordReferralVisit(refCode) { - try { - // 获取openId(如果有) - const openId = this.globalData.openId || wx.getStorageSync('openId') || '' - const userId = this.globalData.userInfo?.id || '' - - await this.request('/api/miniprogram/referral/visit', { - method: 'POST', - data: { - referralCode: refCode, - visitorOpenId: openId, - visitorId: userId, - source: 'miniprogram', - page: getCurrentPages()[getCurrentPages().length - 1]?.route || '' - }, - silent: true - }) - console.log('[App] 记录推荐访问成功') - } catch (e) { - console.log('[App] 记录推荐访问失败:', e.message) - // 忽略错误,不影响用户体验 - } - }, - - // 绑定推荐码到用户(自己的推荐码不请求接口,避免 400 与控制台报错) - async bindReferralCode(refCode) { - try { - const userId = this.globalData.userInfo?.id - if (!userId || !refCode) return - - const myCode = this.getMyReferralCode() - if (myCode && this._normalizeReferralCode(refCode) === this._normalizeReferralCode(myCode)) { - console.log('[App] 跳过绑定:不能使用自己的推荐码') - this.globalData.pendingReferralCode = null - wx.removeStorageSync('pendingReferralCode') - return - } - - console.log('[App] 绑定推荐码:', refCode, '到用户:', userId) - - const res = await this.request('/api/miniprogram/referral/bind', { - method: 'POST', - data: { - userId, - referralCode: refCode - }, - silent: true - }) - - if (res.success) { - console.log('[App] 推荐码绑定成功') - wx.setStorageSync('boundReferralCode', refCode) - this.globalData.pendingReferralCode = null - wx.removeStorageSync('pendingReferralCode') - } - } catch (e) { - const msg = (e && e.message) ? String(e.message) : '' - if (msg.indexOf('不能使用自己的推荐码') !== -1) { - console.log('[App] 跳过绑定:不能使用自己的推荐码') - this.globalData.pendingReferralCode = null - wx.removeStorageSync('pendingReferralCode') - } else { - console.error('[App] 绑定推荐码失败:', e) - } - } - }, - - // 推荐码归一化后比较(忽略大小写、短横线等) - _normalizeReferralCode(code) { - if (!code || typeof code !== 'string') return '' - return code.replace(/[\s\-_]/g, '').toUpperCase().trim() - }, - - // 判断用户资料是否完善(昵称 + 头像) - _isProfileIncomplete(user) { - if (!user) return true - const nickname = (user.nickname || '').trim() - const avatar = (user.avatar || '').trim() - const isDefaultNickname = !nickname || nickname === '微信用户' - const noAvatar = !avatar - return isDefaultNickname || noAvatar - }, - - // 登录后若资料未完善,引导跳转到资料编辑页 - _ensureProfileCompletedAfterLogin(user) { - try { - if (!user || !this._isProfileIncomplete(user)) return - const pages = getCurrentPages() - const current = pages[pages.length - 1] - // 避免在资料页内重复跳转 - if (current && current.route === 'pages/profile-edit/profile-edit') return - wx.showToast({ title: '请先完善头像和昵称', icon: 'none', duration: 2000 }) - wx.navigateTo({ url: '/pages/profile-edit/profile-edit' }) - } catch (e) { - console.warn('[App] 跳转资料编辑页失败:', e) - } - }, - - // 根据业务 id 从 bookData 查 mid(用于跳转) - getSectionMid(sectionId) { - const list = this.globalData.bookData || [] - const ch = list.find(c => c.id === sectionId) - return ch?.mid || 0 - }, - - // 获取当前用户的邀请码(用于分享带 ref,未登录返回空字符串) - getMyReferralCode() { - const user = this.globalData.userInfo - if (!user) return '' - if (user.referralCode) return user.referralCode - if (user.id) return 'SOUL' + String(user.id).toUpperCase().slice(-6) - return '' - }, - - /** - * 自定义导航栏「返回」:有上一页则返回,否则跳转首页(解决从分享进入时点返回无效的问题) - */ - goBackOrToHome() { - const pages = getCurrentPages() - if (pages.length <= 1) { - wx.switchTab({ url: '/pages/index/index' }) - } else { - wx.navigateBack() - } - }, - - // 获取系统信息 - getSystemInfo() { - try { - const systemInfo = wx.getSystemInfoSync() - this.globalData.systemInfo = systemInfo - this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44 - - // 微信在单页模式下会在 systemInfo.mode 标记 singlePage - if (systemInfo.mode === 'singlePage') { - this.globalData.isSinglePageMode = true - } - - // 计算导航栏高度 - const menuButton = wx.getMenuButtonBoundingClientRect() - if (menuButton) { - this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight - } - } catch (e) { - console.error('获取系统信息失败:', e) - } - }, - - /** - * 若当前处于朋友圈等「单页模式」,在尝试登录/购买前给用户友好提示, - * 引导用户点击底部「前往小程序」进入完整小程序再操作。 - * 返回 false 表示应中断当前操作。 - */ - ensureFullAppForAuth() { - // 每次调用时再做一次兜底检测,避免全局标记遗漏 - try { - const sys = wx.getSystemInfoSync() - if (sys && sys.mode === 'singlePage') { - this.globalData.isSinglePageMode = true - } - } catch (e) { - console.warn('[App] ensureFullAppForAuth getSystemInfoSync error:', e) - } - - if (!this.globalData.isSinglePageMode) return true - - wx.showModal({ - title: '请前往完整小程序', - content: '当前为朋友圈单页,仅支持部分浏览。如需登录和解锁内容,请点击底部「前往小程序」后再操作。', - showCancel: false, - confirmText: '我知道了', - }) - return false - }, - - // 检查登录状态 - checkLoginStatus() { - try { - const userInfo = wx.getStorageSync('userInfo') - const token = wx.getStorageSync('token') - - if (userInfo && token) { - this.globalData.userInfo = userInfo - this.globalData.isLoggedIn = true - this.globalData.purchasedSections = userInfo.purchasedSections || [] - this.globalData.hasFullBook = userInfo.hasFullBook || false - this.globalData.isVip = userInfo.isVip || false - this.globalData.vipExpireDate = userInfo.vipExpireDate || '' - } - } catch (e) { - console.error('检查登录状态失败:', e) - } - }, - - async loadRuntimeConfig() { - try { - const res = await this.request({ url: '/api/miniprogram/config', silent: true, timeout: 5000 }) - const mpConfig = res?.mpConfig || {} - this.globalData.baseUrl = mpConfig.apiDomain || this.globalData.baseUrl - this.globalData.appId = mpConfig.appId || this.globalData.appId - this.globalData.mchId = mpConfig.mchId || this.globalData.mchId - this.globalData.withdrawSubscribeTmplId = mpConfig.withdrawSubscribeTmplId || this.globalData.withdrawSubscribeTmplId - this.globalData.supportWechat = mpConfig.supportWechat || mpConfig.customerWechat || mpConfig.serviceWechat || '' - try { - wx.setStorageSync('apiBaseUrl', this.globalData.baseUrl) - } catch (_) {} - } catch (e) { - console.warn('[App] 加载运行配置失败,继续使用默认配置:', e) - } - }, - - // 加载书籍数据 - async loadBookData() { - try { - // 先从缓存加载 - const cachedData = wx.getStorageSync('bookData') - if (cachedData) { - this.globalData.bookData = cachedData - } - - // 从服务器获取最新数据 - const res = await this.request('/api/miniprogram/book/all-chapters') - if (res && (res.data || res.chapters)) { - const chapters = res.data || res.chapters || [] - this.globalData.bookData = chapters - this.globalData.totalSections = res.total || chapters.length || 0 - wx.setStorageSync('bookData', chapters) - } - } catch (e) { - console.error('加载书籍数据失败:', e) - } - }, - - /** - * 小程序更新检测(基于 wx.getUpdateManager) - * - 启动时检测;从后台切回前台时也检测(间隔至少 5 分钟,避免频繁请求) - */ - checkUpdate() { - try { - if (!wx.canIUse('getUpdateManager')) return - const now = Date.now() - const lastCheck = this.globalData.lastUpdateCheck || 0 - if (lastCheck && now - lastCheck < 5 * 60 * 1000) return // 5 分钟内不重复检测 - this.globalData.lastUpdateCheck = now - - const updateManager = wx.getUpdateManager() - updateManager.onCheckForUpdate((res) => { - if (res.hasUpdate) { - console.log('[App] 发现新版本,正在下载...') - } - }) - updateManager.onUpdateReady(() => { - wx.showModal({ - title: '更新提示', - content: '新版本已准备好,重启后即可使用', - confirmText: '立即重启', - cancelText: '稍后', - success: (res) => { - if (res.confirm) { - updateManager.applyUpdate() - } - } - }) - }) - updateManager.onUpdateFailed(() => { - wx.showToast({ - title: '更新失败,请稍后重试', - icon: 'none', - duration: 2500 - }) - }) - } catch (e) { - console.warn('[App] checkUpdate failed:', e) - } - }, - - /** - * 从 soul-api 返回体中取错误提示文案(兼容 message / error 字段) - */ - _getApiErrorMsg(data, defaultMsg = '请求失败') { - if (!data || typeof data !== 'object') return defaultMsg - const msg = data.message || data.error - return (msg && String(msg).trim()) ? String(msg).trim() : defaultMsg - }, - - /** - * 统一请求方法。接口失败时会弹窗提示(与 soul-api 返回的 message/error 一致)。 - * @param {string|object} urlOrOptions - 接口路径,或 { url, method, data, header, silent } - * @param {object} options - { method, data, header, silent } - * @param {boolean} options.silent - 为 true 时不弹窗,仅 reject(用于静默请求如访问统计) - */ - request(urlOrOptions, options = {}) { - let url - if (typeof urlOrOptions === 'string') { - url = urlOrOptions - } else if (urlOrOptions && typeof urlOrOptions === 'object' && urlOrOptions.url) { - url = urlOrOptions.url - options = { ...urlOrOptions, url: undefined } - } else { - url = '' - } - const silent = !!options.silent - const showError = (msg) => { - if (!silent && msg) { - wx.showToast({ title: msg, icon: 'none', duration: 2500 }) - } - } - - return new Promise((resolve, reject) => { - const token = wx.getStorageSync('token') - - wx.request({ - url: this.globalData.baseUrl + url, - method: options.method || 'GET', - data: options.data || {}, - timeout: options.timeout || 15000, - header: { - 'Content-Type': 'application/json', - 'Authorization': token ? `Bearer ${token}` : '', - ...options.header - }, - success: (res) => { - const data = res.data - if (res.statusCode === 200) { - // 业务失败:success === false,soul-api 用 message 或 error 返回原因 - if (data && data.success === false) { - const msg = this._getApiErrorMsg(data, '操作失败') - // 登录态不一致:本地有 token/userInfo,但后端查不到该用户 - // 典型原因:切换环境(baseUrl)、换库/清库、用户被删除、token 与用户不匹配 - if (msg && (msg.includes('用户不存在') || msg.toLowerCase().includes('user not found'))) { - this.logout() - } - showError(msg) - reject(new Error(msg)) - return - } - resolve(data) - return - } - if (res.statusCode === 401) { - this.logout() - showError('未授权,请重新登录') - reject(new Error('未授权')) - return - } - // 4xx/5xx:优先用返回体的 message/error - const msg = this._getApiErrorMsg(data, res.statusCode >= 500 ? '服务器异常,请稍后重试' : '请求失败') - showError(msg) - reject(new Error(msg)) - }, - fail: (err) => { - const msg = (err && err.errMsg) ? (err.errMsg.indexOf('timeout') !== -1 ? '请求超时,请重试' : '网络异常,请重试') : '网络异常,请重试' - showError(msg) - reject(new Error(msg)) - } - }) - }) - }, - - // 登录方法 - 获取openId用于支付(加固错误处理,避免审核报“登录报错”) - async login() { - if (!this.ensureFullAppForAuth()) { - return null - } - try { - const loginRes = await new Promise((resolve, reject) => { - wx.login({ success: resolve, fail: reject }) - }) - if (!loginRes || !loginRes.code) { - console.warn('[App] wx.login 未返回 code') - wx.showToast({ title: '获取登录态失败,请重试', icon: 'none' }) - return null - } - try { - const res = await this.request('/api/miniprogram/login', { - method: 'POST', - data: { code: loginRes.code } - }) - - if (res.success && res.data) { - // 保存openId - if (res.data.openId) { - this.globalData.openId = res.data.openId - wx.setStorageSync('openId', res.data.openId) - console.log('[App] 获取openId成功') - } - - // 保存用户信息 - if (res.data.user) { - const user = res.data.user - this.globalData.userInfo = user - this.globalData.isLoggedIn = true - this.globalData.purchasedSections = user.purchasedSections || [] - this.globalData.hasFullBook = user.hasFullBook || false - - wx.setStorageSync('userInfo', user) - wx.setStorageSync('token', res.data.token || '') - - // 登录成功后,检查待绑定的推荐码并执行绑定 - const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode - if (pendingRef) { - console.log('[App] 登录后自动绑定推荐码:', pendingRef) - this.bindReferralCode(pendingRef) - } - - // 登录后引导完善资料 - this._ensureProfileCompletedAfterLogin(user) - } - - return res.data - } - } catch (apiError) { - console.log('[App] API登录失败:', apiError.message) - // 不使用模拟登录,提示用户网络问题 - wx.showToast({ title: '网络异常,请重试', icon: 'none' }) - return null - } - - return null - } catch (e) { - console.error('[App] 登录失败:', e) - wx.showToast({ title: '登录失败,请重试', icon: 'none' }) - return null - } - }, - - // 获取openId (支付必需) - async getOpenId() { - if (!this.ensureFullAppForAuth()) { - return null - } - // 先检查缓存 - const cachedOpenId = wx.getStorageSync('openId') - if (cachedOpenId) { - this.globalData.openId = cachedOpenId - return cachedOpenId - } - - // 没有缓存则登录获取 - try { - const loginRes = await new Promise((resolve, reject) => { - wx.login({ success: resolve, fail: reject }) - }) - - const res = await this.request('/api/miniprogram/login', { - method: 'POST', - data: { code: loginRes.code } - }) - - if (res.success && res.data?.openId) { - this.globalData.openId = res.data.openId - wx.setStorageSync('openId', res.data.openId) - // 接口同时返回 user 时视为登录,补全登录态并从登录开始绑定推荐码 - if (res.data.user) { - const user = res.data.user - this.globalData.userInfo = user - this.globalData.isLoggedIn = true - this.globalData.purchasedSections = user.purchasedSections || [] - this.globalData.hasFullBook = user.hasFullBook || false - wx.setStorageSync('userInfo', user) - wx.setStorageSync('token', res.data.token || '') - const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode - if (pendingRef) { - console.log('[App] getOpenId 登录后自动绑定推荐码:', pendingRef) - this.bindReferralCode(pendingRef) - } - - // 登录后引导完善资料 - this._ensureProfileCompletedAfterLogin(user) - } - return res.data.openId - } - } catch (e) { - console.error('[App] 获取openId失败:', e) - } - - return null - }, - - // 手机号登录:需同时传 wx.login 的 code 与 getPhoneNumber 的 phoneCode - async loginWithPhone(phoneCode) { - if (!this.ensureFullAppForAuth()) { - return null - } - try { - const loginRes = await new Promise((resolve, reject) => { - wx.login({ success: resolve, fail: reject }) - }) - if (!loginRes.code) { - wx.showToast({ title: '获取登录态失败', icon: 'none' }) - return null - } - const res = await this.request('/api/miniprogram/phone-login', { - method: 'POST', - data: { code: loginRes.code, phoneCode } - }) - - if (res.success && res.data) { - const user = res.data.user - this.globalData.userInfo = user - this.globalData.isLoggedIn = true - this.globalData.purchasedSections = user.purchasedSections || [] - this.globalData.hasFullBook = user.hasFullBook || false - - wx.setStorageSync('userInfo', user) - wx.setStorageSync('token', res.data.token) - - // 登录成功后绑定推荐码 - const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode - if (pendingRef) { - console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef) - this.bindReferralCode(pendingRef) - } - - // 登录后引导完善资料 - this._ensureProfileCompletedAfterLogin(user) - - return res.data - } - } catch (e) { - console.log('[App] 手机号登录失败:', e) - wx.showToast({ title: '登录失败,请重试', icon: 'none' }) - } - - return null - }, - - // 退出登录 - logout() { - this.globalData.userInfo = null - this.globalData.isLoggedIn = false - this.globalData.purchasedSections = [] - this.globalData.hasFullBook = false - - wx.removeStorageSync('userInfo') - wx.removeStorageSync('token') - }, - - // 检查是否已购买章节 - hasPurchased(sectionId) { - if (this.globalData.hasFullBook) return true - return this.globalData.purchasedSections.includes(sectionId) - }, - - // 标记章节为已读(仅在有权限打开时由阅读页调用,用于首页已读/待读统计) - markSectionAsRead(sectionId) { - if (!sectionId) return - const list = this.globalData.readSectionIds || [] - if (list.includes(sectionId)) return - list.push(sectionId) - this.globalData.readSectionIds = list - wx.setStorageSync('readSectionIds', list) - }, - - // 已读章节数(用于首页展示) - getReadCount() { - return (this.globalData.readSectionIds || []).length - }, - - // 获取章节总数 - getTotalSections() { - return this.globalData.totalSections - }, - - // 切换TabBar - switchTab(index) { - this.globalData.currentTab = index - }, - - // 显示Toast - showToast(title, icon = 'none') { - wx.showToast({ - title, - icon, - duration: 2000 - }) - }, - - // 显示Loading - showLoading(title = '加载中...') { - wx.showLoading({ - title, - mask: true - }) - }, - - // 隐藏Loading - hideLoading() { - wx.hideLoading() - } -}) diff --git a/new-soul/miniprogram/app.json b/new-soul/miniprogram/app.json deleted file mode 100644 index b3746d6f..00000000 --- a/new-soul/miniprogram/app.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "pages": [ - "pages/chapters/chapters", - "pages/index/index", - "pages/match/match", - "pages/my/my", - "pages/read/read", - "pages/link-preview/link-preview", - "pages/about/about", - "pages/agreement/agreement", - "pages/privacy/privacy", - "pages/referral/referral", - "pages/purchases/purchases", - "pages/settings/settings", - "pages/search/search", - "pages/addresses/addresses", - "pages/addresses/edit", - "pages/withdraw-records/withdraw-records", - "pages/vip/vip", - "pages/member-detail/member-detail", - "pages/mentors/mentors", - "pages/mentor-detail/mentor-detail", - "pages/profile-show/profile-show", - "pages/profile-edit/profile-edit", - "pages/wallet/wallet" - ], - "window": { - "backgroundTextStyle": "light", - "navigationBarBackgroundColor": "#000000", - "navigationBarTitleText": "Soul创业派对", - "navigationBarTextStyle": "white", - "backgroundColor": "#000000", - "navigationStyle": "custom" - }, - "tabBar": { - "custom": true, - "color": "#8e8e93", - "selectedColor": "#00CED1", - "backgroundColor": "#1c1c1e", - "borderStyle": "black", - "list": [ - { - "pagePath": "pages/index/index", - "text": "首页" - }, - { - "pagePath": "pages/chapters/chapters", - "text": "目录" - }, - { - "pagePath": "pages/match/match", - "text": "找伙伴" - }, - { - "pagePath": "pages/my/my", - "text": "我的" - } - ] - }, - "usingComponents": { - "icon": "/components/icon/icon" - }, - "navigateToMiniProgramAppIdList": [ - "wx6489c26045912fe1", - "wx3d15ed02e98b04e3" - ], - "__usePrivacyCheck__": true, - "lazyCodeLoading": "requiredComponents", - "style": "v2", - "sitemapLocation": "sitemap.json" -} \ No newline at end of file diff --git a/new-soul/miniprogram/app.wxss b/new-soul/miniprogram/app.wxss deleted file mode 100644 index 9ce22a06..00000000 --- a/new-soul/miniprogram/app.wxss +++ /dev/null @@ -1,606 +0,0 @@ -/** - * Soul创业实验 - 全局样式 - * 主题色: #00CED1 (Soul青色) - * 开发: 卡若 - */ - -/* ===== CSS 变量系统 ===== */ -page { - /* 品牌色 */ - --app-brand: #00CED1; - --app-brand-light: rgba(0, 206, 209, 0.1); - --app-brand-dark: #20B2AA; - - /* 背景色 */ - --app-bg-primary: #000000; - --app-bg-secondary: #1c1c1e; - --app-bg-tertiary: #2c2c2e; - - /* 文字色 */ - --app-text-primary: #ffffff; - --app-text-secondary: rgba(255, 255, 255, 0.7); - --app-text-tertiary: rgba(255, 255, 255, 0.4); - - /* 分隔线 */ - --app-separator: rgba(255, 255, 255, 0.05); - - /* iOS 系统色 */ - --ios-indigo: #5856D6; - --ios-green: #30d158; - --ios-red: #FF3B30; - --ios-orange: #FF9500; - --ios-yellow: #FFD700; - - /* 金色 */ - --gold: #FFD700; - --gold-light: #FFA500; - - /* 粉色 */ - --pink: #E91E63; - - /* 紫色 */ - --purple: #7B61FF; -} - -/* ===== 页面基础样式 ===== */ -page { - background-color: var(--app-bg-primary); - color: var(--app-text-primary); - font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif; - font-size: 28rpx; - line-height: 1.5; - -webkit-font-smoothing: antialiased; -} - -/* ===== 全局容器 ===== */ -.container { - min-height: 100vh; - padding: 0; - background: #000000; - padding-bottom: env(safe-area-inset-bottom); -} - -/* ===== 品牌色系 ===== */ -.brand-color { - color: #00CED1; -} - -.brand-bg { - background-color: #00CED1; -} - -.brand-gradient { - background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); -} - -.gold-color { - color: #FFD700; -} - -.gold-bg { - background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); -} - -/* ===== 文字渐变 ===== */ -.gradient-text { - background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.gold-gradient-text { - background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* ===== 按钮样式 ===== */ -.btn-primary { - background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); - color: #ffffff; - border: none; - border-radius: 48rpx; - padding: 28rpx 48rpx; - font-size: 32rpx; - font-weight: 600; - box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3); - display: flex; - align-items: center; - justify-content: center; -} - -.btn-primary::after { - border: none; -} - -.btn-primary:active { - opacity: 0.85; - transform: scale(0.98); -} - -.btn-secondary { - background: rgba(0, 206, 209, 0.1); - color: #00CED1; - border: 2rpx solid rgba(0, 206, 209, 0.3); - border-radius: 48rpx; - padding: 28rpx 48rpx; - font-size: 32rpx; - font-weight: 500; -} - -.btn-secondary::after { - border: none; -} - -.btn-secondary:active { - background: rgba(0, 206, 209, 0.2); -} - -.btn-ghost { - background: rgba(255, 255, 255, 0.05); - color: #ffffff; - border: 2rpx solid rgba(255, 255, 255, 0.1); - border-radius: 48rpx; - padding: 28rpx 48rpx; - font-size: 32rpx; -} - -.btn-ghost::after { - border: none; -} - -.btn-ghost:active { - background: rgba(255, 255, 255, 0.1); -} - -.btn-gold { - background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); - color: #000000; - border: none; - border-radius: 48rpx; - padding: 28rpx 48rpx; - font-size: 32rpx; - font-weight: 600; - box-shadow: 0 8rpx 24rpx rgba(255, 215, 0, 0.3); -} - -.btn-gold::after { - border: none; -} - -/* ===== 卡片样式 ===== */ -.card { - background: rgba(28, 28, 30, 0.9); - border-radius: 32rpx; - padding: 32rpx; - margin: 24rpx 32rpx; - border: 2rpx solid rgba(255, 255, 255, 0.05); -} - -.card-light { - background: rgba(44, 44, 46, 0.8); - border-radius: 24rpx; - padding: 24rpx; - border: 2rpx solid rgba(255, 255, 255, 0.08); -} - -.card-gradient { - background: linear-gradient(135deg, rgba(28, 28, 30, 1) 0%, rgba(44, 44, 46, 1) 100%); - border-radius: 32rpx; - padding: 32rpx; - border: 2rpx solid rgba(0, 206, 209, 0.2); -} - -.card-brand { - background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.05) 100%); - border-radius: 32rpx; - padding: 32rpx; - border: 2rpx solid rgba(0, 206, 209, 0.2); -} - -/* ===== 输入框样式 ===== */ -.input-ios { - background: rgba(0, 0, 0, 0.3); - border: 2rpx solid rgba(255, 255, 255, 0.1); - border-radius: 24rpx; - padding: 28rpx 32rpx; - font-size: 32rpx; - color: #ffffff; -} - -.input-ios:focus { - border-color: rgba(0, 206, 209, 0.5); -} - -.input-ios-placeholder { - color: rgba(255, 255, 255, 0.3); -} - -/* ===== 列表项样式 ===== */ -.list-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 28rpx 32rpx; - background: rgba(28, 28, 30, 0.9); - border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); -} - -.list-item:first-child { - border-radius: 24rpx 24rpx 0 0; -} - -.list-item:last-child { - border-radius: 0 0 24rpx 24rpx; - border-bottom: none; -} - -.list-item:only-child { - border-radius: 24rpx; -} - -.list-item:active { - background: rgba(44, 44, 46, 1); -} - -/* ===== 标签样式 ===== */ -.tag { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 8rpx 20rpx; - min-width: 80rpx; - border-radius: 8rpx; - font-size: 22rpx; - font-weight: 500; - box-sizing: border-box; - text-align: center; -} - -.tag-brand { - background: rgba(0, 206, 209, 0.1); - color: #00CED1; -} - -.tag-gold { - background: rgba(255, 215, 0, 0.1); - color: #FFD700; -} - -.tag-pink { - background: rgba(233, 30, 99, 0.1); - color: #E91E63; -} - -.tag-purple { - background: rgba(123, 97, 255, 0.1); - color: #7B61FF; -} - -.tag-free { - background: rgba(0, 206, 209, 0.1); - color: #00CED1; -} - -/* ===== 分隔线 ===== */ -.divider { - height: 1rpx; - background: rgba(255, 255, 255, 0.05); - margin: 24rpx 0; -} - -.divider-vertical { - width: 2rpx; - height: 48rpx; - background: rgba(255, 255, 255, 0.1); -} - -/* ===== 骨架屏动画 ===== */ -.skeleton { - background: linear-gradient(90deg, - rgba(28, 28, 30, 1) 25%, - rgba(44, 44, 46, 1) 50%, - rgba(28, 28, 30, 1) 75% - ); - background-size: 200% 100%; - animation: skeleton-loading 1.5s ease-in-out infinite; - border-radius: 8rpx; -} - -@keyframes skeleton-loading { - 0% { - background-position: 200% 0; - } - 100% { - background-position: -200% 0; - } -} - -/* ===== 页面过渡动画 ===== */ -.page-transition { - animation: fadeIn 0.3s ease-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(20rpx); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* ===== 弹窗动画 ===== */ -.modal-overlay { - animation: modalOverlayIn 0.25s ease-out; -} - -.modal-content { - animation: modalContentIn 0.3s cubic-bezier(0.32, 0.72, 0, 1); -} - -@keyframes modalOverlayIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes modalContentIn { - from { - opacity: 0; - transform: scale(0.95) translateY(20rpx); - } - to { - opacity: 1; - transform: scale(1) translateY(0); - } -} - -/* ===== 脉动动画 ===== */ -.pulse { - animation: pulse 2s ease-in-out infinite; -} - -@keyframes pulse { - 0%, 100% { - transform: scale(1); - opacity: 1; - } - 50% { - transform: scale(1.05); - opacity: 0.8; - } -} - -/* ===== 发光效果 ===== */ -.glow { - box-shadow: 0 0 40rpx rgba(0, 206, 209, 0.3); -} - -.glow-gold { - box-shadow: 0 0 40rpx rgba(255, 215, 0, 0.3); -} - -/* ===== 文字样式 ===== */ -.text-xs { - font-size: 22rpx; -} - -.text-sm { - font-size: 26rpx; -} - -.text-base { - font-size: 28rpx; -} - -.text-lg { - font-size: 32rpx; -} - -.text-xl { - font-size: 36rpx; -} - -.text-2xl { - font-size: 44rpx; -} - -.text-3xl { - font-size: 56rpx; -} - -.text-white { - color: #ffffff; -} - -.text-gray { - color: rgba(255, 255, 255, 0.6); -} - -.text-muted { - color: rgba(255, 255, 255, 0.4); -} - -.text-center { - text-align: center; -} - -.font-medium { - font-weight: 500; -} - -.font-semibold { - font-weight: 600; -} - -.font-bold { - font-weight: 700; -} - -/* ===== Flex布局 ===== */ -.flex { - display: flex; -} - -.flex-col { - flex-direction: column; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-around { - justify-content: space-around; -} - -.flex-1 { - flex: 1; -} - -.gap-1 { - gap: 8rpx; -} - -.gap-2 { - gap: 16rpx; -} - -.gap-3 { - gap: 24rpx; -} - -.gap-4 { - gap: 32rpx; -} - -/* ===== 间距 ===== */ -.p-2 { padding: 16rpx; } -.p-3 { padding: 24rpx; } -.p-4 { padding: 32rpx; } -.p-5 { padding: 40rpx; } - -.px-4 { padding-left: 32rpx; padding-right: 32rpx; } -.py-2 { padding-top: 16rpx; padding-bottom: 16rpx; } -.py-3 { padding-top: 24rpx; padding-bottom: 24rpx; } - -.m-4 { margin: 32rpx; } -.mx-4 { margin-left: 32rpx; margin-right: 32rpx; } -.my-3 { margin-top: 24rpx; margin-bottom: 24rpx; } -.mb-2 { margin-bottom: 16rpx; } -.mb-3 { margin-bottom: 24rpx; } -.mb-4 { margin-bottom: 32rpx; } -.mt-4 { margin-top: 32rpx; } - -/* ===== 圆角 ===== */ -.rounded { border-radius: 8rpx; } -.rounded-lg { border-radius: 16rpx; } -.rounded-xl { border-radius: 24rpx; } -.rounded-2xl { border-radius: 32rpx; } -.rounded-full { border-radius: 50%; } - -/* ===== 安全区域 ===== */ -.safe-bottom { - padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); -} - -.pb-tabbar { - padding-bottom: 200rpx; -} - -/* ===== 头部导航占位 ===== */ -.nav-placeholder { - height: calc(88rpx + env(safe-area-inset-top, 44rpx)); -} - -/* ===== 隐藏滚动条 ===== */ -::-webkit-scrollbar { - display: none; - width: 0; - height: 0; -} - -/* ===== 触摸反馈 ===== */ -.touch-feedback { - transition: all 0.15s ease; -} - -.touch-feedback:active { - opacity: 0.7; - transform: scale(0.98); -} - -/* ===== 进度条 ===== */ -.progress-bar { - height: 8rpx; - background: rgba(44, 44, 46, 1); - border-radius: 4rpx; - overflow: hidden; -} - -.progress-fill { - height: 100%; - background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); - border-radius: 4rpx; - transition: width 0.3s ease; -} - -/* ===== 头像样式 ===== */ -.avatar { - width: 80rpx; - height: 80rpx; - border-radius: 50%; - background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%); - display: flex; - align-items: center; - justify-content: center; - color: #00CED1; - font-weight: 700; - font-size: 32rpx; - border: 4rpx solid rgba(0, 206, 209, 0.3); -} - -.avatar-lg { - width: 120rpx; - height: 120rpx; - font-size: 48rpx; -} - -/* ===== 图标容器 ===== */ -.icon-box { - width: 64rpx; - height: 64rpx; - border-radius: 16rpx; - display: flex; - align-items: center; - justify-content: center; -} - -.icon-box-brand { - background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%); -} - -.icon-box-gold { - background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(255, 165, 0, 0.1) 100%); -} - -/* ===== 渐变背景 ===== */ -.bg-gradient-dark { - background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%); -} - -.bg-gradient-brand { - background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%); -} diff --git a/new-soul/miniprogram/assets/icons/alert-circle.svg b/new-soul/miniprogram/assets/icons/alert-circle.svg deleted file mode 100644 index f5a441f3..00000000 --- a/new-soul/miniprogram/assets/icons/alert-circle.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/arrow-right.svg b/new-soul/miniprogram/assets/icons/arrow-right.svg deleted file mode 100644 index 1dc64d3f..00000000 --- a/new-soul/miniprogram/assets/icons/arrow-right.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/bell.svg b/new-soul/miniprogram/assets/icons/bell.svg deleted file mode 100644 index 0e7e405b..00000000 --- a/new-soul/miniprogram/assets/icons/bell.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/book-arrow-teal.svg b/new-soul/miniprogram/assets/icons/book-arrow-teal.svg deleted file mode 100644 index 6a861630..00000000 --- a/new-soul/miniprogram/assets/icons/book-arrow-teal.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/book-arrow.svg b/new-soul/miniprogram/assets/icons/book-arrow.svg deleted file mode 100644 index 2b1320d0..00000000 --- a/new-soul/miniprogram/assets/icons/book-arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/book-open-teal.svg b/new-soul/miniprogram/assets/icons/book-open-teal.svg deleted file mode 100644 index 482f4093..00000000 --- a/new-soul/miniprogram/assets/icons/book-open-teal.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/book-open.svg b/new-soul/miniprogram/assets/icons/book-open.svg deleted file mode 100644 index d833e86b..00000000 --- a/new-soul/miniprogram/assets/icons/book-open.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/book.svg b/new-soul/miniprogram/assets/icons/book.svg deleted file mode 100644 index 93579576..00000000 --- a/new-soul/miniprogram/assets/icons/book.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/chevron-left.svg b/new-soul/miniprogram/assets/icons/chevron-left.svg deleted file mode 100644 index e406b2b9..00000000 --- a/new-soul/miniprogram/assets/icons/chevron-left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/new-soul/miniprogram/assets/icons/clock-teal.svg b/new-soul/miniprogram/assets/icons/clock-teal.svg deleted file mode 100644 index 96a928e3..00000000 --- a/new-soul/miniprogram/assets/icons/clock-teal.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/clock.svg b/new-soul/miniprogram/assets/icons/clock.svg deleted file mode 100644 index 547f24a7..00000000 --- a/new-soul/miniprogram/assets/icons/clock.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/edit-gray.svg b/new-soul/miniprogram/assets/icons/edit-gray.svg deleted file mode 100644 index 4d821839..00000000 --- a/new-soul/miniprogram/assets/icons/edit-gray.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/eye-off.svg b/new-soul/miniprogram/assets/icons/eye-off.svg deleted file mode 100644 index 05b73282..00000000 --- a/new-soul/miniprogram/assets/icons/eye-off.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/new-soul/miniprogram/assets/icons/eye-teal.svg b/new-soul/miniprogram/assets/icons/eye-teal.svg deleted file mode 100644 index cdd62e60..00000000 --- a/new-soul/miniprogram/assets/icons/eye-teal.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/eye.svg b/new-soul/miniprogram/assets/icons/eye.svg deleted file mode 100644 index 10ff508d..00000000 --- a/new-soul/miniprogram/assets/icons/eye.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/folder-teal.svg b/new-soul/miniprogram/assets/icons/folder-teal.svg deleted file mode 100644 index 28003510..00000000 --- a/new-soul/miniprogram/assets/icons/folder-teal.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/new-soul/miniprogram/assets/icons/folder.svg b/new-soul/miniprogram/assets/icons/folder.svg deleted file mode 100644 index 5e5b7042..00000000 --- a/new-soul/miniprogram/assets/icons/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/new-soul/miniprogram/assets/icons/gift.svg b/new-soul/miniprogram/assets/icons/gift.svg deleted file mode 100644 index 66ac806c..00000000 --- a/new-soul/miniprogram/assets/icons/gift.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/new-soul/miniprogram/assets/icons/home-active.png b/new-soul/miniprogram/assets/icons/home-active.png deleted file mode 100644 index b6090d87..00000000 Binary files a/new-soul/miniprogram/assets/icons/home-active.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/home.png b/new-soul/miniprogram/assets/icons/home.png deleted file mode 100644 index 0ffba614..00000000 Binary files a/new-soul/miniprogram/assets/icons/home.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/home.svg b/new-soul/miniprogram/assets/icons/home.svg deleted file mode 100644 index 76244091..00000000 --- a/new-soul/miniprogram/assets/icons/home.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/image.svg b/new-soul/miniprogram/assets/icons/image.svg deleted file mode 100644 index 50ed9e6d..00000000 --- a/new-soul/miniprogram/assets/icons/image.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/info-blue.svg b/new-soul/miniprogram/assets/icons/info-blue.svg deleted file mode 100644 index 0f5bb1d2..00000000 --- a/new-soul/miniprogram/assets/icons/info-blue.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/info.svg b/new-soul/miniprogram/assets/icons/info.svg deleted file mode 100644 index 11d0f224..00000000 --- a/new-soul/miniprogram/assets/icons/info.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/new-soul/miniprogram/assets/icons/list.svg b/new-soul/miniprogram/assets/icons/list.svg deleted file mode 100644 index 688326aa..00000000 --- a/new-soul/miniprogram/assets/icons/list.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/new-soul/miniprogram/assets/icons/match-active.png b/new-soul/miniprogram/assets/icons/match-active.png deleted file mode 100644 index da62b436..00000000 Binary files a/new-soul/miniprogram/assets/icons/match-active.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/match.png b/new-soul/miniprogram/assets/icons/match.png deleted file mode 100644 index b15582e3..00000000 Binary files a/new-soul/miniprogram/assets/icons/match.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/message-circle.svg b/new-soul/miniprogram/assets/icons/message-circle.svg deleted file mode 100644 index 037560e9..00000000 --- a/new-soul/miniprogram/assets/icons/message-circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/new-soul/miniprogram/assets/icons/my-active.png b/new-soul/miniprogram/assets/icons/my-active.png deleted file mode 100644 index da62b436..00000000 Binary files a/new-soul/miniprogram/assets/icons/my-active.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/my.png b/new-soul/miniprogram/assets/icons/my.png deleted file mode 100644 index b15582e3..00000000 Binary files a/new-soul/miniprogram/assets/icons/my.png and /dev/null differ diff --git a/new-soul/miniprogram/assets/icons/partners.svg b/new-soul/miniprogram/assets/icons/partners.svg deleted file mode 100644 index 80668312..00000000 --- a/new-soul/miniprogram/assets/icons/partners.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - diff --git a/new-soul/miniprogram/assets/icons/settings-gray.svg b/new-soul/miniprogram/assets/icons/settings-gray.svg deleted file mode 100644 index d7098fc1..00000000 --- a/new-soul/miniprogram/assets/icons/settings-gray.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/settings.svg b/new-soul/miniprogram/assets/icons/settings.svg deleted file mode 100644 index c7006ea8..00000000 --- a/new-soul/miniprogram/assets/icons/settings.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/share.svg b/new-soul/miniprogram/assets/icons/share.svg deleted file mode 100644 index 93179fc2..00000000 --- a/new-soul/miniprogram/assets/icons/share.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/new-soul/miniprogram/assets/icons/sparkles.svg b/new-soul/miniprogram/assets/icons/sparkles.svg deleted file mode 100644 index e2a4461f..00000000 --- a/new-soul/miniprogram/assets/icons/sparkles.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/new-soul/miniprogram/assets/icons/user-edit-gray.svg b/new-soul/miniprogram/assets/icons/user-edit-gray.svg deleted file mode 100644 index 884c3651..00000000 --- a/new-soul/miniprogram/assets/icons/user-edit-gray.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/user.svg b/new-soul/miniprogram/assets/icons/user.svg deleted file mode 100644 index 8b190427..00000000 --- a/new-soul/miniprogram/assets/icons/user.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/icons/users-teal.svg b/new-soul/miniprogram/assets/icons/users-teal.svg deleted file mode 100644 index ed97706f..00000000 --- a/new-soul/miniprogram/assets/icons/users-teal.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/new-soul/miniprogram/assets/icons/users.svg b/new-soul/miniprogram/assets/icons/users.svg deleted file mode 100644 index 4816094b..00000000 --- a/new-soul/miniprogram/assets/icons/users.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/new-soul/miniprogram/assets/icons/wallet.svg b/new-soul/miniprogram/assets/icons/wallet.svg deleted file mode 100644 index 6d431e54..00000000 --- a/new-soul/miniprogram/assets/icons/wallet.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/new-soul/miniprogram/assets/images/author-avatar.png b/new-soul/miniprogram/assets/images/author-avatar.png deleted file mode 100644 index c02ae73f..00000000 Binary files a/new-soul/miniprogram/assets/images/author-avatar.png and /dev/null differ diff --git a/new-soul/miniprogram/components/icon/README.md b/new-soul/miniprogram/components/icon/README.md deleted file mode 100644 index 34e394c8..00000000 --- a/new-soul/miniprogram/components/icon/README.md +++ /dev/null @@ -1,175 +0,0 @@ -# Icon 图标组件 - -SVG 图标组件,参考 lucide-react 实现,用于在小程序中使用矢量图标。 - -**技术实现**: 使用 Base64 编码的 SVG + image 组件(小程序不支持直接使用 SVG 标签) - ---- - -## 使用方法 - -### 1. 在页面 JSON 中引入组件 - -```json -{ - "usingComponents": { - "icon": "/components/icon/icon" - } -} -``` - -### 2. 在 WXML 中使用 - -```xml - - - - - - - - - - - - - - - - - -``` - ---- - -## 属性说明 - -| 属性 | 类型 | 默认值 | 说明 | -|-----|------|--------|-----| -| name | String | 'share' | 图标名称 | -| size | Number | 48 | 图标大小(rpx) | -| color | String | 'currentColor' | 图标颜色 | -| customClass | String | '' | 自定义类名 | -| customStyle | String | '' | 自定义样式 | - ---- - -## 可用图标 - -| 图标名称 | 说明 | 对应 lucide-react | -|---------|------|-------------------| -| `share` | 分享 | `` | -| `arrow-up-right` | 右上箭头 | `` | -| `chevron-left` | 左箭头 | `` | -| `search` | 搜索 | `` | -| `heart` | 心形 | `` | - ---- - -## 添加新图标 - -在 `icon.js` 的 `getSvgPath` 方法中添加新图标: - -```javascript -getSvgPath(name) { - const svgMap = { - 'new-icon': '', - // ... 其他图标 - } - return svgMap[name] || '' -} -``` - -**获取 SVG 代码**: 访问 [lucide.dev](https://lucide.dev) 搜索图标,复制 SVG 内容。 -**注意**: 颜色使用 `COLOR` 占位符,组件会自动替换。 - ---- - -## 样式定制 - -### 1. 使用 customClass - -```xml - -``` - -```css -.my-icon-class { - opacity: 0.8; -} -``` - -### 2. 使用 customStyle - -```xml - -``` - ---- - -## 技术说明 - -### 为什么使用 Base64 + image? - -1. **矢量图标**:任意缩放不失真 -2. **灵活着色**:通过 `COLOR` 占位符动态改变颜色 -3. **轻量级**:无需加载字体文件或外部图片 -4. **兼容性**:小程序不支持直接使用 SVG 标签,image 组件支持 Base64 SVG - -### 为什么不用字体图标? - -小程序对字体文件有限制,Base64 编码字体文件会增加包体积,SVG 图标更轻量。 - -### 与 lucide-react 的对应关系 - -- **lucide-react**: React 组件库,使用 SVG -- **本组件**: 小程序自定义组件,也使用 SVG -- **SVG path 数据**: 完全相同,从 lucide 官网复制 - ---- - -## 示例 - -### 悬浮分享按钮 - -```xml - -``` - -```css -.fab-share { - position: fixed; - right: 32rpx; - bottom: calc(120rpx + env(safe-area-inset-bottom)); - width: 96rpx; - height: 96rpx; - border-radius: 50%; - background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); - display: flex; - align-items: center; - justify-content: center; -} -``` - ---- - -## 扩展图标库 - -可以继续添加更多 lucide-react 图标: - -- `star` - 星星 -- `wallet` - 钱包 -- `gift` - 礼物 -- `info` - 信息 -- `settings` - 设置 -- `user` - 用户 -- `book-open` - 打开的书 -- `eye` - 眼睛 -- `clock` - 时钟 -- `users` - 用户组 - ---- - -**图标组件创建完成!** 🎉 diff --git a/new-soul/miniprogram/components/icon/icon.js b/new-soul/miniprogram/components/icon/icon.js deleted file mode 100644 index b2dec23f..00000000 --- a/new-soul/miniprogram/components/icon/icon.js +++ /dev/null @@ -1,83 +0,0 @@ -// components/icon/icon.js -Component({ - properties: { - // 图标名称 - name: { - type: String, - value: 'share', - observer: 'updateIcon' - }, - // 图标大小(rpx) - size: { - type: Number, - value: 48 - }, - // 图标颜色 - color: { - type: String, - value: '#ffffff', - observer: 'updateIcon' - }, - // 自定义类名 - customClass: { - type: String, - value: '' - }, - // 自定义样式 - customStyle: { - type: String, - value: '' - } - }, - - data: { - svgData: '' - }, - - lifetimes: { - attached() { - this.updateIcon() - } - }, - - methods: { - // SVG 图标数据映射 - getSvgPath(name) { - const svgMap = { - 'share': '', - - 'arrow-up-right': '', - - 'chevron-left': '', - - 'search': '', - - 'heart': '' - } - - return svgMap[name] || '' - }, - - // 更新图标 - updateIcon() { - const { name, color } = this.data - let svgString = this.getSvgPath(name) - - if (svgString) { - // 替换颜色占位符 - svgString = svgString.replace(/COLOR/g, color) - - // 转换为 Base64 Data URL - const svgData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}` - - this.setData({ - svgData: svgData - }) - } else { - this.setData({ - svgData: '' - }) - } - } - } -}) diff --git a/new-soul/miniprogram/components/icon/icon.json b/new-soul/miniprogram/components/icon/icon.json deleted file mode 100644 index a89ef4db..00000000 --- a/new-soul/miniprogram/components/icon/icon.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "component": true, - "usingComponents": {} -} diff --git a/new-soul/miniprogram/components/icon/icon.wxml b/new-soul/miniprogram/components/icon/icon.wxml deleted file mode 100644 index b1c29a25..00000000 --- a/new-soul/miniprogram/components/icon/icon.wxml +++ /dev/null @@ -1,5 +0,0 @@ - - - - {{name}} - diff --git a/new-soul/miniprogram/components/icon/icon.wxss b/new-soul/miniprogram/components/icon/icon.wxss deleted file mode 100644 index d12d2a0a..00000000 --- a/new-soul/miniprogram/components/icon/icon.wxss +++ /dev/null @@ -1,18 +0,0 @@ -/* components/icon/icon.wxss */ -.icon { - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.icon-image { - display: block; - width: 100%; - height: 100%; -} - -.icon-text { - font-size: 24rpx; - color: currentColor; -} diff --git a/new-soul/miniprogram/custom-tab-bar/index.js b/new-soul/miniprogram/custom-tab-bar/index.js deleted file mode 100644 index 4acd9546..00000000 --- a/new-soul/miniprogram/custom-tab-bar/index.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Soul创业实验 - 自定义TabBar组件 - * 根据后台配置动态显示/隐藏"找伙伴"按钮 - */ - -console.log('[TabBar] ===== 组件文件开始加载 =====') - -const app = getApp() -console.log('[TabBar] App 对象:', app) - -Component({ - data: { - selected: 0, - color: '#8e8e93', - selectedColor: '#00CED1', - matchEnabled: false, // 找伙伴功能开关,默认关闭 - list: [ - { - pagePath: '/pages/index/index', - text: '首页', - iconType: 'home' - }, - { - pagePath: '/pages/chapters/chapters', - text: '目录', - iconType: 'list' - }, - { - pagePath: '/pages/match/match', - text: '找伙伴', - iconType: 'match', - isSpecial: true - }, - { - pagePath: '/pages/my/my', - text: '我的', - iconType: 'user' - } - ] - }, - - lifetimes: { - attached() { - console.log('[TabBar] Component attached 生命周期触发') - this.loadFeatureConfig() - }, - ready() { - console.log('[TabBar] Component ready 生命周期触发') - // 如果 attached 中没有成功加载,在 ready 中再次尝试 - if (this.data.matchEnabled === undefined || this.data.matchEnabled === null) { - console.log('[TabBar] 在 ready 中重新加载配置') - this.loadFeatureConfig() - } - } - }, - - // 页面加载时也调用(兼容性更好) - attached() { - console.log('[TabBar] attached() 方法触发') - this.loadFeatureConfig() - }, - - methods: { - // 加载功能配置 - async loadFeatureConfig() { - try { - console.log('[TabBar] 开始加载功能配置...') - console.log('[TabBar] API地址:', app.globalData.baseUrl + '/api/miniprogram/config') - - // app.request 的第一个参数是 url 字符串,第二个参数是 options 对象 - const res = await app.request('/api/miniprogram/config', { - method: 'GET' - }) - - - // 兼容两种返回格式 - let matchEnabled = false - - if (res && res.success && res.features) { - console.log('[TabBar] features配置:', JSON.stringify(res.features)) - matchEnabled = res.features.matchEnabled === true - console.log('[TabBar] matchEnabled值:', matchEnabled) - } else if (res && res.configs && res.configs.feature_config) { - // 备用格式:从 configs.feature_config 读取 - console.log('[TabBar] 使用备用格式,从configs读取') - matchEnabled = res.configs.feature_config.matchEnabled === true - console.log('[TabBar] matchEnabled值:', matchEnabled) - } else { - console.log('[TabBar] ⚠️ 未找到features配置,使用默认值false') - console.log('[TabBar] res对象keys:', Object.keys(res || {})) - } - - this.setData({ matchEnabled }, () => { - console.log('[TabBar] ✅ matchEnabled已设置为:', this.data.matchEnabled) - // 配置加载完成后,根据当前路由设置选中状态 - this.updateSelected() - }) - - // 如果当前在找伙伴页面,但功能已关闭,跳转到首页 - if (!matchEnabled) { - const pages = getCurrentPages() - const currentPage = pages[pages.length - 1] - if (currentPage && currentPage.route === 'pages/match/match') { - console.log('[TabBar] 找伙伴功能已关闭,从match页面跳转到首页') - wx.switchTab({ url: '/pages/index/index' }) - } - } - } catch (error) { - console.log('[TabBar] ❌ 加载功能配置失败:', error) - console.log('[TabBar] 错误详情:', error.message || error) - // 默认关闭找伙伴功能 - this.setData({ matchEnabled: false }, () => { - this.updateSelected() - }) - } - }, - - // 根据当前路由更新选中状态 - updateSelected() { - const pages = getCurrentPages() - if (pages.length === 0) return - - const currentPage = pages[pages.length - 1] - const route = currentPage.route - - let selected = 0 - const { matchEnabled } = this.data - - // 根据路由匹配对应的索引 - if (route === 'pages/index/index') { - selected = 0 - } else if (route === 'pages/chapters/chapters') { - selected = 1 - } else if (route === 'pages/match/match') { - selected = 2 - } else if (route === 'pages/my/my') { - selected = matchEnabled ? 3 : 2 - } - - this.setData({ selected }) - }, - - switchTab(e) { - const data = e.currentTarget.dataset - const url = data.path - const index = data.index - - if (this.data.selected === index) return - - wx.switchTab({ url }) - } - } -}) diff --git a/new-soul/miniprogram/custom-tab-bar/index.json b/new-soul/miniprogram/custom-tab-bar/index.json deleted file mode 100644 index 467ce294..00000000 --- a/new-soul/miniprogram/custom-tab-bar/index.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "component": true -} diff --git a/new-soul/miniprogram/custom-tab-bar/index.wxml b/new-soul/miniprogram/custom-tab-bar/index.wxml deleted file mode 100644 index 73369b2a..00000000 --- a/new-soul/miniprogram/custom-tab-bar/index.wxml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - {{list[0].text}} - - - - - - - - {{list[1].text}} - - - - - - - - {{list[2].text}} - - - - - - - - {{list[3].text}} - - diff --git a/new-soul/miniprogram/custom-tab-bar/index.wxss b/new-soul/miniprogram/custom-tab-bar/index.wxss deleted file mode 100644 index 98036655..00000000 --- a/new-soul/miniprogram/custom-tab-bar/index.wxss +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Soul创业实验 - 自定义TabBar样式 - * 实现中间突出的"找伙伴"按钮 - */ - -.tab-bar { - position: fixed; - bottom: 0; - left: 0; - right: 0; - height: 100rpx; - background: rgba(28, 28, 30, 0.95); - backdrop-filter: blur(40rpx); - -webkit-backdrop-filter: blur(40rpx); - display: flex; - align-items: flex-end; - padding-bottom: env(safe-area-inset-bottom); - z-index: 999; -} - -/* 三个tab布局(找伙伴功能关闭时) */ -.tab-bar-three .tab-bar-item { - flex: 1; -} - -/* 四个tab布局(找伙伴功能开启时) */ -.tab-bar-four .tab-bar-item { - flex: 1; -} - -.tab-bar-border { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1rpx; - background: rgba(255, 255, 255, 0.05); -} - -.tab-bar-item { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 10rpx 0 16rpx; -} - -.icon-wrapper { - width: 48rpx; - height: 48rpx; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 4rpx; -} - -.icon { - width: 44rpx; - height: 44rpx; - display: flex; - align-items: center; - justify-content: center; -} - -.tab-bar-text { - font-size: 22rpx; - line-height: 1; -} - -/* ===== SVG 图标样式 ===== */ -.tab-icon { - width: 48rpx; - height: 48rpx; - display: block; - filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); -} - -.tab-icon.icon-active { - filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); -} - - -/* ===== 找伙伴 - 中间特殊按钮 ===== */ -.special-item { - position: relative; - margin-top: -32rpx; -} - -.special-button { - width: 112rpx; - height: 112rpx; - border-radius: 50%; - background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4); - margin-bottom: 4rpx; - transition: all 0.2s ease; -} - -.special-button:active { - transform: scale(0.95); -} - -.special-active { - box-shadow: 0 8rpx 40rpx rgba(0, 206, 209, 0.6); -} - -.special-text { - margin-top: 4rpx; -} - -/* ===== 找伙伴特殊按钮图标 ===== */ -.special-icon { - width: 80rpx; - height: 80rpx; - display: block; - filter: brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%); -} diff --git a/new-soul/miniprogram/pages/about/about.js b/new-soul/miniprogram/pages/about/about.js deleted file mode 100644 index af04d83d..00000000 --- a/new-soul/miniprogram/pages/about/about.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Soul创业派对 - 关于作者页 - * 开发: 卡若 - */ -const app = getApp() - -Page({ - data: { - statusBarHeight: 44, - authorLoading: true, - author: { - name: '卡若', - avatar: 'K', - avatarImg: '/assets/images/author-avatar.png', - title: '', - bio: '', - stats: [], - highlights: [] - }, - bookInfo: { - title: '一场Soul的创业实验', - totalChapters: 62, - parts: [ - { name: '真实的人', chapters: 10 }, - { name: '真实的行业', chapters: 15 }, - { name: '真实的错误', chapters: 9 }, - { name: '真实的赚钱', chapters: 20 }, - { name: '真实的社会', chapters: 9 } - ], - price: 9.9 - } - }, - - onLoad() { - wx.showShareMenu({ withShareTimeline: true }) - this.setData({ - statusBarHeight: app.globalData.statusBarHeight - }) - this.loadAuthor() - this.loadBookStats() - }, - - async loadAuthor() { - this.setData({ authorLoading: true }) - try { - const res = await app.request({ url: '/api/miniprogram/about/author', silent: true }) - if (res?.success && res.data) { - const d = res.data - let avatarImg = d.avatarImg || '' - if (avatarImg && !avatarImg.startsWith('http')) { - const base = (app.globalData.baseUrl || '').replace(/\/$/, '') - avatarImg = base ? base + (avatarImg.startsWith('/') ? avatarImg : '/' + avatarImg) : avatarImg - } - this.setData({ - author: { - name: d.name || '卡若', - avatar: d.avatar || 'K', - avatarImg: avatarImg || '/assets/images/author-avatar.png', - title: d.title || '', - bio: d.bio || '', - stats: Array.isArray(d.stats) ? d.stats : [ - { label: '商业案例', value: '62' }, - { label: '连续直播', value: '365天' }, - { label: '派对分享', value: '1000+' } - ], - highlights: Array.isArray(d.highlights) ? d.highlights : [] - }, - authorLoading: false - }) - } else { - this.setData({ authorLoading: false }) - } - } catch (e) { - console.log('[About] 加载作者配置失败,使用默认') - this.setData({ authorLoading: false }) - } - }, - - // 加载书籍统计(合并到作者统计第一项「商业案例」) - async loadBookStats() { - try { - const res = await app.request({ url: '/api/miniprogram/book/stats', silent: true }) - if (res?.success && res.data) { - const total = res.data?.totalChapters || 62 - this.setData({ 'bookInfo.totalChapters': total }) - const stats = this.data.author?.stats || [] - const idx = stats.findIndex((s) => s && (s.label === '商业案例' || s.label === '章节')) - if (idx >= 0 && stats[idx]) { - const next = [...stats] - next[idx] = { ...stats[idx], value: String(total) } - this.setData({ 'author.stats': next }) - } else if (stats.length === 0) { - this.setData({ - 'author.stats': [ - { label: '商业案例', value: String(total) }, - { label: '连续直播', value: '365天' }, - { label: '派对分享', value: '1000+' } - ] - }) - } - } - } catch (e) { - console.log('[About] 加载书籍统计失败,使用默认值') - } - }, - - // 联系方式功能已禁用 - copyWechat() { - wx.showToast({ title: '请在派对房联系作者', icon: 'none' }) - }, - - callPhone() { - wx.showToast({ title: '请在派对房联系作者', icon: 'none' }) - }, - - // 返回 - goBack() { - getApp().goBackOrToHome() - }, - - onShareAppMessage() { - const ref = app.getMyReferralCode() - return { - title: 'Soul创业派对 - 关于', - path: ref ? `/pages/about/about?ref=${ref}` : '/pages/about/about' - } - }, - - onShareTimeline() { - const ref = app.getMyReferralCode() - return { title: 'Soul创业派对 - 关于', query: ref ? `ref=${ref}` : '' } - } -}) diff --git a/new-soul/miniprogram/pages/about/about.json b/new-soul/miniprogram/pages/about/about.json deleted file mode 100644 index e90e9960..00000000 --- a/new-soul/miniprogram/pages/about/about.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "usingComponents": {}, - "navigationStyle": "custom" -} diff --git a/new-soul/miniprogram/pages/about/about.wxml b/new-soul/miniprogram/pages/about/about.wxml deleted file mode 100644 index 5966329f..00000000 --- a/new-soul/miniprogram/pages/about/about.wxml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - 关于作者 - - - - - - 加载中... - - - - - {{author.avatar}} - - {{author.name}} - {{author.title}} - {{author.bio}} - - - - - {{item.value}} - {{item.label}} - - - - - - - - {{item}} - - - - - - - {{bookInfo.title}} - - - {{bookInfo.totalChapters}} - 篇章节 - - - 5 - 大篇章 - - - ¥{{bookInfo.price}} - 全书价格 - - - - - {{item.name}} - {{item.chapters}}节 - - - - - - - 联系作者 - - - - Soul派对房 - 每天早上6-9点开播 - - - - 在Soul App搜索"创业实验"或"卡若",加入派对房直接交流 - - - - diff --git a/new-soul/miniprogram/pages/about/about.wxss b/new-soul/miniprogram/pages/about/about.wxss deleted file mode 100644 index 06ad5a00..00000000 --- a/new-soul/miniprogram/pages/about/about.wxss +++ /dev/null @@ -1,43 +0,0 @@ -.page { min-height: 100vh; background: #000; } -.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; } -.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; } -.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; } -.nav-placeholder { width: 72rpx; } -.content { padding: 32rpx; } -.loading-row { text-align: center; color: rgba(255,255,255,0.6); font-size: 28rpx; padding: 48rpx 0; } -.author-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-radius: 32rpx; padding: 48rpx; text-align: center; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); } -.author-avatar-wrap { width: 160rpx; height: 160rpx; margin: 0 auto 24rpx; overflow: hidden; border-radius: 50%; border: 4rpx solid rgba(0,206,209,0.3); flex-shrink: 0; } -.author-avatar-img { width: 100%; height: 100%; display: block; } -.author-avatar { width: 100%; height: 100%; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; font-size: 64rpx; color: #fff; font-weight: 700; } -.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; } -.author-title { font-size: 26rpx; color: #00CED1; display: block; margin-bottom: 24rpx; } -.author-bio { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.8; display: block; margin-bottom: 32rpx; } -.stats-row { display: flex; justify-content: space-around; padding-top: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.1); } -.stat-item { text-align: center; } -.stat-value { font-size: 36rpx; font-weight: 700; color: #00CED1; display: block; } -.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); } -.contact-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; } -.card-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 24rpx; } -.contact-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 16rpx; } -.contact-item:last-child { margin-bottom: 0; } -.contact-icon { font-size: 40rpx; } -.contact-info { flex: 1; } -.contact-label { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; } -.contact-value { font-size: 28rpx; color: #fff; } -.contact-btn { padding: 12rpx 24rpx; background: rgba(0,206,209,0.2); color: #00CED1; font-size: 24rpx; border-radius: 16rpx; } - -/* 亮点标签 */ -.highlights { display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); justify-content: center; } -.highlight-tag { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; background: rgba(0,206,209,0.15); border-radius: 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); } -.tag-icon { color: #00CED1; font-size: 22rpx; } - -/* 书籍信息卡片 */ -.book-info-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; } -.book-stats { display: flex; justify-content: space-around; padding: 24rpx 0; margin: 16rpx 0; background: rgba(0,0,0,0.3); border-radius: 16rpx; } -.book-stat { text-align: center; } -.book-stat-value { font-size: 36rpx; font-weight: 700; color: #FFD700; display: block; } -.book-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); } -.parts-list { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 16rpx; } -.part-item { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: rgba(255,255,255,0.05); border-radius: 12rpx; } -.part-name { font-size: 24rpx; color: rgba(255,255,255,0.8); } -.part-chapters { font-size: 22rpx; color: #00CED1; } diff --git a/new-soul/miniprogram/pages/addresses/addresses.js b/new-soul/miniprogram/pages/addresses/addresses.js deleted file mode 100644 index 0dbd7f7e..00000000 --- a/new-soul/miniprogram/pages/addresses/addresses.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * 收货地址列表页 - * 参考 Next.js: app/view/my/addresses/page.tsx - */ - -const app = getApp() - -Page({ - data: { - statusBarHeight: 44, - isLoggedIn: false, - addressList: [], - loading: true - }, - - onLoad() { - wx.showShareMenu({ withShareTimeline: true }) - this.setData({ - statusBarHeight: app.globalData.statusBarHeight || 44 - }) - this.checkLogin() - }, - - onShow() { - if (this.data.isLoggedIn) { - this.loadAddresses() - } - }, - - // 检查登录状态 - checkLogin() { - const isLoggedIn = app.globalData.isLoggedIn - const userId = app.globalData.userInfo?.id - - if (!isLoggedIn || !userId) { - wx.showModal({ - title: '需要登录', - content: '请先登录后再管理收货地址', - confirmText: '去登录', - success: (res) => { - if (res.confirm) { - wx.switchTab({ url: '/pages/my/my' }) - } else { - getApp().goBackOrToHome() - } - } - }) - return - } - - this.setData({ isLoggedIn: true }) - this.loadAddresses() - }, - - // 加载地址列表 - async loadAddresses() { - const userId = app.globalData.userInfo?.id - if (!userId) return - - this.setData({ loading: true }) - - try { - const res = await app.request(`/api/miniprogram/user/addresses?userId=${userId}`) - if (res.success && res.list) { - this.setData({ - addressList: res.list, - loading: false - }) - } else { - this.setData({ addressList: [], loading: false }) - } - } catch (e) { - console.error('加载地址列表失败:', e) - this.setData({ loading: false }) - wx.showToast({ title: '加载失败', icon: 'none' }) - } - }, - - // 编辑地址 - editAddress(e) { - const id = e.currentTarget.dataset.id - wx.navigateTo({ url: `/pages/addresses/edit?id=${id}` }) - }, - - // 删除地址 - deleteAddress(e) { - const id = e.currentTarget.dataset.id - - wx.showModal({ - title: '确认删除', - content: '确定要删除该收货地址吗?', - confirmColor: '#FF3B30', - success: async (res) => { - if (res.confirm) { - try { - const result = await app.request(`/api/miniprogram/user/addresses/${id}`, { - method: 'DELETE' - }) - - if (result.success) { - wx.showToast({ title: '删除成功', icon: 'success' }) - this.loadAddresses() - } else { - wx.showToast({ title: result.message || '删除失败', icon: 'none' }) - } - } catch (e) { - console.error('删除地址失败:', e) - wx.showToast({ title: '删除失败', icon: 'none' }) - } - } - } - }) - }, - - // 新增地址 - addAddress() { - wx.navigateTo({ url: '/pages/addresses/edit' }) - }, - - // 返回 - goBack() { - getApp().goBackOrToHome() - }, - - onShareAppMessage() { - const ref = app.getMyReferralCode() - return { - title: 'Soul创业派对 - 地址管理', - path: ref ? `/pages/addresses/addresses?ref=${ref}` : '/pages/addresses/addresses' - } - }, - - onShareTimeline() { - const ref = app.getMyReferralCode() - return { title: 'Soul创业派对 - 地址管理', query: ref ? `ref=${ref}` : '' } - } -}) diff --git a/new-soul/miniprogram/pages/addresses/addresses.json b/new-soul/miniprogram/pages/addresses/addresses.json deleted file mode 100644 index 2e45b65e..00000000 --- a/new-soul/miniprogram/pages/addresses/addresses.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "usingComponents": {}, - "navigationStyle": "custom", - "enablePullDownRefresh": false -} diff --git a/new-soul/miniprogram/pages/addresses/addresses.wxml b/new-soul/miniprogram/pages/addresses/addresses.wxml deleted file mode 100644 index ba84223a..00000000 --- a/new-soul/miniprogram/pages/addresses/addresses.wxml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - 收货地址 - - - - - - - - 加载中... - - - - - - 暂无收货地址 - 点击下方按钮添加 - - - - - - - {{item.name}} - {{item.phone}} - 默认 - - {{item.fullAddress}} - - - ✏️ - 编辑 - - - 🗑️ - 删除 - - - - - - - - - 新增收货地址 - - - diff --git a/new-soul/miniprogram/pages/addresses/addresses.wxss b/new-soul/miniprogram/pages/addresses/addresses.wxss deleted file mode 100644 index 9ff21637..00000000 --- a/new-soul/miniprogram/pages/addresses/addresses.wxss +++ /dev/null @@ -1,217 +0,0 @@ -/** - * 收货地址列表页样式 - */ - -.page { - min-height: 100vh; - background: #000000; - padding-bottom: 200rpx; -} - -/* ===== 导航栏 ===== */ -.nav-bar { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 100; - background: rgba(0, 0, 0, 0.9); - backdrop-filter: blur(40rpx); - border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); -} - -.nav-bar { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 32rpx; - height: 88rpx; -} - -.nav-back { - width: 64rpx; - height: 64rpx; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); - display: flex; - align-items: center; - justify-content: center; -} - -.nav-back:active { - background: rgba(255, 255, 255, 0.15); -} - -.back-icon { - font-size: 48rpx; - color: #ffffff; - line-height: 1; -} - -.nav-title { - flex: 1; - text-align: center; - font-size: 36rpx; - font-weight: 600; - color: #ffffff; -} - -.nav-placeholder { - width: 64rpx; -} - -/* ===== 内容区 ===== */ -.content { - padding: 32rpx; -} - -/* ===== 加载状态 ===== */ -.loading-state { - padding: 240rpx 0; - text-align: center; -} - -.loading-text { - font-size: 28rpx; - color: rgba(255, 255, 255, 0.4); -} - -/* ===== 空状态 ===== */ -.empty-state { - padding: 240rpx 0; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; -} - -.empty-icon { - font-size: 96rpx; - margin-bottom: 24rpx; - opacity: 0.3; -} - -.empty-text { - font-size: 28rpx; - color: rgba(255, 255, 255, 0.6); - margin-bottom: 16rpx; -} - -.empty-tip { - font-size: 24rpx; - color: rgba(255, 255, 255, 0.4); -} - -/* ===== 地址列表 ===== */ -.address-list { - margin-bottom: 24rpx; -} - -.address-card { - background: #1c1c1e; - border-radius: 24rpx; - border: 2rpx solid rgba(255, 255, 255, 0.05); - padding: 32rpx; - margin-bottom: 24rpx; -} - -/* 地址头部 */ -.address-header { - display: flex; - align-items: center; - gap: 16rpx; - margin-bottom: 16rpx; -} - -.receiver-name { - font-size: 32rpx; - font-weight: 600; - color: #ffffff; -} - -.receiver-phone { - font-size: 28rpx; - color: rgba(255, 255, 255, 0.5); -} - -.default-tag { - font-size: 22rpx; - color: #00CED1; - background: rgba(0, 206, 209, 0.2); - padding: 6rpx 16rpx; - border-radius: 8rpx; - margin-left: auto; -} - -/* 地址文本 */ -.address-text { - font-size: 28rpx; - color: rgba(255, 255, 255, 0.6); - line-height: 1.6; - display: block; - margin-bottom: 24rpx; - padding-bottom: 24rpx; - border-bottom: 2rpx solid rgba(255, 255, 255, 0.05); -} - -/* 操作按钮 */ -.address-actions { - display: flex; - justify-content: flex-end; - gap: 32rpx; -} - -.action-btn { - display: flex; - align-items: center; - gap: 8rpx; - padding: 8rpx 0; -} - -.action-btn:active { - opacity: 0.6; -} - -.edit-btn { - color: #00CED1; -} - -.delete-btn { - color: #FF3B30; -} - -.action-icon { - font-size: 28rpx; -} - -.action-text { - font-size: 28rpx; -} - -/* ===== 新增按钮 ===== */ -.add-btn { - display: flex; - align-items: center; - justify-content: center; - gap: 16rpx; - padding: 32rpx; - background: #00CED1; - border-radius: 24rpx; - font-weight: 600; - margin-top: 48rpx; -} - -.add-btn:active { - opacity: 0.8; - transform: scale(0.98); -} - -.add-icon { - font-size: 36rpx; - color: #000000; -} - -.add-text { - font-size: 32rpx; - color: #000000; -} diff --git a/new-soul/miniprogram/pages/addresses/edit.js b/new-soul/miniprogram/pages/addresses/edit.js deleted file mode 100644 index a7d6925e..00000000 --- a/new-soul/miniprogram/pages/addresses/edit.js +++ /dev/null @@ -1,215 +0,0 @@ -/** - * 地址编辑页(新增/编辑) - * 参考 Next.js: app/view/my/addresses/[id]/page.tsx - */ - -const app = getApp() - -Page({ - data: { - statusBarHeight: 44, - isEdit: false, // 是否为编辑模式 - addressId: null, - - // 表单数据 - name: '', - phone: '', - province: '', - city: '', - district: '', - detail: '', - isDefault: false, - - // 地区选择器 - region: [], - - saving: false - }, - - onLoad(options) { - wx.showShareMenu({ withShareTimeline: true }) - this.setData({ - statusBarHeight: app.globalData.statusBarHeight || 44 - }) - - // 如果有 id 参数,则为编辑模式 - if (options.id) { - this.setData({ - isEdit: true, - addressId: options.id - }) - this.loadAddress(options.id) - } - }, - - // 加载地址详情(编辑模式) - async loadAddress(id) { - wx.showLoading({ title: '加载中...', mask: true }) - - try { - const res = await app.request(`/api/miniprogram/user/addresses/${id}`) - if (res.success && res.data) { - const addr = res.data - this.setData({ - name: addr.name || '', - phone: addr.phone || '', - province: addr.province || '', - city: addr.city || '', - district: addr.district || '', - detail: addr.detail || '', - isDefault: addr.isDefault || false, - region: [addr.province, addr.city, addr.district] - }) - } else { - wx.showToast({ title: '加载失败', icon: 'none' }) - } - } catch (e) { - console.error('加载地址详情失败:', e) - wx.showToast({ title: '加载失败', icon: 'none' }) - } finally { - wx.hideLoading() - } - }, - - // 表单输入 - onNameInput(e) { - this.setData({ name: e.detail.value }) - }, - - onPhoneInput(e) { - this.setData({ phone: e.detail.value.replace(/\D/g, '').slice(0, 11) }) - }, - - onDetailInput(e) { - this.setData({ detail: e.detail.value }) - }, - - // 地区选择 - onRegionChange(e) { - const region = e.detail.value - this.setData({ - region, - province: region[0], - city: region[1], - district: region[2] - }) - }, - - // 切换默认地址 - onDefaultChange(e) { - this.setData({ isDefault: e.detail.value }) - }, - - // 表单验证 - validateForm() { - const { name, phone, province, city, district, detail } = this.data - - if (!name || name.trim().length === 0) { - wx.showToast({ title: '请输入收货人姓名', icon: 'none' }) - return false - } - - if (!phone || phone.length !== 11) { - wx.showToast({ title: '请输入正确的手机号', icon: 'none' }) - return false - } - - if (!province || !city || !district) { - wx.showToast({ title: '请选择省市区', icon: 'none' }) - return false - } - - if (!detail || detail.trim().length === 0) { - wx.showToast({ title: '请输入详细地址', icon: 'none' }) - return false - } - - return true - }, - - // 保存地址 - async saveAddress() { - if (!this.validateForm()) return - if (this.data.saving) return - - this.setData({ saving: true }) - wx.showLoading({ title: '保存中...', mask: true }) - - const { isEdit, addressId, name, phone, province, city, district, detail, isDefault } = this.data - const userId = app.globalData.userInfo?.id - - if (!userId) { - wx.hideLoading() - wx.showToast({ title: '请先登录', icon: 'none' }) - this.setData({ saving: false }) - return - } - - const addressData = { - userId, - name, - phone, - province, - city, - district, - detail, - fullAddress: `${province}${city}${district}${detail}`, - isDefault - } - - try { - let res - if (isEdit) { - // 编辑模式 - PUT 请求 - res = await app.request(`/api/miniprogram/user/addresses/${addressId}`, { - method: 'PUT', - data: addressData - }) - } else { - // 新增模式 - POST 请求 - res = await app.request('/api/miniprogram/user/addresses', { - method: 'POST', - data: addressData - }) - } - - if (res.success) { - wx.hideLoading() - wx.showToast({ - title: isEdit ? '保存成功' : '添加成功', - icon: 'success' - }) - setTimeout(() => { - getApp().goBackOrToHome() - }, 1500) - } else { - wx.hideLoading() - wx.showToast({ title: res.message || '保存失败', icon: 'none' }) - this.setData({ saving: false }) - } - } catch (e) { - console.error('保存地址失败:', e) - wx.hideLoading() - wx.showToast({ title: '保存失败', icon: 'none' }) - this.setData({ saving: false }) - } - }, - - // 返回 - goBack() { - getApp().goBackOrToHome() - }, - - onShareAppMessage() { - const ref = app.getMyReferralCode() - return { - title: 'Soul创业派对 - 编辑地址', - path: ref ? `/pages/addresses/edit?ref=${ref}` : '/pages/addresses/edit' - } - }, - - onShareTimeline() { - const ref = app.getMyReferralCode() - return { title: 'Soul创业派对 - 编辑地址', query: ref ? `ref=${ref}` : '' } - } -}) diff --git a/new-soul/miniprogram/pages/addresses/edit.json b/new-soul/miniprogram/pages/addresses/edit.json deleted file mode 100644 index 2e45b65e..00000000 --- a/new-soul/miniprogram/pages/addresses/edit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "usingComponents": {}, - "navigationStyle": "custom", - "enablePullDownRefresh": false -} diff --git a/new-soul/miniprogram/pages/addresses/edit.wxml b/new-soul/miniprogram/pages/addresses/edit.wxml deleted file mode 100644 index c5429207..00000000 --- a/new-soul/miniprogram/pages/addresses/edit.wxml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - {{isEdit ? '编辑地址' : '新增地址'}} - - - - - - - - - - 👤 - 收货人 - - - - - - - - 📱 - 手机号 - - - - - - - - 📍 - 所在地区 - - - - {{province || city || district ? province + ' ' + city + ' ' + district : '请选择省市区'}} - - - - - - - - 🏠 - 详细地址 - -