优化个人中心页面,调整导航栏布局以避让右上角胶囊,增强用户体验。更新匹配功能逻辑,增加未开放提示,确保用户在使用时获得明确反馈。

This commit is contained in:
Alex-larget
2026-03-06 12:12:13 +08:00
parent 7e3d36d67f
commit 3b193fb5a8
72 changed files with 1970 additions and 2987 deletions

View File

@@ -0,0 +1,12 @@
# 产品经理 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议
- **分支冲突后需优先核对需求文档与实现一致性**:避免「文档在合并中丢失」导致需求与实现脱节。重点核对 `开发文档/1、需求/``临时需求池/` 与当前实现是否一致。
- **重点确认项**:个人资料页、增值版/普通版计价、找伙伴联系方式完善弹窗等近期需求是否按文档落地。
## 文章详情 @某人 高亮与一键加好友方案讨论
- **需求**:文章中支持 @某人,名称高亮、点击添加好友;编辑侧能插入 @用户并落库(含用户 id
- **验收**:详情页 @ 高亮可点击;点击调起添加好友并提示;管理端插入 @ 后保存再编辑不错位。
- **待确认**:添加好友接口 path、入参/出参及业务规则(已是好友、重复请求等);开发文档中接口放置位置。

View File

@@ -2,3 +2,5 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-03-05 | 分支冲突后需求文档与实现一致性核对 | [2026-03-05.md](./2026-03-05.md) |
| 2026-03-05 | 文章详情@某人高亮与一键加好友验收标准与待确认 | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -0,0 +1,13 @@
# 后端工程师 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议
- **soul-api 被 gitignore 时**:需在 soul-api 所在位置单独确认 git 状态与合并情况,无法从本仓库直接检查。
- **重点核对接口**`/api/miniprogram/orders`(购买记录)、`/api/db/distribution`(分销)是否已实现并挂载到对应路由组。
- **建议**:对照「三端需求业务对齐」文档逐项核对接口注册与实现;明确 soul-api 的版本管理与合并策略。
## 文章详情 @某人 高亮与一键加好友方案讨论
- **内容存储**:推荐正文内嵌 @ 标记,格式 `@[昵称](userId)`(或 `{{@userId:昵称}}`),后端只存不解析;章节/文章接口原样返回 content。
- **添加好友接口**:在 miniprogram 路由组新增,如 `POST /api/miniprogram/friend/add`,入参 `targetUserId`;与存客宝 api_v1 分离,在开发文档中单独说明或 api_v1 新增小节。
- **数据模型**:无需单独 mention 表,扩展内容表 content 存带 @ 标记字符串即可。

View File

@@ -2,3 +2,5 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-03-05 | soul-api 合并状态确认orders、distribution 接口核对 | [2026-03-05.md](./2026-03-05.md) |
| 2026-03-05 | 文章详情@某人content 内嵌 @ 标记、miniprogram 添加好友接口 | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -0,0 +1,17 @@
# 团队 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议(跨角色共享)
- **分支冲突后各端需做完整性自查**
- 产品:核对需求文档与实现一致性
- 后端:在 soul-api 确认合并状态,核对 orders、distribution 等接口
- 管理端:全功能自测,记录 404/异常接口
- 小程序:核心流程自测,确认 orders 接口;修正 app.json 格式
- 测试:制定「分支合并后回归清单」
- **待确认**`完整的` 分支上的提交是否已全部并入 devlopsoul-api 的版本管理与合并策略。
## 文章详情 @某人 高亮与一键加好友方案讨论(跨角色共享)
- **内容格式**:正文内嵌 @ 标记,统一格式 `@[昵称](userId)`(或约定等价格式),后端/管理端/小程序共用。
- **添加好友接口**:归属 soul-api 的 miniprogram 组,与存客宝 api_v1 分离path、入参由后端/产品确认后写入开发文档。
- **分工**:管理端编辑侧插入 @ 并写入 content小程序解析、高亮、点击调添加好友后端提供章节 content 与添加好友接口。

View File

@@ -4,3 +4,4 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-03-05 | 分支冲突后各端完整性自查流程 | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -0,0 +1,13 @@
# 小程序开发工程师 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议
- **app.json 多页面配置**:建议拆行便于维护,避免多页面写在同一行(如第 21 行)。
- **分支合并后需做核心流程自测**登录→阅读→购买→提现→找伙伴→个人资料→VIP确认无白屏、无接口报错。
- **重点确认**`/api/miniprogram/orders`(购买记录)是否已实现;`完整的` 分支上的优化(如 5a5f0087 卡片边距)是否已并入 devlop。
## 文章详情 @某人 高亮与一键加好友方案讨论
- **展示**:阅读页 content 需解析「@ 标记」格式(如 `@[昵称](userId)`),将 @ 片段渲染为高亮可点击 `<text data-user-id>`,点击调 miniprogram 添加好友接口。
- **接口**:添加好友接口由后端在 miniprogram 组提供(如 `POST /api/miniprogram/friend/add`),入参 `targetUserId`;不调用 api_v1 存客宝接口。
- **实现要点**:在现有 contentParagraphs 按行渲染基础上,对每行再做 mention 解析,得到 text/mention 片段数组后 WXML 分支渲染;不改动现有阅读权限、进度、购买逻辑。

View File

@@ -4,3 +4,5 @@
|------|------|------|
| 2026-02-28 | input 边距口诀、match 资源对接弹窗修正 | [2026-02-28.md](./2026-02-28.md) |
| 2026-03-03 | 我的页面卡片区边距优化16rpx 推荐值 | [2026-03-03.md](./2026-03-03.md) |
| 2026-03-05 | 分支合并后核心流程自测app.json 拆行orders 接口确认 | [2026-03-05.md](./2026-03-05.md) |
| 2026-03-05 | 文章详情@某人高亮与一键加好友(解析@、调添加好友接口) | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -17,9 +17,11 @@ Soul 创业派对产品定位:面向创业者的社区/工具型小程序。
| 2026-02-26 | 项目索引初始化,.cursor 规则优化完成 | 已完成 |
| 2026-02-27 | 开发进度同步会议汇报进度待办「资料不解锁」补充、≥3 章弹窗明确 | 已完成 |
| 2026-02-28 | stitch_soul 需求评审:内容→会员→导师变现路径,待产品补充正式需求文档 | 待续 |
| 2026-03-05 | 分支冲突后功能完整性分析会议:核对需求文档与实现一致性 | 待续 |
| 2026-03-05 | 文章详情@某人加好友方案讨论:验收标准、添加好友接口 path 待确认 | 待续 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD状态用已完成 / 进行中 / 待续 / 搁置
---
**最后更新**2026-02-28
**最后更新**2026-03-05

View File

@@ -18,9 +18,11 @@ soul-apiGo + Gin + GORM + MySQL提供三组路由`/api/miniprogram/*`
| 2026-02-27 | 开发进度汇报computeOrderCommission 会员分润差异化20%/10%已实现vip_roles、vip_activated_at、referral_config 扩展已完成miniprogram/admin/db 三组路由就绪 | 已完成 |
| 2026-02-27 | 开发进度同步会议:进度已同步至开发文档,待办资料完善校验 | 已完成 |
| 2026-02-28 | stitch_soul 需求评审:需梳理 chapter/book/vip设计导师/预约/会员权益模型与接口 | 待续 |
| 2026-03-05 | 分支冲突后功能完整性分析会议:在 soul-api 确认合并状态,核对 orders、distribution 接口 | 待续 |
| 2026-03-05 | 文章详情@某人加好友方案讨论content 内嵌 @ 标记、miniprogram 添加好友接口 | 待续 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD状态用已完成 / 进行中 / 待续 / 搁置
---
**最后更新**2026-02-28
**最后更新**2026-03-05

View File

@@ -21,9 +21,11 @@
| 2026-02-28 | stitch_soul 需求评审:首页/目录/导师/会员/资料五类页面,待需求与接口确定后分阶段实现 | 待续 |
| 2026-02-28 | 吸收经验input 边距口诀「外边包 view、内部 width 100%」写入 Skill §6match 资源对接弹窗已按规范修正 | 已完成 |
| 2026-03-03 | 吸收经验我的页面卡片区边距优化16rpx 为个人中心类页面推荐值,已升级 SKILL §8 | 已完成 |
| 2026-03-05 | 分支冲突后功能完整性分析会议:修正 app.json 拆行、核心流程自测、确认 orders 接口 | 待续 |
| 2026-03-05 | 文章详情@某人加好友方案讨论:阅读页解析 @、高亮可点击、调添加好友接口 | 待续 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD状态用已完成 / 进行中 / 待续 / 搁置
---
**最后更新**2026-03-03
**最后更新**2026-03-05

View File

@@ -17,7 +17,9 @@
| 2026-02-27 | 测试人员角色与 Skill 初始化 | 已完成 |
| 2026-02-27 | 小程序静态审查API 路径、页面接口、手工验证建议) | 已完成 |
| 2026-02-28 | stitch_soul 需求评审:关键场景为阅读/付费/会员/导师预约/资料;待需求确定后补充联调用例 | 待续 |
| 2026-03-05 | 分支冲突后功能完整性分析会议:制定「分支合并后回归清单」 | 待续 |
| 2026-03-05 | 文章详情@某人加好友方案讨论@ 展示与添加好友用例、联调与回归 | 待续 |
---
**最后更新**2026-02-28
**最后更新**2026-03-05

View File

@@ -18,9 +18,11 @@
| 2026-02-27 | 开发进度汇报:内容管理仅 API 按钮、推广中心、SetVipModal、VIP 角色管理、推广设置会员分润配置、VIP 排序等均已落地 | 已完成 |
| 2026-02-27 | 开发进度同步会议:进度已同步至运营与变更 | 已完成 |
| 2026-02-28 | stitch_soul 需求评审:待后端方案确定后规划章节/导师/会员/预约管理页面 | 待续 |
| 2026-03-05 | 分支冲突后功能完整性分析会议:全功能自测,记录 404/异常接口 | 待续 |
| 2026-03-05 | 文章详情@某人加好友方案讨论:编辑页插入 @用户、保存约定 content 格式 | 待续 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD状态用已完成 / 进行中 / 待续 / 搁置
---
**最后更新**2026-02-28
**最后更新**2026-03-05

View File

@@ -0,0 +1,12 @@
# 管理端开发工程师 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议
- **路由与页面结构完整时**:主要风险在接口可用性。全功能自测可快速暴露 404 或异常接口。
- **重点确认**`DistributionPage` 调用的 `GET /api/db/distribution` 是否已实现;`OrdersPage` 的订单接口路径是否与 soul-api 一致。
- **建议**分支合并后做一次管理端全流程自测登录→订单→提现→分销→VIP 角色→导师→配置),记录异常接口反馈后端。
## 文章详情 @某人 高亮与一键加好友方案讨论
- **编辑侧**:文章/章节编辑页增加「插入 @用户」,选择用户后插入到光标位置,保存时写入约定格式 content`@[昵称](userId)`);仅调 `/api/admin/*``/api/db/*`
- **一致性**:与小程序、后端共用同一 content 格式,避免多套标记;列表/预览可简单高亮或原样显示。

View File

@@ -2,3 +2,5 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-03-05 | 分支合并后全功能自测404/异常接口记录 | [2026-03-05.md](./2026-03-05.md) |
| 2026-03-05 | 文章详情@某人:编辑页插入 @用户、保存约定 content 格式 | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -0,0 +1,13 @@
# 软件测试 经验记录 - 2026-03-05
## 分支冲突后功能完整性分析会议
- **分支合并后应制定「回归清单」**覆盖三端联调关键路径登录、VIP、阅读、分销、提现、找伙伴、个人资料、导师、购买记录
- **soul-api 不在仓库时**:需与后端协作确认接口契约,无法在仓库内直接检查。
- **多分支合并**`完整的``soul-content``yongpxu-soul` 等分支合并结果需确认,各端自测 + 测试抽检。
## 文章详情 @某人 高亮与一键加好友方案讨论
- **用例**:无 @ 行为不变;有 @ 高亮且点击调起添加好友并提示;重复点击、未登录、无权限等边界;管理端插入 @ 后保存再编辑不错位。
- **联调**小程序↔章节接口content 含 @)、小程序↔添加好友接口;管理端↔内容保存与用户列表。
- **回归**:阅读页进度、购买、分享等不受 @ 功能影响。

View File

@@ -2,3 +2,5 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-03-05 | 分支合并后回归清单制定;三端联调验证 | [2026-03-05.md](./2026-03-05.md) |
| 2026-03-05 | 文章详情@某人@ 展示与添加好友用例、联调与回归点 | [2026-03-05.md](./2026-03-05.md) |

View File

@@ -0,0 +1,122 @@
# 会议纪要 - 2026-03-05 | 分支冲突后功能完整性分析
> 本文件由**助理橙子**在会议结束后自动生成。
---
## 基本信息
- **时间**2026-03-05
- **议题**:分支冲突后可能有功能缺失,各成员分析自身项目完整性
- **触发方式**:开个会议
- **参与角色**:产品经理、后端开发、管理端开发工程师、小程序开发工程师、测试人员
---
## 各角色发言
### 【产品经理】
- 需求文档stitch_soul、个人资料页、文章类型等已记录需核对 `开发文档/1、需求/``临时需求池/` 与实现是否一致
- 风险:分支合并后文档可能被覆盖,导致需求与实现脱节
- 重点确认:个人资料页、增值版/普通版计价、找伙伴联系方式完善弹窗
### 【后端开发】
- soul-api 被 .gitignore 排除,当前仓库无法直接检查
- 风险:`/api/miniprogram/orders``/api/db/distribution` 等接口是否已实现需在 soul-api 所在位置核对
- 建议:在 soul-api 仓库/目录确认合并状态,对照「三端需求业务对齐」逐项核对接口
### 【管理端开发工程师】
- 21 个路由与页面一一对应,无缺失;仅调用 `/api/admin/*``/api/db/*`,符合边界
- 风险:`DistributionPage``GET /api/db/distribution` 若未实现会 404
- 建议:全功能自测,记录 404 或异常接口反馈后端
### 【小程序开发工程师】
- 21 个页面均有对应目录和文件,无缺失;全部 `/api/miniprogram/*`,符合边界
- 小问题app.json 第 21 行多页面写同一行,建议拆行
- 风险:`/api/miniprogram/orders` 是否已实现;`完整的` 分支上的优化(如 5a5f0087 卡片边距)是否已并入 devlop
- 建议核心流程自测登录→阅读→购买→提现→找伙伴→个人资料→VIP
### 【测试人员】
- 三端联调需逐项验证回归清单应覆盖登录、VIP、阅读、分销、提现、找伙伴、个人资料、导师、购买记录
- 风险soul-api 无法在仓库内检查多分支完整的、soul-content、yongpxu-soul合并结果需确认
- 建议:制定「分支合并后回归清单」,各端自测 + 抽检
---
## 讨论过程
- **乘风**reflog 显示 devlop 曾 reset 到 58d4c0b6后又提交 7e3d36d6`完整的` 分支有 5a5f0087 等提交,是否已并入 devlop
- **小程序**:需对比 devlop 与 `完整的` 的 miniprogram 差异,确认 2026-03-03 卡片边距等优化是否保留
- **后端**soul-api 不在本仓库,需在 soul-api 所在位置单独确认 git 状态与合并情况
- **管理端**:结构完整,主要风险在接口可用性,需联调确认
---
## 会议决议
1. **小程序端**:修正 app.json 第 21 行多页面拆行;核心流程自测;确认 `/api/miniprogram/orders` 是否可用
2. **管理端**:全功能自测,记录 404/异常接口反馈后端
3. **后端**:在 soul-api 所在仓库确认当前分支与合并状态;核对 orders、distribution 等接口是否已实现并挂载
4. **产品**:核对 `开发文档/``临时需求池/` 与实现一致性;重点确认个人资料、增值版/普通版、找伙伴
5. **测试**制定「分支合并后回归清单」覆盖登录、VIP、阅读、分销、提现、找伙伴、个人资料、导师、购买记录
6. **待确认**`完整的` 分支上的提交是否已全部并入 devlopsoul-api 的版本管理与合并策略
---
## 待办事项
| 责任角色 | 任务 | 优先级 | 截止建议 |
|---------|------|--------|---------|
| 小程序开发工程师 | 修正 app.json 第 21 行;核心流程自测;确认 orders 接口 | 高 | 2026-03-06 |
| 管理端开发工程师 | 全功能自测,记录 404/异常接口 | 高 | 2026-03-06 |
| 后端开发 | 在 soul-api 确认合并状态;核对 orders、distribution 接口 | 高 | 2026-03-06 |
| 产品经理 | 核对需求文档与实现一致性 | 中 | 2026-03-06 |
| 测试人员 | 制定「分支合并后回归清单」 | 中 | 2026-03-06 |
---
## 问题与作答区
| # | 问题 | 责任角色 | 作答 |
|---|------|---------|------|
| 1 | `完整的` 分支上的提交5a5f0087 等)是否已全部并入 devlop | 小程序开发工程师 | (待补充) |
| 2 | soul-api 的版本管理与合并策略是什么?是否在独立仓库? | 后端开发 | (待补充) |
| 3 | `/api/miniprogram/orders` 是否已实现并挂载到 miniprogram 组? | 后端开发 | (待补充) |
| 4 | `/api/db/distribution` 是否已实现? | 后端开发 | (待补充) |
---
## 各角色经验与业务理解更新
### 产品经理
- 分支冲突后需优先核对需求文档与实现一致性,避免「文档在合并中丢失」导致需求脱节
### 后端开发
- soul-api 被 gitignore 时,需在 soul-api 所在位置单独确认合并状态;重点核对 orders、distribution 等接口是否已实现并挂载
### 管理端开发工程师
- 路由与页面结构完整时,主要风险在接口可用性;全功能自测可快速暴露 404 或异常接口
### 小程序开发工程师
- app.json 多页面配置建议拆行便于维护;分支合并后需做核心流程自测,确认 orders 等依赖接口可用
### 测试人员
- 分支合并后应制定「回归清单」覆盖三端联调关键路径soul-api 不在仓库时需与后端协作确认接口契约
### 团队共享
- 分支冲突后各端需做完整性自查:产品核对需求文档、后端核对接口、管理端/小程序核对页面与功能、测试制定回归清单
---
*会议纪要由助理橙子生成 | 各角色经验已同步至 `agent/{角色}/evolution/2026-03-05.md`*

View File

@@ -0,0 +1,95 @@
# 会议纪要 - 2026-03-05 | 文章详情 @某人 高亮与一键加好友方案讨论
> 本文件由**助理橙子**在会议结束后自动生成。
---
## 基本信息
- **时间**2026-03-05
- **议题**:小程序文章详情中「@某人」高亮、点击添加好友;内容编辑时如何插入并存储用户信息(含用户 id以及是否有更优实现方式
- **触发方式**:开一个开发大会,讨论一下
- **参与角色**:产品经理、后端开发、管理端开发工程师、小程序开发工程师、测试人员
---
## 各角色发言
### 【产品经理】
- 需求:文章中可出现「@某某人」,名称高亮,用户点击后执行添加该人为好友;添加好友能力以接口为准。
- 用户价值:从内容到人的关系链沉淀,提升互动与转化。
- 业务规则:仅支持 @ 已存在用户;展示名以昵称为准;点击统一为「发起添加好友」。
- 验收标准:文章详情内 @ 名称高亮且可点击;点击后调用添加好友流程;管理端/编辑侧能插入 @用户 并落库(含用户 id
### 【后端开发】
- 添加好友接口需在 soul-api 的 **miniprogram** 路由组下提供(如 `POST /api/miniprogram/friend/add``POST /api/miniprogram/user/add-friend`),入参建议 `targetUserId``开发文档/api_v1.md` 当前为存客宝文档,添加好友接口应单独约定或在该文档中新增小节。
- 内容存储推荐**方案 A**:正文存带 @ 标记的字符串,如 `@[昵称](userId)``{{@userId:昵称}}`,后端只存不解析,由前端解析;方案 B正文 + mentions 位置表)需维护偏移量,正文变更易错位。
- 数据模型:内容表扩展 content 存带 @ 标记的字符串即可,无需单独表。
### 【管理端开发工程师】
- 文章/章节编辑页增加「插入 @用户」:选择用户后插入到光标位置,保存时写入约定格式的 content仅调 `/api/admin/*``/api/db/*`
- 管理端列表/预览可简单高亮或原样显示,与小程序约定同一 content 格式即可。
### 【小程序开发工程师】
- 阅读页当前为按行渲染纯文本;需将 content 解析为「段落 + 片段」(普通文本 / mentionWXML 中对 mention 渲染为可点击 `<text data-user-id>` 并高亮,点击时调用 miniprogram 添加好友接口。
- 继续使用现有章节内容接口,保证返回的 content 含 @ 标记;添加好友调用 miniprogram 组新接口。
### 【测试人员】
- 用例:无 @ 行为不变;有 @ 高亮且点击调起添加好友并提示;重复点击、未登录、无权限等边界;管理端插入 @ 后保存再编辑,内容与用户 id 不错位。
- 联调:小程序 ↔ 章节接口、添加好友接口;管理端 ↔ 内容保存与用户列表。回归:阅读页其他功能不受影响。
---
## 讨论过程
- 一致同意采用「正文内嵌 @ 标记」方案,格式统一为 `@[昵称](userId)`(或约定等价格式)。
- 添加好友接口不混用存客宝 api_v1在 soul-api miniprogram 组新增并在开发文档中明确。
- 管理端负责插入 @ 并写入同一 content 格式;小程序负责解析、展示与点击加好友。
---
## 会议决议
1. **内容格式**:正文使用内嵌 @ 标记,格式 `@[昵称](userId)`(或团队约定的等价格式),后端/管理端/小程序统一。
2. **小程序**:阅读页解析 content 中 @,渲染为高亮可点击;点击时调 miniprogram 添加好友接口(如 `targetUserId`),并做结果提示。
3. **管理端**:编辑页支持「插入 @用户」,保存时写入约定格式的 content。
4. **后端**:提供 miniprogram 添加好友接口;章节/文章接口返回的 content 支持带 @ 标记的字符串。
5. **待确认**:添加好友接口的最终 path、入参/出参;若已有好友/关注模型需对齐。
---
## 待办事项
| 责任角色 | 任务 | 优先级 | 截止建议 |
|---------|------|--------|---------|
| 后端开发 | 在 miniprogram 组新增添加好友接口并更新开发文档 | 高 | 排期后 |
| 管理端开发工程师 | 文章/章节编辑页支持插入 @用户并保存为约定 content 格式 | 高 | 后端接口与格式确定后 |
| 小程序开发工程师 | 阅读页解析 @ 并高亮可点击,点击调添加好友接口 | 高 | 后端接口就绪后 |
| 产品经理 | 确认添加好友接口 path 与业务规则(已是好友/重复请求等) | 中 | 开发前 |
| 测试人员 | 编写 @ 展示与添加好友用例及回归清单 | 中 | 联调前 |
---
## 问题与作答区
| # | 问题 | 责任角色 | 作答 |
|---|------|---------|------|
| 1 | 添加好友接口的最终 path、入参如 targetUserId、出参及错误码 | 后端开发 | (待补充) |
| 2 | 开发文档中添加好友接口放在 api_v1.md 新小节还是单独文档? | 后端/产品 | (待补充) |
| 3 | 是否已有「好友/关注」表或接口需与本次对接? | 后端开发 | (待补充) |
---
## 各角色经验与业务理解更新
- 本次会议结论已同步至各角色当日经验文件(见 `agent/{角色}/evolution/2026-03-05.md`)。
- 团队共享:内容 @ 采用「正文内嵌 `@[昵称](userId)`」方案;添加好友接口归属 miniprogram 组,与存客宝 api_v1 分离。
---
*会议纪要由助理橙子生成 | 各角色经验已同步至 `agent/{角色}/evolution/2026-03-05.md`*

View File

@@ -0,0 +1,78 @@
# 需求分析 - 超级个体解锁眼睛交互改造
## 基本信息
- **时间**2026-03-05
- **需求来源**:用户反馈
- **涉及页面**`miniprogram/pages/member-detail/member-detail`
---
## 一、需求描述
超级个体详情页点击「解锁眼睛」图标时,需调整交互逻辑:
| 原逻辑 | 新逻辑 |
|--------|--------|
| 弹窗「成为VIP会员并完成匹配后即可查看完整联系方式」→ 确认跳转**找伙伴/匹配页** | **不跳转匹配页**;未登录先登录;已登录按权益处理 |
### 新逻辑细则
1. **未登录**:弹窗「请先登录」→ 确认跳转「我的」页,用户登录后再返回操作
2. **已登录**
- **VIP 会员**`hasFullBook`):直接解锁,可无限次解锁任意超级个体
- **非 VIP**:每人 **1 次免费解锁**,第 2 次起弹窗「免费次数已用完,开通 VIP¥1980/年)可无限解锁」→ 确认跳转 **VIP 会员页**1980 付款页)
---
## 二、已实现修改(小程序端)
### 修改文件
- `miniprogram/pages/member-detail/member-detail.js`
### 实现要点
1. **解锁状态存储**(本地 `wx.setStorageSync`
- Key`member_unlocks_{userId}`
- 值:已解锁的 `memberId` 数组
- 用于判断是否已解锁、是否已用掉免费次数
2. **`unlockContact()` 流程**
```
点击眼睛
→ 未登录Modal「需要登录」→ 去登录 → switchTab 我的
→ 已登录 + VIP直接解锁并写入存储
→ 已登录 + 非VIP + 首次:免费解锁并写入存储
→ 已登录 + 非VIP + 非首次Modal「去开通」→ navigateTo VIP 页
```
3. **`enrichAndFormat` 中 `contactUnlocked` / `wechatUnlocked`**
- 原:仅 `isMatched`(匹配过的人)
- 现:`isMatched || localUnlocked`(本地解锁列表也视为已解锁)
---
## 三、后续可选优化(后端/管理端)
### 1. 后端持久化(可选)
当前免费次数与解锁记录存于**本地**,换设备或清缓存会丢失。若需跨设备、防作弊,可:
- 新增接口:`POST /api/miniprogram/member/unlock`
- 入参:`memberId`、`userId`
- 逻辑:校验免费次数 / VIP 权益,记录解锁关系
- 小程序改为调用该接口,成功后更新本地展示
### 2. 管理端统计(可选)
- 统计「超级个体联系方式解锁」次数
- 按用户、按超级个体维度统计
---
## 四、验收要点
- [ ] 未登录点击眼睛 → 弹窗「需要登录」→ 确认跳转「我的」
- [ ] 已登录 + 非 VIP + 首次 → 免费解锁,展示完整联系方式
- [ ] 已登录 + 非 VIP + 第 2 次起 → 弹窗「去开通」→ 确认跳转 VIP 页¥1980
- [ ] 已登录 + VIP → 直接解锁,不限次数
- [ ] 不跳转匹配页

View File

@@ -62,3 +62,6 @@ YYYY-MM-DD_会议主题.md
| 2026-02-28 | 临时需求池 stitch_soul 需求评审含页面重构专项·10 张图全覆盖) | 产品、后端、管理端、小程序、测试 | [2026-02-28_临时需求池stitch_soul需求评审.md](2026-02-28_临时需求池stitch_soul需求评审.md) |
| 2026-02-28 | 个人资料页实现评估profile-show / profile-edit | 产品、后端、管理端、小程序、测试 | [2026-02-28_个人资料页实现评估.md](2026-02-28_个人资料页实现评估.md) |
| 2026-02-28 | 文章类型(普通版/增值版)需求分析 | 产品、后端、管理端、小程序、测试 | [2026-02-28_文章类型普通版增值版需求分析.md](2026-02-28_文章类型普通版增值版需求分析.md) |
| 2026-03-05 | 分支冲突后功能完整性分析 | 产品、后端、管理端、小程序、测试 | [2026-03-05_分支冲突后功能完整性分析.md](2026-03-05_分支冲突后功能完整性分析.md) |
| 2026-03-05 | 超级个体解锁眼睛需求分析 | 产品、小程序 | [2026-03-05_超级个体解锁眼睛需求分析.md](2026-03-05_超级个体解锁眼睛需求分析.md) |
| 2026-03-05 | 文章详情 @某人 高亮与一键加好友方案讨论 | 产品、后端、管理端、小程序、测试 | [2026-03-05_文章详情@某人加好友方案讨论.md](2026-03-05_文章详情@某人加好友方案讨论.md) |

View File

@@ -8,9 +8,9 @@ const { parseScene } = require('./utils/scene.js')
App({
globalData: {
// API基础地址 - 连接真实后端
baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://souldev.quwanzhi.com',
// baseUrl: 'http://localhost:8080',
baseUrl: 'http://localhost:8080',
// 小程序配置 - 真实AppID

View File

@@ -53,14 +53,6 @@
},
"usingComponents": {},
"__usePrivacyCheck__": true,
"permission": {
"scope.userLocation": {
"desc": "用于匹配附近的书友"
}
},
"requiredPrivateInfos": [
"getLocation"
],
"lazyCodeLoading": "requiredComponents",
"style": "v2",
"sitemapLocation": "sitemap.json"

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="12" cy="7" r="4" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -54,7 +54,11 @@ Page({
partCount: 0,
// 加载状态
loading: true
loading: true,
// 链接卡若 - 留资弹窗
showLeadModal: false,
leadPhone: ''
},
onLoad(options) {
@@ -124,8 +128,8 @@ Page({
// 不再过滤无头像用户,无头像时用首字母展示
members = (Array.isArray(res.data) ? res.data : []).slice(0, 4).map(u => ({
id: u.id,
name: u.vipName || u.vip_name || u.nickname || '会员',
avatar: u.vipAvatar || u.vip_avatar || u.avatar || '',
name: u.nickname || u.vipName || u.vip_name || '会员', // 超级个体:用户资料优先,随「我的」修改实时生效
avatar: u.avatar || '',
isVip: true
}))
if (members.length > 0) {
@@ -301,6 +305,113 @@ Page({
wx.navigateTo({ url: '/pages/about/about' })
},
async onLinkKaruo() {
const app = getApp()
if (!app.globalData.isLoggedIn || !app.globalData.userInfo) {
wx.showModal({
title: '提示',
content: '请先登录后再链接卡若',
confirmText: '去登录',
cancelText: '取消',
success: (res) => {
if (res.confirm) wx.switchTab({ url: '/pages/my/my' })
}
})
return
}
const userId = app.globalData.userInfo.id
const leadKey = 'karuo_lead_' + userId
let phone = (app.globalData.userInfo.phone || '').trim()
let wechatId = (app.globalData.userInfo.wechatId || app.globalData.userInfo.wechat_id || '').trim()
if (!phone && !wechatId) {
try {
const profileRes = await app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true })
if (profileRes?.success && profileRes.data) {
phone = (profileRes.data.phone || '').trim()
wechatId = (profileRes.data.wechatId || profileRes.data.wechat_id || '').trim()
}
} catch (e) {}
}
if (phone || wechatId) {
const hasLead = wx.getStorageSync(leadKey)
if (hasLead) {
wx.showToast({ title: '已提交联系方式,卡若会尽快联系你', icon: 'none' })
return
}
wx.showLoading({ title: '提交中...', mask: true })
try {
const res = await app.request({
url: '/api/miniprogram/ckb/lead',
method: 'POST',
data: {
userId,
phone: phone || undefined,
wechatId: wechatId || undefined,
name: (app.globalData.userInfo.nickname || '').trim() || undefined
}
})
wx.hideLoading()
if (res && res.success) {
wx.setStorageSync(leadKey, true)
wx.showToast({ title: res.message || '提交成功', icon: 'success' })
} else {
wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
}
} catch (e) {
wx.hideLoading()
wx.showToast({ title: e.message || '提交失败', icon: 'none' })
}
return
}
this.setData({ showLeadModal: true, leadPhone: '' })
},
closeLeadModal() {
this.setData({ showLeadModal: false, leadPhone: '' })
},
onLeadPhoneInput(e) {
this.setData({ leadPhone: (e.detail.value || '').trim() })
},
async submitLead() {
const phone = (this.data.leadPhone || '').trim().replace(/\s/g, '')
if (!phone) {
wx.showToast({ title: '请输入手机号', icon: 'none' })
return
}
if (phone.length < 11) {
wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
const app = getApp()
const userId = app.globalData.userInfo?.id
const leadKey = userId ? ('karuo_lead_' + userId) : ''
wx.showLoading({ title: '提交中...', mask: true })
try {
const res = await app.request({
url: '/api/miniprogram/ckb/lead',
method: 'POST',
data: {
userId,
phone,
name: (app.globalData.userInfo?.nickname || '').trim() || undefined
}
})
wx.hideLoading()
this.setData({ showLeadModal: false, leadPhone: '' })
if (res && res.success) {
if (leadKey) wx.setStorageSync(leadKey, true)
wx.showToast({ title: res.message || '提交成功,卡若会尽快联系您', icon: 'success' })
} else {
wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
}
} catch (e) {
wx.hideLoading()
wx.showToast({ title: e.message || '提交失败', icon: 'none' })
}
},
goToSuperList() {
wx.switchTab({ url: '/pages/match/match' })
},
@@ -316,8 +427,22 @@ Page({
if (candidates.length === 0) {
candidates = chapters.filter(exclude)
}
// 解析「第X场」用于倒序最新场次大放在最上方
const sessionNum = (c) => {
const title = c.section_title || c.sectionTitle || c.title || c.chapterTitle || ''
const m = title.match(/第\s*(\d+)\s*场/) || title.match(/第(\d+)场/)
if (m) return parseInt(m[1], 10)
const id = c.id != null ? String(c.id) : ''
if (/^\d+$/.test(id)) return parseInt(id, 10)
return 0
}
const latest = candidates
.sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
.sort((a, b) => {
const na = sessionNum(a)
const nb = sessionNum(b)
if (na !== nb) return nb - na // 场次倒序:最新在上
return new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0)
})
.slice(0, 10)
.map(c => {
const d = new Date(c.updatedAt || c.updated_at || Date.now())

View File

@@ -17,7 +17,7 @@
</view>
</view>
<view class="header-right">
<view class="contact-btn" bindtap="goToAbout">
<view class="contact-btn" bindtap="onLinkKaruo">
<image class="contact-avatar" src="/assets/images/author-avatar.png" mode="aspectFill"/>
<text class="contact-text">点击链接卡若</text>
</view>
@@ -186,4 +186,17 @@
<!-- 底部留白 -->
<view class="bottom-space"></view>
<!-- 链接卡若 - 留资弹窗(未填手机/微信号时) -->
<view class="lead-mask" wx:if="{{showLeadModal}}" catchtap="closeLeadModal">
<view class="lead-box" catchtap="">
<text class="lead-title">留下联系方式</text>
<text class="lead-desc">方便卡若与您联系</text>
<input class="lead-input" placeholder="请输入手机号" type="number" maxlength="11" value="{{leadPhone}}" bindinput="onLeadPhoneInput"/>
<view class="lead-actions">
<button class="lead-btn lead-btn-cancel" bindtap="closeLeadModal">取消</button>
<button class="lead-btn lead-btn-submit" bindtap="submitLead">提交</button>
</view>
</view>
</view>
</view>

View File

@@ -842,3 +842,73 @@
.bottom-space {
height: 40rpx;
}
/* ===== 链接卡若 - 留资弹窗 ===== */
.lead-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 48rpx;
box-sizing: border-box;
}
.lead-box {
width: 100%;
max-width: 560rpx;
background: #1C1C1E;
border-radius: 24rpx;
padding: 48rpx 40rpx;
border: 2rpx solid rgba(56, 189, 172, 0.3);
}
.lead-title {
display: block;
font-size: 34rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 12rpx;
}
.lead-desc {
display: block;
font-size: 26rpx;
color: #A0AEC0;
margin-bottom: 32rpx;
}
.lead-input {
width: 100%;
height: 88rpx;
background: #0a1628;
border: 2rpx solid rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
padding: 0 24rpx;
box-sizing: border-box;
font-size: 30rpx;
color: #ffffff;
margin-bottom: 32rpx;
}
.lead-actions {
display: flex;
gap: 24rpx;
}
.lead-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 500;
border: none;
}
.lead-btn-cancel {
background: rgba(255, 255, 255, 0.1);
color: #A0AEC0;
}
.lead-btn-submit {
background: #38bdac;
color: #ffffff;
}

View File

@@ -14,7 +14,7 @@ const app = getApp()
let MATCH_TYPES = [
{ id: 'partner', label: '找伙伴', matchLabel: '找伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false },
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true, requirePurchase: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '导师顾问', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true }
]
@@ -108,8 +108,15 @@ Page({
})
if (res.success && res.data) {
// 更新全局配置
MATCH_TYPES = res.data.matchTypes || MATCH_TYPES
// 更新全局配置,导师顾问类型强制显示「导师顾问」
let types = res.data.matchTypes || MATCH_TYPES
types = types.map(t => {
if (t.id === 'mentor') {
return { ...t, label: '导师顾问', matchLabel: '导师顾问' }
}
return t
})
MATCH_TYPES = types
FREE_MATCH_LIMIT = res.data.freeMatchLimit || FREE_MATCH_LIMIT
const matchPrice = res.data.matchPrice || 1
@@ -459,7 +466,7 @@ Page({
// 生成模拟匹配数据
generateMockMatch() {
const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者']
const nicknames = ['创业先锋', '资源整合者', '私域专家', '导师顾问', '连续创业者']
const concepts = [
'专注私域流量运营5年帮助100+品牌实现从0到1的增长。',
'连续创业者,擅长商业模式设计和资源整合。',

View File

@@ -1,7 +1,7 @@
/**
* Soul创业派对 - 超级个体/会员详情页
* 接口:优先 /api/miniprogram/vip/members?id=xxVIP回退 /api/miniprogram/users?id=xx任意用户
* 字段映射name/vipName, avatar/vipAvatar, contact/vipContact/phone, wechatId, project/vipProject/projectIntro,
* 头像/昵称统一用用户资料nickname/avatar优先随「我的」修改实时生效
* mbti, region, industry, position, businessScale, skills,
* storyBestMonth→bestMonth, storyAchievement→achievement, storyTurning→turningPoint,
* helpOffer→canHelp, helpNeed→needHelp
@@ -32,8 +32,8 @@ Page({
const u = Array.isArray(dbRes.data) ? dbRes.data[0] : dbRes.data
if (u) {
this.setData({ member: this.enrichAndFormat({
id: u.id, name: u.vipName || u.vip_name || u.nickname || '创业者',
avatar: u.vipAvatar || u.vip_avatar || u.avatar || '', isVip: !!(u.is_vip),
id: u.id, name: u.nickname || u.vipName || u.vip_name || '创业者',
avatar: u.avatar || u.vipAvatar || u.vip_avatar || '', isVip: !!(u.is_vip),
contactRaw: u.vipContact || u.vip_contact || u.phone || '',
wechatId: u.wechatId || u.wechat_id,
project: u.vipProject || u.vip_project || u.projectIntro || u.project_intro || '',
@@ -63,7 +63,7 @@ Page({
const e = (v) => this._emptyIfPlaceholder(v)
const merged = {
id: raw.id,
name: raw.name || raw.vipName || raw.vip_name || raw.nickname || '创业者',
name: raw.nickname || raw.name || raw.vipName || raw.vip_name || '创业者',
avatar: raw.avatar || raw.vipAvatar || raw.vip_avatar || '',
isVip: !!(raw.isVip || raw.is_vip),
mbti: e(raw.mbti),
@@ -85,20 +85,84 @@ Page({
const contact = merged.contactRaw || ''
const wechat = merged.wechatRaw || ''
const isMatched = (app.globalData.matchedUsers || []).includes(merged.id)
const unlockData = this._getUnlockData(merged.id)
merged.contactDisplay = contact ? (contact.slice(0, 3) + '****' + (contact.length > 7 ? contact.slice(-2) : '')) : ''
merged.contactUnlocked = isMatched
merged.contactUnlocked = isMatched || unlockData.contact
merged.contactFull = contact
merged.wechatDisplay = wechat ? (wechat.slice(0, 4) + '****' + (wechat.length > 8 ? wechat.slice(-3) : '')) : ''
merged.wechatUnlocked = isMatched
merged.wechatUnlocked = isMatched || unlockData.wechat
merged.wechatFull = wechat
return merged
},
unlockContact() {
_getUnlockData(memberId) {
const userId = app.globalData.userInfo?.id
if (!userId || !memberId) return { contact: false, wechat: false }
try {
const raw = wx.getStorageSync('member_unlocks_' + userId)
if (Array.isArray(raw) && raw.includes(memberId)) {
return { contact: true, wechat: true }
}
const data = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {}
const member = data[memberId]
return {
contact: !!(member && member.contact),
wechat: !!(member && member.wechat)
}
} catch (e) { return { contact: false, wechat: false } }
},
_addUnlock(memberId, field) {
const userId = app.globalData.userInfo?.id
if (!userId || !memberId || !field) return
let obj = wx.getStorageSync('member_unlocks_' + userId)
if (Array.isArray(obj)) {
obj = obj.reduce((o, id) => { o[id] = { contact: true, wechat: true }; return o }, {})
}
obj = obj && typeof obj === 'object' ? obj : {}
if (!obj[memberId]) obj[memberId] = {}
obj[memberId][field] = true
wx.setStorageSync('member_unlocks_' + userId, obj)
},
_hasUsedFreeForMember(memberId) {
const d = this._getUnlockData(memberId)
return d.contact || d.wechat
},
unlockField(e) {
const field = e.currentTarget.dataset.field
if (!field) return
const member = this.data.member
if (!member?.id || (field !== 'contact' && field !== 'wechat')) return
const isLoggedIn = app.globalData.isLoggedIn
if (!isLoggedIn) {
wx.showModal({
title: '需要登录',
content: '请先登录后再解锁超级个体联系方式',
confirmText: '去登录',
cancelText: '取消',
success: (res) => { if (res.confirm) wx.switchTab({ url: '/pages/my/my' }) }
})
return
}
const d = this._getUnlockData(member.id)
if (d[field]) return
const isVip = app.globalData.hasFullBook
const usedFree = this._hasUsedFreeForMember(member.id)
if (isVip || !usedFree) {
this._addUnlock(member.id, field)
const m = this.enrichAndFormat(member)
this.setData({ member: m })
wx.showToast({ title: field === 'contact' ? '已解锁联系方式' : '已解锁微信号', icon: 'success' })
return
}
wx.showModal({
title: '解锁完整联系方式', content: '成为VIP会员并完成匹配后即可查看完整联系方式',
confirmText: '去匹配', cancelText: '知道了',
success: (res) => { if (res.confirm) wx.switchTab({ url: '/pages/match/match' }) }
title: '解锁' + (field === 'contact' ? '联系方式' : '微信号'),
content: '您的免费解锁次数已用完开通VIP会员¥1980/年)可无限解锁',
confirmText: '去开通',
cancelText: '取消',
success: (res) => { if (res.confirm) wx.navigateTo({ url: '/pages/vip/vip' }) }
})
},

View File

@@ -57,8 +57,8 @@
<view class="field" wx:if="{{member.contactRaw || member.contactDisplay}}">
<text class="f-key">联系方式</text>
<view class="f-row">
<text class="f-val mono">{{member.contactDisplay || member.contactRaw}}</text>
<view class="icon-copy icon-eye-off" wx:if="{{member.contactRaw && !member.contactUnlocked}}" bindtap="unlockContact">
<text class="f-val mono">{{member.contactUnlocked ? member.contactFull : (member.contactDisplay || member.contactRaw)}}</text>
<view class="icon-copy icon-eye-off" wx:if="{{member.contactRaw && !member.contactUnlocked}}" bindtap="unlockField" data-field="contact">
<image class="icon-img" src="/assets/icons/eye-off.svg" mode="aspectFit"/>
</view>
<view class="icon-copy" wx:elif="{{member.contactRaw && member.contactUnlocked}}" bindtap="copyContact">📋</view>
@@ -67,8 +67,8 @@
<view class="field" wx:if="{{member.wechatRaw || member.wechatDisplay}}">
<text class="f-key">微信号</text>
<view class="f-row">
<text class="f-val mono">{{member.wechatDisplay || member.wechatRaw}}</text>
<view class="icon-copy icon-eye-off" wx:if="{{member.wechatRaw && !member.wechatUnlocked}}" bindtap="unlockContact">
<text class="f-val mono">{{member.wechatUnlocked ? member.wechatFull : (member.wechatDisplay || member.wechatRaw)}}</text>
<view class="icon-copy icon-eye-off" wx:if="{{member.wechatRaw && !member.wechatUnlocked}}" bindtap="unlockField" data-field="wechat">
<image class="icon-img" src="/assets/icons/eye-off.svg" mode="aspectFit"/>
</view>
<view class="icon-copy" wx:elif="{{member.wechatRaw && member.wechatUnlocked}}" bindtap="copyWechat">📋</view>

View File

@@ -621,7 +621,7 @@ Page({
wx.switchTab({ url: '/pages/match/match' })
},
// 跳转到推广中心
// 跳转到推广中心(需登录)
goToReferral() {
if (!this.data.isLoggedIn) {
this.showLogin()
@@ -630,7 +630,7 @@ Page({
wx.navigateTo({ url: '/pages/referral/referral' })
},
// 跳转到找伙伴页面
// 跳转到找伙伴
goToMatch() {
wx.switchTab({ url: '/pages/match/match' })
},
@@ -650,14 +650,20 @@ Page({
})
},
// VIP状态查询
// VIP状态查询hasFullBook 优先,兼容模拟支付等本地已置 VIP 的情况)
async loadVipStatus() {
if (app.globalData.hasFullBook) {
this.setData({ isVip: true, vipExpireDate: this.data.vipExpireDate || '' })
}
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true })
if (res?.success) {
this.setData({ isVip: res.data?.isVip, vipExpireDate: res.data?.expireDate || '' })
this.setData({
isVip: res.data?.isVip || app.globalData.hasFullBook,
vipExpireDate: res.data?.expireDate || this.data.vipExpireDate || ''
})
}
} catch (e) { console.log('[My] VIP查询失败', e) }
},

View File

@@ -1,11 +1,11 @@
<!-- 我的页 - 设计稿 1:1 还原 -->
<view class="page">
<!-- 顶部导航:标题 + 右侧设置齿轮 -->
<!-- 顶部导航:左侧资料编辑 + 标题 -->
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<text class="nav-title">我的</text>
<view class="nav-settings" bindtap="handleMenuTap" data-id="settings">
<image class="nav-settings-icon" src="/assets/icons/settings-gray.svg" mode="aspectFit"/>
<view class="nav-settings" bindtap="goToProfileEdit">
<image class="nav-settings-icon" src="/assets/icons/edit-gray.svg" mode="aspectFit"/>
</view>
<text class="nav-title">我的</text>
</view>
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
@@ -38,22 +38,22 @@
</view>
<view class="vip-tags">
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">会员</text>
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">匹配</text>
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToMatch">匹配</text>
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">排行</text>
</view>
<text class="user-wechat" bindtap="copyUserId">微信号: {{userWechat || userIdShort || '--'}}</text>
</view>
</view>
<view class="profile-stats-row">
<view class="profile-stat">
<view class="profile-stat" bindtap="goToChapters">
<text class="profile-stat-val">{{readCount}}</text>
<text class="profile-stat-label">已读章节</text>
</view>
<view class="profile-stat">
<view class="profile-stat" bindtap="goToReferral">
<text class="profile-stat-val">{{referralCount}}</text>
<text class="profile-stat-label">推荐好友</text>
</view>
<view class="profile-stat">
<view class="profile-stat" bindtap="goToReferral">
<text class="profile-stat-val">{{earnings === '-' ? '--' : earnings}}</text>
<text class="profile-stat-label">我的收益</text>
</view>
@@ -70,17 +70,17 @@
<text class="card-title">阅读统计</text>
</view>
<view class="stats-grid">
<view class="stat-box">
<view class="stat-box" bindtap="goToChapters">
<image class="stat-icon-img" src="/assets/icons/book-open-teal.svg" mode="aspectFit"/>
<text class="stat-num">{{readCount}}</text>
<text class="stat-label">已读章节</text>
</view>
<view class="stat-box">
<view class="stat-box" bindtap="goToChapters">
<image class="stat-icon-img" src="/assets/icons/clock-teal.svg" mode="aspectFit"/>
<text class="stat-num">{{totalReadTime}}</text>
<text class="stat-label">阅读分钟</text>
</view>
<view class="stat-box">
<view class="stat-box" bindtap="goToMatch">
<image class="stat-icon-img" src="/assets/icons/users-teal.svg" mode="aspectFit"/>
<text class="stat-num">{{matchHistory}}</text>
<text class="stat-label">匹配伙伴</text>
@@ -208,7 +208,9 @@
<text class="modal-title">修改昵称</text>
</view>
<view class="nickname-input-wrap">
<input class="nickname-input" type="nickname" value="{{editingNickname}}" placeholder="点击输入昵称" placeholder-class="nickname-placeholder" bindchange="onNicknameChange" bindinput="onNicknameInput" maxlength="20"/>
<view class="nickname-input-inner">
<input class="nickname-input" type="nickname" value="{{editingNickname}}" placeholder="点击输入昵称" placeholder-class="nickname-placeholder" bindchange="onNicknameChange" bindinput="onNicknameInput" maxlength="20"/>
</view>
<text class="input-tip">微信用户可点击自动填充昵称</text>
</view>
<view class="modal-actions">

View File

@@ -9,7 +9,7 @@
padding-bottom: calc(220rpx + env(safe-area-inset-bottom, 0px));
}
/* ===== 导航栏(避让右上角系统胶囊) ===== */
/* ===== 导航栏(左侧设置 + 标题居中,右侧避让胶囊) ===== */
.nav-bar {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
background: rgba(18,18,18,0.9); backdrop-filter: blur(8rpx);
@@ -18,8 +18,8 @@
padding: 0 120rpx 0 32rpx; /* 右侧避让胶囊 */
border-bottom: 1rpx solid rgba(255,255,255,0.05);
}
.nav-title { font-size: 40rpx; font-weight: bold; color: #4FD1C5; flex: 1; }
.nav-settings { width: 64rpx; height: 64rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
.nav-settings { width: 64rpx; height: 64rpx; flex-shrink: 0; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
.nav-title { font-size: 40rpx; font-weight: bold; color: #4FD1C5; flex: 1; text-align: center; }
.nav-settings-icon { width: 44rpx; height: 44rpx; opacity: 0.7; }
.nav-placeholder { width: 100%; }
@@ -217,7 +217,11 @@
.nickname-modal .modal-icon { font-size: 48rpx; }
.nickname-modal .modal-title { font-size: 36rpx; font-weight: bold; }
.nickname-input-wrap { margin-bottom: 32rpx; }
.nickname-input { width: 100%; padding: 24rpx; background: #262626; border-radius: 20rpx; font-size: 28rpx; color: #fff; box-sizing: border-box; }
.nickname-input-inner {
padding: 24rpx 32rpx; background: #262626; border-radius: 20rpx;
}
.nickname-input { width: 100%; font-size: 28rpx; color: #fff; background: transparent; box-sizing: border-box; }
.nickname-placeholder { color: #9CA3AF; }
.input-tip { display: block; font-size: 22rpx; color: #9CA3AF; margin-top: 12rpx; }
.modal-actions { display: flex; gap: 24rpx; }
.modal-btn { flex: 1; height: 80rpx; line-height: 80rpx; text-align: center; border-radius: 20rpx; font-size: 28rpx; }

View File

@@ -18,8 +18,10 @@
<!-- 头像 -->
<view class="avatar-section">
<view class="avatar-wrap" bindtap="chooseAvatar">
<image wx:if="{{avatar}}" class="avatar-img" src="{{avatar}}" mode="aspectFill"/>
<view wx:else class="avatar-placeholder">{{nickname ? nickname[0] : '?'}}</view>
<view class="avatar-inner">
<image wx:if="{{avatar}}" class="avatar-img" src="{{avatar}}" mode="aspectFill"/>
<view wx:else class="avatar-placeholder">{{nickname ? nickname[0] : '?'}}</view>
</view>
<view class="avatar-camera">📷</view>
</view>
<text class="avatar-change">更换头像</text>

View File

@@ -31,18 +31,22 @@
.avatar-section { display: flex; flex-direction: column; align-items: center; margin-bottom: 48rpx; }
.avatar-wrap {
position: relative; width: 192rpx; height: 192rpx; border-radius: 50%; overflow: hidden;
position: relative; width: 192rpx; height: 192rpx; border-radius: 50%;
border: 4rpx solid #5EEAD4; box-shadow: 0 0 30rpx rgba(94,234,212,0.3);
}
.avatar-inner {
width: 100%; height: 100%; border-radius: 50%; overflow: hidden;
}
.avatar-img { width: 100%; height: 100%; display: block; }
.avatar-placeholder {
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
font-size: 72rpx; font-weight: bold; color: #5EEAD4; background: rgba(94,234,212,0.2);
}
.avatar-camera {
position: absolute; bottom: 0; right: 0;
position: absolute; bottom: -8rpx; right: -8rpx;
width: 56rpx; height: 56rpx; background: #5EEAD4; color: #000;
border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 28rpx;
border: 4rpx solid #050B14; box-sizing: border-box;
}
.avatar-change { font-size: 28rpx; color: #5EEAD4; font-weight: 500; margin-top: 16rpx; }

View File

@@ -81,6 +81,8 @@ Page({
if (app.globalData.initialSectionMid) delete app.globalData.initialSectionMid
if (app.globalData.initialSectionId) delete app.globalData.initialSectionId
console.log("页面:",mid);
// mid 有值但无 id 时,从 bookData 或 API 解析 id
if (mid && !id) {
const bookData = app.globalData.bookData || []
@@ -126,7 +128,7 @@ Page({
})
// 统一:先拉章节数据,用 isFree/price===0 判断免费
const chapterRes = await app.request({ url: `/api/miniprogram/book/chapter/${id}`, silent: true })
const chapterRes = await app.request({ url: this._getChapterUrl({ id, mid }), silent: true })
const accessState = await accessManager.determineAccessState(id, chapterRes)
const canAccess = accessManager.canAccessFullContent(accessState)
@@ -196,7 +198,7 @@ Page({
const sectionPrice = this.data.sectionPrice ?? 1
let res = prefetchedChapter
if (!res || !res.content) {
res = await app.request({ url: `/api/miniprogram/book/chapter/${id}`, silent: true })
res = await app.request({ url: this._getChapterUrl({ id }), silent: true })
}
const section = {
id: res.id || id,
@@ -295,6 +297,17 @@ Page({
return titles[id] || `章节 ${id}`
},
// 根据 id/mid 构造章节接口路径(优先使用 mid
_getChapterUrl(params = {}) {
const { id, mid } = params
const finalMid = (mid !== undefined && mid !== null) ? mid : this.data.sectionMid
if (finalMid) {
return `/api/miniprogram/book/chapter/by-mid/${finalMid}`
}
const finalId = id || this.data.sectionId
return `/api/miniprogram/book/chapter/${finalId}`
},
// 加载内容 - 三级降级方案API → 本地缓存 → 备用API
async loadContent(id) {
const cacheKey = `chapter_${id}`
@@ -344,7 +357,7 @@ Page({
reject(new Error('请求超时'))
}, timeout)
app.request(`/api/miniprogram/book/chapter/${id}`)
app.request(this._getChapterUrl({ id }))
.then(res => {
clearTimeout(timer)
resolve(res)
@@ -360,14 +373,24 @@ Page({
setChapterContent(res) {
const lines = res.content.split('\n').filter(line => line.trim())
const previewCount = Math.ceil(lines.length * 0.2)
const sectionPrice = this.data.sectionPrice ?? 1
const sectionTitle = (res.sectionTitle || res.title || '').trim()
this.setData({
// 文章详情标题:只使用后端提供的 sectionTitle不再拼接其他本地标题信息
section: {
id: res.id || this.data.sectionId,
title: sectionTitle,
isFree: res.isFree === true || (res.price !== undefined && res.price === 0),
price: res.price ?? sectionPrice
},
content: res.content,
previewContent: lines.slice(0, previewCount).join('\n'),
contentParagraphs: lines,
previewParagraphs: lines.slice(0, previewCount),
partTitle: res.partTitle || '',
chapterTitle: res.chapterTitle || ''
// 导航栏、分享等使用的文章标题,同样统一为 sectionTitle
chapterTitle: sectionTitle
})
},
@@ -504,11 +527,16 @@ Page({
}
},
// 分享到朋友圈:带文章标题,过长时截断(朋友圈卡片标题显示有限)
onShareTimeline() {
const { section, sectionId, sectionMid } = this.data
const { section, sectionId, sectionMid, chapterTitle } = this.data
const ref = app.getMyReferralCode()
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
return { title: section?.title ? `📚 ${section.title}` : 'Soul创业派对 - 真实商业故事', query: ref ? `${q}&ref=${ref}` : q }
const articleTitle = (section?.title || chapterTitle || '').trim()
const title = articleTitle
? (articleTitle.length > 28 ? articleTitle.slice(0, 28) + '...' : articleTitle)
: 'Soul创业派对 - 真实商业故事'
return { title, query: ref ? `${q}&ref=${ref}` : q }
},
// 显示登录弹窗(每次打开协议未勾选,符合审核要求)
@@ -588,7 +616,7 @@ Page({
// 2. 重新拉取章节数据,用 isFree/price 判断免费
const chapterRes = await app.request({
url: `/api/miniprogram/book/chapter/${this.data.sectionId}`,
url: this._getChapterUrl({}),
silent: true
})
const newAccessState = await accessManager.determineAccessState(
@@ -859,7 +887,7 @@ Page({
// 3. 重新拉取章节并判断权限(应为 unlocked_purchased
const chapterRes = await app.request({
url: `/api/miniprogram/book/chapter/${this.data.sectionId}`,
url: this._getChapterUrl({}),
silent: true
})
let newAccessState = await accessManager.determineAccessState(
@@ -1201,7 +1229,7 @@ Page({
// 重新拉取章节,用 isFree/price 判断免费
const chapterRes = await app.request({
url: `/api/miniprogram/book/chapter/${this.data.sectionId}`,
url: this._getChapterUrl({}),
silent: true
})
const newAccessState = await accessManager.determineAccessState(

View File

@@ -13,8 +13,7 @@
<text class="back-arrow">←</text>
</view>
<view class="nav-info">
<text class="nav-part" wx:if="{{partTitle}}">{{partTitle}}</text>
<text class="nav-chapter" wx:if="{{chapterTitle}}">{{chapterTitle}}</text>
<text class="nav-chapter" wx:if="{{section.title || chapterTitle}}">{{section.title || chapterTitle}}</text>
</view>
<view class="nav-right-placeholder"></view>
</view>

View File

@@ -64,67 +64,6 @@ Page({
}
},
// 一键获取收货地址
getAddress() {
wx.chooseAddress({
success: (res) => {
console.log('[Settings] 获取地址成功:', res)
const fullAddress = `${res.provinceName || ''}${res.cityName || ''}${res.countyName || ''}${res.detailInfo || ''}`
if (fullAddress.trim()) {
wx.setStorageSync('user_address', fullAddress)
this.setData({ address: fullAddress })
// 更新用户信息
if (app.globalData.userInfo) {
app.globalData.userInfo.address = fullAddress
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
// 同步到服务器
this.syncAddressToServer(fullAddress)
wx.showToast({ title: '地址已获取', icon: 'success' })
}
},
fail: (e) => {
console.log('[Settings] 获取地址失败:', e)
if (e.errMsg?.includes('cancel')) {
// 用户取消,不提示
return
}
if (e.errMsg?.includes('auth deny') || e.errMsg?.includes('authorize')) {
wx.showModal({
title: '需要授权',
content: '请在设置中允许获取收货地址',
confirmText: '去设置',
success: (res) => {
if (res.confirm) wx.openSetting()
}
})
} else {
wx.showToast({ title: '获取失败,请重试', icon: 'none' })
}
}
})
},
// 同步地址到服务器
async syncAddressToServer(address) {
try {
const userId = app.globalData.userInfo?.id
if (!userId) return
await app.request('/api/miniprogram/user/update', {
method: 'POST',
data: { userId, address }
})
console.log('[Settings] 地址已同步到服务器')
} catch (e) {
console.log('[Settings] 同步地址失败:', e)
}
},
// 切换自动提现
async toggleAutoWithdraw(e) {
const enabled = e.detail.value

View File

@@ -1,3 +1,4 @@
import accessManager from '../../utils/chapterAccessManager'
const app = getApp()
Page({
@@ -88,9 +89,9 @@ Page({
if (payRes?.success && payRes.data?.payParams) {
wx.requestPayment({
...payRes.data.payParams,
success: () => {
success: async () => {
wx.showToast({ title: 'VIP开通成功', icon: 'success' })
this.loadVipInfo()
await this._onVipPaymentSuccess()
},
fail: () => wx.showToast({ title: '支付取消', icon: 'none' })
})
@@ -103,6 +104,28 @@ Page({
} finally { this.setData({ purchasing: false }) }
},
async _onVipPaymentSuccess() {
wx.showLoading({ title: '同步权益中...', mask: true })
try {
await new Promise(r => setTimeout(r, 1500))
await accessManager.refreshUserPurchaseStatus()
await this.loadVipInfo()
app.globalData.hasFullBook = true
const userInfo = app.globalData.userInfo || {}
userInfo.hasFullBook = true
app.globalData.userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
const pages = getCurrentPages()
pages.forEach(p => {
if (typeof p.initUserStatus === 'function') p.initUserStatus()
else if (typeof p.updateUserStatus === 'function') p.updateUserStatus()
})
} catch (e) {
console.error('[VIP] 支付后同步失败:', e)
}
wx.hideLoading()
},
goBack() { wx.navigateBack() },
onShareAppMessage() {

View File

@@ -24,81 +24,11 @@
"miniprogram": {
"list": [
{
"name": "pages/vip/vip",
"pathName": "pages/vip/vip",
"query": "",
"name": "pages/read/read",
"pathName": "pages/read/read",
"query": "mid=1",
"scene": null,
"launchMode": "default"
},
{
"name": "pages/member-detail/member-detail",
"pathName": "pages/member-detail/member-detail",
"query": "id=ogpTW5QBRQNUOm4-zvg8it2XySrI",
"launchMode": "default",
"scene": null
},
{
"name": "pages/vip/vip",
"pathName": "pages/vip/vip",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/member-detail/member-detail",
"pathName": "pages/member-detail/member-detail",
"query": "id=ogpTW5cteGWqwOsRzh_CCIdKWGSE",
"launchMode": "default",
"scene": null
},
{
"name": "pages/about/about",
"pathName": "pages/about/about",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/about/about",
"pathName": "pages/about/about",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/vip/vip",
"pathName": "pages/vip/vip",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/profile-edit/profile-edit",
"pathName": "pages/profile-edit/profile-edit",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "个人资料",
"pathName": "pages/profile-show/profile-show",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/mentors/mentors",
"pathName": "pages/mentors/mentors",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的",
"pathName": "pages/my/my",
"query": "",
"launchMode": "default",
"scene": null
}
]
}

470
soul-admin/dist/assets/index-CAf6YeOV.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>管理后台 - Soul创业派对</title>
<script type="module" crossorigin src="/assets/index-CKRjkwdJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D0nEGm4H.css">
<script type="module" crossorigin src="/assets/index-CAf6YeOV.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DP984f3K.css">
</head>
<body>
<div id="root"></div>

View File

@@ -20,20 +20,20 @@ import {
import { get, post } from '@/api/client'
import { clearAdminToken } from '@/api/auth'
// 主菜单(折叠后可见 4-5 项)
// 主菜单(核心运营 3 项)
const primaryMenuItems = [
{ icon: LayoutDashboard, label: '数据概览', href: '/dashboard' },
{ icon: BookOpen, label: '内容管理', href: '/content' },
{ icon: Users, label: '用户管理', href: '/users' },
]
// 折叠区「更多」(字典类 + 业务)
const moreMenuItems = [
{ icon: Crown, label: 'VIP 角色', href: '/vip-roles' },
{ icon: User, label: '作者详情', href: '/author-settings' },
]
// 折叠区「更多」
const moreMenuItems = [
{ icon: ShieldCheck, label: '管理员', href: '/admin-users' },
{ icon: GraduationCap, label: '导师管理', href: '/mentors' },
{ icon: Calendar, label: '导师预约', href: '/mentor-consultations' },
{ icon: Wallet, label: '交易中心', href: '/distribution' },
{ icon: Wallet, label: '推广中心', href: '/distribution' },
{ icon: GitMerge, label: '匹配记录', href: '/match-records' },
{ icon: CreditCard, label: '推广设置', href: '/referral-settings' },
]

View File

@@ -2,8 +2,8 @@
* 章节树 - 仿照 catalog 设计,支持篇、章、节拖拽排序
* 整行可拖拽;节和章可跨篇
*/
import { useCallback } from 'react'
import { ChevronRight, ChevronDown, BookOpen, Eye, Edit3, Trash2 } from 'lucide-react'
import { useCallback, useState } from 'react'
import { ChevronRight, ChevronDown, BookOpen, Eye, Edit3, Trash2, GripVertical, Plus } from 'lucide-react'
import { Button } from '@/components/ui/button'
const PART_LABELS = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
@@ -51,6 +51,9 @@ interface ChapterTreeProps {
onReorder: (items: { id: string; partId: string; partTitle: string; chapterId: string; chapterTitle: string }[]) => Promise<void>
onReadSection: (s: SectionItem) => void
onDeleteSection: (s: SectionItem) => void
onAddSectionInPart?: (part: PartItem) => void
onEditPart?: (part: PartItem) => void
onDeletePart?: (part: PartItem) => void
}
export function ChapterTree({
@@ -60,7 +63,16 @@ export function ChapterTree({
onReorder,
onReadSection,
onDeleteSection,
onAddSectionInPart,
onEditPart,
onDeletePart,
}: ChapterTreeProps) {
const [draggingItem, setDraggingItem] = useState<{ type: DragType; id: string } | null>(null)
const [dragOverTarget, setDragOverTarget] = useState<{ type: DragType; id: string } | null>(null)
const isDragging = (type: DragType, id: string) => draggingItem?.type === type && draggingItem?.id === id
const isDragOver = (type: DragType, id: string) => dragOverTarget?.type === type && dragOverTarget?.id === id
const buildSectionsList = useCallback(
(): { id: string; partId: string; partTitle: string; chapterId: string; chapterTitle: string }[] => {
const list: { id: string; partId: string; partTitle: string; chapterId: string; chapterTitle: string }[] = []
@@ -223,13 +235,17 @@ export function ChapterTree({
e.preventDefault()
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
setDragOverTarget({ type, id })
},
onDragOver: (e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
setDragOverTarget({ type, id })
},
onDragLeave: () => setDragOverTarget(null),
onDrop: (e: React.DragEvent) => {
setDragOverTarget(null)
const from = parseDragData(e.dataTransfer.getData('text/plain'))
if (!from) return
if (type === 'section' && from.type === 'section' && from.id === id) return
@@ -395,10 +411,11 @@ export function ChapterTree({
}
// 普通篇:卡片 + 章/节
const partDragOver = isDragOver('part', part.id)
return (
<div
key={part.id}
className="rounded-xl border border-gray-700/50 bg-[#1C1C1E] overflow-hidden"
className={`rounded-xl border bg-[#1C1C1E] overflow-hidden transition-all duration-200 ${partDragOver ? 'border-[#38bdac] ring-2 ring-[#38bdac]/40 bg-[#38bdac]/5' : 'border-gray-700/50'}`}
{...droppableHandlers('part', part.id, {
partId: part.id,
partTitle: part.title,
@@ -412,11 +429,14 @@ export function ChapterTree({
e.stopPropagation()
e.dataTransfer.setData('text/plain', 'part:' + part.id)
e.dataTransfer.effectAllowed = 'move'
setDraggingItem({ type: 'part', id: part.id })
}}
className="flex items-center justify-between p-4 cursor-grab active:cursor-grabbing select-none hover:bg-[#162840]/50 transition-colors"
onDragEnd={() => { setDraggingItem(null); setDragOverTarget(null) }}
className={`flex items-center justify-between p-4 cursor-grab active:cursor-grabbing select-none transition-all duration-200 ${isDragging('part', part.id) ? 'opacity-60 scale-[0.98] ring-2 ring-[#38bdac] rounded-xl shadow-xl shadow-[#38bdac]/20' : 'hover:bg-[#162840]/50'}`}
onClick={() => onTogglePart(part.id)}
>
<div className="flex items-center gap-3 min-w-0">
<GripVertical className="w-5 h-5 text-gray-500 shrink-0 opacity-60" />
<div className="w-10 h-10 rounded-xl bg-[#38bdac] flex items-center justify-center text-white font-bold shadow-lg shadow-[#38bdac]/30 shrink-0">
{partLabel(partIndex)}
</div>
@@ -425,7 +445,26 @@ export function ChapterTree({
<p className="text-xs text-gray-500 mt-0.5"> {sectionCount} </p>
</div>
</div>
<div className="flex items-center gap-2 shrink-0">
<div
className="flex items-center gap-2 shrink-0"
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
>
{onAddSectionInPart && (
<Button draggable={false} variant="ghost" size="sm" onClick={() => onAddSectionInPart(part)} className="text-gray-500 hover:text-[#38bdac] h-7 px-2" title="在本篇下新增章节">
<Plus className="w-3.5 h-3.5" />
</Button>
)}
{onEditPart && (
<Button draggable={false} variant="ghost" size="sm" onClick={() => onEditPart(part)} className="text-gray-500 hover:text-[#38bdac] h-7 px-2" title="编辑篇名">
<Edit3 className="w-3.5 h-3.5" />
</Button>
)}
{onDeletePart && (
<Button draggable={false} variant="ghost" size="sm" onClick={() => onDeletePart(part)} className="text-gray-500 hover:text-red-400 h-7 px-2" title="删除本篇">
<Trash2 className="w-3.5 h-3.5" />
</Button>
)}
<span className="text-xs text-gray-500">{chapterCount}</span>
{isExpanded ? (
<ChevronDown className="w-5 h-5 text-gray-500" />
@@ -437,7 +476,9 @@ export function ChapterTree({
{isExpanded && (
<div className="border-t border-gray-700/50 pl-4 pr-4 pb-4 pt-3 space-y-4">
{part.chapters.map((chapter, chIndex) => (
{part.chapters.map((chapter, chIndex) => {
const chDragOver = isDragOver('chapter', chapter.id)
return (
<div key={chapter.id} className="space-y-2">
<div
draggable
@@ -445,30 +486,39 @@ export function ChapterTree({
e.stopPropagation()
e.dataTransfer.setData('text/plain', 'chapter:' + chapter.id)
e.dataTransfer.effectAllowed = 'move'
setDraggingItem({ type: 'chapter', id: chapter.id })
}}
className="py-2 px-2 rounded cursor-grab active:cursor-grabbing select-none hover:bg-[#162840]/30 -mx-2"
onDragEnd={() => { setDraggingItem(null); setDragOverTarget(null) }}
onDragEnter={(e) => {
e.preventDefault()
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
setDragOverTarget({ type: 'chapter', id: chapter.id })
}}
onDragOver={(e) => {
e.preventDefault()
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
setDragOverTarget({ type: 'chapter', id: chapter.id })
}}
onDragLeave={() => setDragOverTarget(null)}
onDrop={(e) => {
setDragOverTarget(null)
const from = parseDragData(e.dataTransfer.getData('text/plain'))
if (!from) return
const ctx = { partId: part.id, partTitle: part.title, chapterId: chapter.id, chapterTitle: chapter.title }
if (from.type === 'section') handleDrop(e, 'chapter', chapter.id, ctx)
else if (from.type === 'chapter') handleDrop(e, 'chapter', chapter.id, ctx)
}}
className={`py-2 px-2 rounded cursor-grab active:cursor-grabbing select-none -mx-2 transition-all duration-200 flex items-center gap-2 ${chDragOver ? 'bg-[#38bdac]/15 ring-1 ring-[#38bdac]/50' : ''} ${isDragging('chapter', chapter.id) ? 'opacity-60 scale-[0.98] ring-2 ring-[#38bdac]' : 'hover:bg-[#162840]/30'}`}
>
<GripVertical className="w-4 h-4 text-gray-500 shrink-0 opacity-50" />
<p className="text-xs text-gray-500 pb-1">{chIndex + 1} | {chapter.title}</p>
</div>
<div className="space-y-1 pl-2">
{chapter.sections.map((section) => (
{chapter.sections.map((section) => {
const secDragOver = isDragOver('section', section.id)
return (
<div
key={section.id}
draggable
@@ -476,8 +526,10 @@ export function ChapterTree({
e.stopPropagation()
e.dataTransfer.setData('text/plain', 'section:' + section.id)
e.dataTransfer.effectAllowed = 'move'
setDraggingItem({ type: 'section', id: section.id })
}}
className="flex items-center justify-between py-2 px-3 rounded-lg hover:bg-[#162840]/50 group cursor-grab active:cursor-grabbing select-none min-h-[40px]"
onDragEnd={() => { setDraggingItem(null); setDragOverTarget(null) }}
className={`flex items-center justify-between py-2 px-3 rounded-lg group cursor-grab active:cursor-grabbing select-none min-h-[40px] transition-all duration-200 ${secDragOver ? 'bg-[#38bdac]/15 ring-2 ring-[#38bdac]/50' : ''} ${isDragging('section', section.id) ? 'opacity-60 scale-[0.98] ring-2 ring-[#38bdac] shadow-lg' : 'hover:bg-[#162840]/50'}`}
{...droppableHandlers('section', section.id, {
partId: part.id,
partTitle: part.title,
@@ -486,6 +538,7 @@ export function ChapterTree({
})}
>
<div className="flex items-center gap-3 min-w-0 flex-1">
<GripVertical className="w-4 h-4 text-gray-500 shrink-0 opacity-50" />
<div
className={`w-2 h-2 rounded-full shrink-0 ${section.price === 0 || section.isFree ? 'border-2 border-[#38bdac] bg-transparent' : 'bg-gray-500'}`}
/>
@@ -520,10 +573,12 @@ export function ChapterTree({
<ChevronRight className="w-4 h-4 text-gray-500" />
</div>
</div>
))}
)
})}
</div>
</div>
))}
)
})}
</div>
)}
</div>

View File

@@ -33,7 +33,6 @@ import {
X,
RefreshCw,
Link2,
Eye,
Plus,
Image as ImageIcon,
Search,
@@ -154,6 +153,9 @@ export function ContentPage() {
content: '',
})
const [editingPart, setEditingPart] = useState<{ id: string; title: string } | null>(null)
const [isSavingPartTitle, setIsSavingPartTitle] = useState(false)
const tree = buildTree(sectionsList)
const totalSections = sectionsList.length
@@ -342,6 +344,63 @@ export function ContentPage() {
}
}
const handleAddSectionInPart = (part: Part) => {
setNewSection((prev) => ({
...prev,
partId: part.id,
chapterId: part.chapters[0]?.id ?? 'chapter-1',
}))
setShowNewSectionModal(true)
}
const handleEditPartTitle = (part: Part) => {
setEditingPart({ id: part.id, title: part.title })
}
const handleSavePartTitle = async () => {
if (!editingPart?.title?.trim()) return
setIsSavingPartTitle(true)
try {
const items = sectionsList.map((s) => ({
id: s.id,
partId: s.partId || 'part-1',
partTitle: s.partId === editingPart.id ? editingPart.title.trim() : (s.partTitle || ''),
chapterId: s.chapterId || 'chapter-1',
chapterTitle: s.chapterTitle || '',
}))
const res = await put<{ success?: boolean; error?: string }>('/api/db/book', {
action: 'reorder',
items,
})
if (res && (res as { success?: boolean }).success !== false) {
setEditingPart(null)
loadList()
} else {
alert('更新篇名失败: ' + (res && typeof res === 'object' && 'error' in res ? (res as { error?: string }).error : '未知错误'))
}
} catch (e) {
console.error(e)
alert('更新篇名失败')
} finally {
setIsSavingPartTitle(false)
}
}
const handleDeletePart = async (part: Part) => {
const sectionIds = sectionsList.filter((s) => s.partId === part.id).map((s) => s.id)
if (sectionIds.length === 0) return
if (!confirm(`确定要删除「${part.title}」整篇吗?将删除共 ${sectionIds.length} 节内容,此操作不可恢复。`)) return
try {
for (const id of sectionIds) {
await del<{ success?: boolean; error?: string }>(`/api/db/book?id=${encodeURIComponent(id)}`)
}
loadList()
} catch (e) {
console.error(e)
alert('删除失败')
}
}
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) return
@@ -542,6 +601,57 @@ export function ContentPage() {
</DialogContent>
</Dialog>
{/* 编辑篇名弹窗 */}
<Dialog open={!!editingPart} onOpenChange={(open) => !open && setEditingPart(null)}>
<DialogContent className="bg-[#0f2137] border-gray-700 text-white max-w-md" showCloseButton>
<DialogHeader>
<DialogTitle className="text-white flex items-center gap-2">
<Edit3 className="w-5 h-5 text-[#38bdac]" />
</DialogTitle>
</DialogHeader>
{editingPart && (
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={editingPart.title}
onChange={(e) => setEditingPart({ ...editingPart, title: e.target.value })}
placeholder="输入篇名"
/>
</div>
</div>
)}
<DialogFooter>
<Button
variant="outline"
onClick={() => setEditingPart(null)}
className="border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent"
>
</Button>
<Button
onClick={handleSavePartTitle}
disabled={isSavingPartTitle || !editingPart?.title?.trim()}
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
>
{isSavingPartTitle ? (
<>
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
<>
<Save className="w-4 h-4 mr-2" />
</>
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 章节编辑弹窗 */}
<Dialog open={!!editingSection} onOpenChange={() => setEditingSection(null)}>
<DialogContent className="bg-[#0f2137] border-gray-700 text-white max-w-4xl max-h-[90vh] overflow-y-auto" showCloseButton>
@@ -752,6 +862,9 @@ export function ContentPage() {
onReorder={handleReorderTree}
onReadSection={handleReadSection}
onDeleteSection={handleDeleteSection}
onAddSectionInPart={handleAddSectionInPart}
onEditPart={handleEditPartTitle}
onDeletePart={handleDeletePart}
/>
)}
</TabsContent>

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useRef } from 'react'
import { Card, CardContent } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
@@ -18,8 +18,9 @@ import {
DialogTitle,
DialogFooter,
} from '@/components/ui/dialog'
import { Users, Plus, Edit3, Trash2, X, Save } from 'lucide-react'
import { get, post, put, del } from '@/api/client'
import { Users, Plus, Edit3, Trash2, X, Save, Upload } from 'lucide-react'
import { get, post, put, del, apiUrl } from '@/api/client'
import { getAdminToken } from '@/api/auth'
interface Mentor {
id: number
@@ -59,6 +60,40 @@ export function MentorsPage() {
enabled: true,
})
const [saving, setSaving] = useState(false)
const [uploadingAvatar, setUploadingAvatar] = useState(false)
const avatarInputRef = useRef<HTMLInputElement>(null)
const handleAvatarUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) return
setUploadingAvatar(true)
try {
const formData = new FormData()
formData.append('file', file)
formData.append('folder', 'mentors')
const token = getAdminToken()
const headers: HeadersInit = {}
if (token) headers['Authorization'] = `Bearer ${token}`
const res = await fetch(apiUrl('/api/upload'), {
method: 'POST',
body: formData,
credentials: 'include',
headers,
})
const data = await res.json()
if (data?.success && data?.url) {
setForm((f) => ({ ...f, avatar: data.url }))
} else {
alert('上传失败: ' + (data?.error || '未知错误'))
}
} catch (err) {
console.error(err)
alert('上传失败')
} finally {
setUploadingAvatar(false)
if (avatarInputRef.current) avatarInputRef.current.value = ''
}
}
async function load() {
setLoading(true)
@@ -293,13 +328,42 @@ export function MentorsPage() {
</div>
</div>
<div className="space-y-2">
<Label className="text-gray-300"> URL</Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
placeholder="https://..."
value={form.avatar}
onChange={(e) => setForm((f) => ({ ...f, avatar: e.target.value }))}
/>
<Label className="text-gray-300"></Label>
<div className="flex gap-3 items-center">
<Input
className="flex-1 bg-[#0a1628] border-gray-700 text-white"
value={form.avatar}
onChange={(e) => setForm((f) => ({ ...f, avatar: e.target.value }))}
placeholder="点击上传或粘贴图片地址"
/>
<input
ref={avatarInputRef}
type="file"
accept="image/*"
className="hidden"
onChange={handleAvatarUpload}
/>
<Button
type="button"
variant="outline"
size="sm"
className="border-gray-600 text-gray-400 shrink-0"
disabled={uploadingAvatar}
onClick={() => avatarInputRef.current?.click()}
>
<Upload className="w-4 h-4 mr-2" />
{uploadingAvatar ? '上传中...' : '上传'}
</Button>
</div>
{form.avatar && (
<div className="mt-2">
<img
src={form.avatar.startsWith('http') ? form.avatar : apiUrl(form.avatar)}
alt="头像预览"
className="w-20 h-20 rounded-full object-cover border border-gray-600"
/>
</div>
)}
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>

View File

@@ -1 +1 @@
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/auth.ts","./src/api/client.ts","./src/components/modules/user/setvipmodal.tsx","./src/components/modules/user/userdetailmodal.tsx","./src/components/ui/pagination.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/select.tsx","./src/components/ui/slider.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/hooks/usedebounce.ts","./src/layouts/adminlayout.tsx","./src/lib/utils.ts","./src/pages/admin-users/adminuserspage.tsx","./src/pages/api-doc/apidocpage.tsx","./src/pages/author-settings/authorsettingspage.tsx","./src/pages/content/chaptertree.tsx","./src/pages/content/contentpage.tsx","./src/pages/dashboard/dashboardpage.tsx","./src/pages/distribution/distributionpage.tsx","./src/pages/login/loginpage.tsx","./src/pages/match/matchpage.tsx","./src/pages/match-records/matchrecordspage.tsx","./src/pages/mentor-consultations/mentorconsultationspage.tsx","./src/pages/mentors/mentorspage.tsx","./src/pages/not-found/notfoundpage.tsx","./src/pages/orders/orderspage.tsx","./src/pages/payment/paymentpage.tsx","./src/pages/qrcodes/qrcodespage.tsx","./src/pages/referral-settings/referralsettingspage.tsx","./src/pages/settings/settingspage.tsx","./src/pages/site/sitepage.tsx","./src/pages/users/userspage.tsx","./src/pages/vip-roles/viprolespage.tsx","./src/pages/withdrawals/withdrawalspage.tsx"],"errors":true,"version":"5.6.3"}
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/auth.ts","./src/api/client.ts","./src/components/modules/user/setvipmodal.tsx","./src/components/modules/user/userdetailmodal.tsx","./src/components/ui/pagination.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/select.tsx","./src/components/ui/slider.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/hooks/usedebounce.ts","./src/layouts/adminlayout.tsx","./src/lib/utils.ts","./src/pages/admin-users/adminuserspage.tsx","./src/pages/api-doc/apidocpage.tsx","./src/pages/author-settings/authorsettingspage.tsx","./src/pages/content/chaptertree.tsx","./src/pages/content/contentpage.tsx","./src/pages/dashboard/dashboardpage.tsx","./src/pages/distribution/distributionpage.tsx","./src/pages/login/loginpage.tsx","./src/pages/match/matchpage.tsx","./src/pages/match-records/matchrecordspage.tsx","./src/pages/mentor-consultations/mentorconsultationspage.tsx","./src/pages/mentors/mentorspage.tsx","./src/pages/not-found/notfoundpage.tsx","./src/pages/orders/orderspage.tsx","./src/pages/payment/paymentpage.tsx","./src/pages/qrcodes/qrcodespage.tsx","./src/pages/referral-settings/referralsettingspage.tsx","./src/pages/settings/settingspage.tsx","./src/pages/site/sitepage.tsx","./src/pages/users/userspage.tsx","./src/pages/vip-roles/viprolespage.tsx","./src/pages/withdrawals/withdrawalspage.tsx"],"version":"5.6.3"}

View File

@@ -1,291 +0,0 @@
<!DOCTYPE html>
<html class="dark" lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Clean Dark Mode Catalog with NEW Tags</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5",
"background-light": "#f3f4f6",
"background-dark": "#000000",
"card-light": "#ffffff",
"card-dark": "#1C1C1E",
"text-secondary-dark": "#8E8E93",
"accent-dark": "#2C2C2E",
},
fontFamily: {
display: ["Noto Sans SC", "sans-serif"],
},
borderRadius: {
DEFAULT: "12px",
"xl": "16px",
"2xl": "20px",
},
},
},
};
</script>
<style>
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.fab-shadow {
box-shadow: 0 4px 15px rgba(79, 209, 197, 0.4);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark font-display text-gray-900 dark:text-gray-100 min-h-screen pb-24">
<header class="fixed top-0 left-0 right-0 z-50 bg-background-light/95 dark:bg-background-dark/95 backdrop-blur-sm px-4 pt-4 pb-2 flex justify-between items-center border-b border-gray-200 dark:border-gray-800">
<button class="w-9 h-9 rounded-full bg-gray-200 dark:bg-accent-dark flex items-center justify-center text-gray-500 dark:text-gray-400">
<span class="material-icons text-lg">search</span>
</button>
<h1 class="text-lg font-bold text-primary tracking-wide">目录</h1>
<div class="flex gap-3">
<button class="w-9 h-9 rounded-full bg-gray-200 dark:bg-accent-dark flex items-center justify-center text-gray-500 dark:text-gray-400">
<span class="material-icons text-lg">more_horiz</span>
</button>
<button class="w-9 h-9 rounded-full bg-gray-200 dark:bg-accent-dark flex items-center justify-center text-gray-500 dark:text-gray-400">
<span class="material-icons text-lg">radio_button_checked</span>
</button>
</div>
</header>
<main class="mt-20 px-4 space-y-4">
<div class="bg-white dark:bg-card-dark rounded-2xl p-4 flex items-center justify-between shadow-sm border border-gray-100 dark:border-gray-800/30">
<div class="flex items-center gap-4">
<div class="w-12 h-12 bg-primary rounded-xl flex items-center justify-center text-white shadow-lg shadow-primary/20">
<span class="material-icons text-2xl">menu_book</span>
</div>
<div>
<h2 class="font-bold text-base dark:text-white leading-tight mb-1">一场SOUL的创业实验场</h2>
<p class="text-xs text-gray-500 dark:text-text-secondary-dark">来自Soul派对房的真实商业故事</p>
</div>
</div>
<div class="text-center">
<span class="block text-2xl font-bold text-primary">73</span>
<span class="text-xs text-gray-500 dark:text-gray-500">章节</span>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-xl p-4 flex items-center justify-between shadow-sm border border-gray-100 dark:border-gray-800/30">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-8 h-8 bg-gray-700/50 dark:bg-accent-dark rounded-lg flex items-center justify-center flex-shrink-0">
<span class="material-icons text-white dark:text-gray-400 text-sm">import_contacts</span>
</div>
<h3 class="font-medium text-sm truncate dark:text-gray-200">序言 | 为什么我每天早上6点在...</h3>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="px-2 py-1 bg-primary/20 text-primary text-[10px] font-medium rounded">免费</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-100 dark:border-gray-800/30">
<div class="flex items-start justify-between mb-4">
<div class="flex gap-3">
<div class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center flex-shrink-0 text-white font-bold shadow-lg shadow-primary/30">
</div>
<div>
<h3 class="font-bold dark:text-white text-base">真实的人</h3>
<p class="text-xs text-gray-500 dark:text-text-secondary-dark mt-0.5">人与人之间的底层逻辑</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500">2章</span>
<span class="material-icons text-gray-500 text-sm">arrow_downward</span>
</div>
</div>
<div class="space-y-4 pl-1">
<p class="text-xs text-gray-500 dark:text-gray-500 pt-2 pb-1">第1章 | 人与人之间的底层逻辑</p>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full border border-primary flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">1.1 荷包:电动车出租的被动收入模式</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="px-2 py-1 bg-primary/20 text-primary text-[10px] font-medium rounded">免费</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">1.2 老墨:资源整合高手的社交方法</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">1.3 笑声背后的MBTI为什么ENTJ适合做...</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">1.4 人性的三角结构:利益、情感、价值观</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">1.5 沟通差的问题:为什么你说的别人听...</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<p class="text-xs text-gray-500 dark:text-gray-500 pt-4 pb-1">第2章 | 人性困境案例</p>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">2.1 相亲故事:你以为找的是人,实际是在找...</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="px-2 py-1 bg-primary/20 text-primary text-[10px] font-medium rounded">NEW</span>
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">2.2 找工作迷茫者:为什么简历解决不了人生</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
<div class="flex items-center justify-between group">
<div class="flex items-center gap-3 overflow-hidden">
<div class="w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600 flex-shrink-0"></div>
<span class="text-sm text-gray-700 dark:text-gray-200 truncate">2.3 撸运费险:小钱困住大脑的真实心理</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="px-2 py-1 bg-primary/20 text-primary text-[10px] font-medium rounded">NEW</span>
<span class="text-xs text-gray-500">¥1</span>
<span class="material-icons text-gray-600 dark:text-gray-500 text-xs">chevron_right</span>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-100 dark:border-gray-800/30 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center flex-shrink-0 text-white font-bold shadow-lg shadow-primary/30">
</div>
<div>
<h3 class="font-bold dark:text-white text-base">真实的行业</h3>
<p class="text-xs text-gray-500 dark:text-text-secondary-dark mt-0.5">电商、内容、传统行业解析</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500">3章</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-100 dark:border-gray-800/30 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center flex-shrink-0 text-white font-bold shadow-lg shadow-primary/30">
</div>
<div>
<h3 class="font-bold dark:text-white text-base">真实的错误</h3>
<p class="text-xs text-gray-500 dark:text-text-secondary-dark mt-0.5">我和别人犯过的错</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500">2章</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-100 dark:border-gray-800/30 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center flex-shrink-0 text-white font-bold shadow-lg shadow-primary/30">
</div>
<div>
<h3 class="font-bold dark:text-white text-base">真实的赚钱</h3>
<p class="text-xs text-gray-500 dark:text-text-secondary-dark mt-0.5">底层结构与真实案例</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500">2章</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
</div>
<div class="bg-white dark:bg-card-dark rounded-2xl p-5 shadow-sm border border-gray-100 dark:border-gray-800/30">
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-4">附录</h3>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-sm dark:text-gray-300">附录1 | Soul派对房精选对话</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
<div class="w-full h-[1px] bg-gray-100 dark:bg-gray-800"></div>
<div class="flex justify-between items-center">
<span class="text-sm dark:text-gray-300">附录2 | 创业者自检清单</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
<div class="w-full h-[1px] bg-gray-100 dark:bg-gray-800"></div>
<div class="flex justify-between items-center">
<span class="text-sm dark:text-gray-300">附录3 | 本书提到的工具和资源</span>
<span class="material-icons text-gray-500 text-sm">arrow_forward</span>
</div>
</div>
</div>
</main>
<nav class="fixed bottom-0 left-0 right-0 bg-white dark:bg-background-dark border-t border-gray-200 dark:border-gray-800 pb-safe pt-2 px-6 flex justify-between items-center z-50 h-[80px]">
<a class="flex flex-col items-center gap-1 text-gray-400 dark:text-gray-500" href="#">
<span class="material-icons text-2xl">home</span>
<span class="text-[10px]">首页</span>
</a>
<a class="flex flex-col items-center gap-1 text-primary" href="#">
<span class="material-icons text-2xl">list</span>
<span class="text-[10px]">目录</span>
</a>
<div class="relative -top-6">
<button class="w-14 h-14 bg-gradient-to-br from-primary to-primary/80 rounded-full flex items-center justify-center text-white fab-shadow border-4 border-white dark:border-background-dark">
<span class="material-icons text-2xl">groups</span>
</button>
<span class="absolute -bottom-5 left-1/2 transform -translate-x-1/2 text-[10px] text-gray-400 w-max">找伙伴</span>
</div>
<a class="flex flex-col items-center gap-1 text-gray-400 dark:text-gray-500" href="#">
<span class="material-icons text-2xl">person</span>
<span class="text-[10px]">我的</span>
</a>
</nav>
<div class="h-8"></div>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

View File

@@ -1,215 +0,0 @@
<!DOCTYPE html>
<html class="dark" lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Comprehensive Profile Editor V1</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
background-color: #000000;
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #2D3748;
border-radius: 10px;
}
</style>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5",
"background-dark": "#000000",
"input-dark": "#1F2937",
"border-dark": "#374151",
},
borderRadius: {
DEFAULT: "12px",
},
},
},
};
</script>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-white min-h-screen flex flex-col">
<header class="sticky top-0 z-50 bg-black/80 backdrop-blur-md px-4 py-4 flex items-center justify-between border-b border-white/10">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-2xl cursor-pointer">chevron_left</span>
<h1 class="text-lg font-semibold">编辑资料</h1>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center bg-gray-900 rounded-full px-3 py-1 gap-2 border border-white/10">
<span class="material-symbols-outlined text-sm">more_horiz</span>
<div class="w-[1px] h-3 bg-white/20"></div>
<div class="w-3 h-3 rounded-full border-2 border-white/40"></div>
</div>
</div>
</header>
<main class="flex-grow max-w-md mx-auto px-4 py-6 space-y-8 pb-32">
<div class="bg-primary/10 border border-primary/20 p-4 rounded-xl flex items-start gap-3">
<span class="material-symbols-outlined text-primary text-xl">info</span>
<p class="text-sm text-primary/90 leading-relaxed font-medium">
温馨提示:需完善手机号和微信号才能使用提现和找伙伴功能
</p>
</div>
<div class="flex flex-col items-center gap-4">
<div class="relative">
<div class="w-24 h-24 rounded-full p-1 border-2 border-primary shadow-[0_0_15px_rgba(79,209,197,0.3)]">
<img alt="User Avatar" class="w-full h-full rounded-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuByaFYqZutDoN6LuA2I7fT4RcZhrFxMFneSEUoMGyHZX55vvf-taz_AAEYDjMagOLNszcrdxJMszI1W-71DtHgK9OaVfJIW6qH0yb33bfdP8nNg9zpyE-F4ujPwa_E8AlZSX9CHISvTfkiB6aUMH8hdz1sA8ptLervlhVR4Cst5N38a4ZiB_zRETMlVJOaPZ1-Ry_XeDXTXAaZQN1xy-WIGX-1rlgs1JUDXg4Ihcv0Am5p4hgwoFat9JdcoYRQN6FfP4b5zzapLoBk"/>
</div>
<div class="absolute bottom-0 right-0 bg-primary text-black p-1.5 rounded-full border-2 border-background-dark flex items-center justify-center">
<span class="material-symbols-outlined text-xs">photo_camera</span>
</div>
</div>
<button class="text-primary text-sm font-medium">更换头像</button>
</div>
<form class="space-y-6">
<div class="space-y-4">
<div class="grid grid-cols-1 gap-4">
<label class="block">
<span class="text-sm text-gray-400 ml-1">昵称</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-500" type="text" value="乘风"/>
</label>
</div>
<div class="grid grid-cols-2 gap-4">
<label class="block">
<span class="text-sm text-gray-400 ml-1">MBTI</span>
<div class="relative mt-1">
<select class="block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white appearance-none py-2 px-3 pr-8">
<option>INTJ</option>
<option>INFP</option>
<option>ENTP</option>
<option>ENFJ</option>
</select>
<span class="material-symbols-outlined absolute right-2 top-2 text-gray-500 pointer-events-none">expand_more</span>
</div>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">地区</span>
<div class="relative mt-1">
<input class="block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white pr-8" type="text" value="杭州 · 余杭区"/>
<span class="material-symbols-outlined absolute right-2 top-2 text-gray-500 text-lg">location_on</span>
</div>
</label>
</div>
<div class="grid grid-cols-1 gap-4">
<label class="block">
<span class="text-sm text-gray-400 ml-1">行业</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="例如:新媒体 / 电商" type="text"/>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">业务体量</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="例如年GMV 5000万+" type="text"/>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">职位</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="例如:创始人 / 联合创始人" type="text"/>
</label>
</div>
</div>
<div class="space-y-4 pt-4 border-t border-white/10">
<h3 class="font-semibold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-xl">contact_phone</span>
核心联系方式
</h3>
<div class="grid grid-cols-1 gap-4">
<label class="block">
<span class="text-sm text-gray-400 ml-1">手机号</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="请输入手机号" type="tel"/>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">微信号</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="请输入微信号" type="text"/>
</label>
</div>
</div>
<div class="space-y-4 pt-4 border-t border-white/10">
<h3 class="font-semibold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-yellow-400 text-xl">emoji_objects</span>
个人故事
</h3>
<label class="block">
<span class="text-sm text-gray-400 ml-1">你最赚钱的一个月做的是什么</span>
<textarea class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder:text-gray-600" placeholder="例如2021年主导电商大促单月GMV突破500W..." rows="3"></textarea>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">最有成就感的一件事</span>
<textarea class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder:text-gray-600" placeholder="例如帮助3个素人打造个人IP每月稳定变现5万+" rows="3"></textarea>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">人生的转折点</span>
<textarea class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder:text-gray-600" placeholder="例如:辞去大厂工作开始做自媒体..." rows="3"></textarea>
</label>
</div>
<div class="space-y-4 pt-4 border-t border-white/10">
<h3 class="font-semibold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-orange-400 text-xl">handshake</span>
互助需求
</h3>
<label class="block">
<span class="text-sm text-gray-400 ml-1">我能帮助大家什么</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="例如:短视频脚本、账号冷启动、私域转化" type="text"/>
</label>
<label class="block">
<span class="text-sm text-gray-400 ml-1">我需要什么帮助</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder-gray-600" placeholder="例如:寻找供应链资源、线下活动合作" type="text"/>
</label>
</div>
<div class="space-y-4 pt-4 border-t border-white/10">
<h3 class="font-semibold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-blue-400 text-xl">rocket_launch</span>
项目介绍
</h3>
<label class="block">
<textarea class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg focus:border-primary focus:ring-0 text-white placeholder:text-gray-600" placeholder="详细介绍您的项目,让潜在伙伴更好地了解您..." rows="4"></textarea>
</label>
</div>
<div class="pt-6 pb-12">
<button class="w-full bg-primary hover:bg-teal-400 text-black font-bold py-4 rounded-xl text-lg transition-colors" type="submit">
保存
</button>
</div>
</form>
</main>
<nav class="fixed bottom-0 left-0 right-0 bg-black border-t border-white/10 px-6 py-3 pb-6 flex justify-between items-center z-50">
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined">home</span>
<span class="text-[10px]">首页</span>
</div>
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined">list_alt</span>
<span class="text-[10px]">目录</span>
</div>
<div class="flex flex-col items-center -mt-8">
<div class="bg-gray-800 rounded-full p-3 border-4 border-black">
<span class="material-symbols-outlined text-3xl text-gray-400">group</span>
</div>
<span class="text-[10px] text-gray-500 mt-1">找伙伴</span>
</div>
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined">chat_bubble</span>
<span class="text-[10px]">消息</span>
</div>
<div class="flex flex-col items-center gap-1 text-primary">
<span class="material-symbols-outlined">person</span>
<span class="text-[10px] font-medium">我的</span>
</div>
</nav>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -1,158 +0,0 @@
<!DOCTYPE html><html class="dark" lang="zh-CN"><head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Simplified Info Completion Popup</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet">
<style type="text/tailwindcss">
:root {
--primary-color: #4FD1C5;
--bg-dark: #000000;
--card-dark: #1A1A1A;
--input-bg: #262626;
--border-color: #333333;
}
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
background-color: var(--bg-dark);
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.modal-overlay {
background-color: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(4px);
}
</style>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5",
"background-dark": "#000000",
"input-dark": "#1F2937",
"border-dark": "#374151",
},
borderRadius: {
DEFAULT: "12px",
},
},
},
};
</script>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-white min-h-screen flex flex-col relative overflow-hidden">
<header class="sticky top-0 z-40 bg-black/80 backdrop-blur-md px-4 py-4 flex items-center justify-between border-b border-white/10">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-2xl cursor-pointer" style="">chevron_left</span>
<h1 class="text-lg font-semibold" style="">编辑资料</h1>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center bg-gray-900 rounded-full px-3 py-1 gap-2 border border-white/10">
<span class="material-symbols-outlined text-sm" style="">more_horiz</span>
<div class="w-[1px] h-3 bg-white/20"></div>
<div class="w-3 h-3 rounded-full border-2 border-white/40"></div>
</div>
</div>
</header>
<main class="flex-grow max-w-md mx-auto px-4 py-6 space-y-8 pb-32 opacity-50 pointer-events-none">
<div class="bg-primary/10 border border-primary/20 p-4 rounded-xl flex items-start gap-3">
<span class="material-symbols-outlined text-primary text-xl" style="">info</span>
<p class="text-sm text-primary/90 leading-relaxed font-medium" style="">
温馨提示:需完善手机号和微信号才能使用提现和找伙伴功能
</p>
</div>
<div class="flex flex-col items-center gap-4">
<div class="relative">
<div class="w-24 h-24 rounded-full p-1 border-2 border-primary shadow-[0_0_15px_rgba(79,209,197,0.3)]">
<img alt="User Avatar" class="w-full h-full rounded-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuByaFYqZutDoN6LuA2I7fT4RcZhrFxMFneSEUoMGyHZX55vvf-taz_AAEYDjMagOLNszcrdxJMszI1W-71DtHgK9OaVfJIW6qH0yb33bfdP8nNg9zpyE-F4ujPwa_E8AlZSX9CHISvTfkiB6aUMH8hdz1sA8ptLervlhVR4Cst5N38a4ZiB_zRETMlVJOaPZ1-Ry_XeDXTXAaZQN1xy-WIGX-1rlgs1JUDXg4Ihcv0Am5p4hgwoFat9JdcoYRQN6FfP4b5zzapLoBk" style="">
</div>
</div>
</div>
<form class="space-y-6">
<div class="space-y-4">
<label class="block" style="">
<span class="text-sm text-gray-400 ml-1" style="">昵称</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg text-white" type="text" value="乘风">
</label>
<div class="grid grid-cols-2 gap-4">
<label class="block" style="">
<span class="text-sm text-gray-400 ml-1" style="">MBTI</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg text-white" type="text" value="INTJ">
</label>
<label class="block" style="">
<span class="text-sm text-gray-400 ml-1" style="">地区</span>
<input class="mt-1 block w-full bg-input-dark border-border-dark rounded-lg text-white" type="text" value="杭州">
</label>
</div>
</div>
</form>
</main>
<div class="fixed inset-0 z-[60] flex items-center justify-center px-6 modal-overlay">
<div class="w-full max-w-sm bg-[#1A1A1A] rounded-[24px] overflow-hidden border border-white/10 shadow-2xl">
<div class="px-6 pt-10 pb-6 space-y-6">
<div class="text-center">
<h2 class="text-xl font-bold text-white tracking-wide" style="">请完善联系方式</h2>
</div>
<div class="space-y-4">
<div class="space-y-1.5">
<label class="text-xs font-medium text-gray-400 ml-1" style="">手机号</label>
<div class="relative">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 text-lg" style="">smartphone</span>
<input class="w-full bg-[#262626] border-transparent rounded-xl py-3 pl-10 pr-4 text-white placeholder:text-gray-600 focus:ring-1 focus:ring-primary focus:border-primary transition-all text-sm" placeholder="请输入您的手机号" type="tel">
</div>
</div>
<div class="space-y-1.5">
<label class="text-xs font-medium text-gray-400 ml-1" style="">微信号</label>
<div class="relative">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 text-lg" style="">chat</span>
<input class="w-full bg-[#262626] border-transparent rounded-xl py-3 pl-10 pr-4 text-white placeholder:text-gray-600 focus:ring-1 focus:ring-primary focus:border-primary transition-all text-sm" placeholder="请输入您的微信号" type="text">
</div>
</div>
</div>
<div class="pt-2">
<button class="w-full bg-primary hover:bg-[#45BCB0] text-black font-bold py-3.5 rounded-xl text-base transition-all active:scale-[0.98] shadow-lg shadow-primary/20" style="">
保存
</button>
<button class="w-full mt-3 text-gray-500 text-sm font-medium py-2" style="">
取消
</button>
</div>
</div>
</div>
</div>
<nav class="fixed bottom-0 left-0 right-0 bg-black border-t border-white/10 px-6 py-3 pb-6 flex justify-between items-center z-50 opacity-50 pointer-events-none">
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined" style="">home</span>
<span class="text-[10px]" style="">首页</span>
</div>
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined" style="">list_alt</span>
<span class="text-[10px]" style="">目录</span>
</div>
<div class="flex flex-col items-center -mt-8">
<div class="bg-gray-800 rounded-full p-3 border-4 border-black">
<span class="material-symbols-outlined text-3xl text-gray-400" style="">group</span>
</div>
<span class="text-[10px] text-gray-500 mt-1" style="">找伙伴</span>
</div>
<div class="flex flex-col items-center gap-1 text-gray-500">
<span class="material-symbols-outlined" style="">chat_bubble</span>
<span class="text-[10px]" style="">消息</span>
</div>
<div class="flex flex-col items-center gap-1 text-primary">
<span class="material-symbols-outlined" style="">person</span>
<span class="text-[10px] font-medium" style="">我的</span>
</div>
</nav>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -1,206 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Profile with Hidden Info Icons</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
accent: "#5EEAD4",
"background-dark": "#050B14",
"card-dark": "#0F1720",
"card-inner": "#17212F",
"text-label": "#94A3B8",
"text-value": "#FFFFFF",
"highlight-gold": "#F59E0B",
},
fontFamily: {
display: ['"Noto Sans SC"', "sans-serif"],
sans: ['"Noto Sans SC"', "sans-serif"],
},
borderRadius: {
DEFAULT: "0.5rem",
'xl': '0.75rem',
'2xl': '1rem',
'3xl': '1.5rem',
},
},
},
};
</script>
<style type="text/tailwindcss">
html { scroll-behavior: smooth; }
::-webkit-scrollbar { width: 0px; background: transparent; }
body { -webkit-tap-highlight-color: transparent; }
.border-subtle {
border: 1px solid rgba(255, 255, 255, 0.08);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark font-display text-gray-100 min-h-screen flex flex-col antialiased pb-32">
<header class="fixed top-0 w-full z-50 bg-background-dark/90 backdrop-blur-md px-4 py-3 flex items-center justify-between border-b border-white/5">
<button class="p-2 -ml-2 rounded-full hover:bg-white/10 transition-colors">
<span class="material-symbols-outlined text-accent text-[20px]">arrow_back_ios_new</span>
</button>
<h1 class="text-[17px] font-bold text-white tracking-wide">个人资料</h1>
<div class="flex items-center gap-2">
<button class="p-2 rounded-full hover:bg-white/10 transition-colors">
<span class="material-symbols-outlined text-white text-[24px]">more_horiz</span>
</button>
<button class="p-2 -mr-2 rounded-full hover:bg-white/10 transition-colors">
<span class="material-symbols-outlined text-white text-[24px]">radio_button_checked</span>
</button>
</div>
</header>
<main class="flex-grow px-4 pt-20 w-full mx-auto space-y-4 max-w-lg">
<div class="bg-card-dark border-subtle rounded-2xl p-8 flex flex-col items-center justify-center relative overflow-hidden shadow-lg shadow-black/20">
<div class="absolute top-0 inset-x-0 h-32 bg-gradient-to-b from-[#1E3A45]/30 to-transparent pointer-events-none"></div>
<div class="relative z-10 flex flex-col items-center">
<div class="w-[88px] h-[88px] rounded-full overflow-hidden border border-white/10 mb-4 shadow-xl">
<img alt="Profile Picture" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAkEMaAvAMloIMKdLUz6H3J7wK8hsWRTMPvWXscytVPPy0JGqD0z780NFUWH-LxpGJFc0zhWr-x_dlFJQjPP1TnCUVPzLpGMIS8Rvt7Tas75-TkbQqteNl_6BxURNsrKRf3_9nK2ucsTeFrF20Z9HcbubIYiUwL-55uX5lJV7xfS2GvSxPycZHm7A62PLGxA0eoMAnZwmmgMKOPsQkmTu4W6_1RBXHWFkf1Znjgl9Lsr4dDyIZF3dsbOgJTjudl1i4SRH8Q-6WbGJo"/>
</div>
<h2 class="text-xl font-bold text-white mb-3 tracking-wide">乘风</h2>
<div class="flex items-center justify-center gap-3 text-xs font-medium">
<span class="px-3 py-1 bg-[#134E4A] text-accent rounded-full border border-accent/20">INFP</span>
<span class="px-3 py-1 bg-[#1F2937] text-gray-300 rounded-full border border-white/10 flex items-center gap-1.5 pl-2">
<span class="material-symbols-outlined text-red-500 text-[14px] -ml-0.5">push_pin</span> 杭州
</span>
</div>
</div>
</div>
<section class="bg-card-dark border-subtle rounded-2xl p-5 shadow-lg shadow-black/20">
<div class="flex items-center gap-2.5 mb-5">
<span class="material-symbols-outlined text-gray-400 text-[20px]">person</span>
<h3 class="text-[15px] font-bold text-white tracking-wide">基本信息</h3>
</div>
<div class="space-y-6">
<div>
<p class="text-[13px] text-text-label mb-2">行业</p>
<p class="text-[15px] font-medium text-text-value">新媒体 / 电商</p>
</div>
<div>
<p class="text-[13px] text-text-label mb-2">职位</p>
<p class="text-[15px] font-medium text-text-value">创始人</p>
</div>
<div>
<p class="text-[13px] text-text-label mb-2">业务体量</p>
<p class="text-[15px] font-medium text-text-value">年GMV 5000万+</p>
</div>
<div class="h-px bg-white/5 w-full"></div>
<div>
<p class="text-[13px] text-text-label mb-2">我擅长</p>
<p class="text-[15px] font-medium text-text-value leading-relaxed">
短视频制作、IP打造、私域运营
</p>
</div>
<div>
<p class="text-[13px] text-text-label mb-2">联系方式</p>
<div class="flex items-center gap-2">
<span class="text-[15px] font-mono text-text-value tracking-wide">139****02</span>
<span class="material-symbols-outlined text-text-label text-[18px] opacity-60">visibility_off</span>
</div>
</div>
<div>
<p class="text-[13px] text-text-label mb-2">微信号</p>
<div class="flex items-center gap-2">
<span class="text-[15px] font-mono text-text-value tracking-wide">wxid_****abc</span>
<span class="material-symbols-outlined text-text-label text-[18px] opacity-60">visibility_off</span>
</div>
</div>
</div>
</section>
<section class="bg-card-dark border-subtle rounded-2xl p-5 shadow-lg shadow-black/20">
<div class="flex items-center gap-2.5 mb-5">
<span class="material-symbols-outlined text-[#FCD34D] text-[20px]">lightbulb</span>
<h3 class="text-[15px] font-bold text-white tracking-wide">个人故事</h3>
</div>
<div class="space-y-6">
<div>
<div class="flex items-center gap-2 mb-2">
<span class="text-base">🏆</span>
<h4 class="text-[13px] font-medium text-text-label">最赚钱的一个月做的是什么</h4>
</div>
<p class="text-[14px] text-text-value leading-relaxed text-gray-200">
旅游账号30天涨粉10万带货佣金收入12万
</p>
</div>
<div class="h-px bg-white/5 w-full"></div>
<div>
<div class="flex items-center gap-2 mb-2">
<span class="text-base">⭐️</span>
<h4 class="text-[13px] font-medium text-text-label">最有成就感的一件事</h4>
</div>
<p class="text-[14px] text-text-value leading-relaxed text-gray-200">
帮助3个素人打造个人IP每个月稳定变现5万+
</p>
</div>
<div class="h-px bg-white/5 w-full"></div>
<div>
<div class="flex items-center gap-2 mb-2">
<span class="material-symbols-outlined text-blue-400 text-[18px]">restart_alt</span>
<h4 class="text-[13px] font-medium text-text-label">人生的转折点</h4>
</div>
<p class="text-[14px] text-text-value leading-relaxed text-gray-200">
辞去互联网大厂工作开始做自媒体,第三个月就超过原薪资
</p>
</div>
</div>
</section>
<section class="bg-card-dark border-subtle rounded-2xl p-5 shadow-lg shadow-black/20">
<div class="flex items-center gap-2.5 mb-5">
<span class="text-[20px]">🤝</span>
<h3 class="text-[15px] font-bold text-white tracking-wide">互助需求</h3>
</div>
<div class="space-y-3">
<div class="bg-card-inner rounded-lg p-4 border border-white/5">
<span class="inline-block px-2 py-0.5 mb-2 text-[11px] font-medium text-accent bg-[#112D2A] rounded">
我能帮你
</span>
<p class="text-[13px] text-text-value leading-relaxed tracking-wide">
短视频脚本、账号冷启动、私域转化设计
</p>
</div>
<div class="bg-card-inner rounded-lg p-4 border border-white/5">
<span class="inline-block px-2 py-0.5 mb-2 text-[11px] font-medium text-[#F59E0B] bg-[#2D1F0D] rounded">
我需要帮助
</span>
<p class="text-[13px] text-text-value leading-relaxed tracking-wide">
寻找供应链资源和线下活动合作
</p>
</div>
</div>
</section>
<section class="bg-card-dark border-subtle rounded-2xl p-5 mb-6 shadow-lg shadow-black/20">
<div class="flex items-center gap-2.5 mb-3">
<span class="material-symbols-outlined text-gray-400 text-[20px]">rocket_launch</span>
<h3 class="text-[15px] font-bold text-white tracking-wide">项目介绍</h3>
</div>
<p class="text-[14px] text-text-value leading-relaxed text-gray-200">
旅游+生活方式自媒体矩阵全网粉丝50万+
</p>
</section>
</main>
<div class="fixed bottom-0 left-0 right-0 p-4 bg-background-dark/95 backdrop-blur border-t border-white/5 z-40 pb-10">
<div class="max-w-lg mx-auto">
<button class="w-full bg-transparent text-[#F59E0B] font-medium py-4 rounded-full border border-[#F59E0B]/30 text-[15px] active:scale-[0.98] transition-all duration-200 flex items-center justify-center gap-2 tracking-wide">
成为超级个体 <span class="material-symbols-outlined text-[18px]">arrow_forward</span>
</button>
</div>
</div>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -1,227 +0,0 @@
<!DOCTYPE html><html lang="zh-CN" class="dark"><head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Mentor Detail Profile</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5", // Teal as requested
"primary-dark": "#38B2AC",
"background-light": "#F7FAFC",
"background-dark": "#000000",
"surface-dark": "#121212",
"surface-light": "#FFFFFF",
"card-dark": "#1E1E1E",
"card-light": "#FFFFFF",
"text-primary-dark": "#FFFFFF",
"text-secondary-dark": "#A0AEC0",
"text-primary-light": "#1A202C",
"text-secondary-light": "#718096",
"highlight-orange": "#ED8936",
},
fontFamily: {
display: ["Noto Sans SC", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.5rem",
'xl': "1rem",
'2xl': "1.5rem",
},
},
},
};
</script>
<style>
body {
font-family: 'Noto Sans SC', sans-serif;
}.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark text-text-primary-light dark:text-text-primary-dark transition-colors duration-200">
<div class="fixed top-0 left-0 right-0 z-50 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur-md px-4 py-3 flex justify-between items-center border-b border-gray-200 dark:border-gray-800">
<button class="text-text-primary-light dark:text-white p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800" style="">
<span class="material-icons" style="">arrow_back</span>
</button>
<h1 class="text-lg font-medium" style="">导师详情</h1>
<button class="text-text-primary-light dark:text-white p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800" style="">
<span class="material-icons" style="">more_horiz</span>
</button>
</div>
<main class="pt-16 pb-24 px-4 max-w-md mx-auto">
<div class="flex flex-col items-center mt-6 mb-8 text-center">
<div class="relative mb-4">
<div class="w-24 h-24 rounded-full overflow-hidden border-2 border-primary p-0.5">
<img alt="Mentor Avatar" class="w-full h-full object-cover rounded-full" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBVtV-57bHdDZe8NP5PbMvnWgtUspxsIEcFOkdZox4jI68GfvFx_cGJi11ZL_iCIhnVxLcFXbRRvgodW1ZtkdJ6N75N_-C7C_Vzoagoh5Ovc-GUD3_Z_uDC1TO9XEA1Mo0CYKykpNzigJbpSL5a2fx93sUEC3PV-Dh8H0mGoI8H7Xu-AHgxEfkULmJHEUnEVoc9yEYBBPQvEGZR6z7KEY_i_QtWjZIG8scxH7VxpSUuuN02PA_UyVyD3pgEEpZTn_w8f63O7i149ak" style="">
</div>
</div>
<h2 class="text-2xl font-bold mb-1" style="">卡若</h2>
<p class="text-primary text-sm font-medium mb-4" style="">结构判断型咨询 · Decision &gt; Execution</p>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-4 border-primary">
<p class="text-sm text-text-secondary-light dark:text-gray-300 leading-relaxed text-left" style="">
大多数人失败,不是因为不努力,而是把精力投进了一个一开始就不该参与的结构里。
</p>
</div>
</div>
<div class="mb-8">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2" style="">01</span>
<h3 class="font-bold text-lg" style="">为什么找卡若?</h3>
</div>
<div class="grid grid-cols-3 gap-3">
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1" style="">做事的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500" style="">方向始终不对</p>
</div>
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1" style="">犹豫的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500" style="">始终不敢决定</p>
</div>
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1" style="">已投入的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500" style="">越投越不确定</p>
</div>
</div>
<p class="text-center text-primary text-xs mt-3 font-medium" style="">共同问题不是能力,而是缺乏一次高质量的判断</p>
</div>
<div class="mb-8">
<div class="flex items-center mb-2">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2" style="">02</span>
<h3 class="font-bold text-lg" style="">提供什么?</h3>
</div>
<p class="text-xs text-text-secondary-light dark:text-gray-500 mb-4 ml-8" style="">不做激励,不做陪伴 · 专注「决策前的结构判断」</p>
<div class="space-y-4">
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2" style="">1</div>
<h4 class="font-bold" style="">项目结构判断</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">是否真实存在赚钱结构</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">回报是否可持续</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">普通人成功概率</span>
</div>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2" style="">2</div>
<h4 class="font-bold" style="">× 项目匹配</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">性格资源阶段是否匹配</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">最大优势与致命短板</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">最可能失败节点</span>
</div>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2" style="">3</div>
<h4 class="font-bold" style="">风险止损判断</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">哪一步是不可逆风险</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">什么时候必须停</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400" style="">避免沉没成本陷阱</span>
</div>
</div>
</div>
</div>
<div class="mb-8">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2" style="">03</span>
<h3 class="font-bold text-lg" style="">收费标准</h3>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl overflow-hidden shadow-sm">
<div class="grid grid-cols-4 bg-primary text-black font-bold text-xs p-3">
<div class="col-span-2" style="">咨询项目</div>
<div class="text-center" style="">时长</div>
<div class="text-right" style="">价格</div>
</div>
<div class="grid grid-cols-4 p-4 border-b border-gray-100 dark:border-gray-800 items-center">
<div class="col-span-2 font-medium text-sm" style="">单次咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400" style="">1小时</div>
<div class="text-right font-bold text-primary" style="">¥980</div>
</div>
<div class="grid grid-cols-4 p-4 border-b border-gray-100 dark:border-gray-800 items-center bg-gray-50 dark:bg-surface-dark/30">
<div class="col-span-2 font-medium text-sm" style="">半年咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400" style="">-</div>
<div class="text-right">
<div class="text-[10px] text-text-secondary-light dark:text-gray-500 line-through" style="">98000</div>
<div class="font-bold text-primary text-sm" style="">¥19,800</div>
</div>
</div>
<div class="grid grid-cols-4 p-4 items-center">
<div class="col-span-2 font-medium text-sm" style="">年度咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400" style="">-</div>
<div class="text-right">
<div class="text-[10px] text-text-secondary-light dark:text-gray-500 line-through" style="">196000</div>
<div class="font-bold text-primary text-sm" style="">¥29,800</div>
</div>
</div>
</div>
<div class="flex flex-wrap justify-center gap-2 mt-4">
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded" style="">项目/副业决策</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded" style="">转型选择</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded" style="">投资判断</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded" style="">关键节点</span>
</div>
</div>
<div class="mb-10">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2" style="">04</span>
<h3 class="font-bold text-lg" style="">判断风格</h3>
</div>
<div class="flex flex-wrap gap-3">
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm" style="">
冷静
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm" style="">
克制
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm" style="">
偏风险视角
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm" style="">
偏长期主义
</div>
</div>
</div>
<p class="text-center text-[10px] text-text-secondary-light dark:text-gray-600 mb-8" style=""><br></p>
</main>
<div class="fixed bottom-0 left-0 right-0 bg-background-light dark:bg-background-dark border-t border-gray-200 dark:border-gray-800 p-4 safe-area-pb">
<div class="max-w-md mx-auto flex gap-3">
<button class="w-full bg-primary hover:bg-primary-dark text-black font-bold py-3 px-4 rounded-xl shadow-lg shadow-primary/20 flex items-center justify-center gap-2" style="">
<span class="material-icons text-sm" style="">chat_bubble</span>
链接导师
</button>
</div>
</div>
<div class="h-6 w-full bg-background-light dark:bg-background-dark"></div>
<script>
// Force dark mode for this demo as requested by user context "dark UI style of Reference Image 1"
// But keeping the capability to toggle if needed.
document.documentElement.classList.add('dark');
</script>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

View File

@@ -1,312 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Mentor Detail Profile</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5", // Teal as requested
"primary-dark": "#38B2AC",
"background-light": "#F7FAFC",
"background-dark": "#000000",
"surface-dark": "#121212",
"surface-light": "#FFFFFF",
"card-dark": "#1E1E1E",
"card-light": "#FFFFFF",
"text-primary-dark": "#FFFFFF",
"text-secondary-dark": "#A0AEC0",
"text-primary-light": "#1A202C",
"text-secondary-light": "#718096",
"highlight-orange": "#ED8936", // Keeping the orange from Ref 2 for contrast if needed, or mapping to Teal
},
fontFamily: {
display: ["Noto Sans SC", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.5rem",
'xl': "1rem",
'2xl': "1.5rem",
},
},
},
};
</script>
<style>
body {
font-family: 'Noto Sans SC', sans-serif;
}.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark text-text-primary-light dark:text-text-primary-dark transition-colors duration-200 relative">
<div class="fixed top-0 left-0 right-0 z-40 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur-md px-4 py-3 flex justify-between items-center border-b border-gray-200 dark:border-gray-800">
<button class="text-text-primary-light dark:text-white p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="text-lg font-medium">导师详情</h1>
<button class="text-text-primary-light dark:text-white p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800">
<span class="material-icons">more_horiz</span>
</button>
</div>
<main class="pt-16 pb-24 px-4 max-w-md mx-auto blur-sm select-none pointer-events-none">
<div class="flex flex-col items-center mt-6 mb-8 text-center">
<div class="relative mb-4">
<div class="w-24 h-24 rounded-full overflow-hidden border-2 border-primary p-0.5">
<img alt="Mentor Avatar" class="w-full h-full object-cover rounded-full" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBVtV-57bHdDZe8NP5PbMvnWgtUspxsIEcFOkdZox4jI68GfvFx_cGJi11ZL_iCIhnVxLcFXbRRvgodW1ZtkdJ6N75N_-C7C_Vzoagoh5Ovc-GUD3_Z_uDC1TO9XEA1Mo0CYKykpNzigJbpSL5a2fx93sUEC3PV-Dh8H0mGoI8H7Xu-AHgxEfkULmJHEUnEVoc9yEYBBPQvEGZR6z7KEY_i_QtWjZIG8scxH7VxpSUuuN02PA_UyVyD3pgEEpZTn_w8f63O7i149ak"/>
</div>
</div>
<h2 class="text-2xl font-bold mb-1">卡若</h2>
<p class="text-primary text-sm font-medium mb-4">结构判断型咨询 · Decision &gt; Execution</p>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-4 border-primary">
<p class="text-sm text-text-secondary-light dark:text-gray-300 leading-relaxed text-left">
大多数人失败,不是因为不努力,而是把精力投进了一个一开始就不该参与的结构里。
</p>
</div>
</div>
<div class="mb-8">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2">01</span>
<h3 class="font-bold text-lg">为什么找卡若?</h3>
</div>
<div class="grid grid-cols-3 gap-3">
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1">做事的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500">方向始终不对</p>
</div>
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1">犹豫的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500">始终不敢决定</p>
</div>
<div class="bg-card-light dark:bg-surface-dark p-3 rounded-lg text-center shadow-sm">
<p class="font-bold text-sm mb-1">已投入的人</p>
<p class="text-xs text-text-secondary-light dark:text-gray-500">越投越不确定</p>
</div>
</div>
<p class="text-center text-primary text-xs mt-3 font-medium">共同问题不是能力,而是缺乏一次高质量的判断</p>
</div>
<div class="mb-8">
<div class="flex items-center mb-2">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2">02</span>
<h3 class="font-bold text-lg">提供什么?</h3>
</div>
<p class="text-xs text-text-secondary-light dark:text-gray-500 mb-4 ml-8">不做激励,不做陪伴 · 专注「决策前的结构判断」</p>
<div class="space-y-4">
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2">1</div>
<h4 class="font-bold">项目结构判断</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">是否真实存在赚钱结构</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">回报是否可持续</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">普通人成功概率</span>
</div>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2">2</div>
<h4 class="font-bold">× 项目匹配</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">性格资源阶段是否匹配</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">最大优势与致命短板</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">最可能失败节点</span>
</div>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl p-4 shadow-sm border-l-2 border-primary relative overflow-hidden">
<div class="flex items-center mb-2">
<div class="bg-primary/20 text-primary w-5 h-5 flex items-center justify-center rounded-full text-xs font-bold mr-2">3</div>
<h4 class="font-bold">风险止损判断</h4>
</div>
<div class="flex flex-wrap gap-2 pl-7">
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">哪一步是不可逆风险</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">什么时候必须停</span>
<span class="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-text-secondary-light dark:text-gray-400">避免沉没成本陷阱</span>
</div>
</div>
</div>
</div>
<div class="mb-8">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2">03</span>
<h3 class="font-bold text-lg">收费标准</h3>
</div>
<div class="bg-card-light dark:bg-card-dark rounded-xl overflow-hidden shadow-sm">
<div class="grid grid-cols-4 bg-primary text-black font-bold text-xs p-3">
<div class="col-span-2">咨询项目</div>
<div class="text-center">时长</div>
<div class="text-right">价格</div>
</div>
<div class="grid grid-cols-4 p-4 border-b border-gray-100 dark:border-gray-800 items-center">
<div class="col-span-2 font-medium text-sm">单次咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400">1小时</div>
<div class="text-right font-bold text-primary">¥980</div>
</div>
<div class="grid grid-cols-4 p-4 border-b border-gray-100 dark:border-gray-800 items-center bg-gray-50 dark:bg-surface-dark/30">
<div class="col-span-2 font-medium text-sm">半年咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400">-</div>
<div class="text-right">
<div class="text-[10px] text-text-secondary-light dark:text-gray-500 line-through">98000</div>
<div class="font-bold text-primary text-sm">¥19,800</div>
</div>
</div>
<div class="grid grid-cols-4 p-4 items-center">
<div class="col-span-2 font-medium text-sm">年度咨询</div>
<div class="text-center text-xs text-text-secondary-light dark:text-gray-400">-</div>
<div class="text-right">
<div class="text-[10px] text-text-secondary-light dark:text-gray-500 line-through">196000</div>
<div class="font-bold text-primary text-sm">¥29,800</div>
</div>
</div>
</div>
<div class="flex flex-wrap justify-center gap-2 mt-4">
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded">项目/副业决策</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded">转型选择</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded">投资判断</span>
<span class="text-[10px] bg-primary/10 text-primary px-2 py-1 rounded">关键节点</span>
</div>
</div>
<div class="mb-10">
<div class="flex items-center mb-4">
<span class="bg-primary text-black text-xs font-bold px-2 py-0.5 rounded-full mr-2">04</span>
<h3 class="font-bold text-lg">判断风格</h3>
</div>
<div class="flex flex-wrap gap-3">
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm">
冷静
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm">
克制
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm">
偏风险视角
</div>
<div class="bg-card-light dark:bg-card-dark border border-gray-100 dark:border-gray-700 px-4 py-2 rounded-full text-sm font-medium shadow-sm">
偏长期主义
</div>
</div>
</div>
<div class="bg-primary/5 dark:bg-card-dark rounded-2xl p-6 text-center border border-primary/20 mb-8">
<h4 class="font-bold mb-4 text-primary">扫码预约咨询</h4>
<div class="w-32 h-32 bg-white mx-auto rounded-lg flex items-center justify-center mb-3">
<span class="material-icons text-6xl text-gray-800">qr_code_2</span>
</div>
<p class="text-xs text-text-secondary-light dark:text-gray-500">卡若 | 结构判断咨询</p>
</div>
<p class="text-center text-[10px] text-text-secondary-light dark:text-gray-600 mb-8">
判断的价值,往往体现在你"没做"的那一步
</p>
</main>
<div class="fixed bottom-0 left-0 right-0 bg-background-light dark:bg-background-dark border-t border-gray-200 dark:border-gray-800 p-4 safe-area-pb z-30 blur-sm pointer-events-none select-none">
<div class="max-w-md mx-auto flex gap-3">
<button class="flex-1 bg-surface-dark dark:bg-surface-dark border border-gray-300 dark:border-gray-700 text-text-primary-light dark:text-white font-bold py-3 px-4 rounded-xl flex items-center justify-center gap-2">
<span class="material-icons text-sm">share</span>
分享
</button>
<button class="flex-[2] bg-primary hover:bg-primary-dark text-black font-bold py-3 px-4 rounded-xl shadow-lg shadow-primary/20 flex items-center justify-center gap-2">
<span class="material-icons text-sm">chat_bubble</span>
联系导师
</button>
</div>
</div>
<div class="h-6 w-full bg-background-light dark:bg-background-dark z-30 blur-sm"></div>
<div class="fixed inset-0 z-50 flex items-end justify-center sm:items-center p-4 bg-black/70 backdrop-blur-sm animate-fade-in">
<div class="bg-surface-dark w-full max-w-sm rounded-t-2xl sm:rounded-2xl overflow-hidden shadow-2xl ring-1 ring-white/10 animate-slide-up">
<div class="px-6 pt-6 pb-4 flex items-center justify-between border-b border-gray-800">
<h3 class="text-xl font-bold text-white">选择咨询项目</h3>
<button class="text-gray-400 hover:text-white transition-colors">
<span class="material-icons">close</span>
</button>
</div>
<div class="p-6 space-y-3">
<label class="block relative group cursor-pointer">
<input checked="" class="peer sr-only" name="service" type="radio"/>
<div class="p-4 rounded-xl bg-card-dark border border-gray-700 peer-checked:border-primary peer-checked:bg-primary/5 transition-all">
<div class="flex justify-between items-center mb-1">
<span class="font-bold text-white text-base">单次咨询</span>
<div class="w-5 h-5 rounded-full border border-gray-500 peer-checked:border-primary peer-checked:bg-primary flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-black rounded-full opacity-0 peer-checked:opacity-100 transition-opacity"></div>
</div>
</div>
<div class="flex justify-between items-end">
<span class="text-xs text-gray-400">1小时深度沟通</span>
<span class="text-lg font-bold text-primary">¥980</span>
</div>
</div>
</label>
<label class="block relative group cursor-pointer">
<input class="peer sr-only" name="service" type="radio"/>
<div class="p-4 rounded-xl bg-card-dark border border-gray-700 peer-checked:border-primary peer-checked:bg-primary/5 transition-all">
<div class="flex justify-between items-center mb-1">
<span class="font-bold text-white text-base">半年咨询</span>
<div class="w-5 h-5 rounded-full border border-gray-500 peer-checked:border-primary peer-checked:bg-primary flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-black rounded-full opacity-0 peer-checked:opacity-100 transition-opacity"></div>
</div>
</div>
<div class="flex justify-between items-end">
<span class="text-xs text-gray-400">不限次数 · 关键节点陪伴</span>
<div class="text-right">
<span class="text-xs text-gray-500 line-through mr-1">¥98,000</span>
<span class="text-lg font-bold text-primary">¥19,800</span>
</div>
</div>
</div>
</label>
<label class="block relative group cursor-pointer">
<input class="peer sr-only" name="service" type="radio"/>
<div class="p-4 rounded-xl bg-card-dark border border-gray-700 peer-checked:border-primary peer-checked:bg-primary/5 transition-all">
<div class="flex justify-between items-center mb-1">
<span class="font-bold text-white text-base">年度咨询</span>
<span class="ml-2 text-[10px] bg-highlight-orange text-white px-1.5 py-0.5 rounded">推荐</span>
<div class="w-5 h-5 rounded-full border border-gray-500 peer-checked:border-primary peer-checked:bg-primary flex items-center justify-center ml-auto">
<div class="w-2.5 h-2.5 bg-black rounded-full opacity-0 peer-checked:opacity-100 transition-opacity"></div>
</div>
</div>
<div class="flex justify-between items-end">
<span class="text-xs text-gray-400">全年度战略顾问</span>
<div class="text-right">
<span class="text-xs text-gray-500 line-through mr-1">¥196,000</span>
<span class="text-lg font-bold text-primary">¥29,800</span>
</div>
</div>
</div>
</label>
</div>
<div class="p-6 pt-2 pb-8 bg-surface-dark border-t border-gray-800">
<button class="w-full bg-primary hover:bg-primary-dark text-black font-bold py-3.5 px-4 rounded-xl shadow-lg shadow-primary/20 transition-colors flex items-center justify-center gap-2">
确认选择
<span class="material-icons text-sm">arrow_forward</span>
</button>
<p class="text-center text-[10px] text-gray-500 mt-3">点击确认即代表同意 <a class="text-gray-400 underline" href="#">服务协议</a></p>
</div>
</div>
</div>
<script>
// Force dark mode for this demo as requested by user context "dark UI style of Reference Image 1"
document.documentElement.classList.add('dark');
</script>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -1,200 +0,0 @@
<!DOCTYPE html>
<html class="dark" lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>导师列表页</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#2DD4BF",
"background-dark": "#000000",
"card-dark": "#1C1C1E",
"nav-dark": "#1C1C1E",
"subtext-dark": "#A1A1AA",
"tag-bg": "#2C2C2E",
"tag-text": "#E5E7EB",
},
fontFamily: {
display: ["PingFang SC", "Heiti SC", "SF Pro Display", "Roboto", "sans-serif"],
body: ["PingFang SC", "Heiti SC", "SF Pro Text", "Roboto", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.5rem",
'xl': '1rem',
'2xl': '1.5rem',
},
},
},
};
</script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&amp;display=swap" rel="stylesheet"/>
<style>
body {
font-family: 'Noto Sans SC', sans-serif;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-gray-100 min-h-screen font-body antialiased selection:bg-primary selection:text-white">
<div class="sticky top-0 z-50 bg-background-dark/95 backdrop-blur-md px-4 pt-12 pb-2 border-b border-gray-800">
<div class="relative flex items-center justify-between h-11 mb-2">
<button class="flex items-center justify-center w-8 h-8 -ml-1 text-white hover:text-gray-300 transition-colors">
<span class="material-symbols-outlined text-[24px]">arrow_back</span>
</button>
<h1 class="absolute left-1/2 transform -translate-x-1/2 text-[17px] font-medium text-white tracking-wide">选择导师</h1>
<button class="flex items-center justify-center w-8 h-8 -mr-1 text-white hover:text-gray-300 transition-colors">
<span class="material-symbols-outlined text-[24px]">more_horiz</span>
</button>
</div>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="material-symbols-outlined text-gray-500 text-[20px]">search</span>
</div>
<input class="block w-full pl-10 pr-3 py-3 border-none rounded-xl bg-card-dark text-white placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-primary/50 shadow-sm text-sm" placeholder="搜索导师、技能或行业..." type="text"/>
</div>
<div class="flex gap-3 overflow-x-auto scrollbar-hide mt-4 pb-2">
<button class="whitespace-nowrap px-4 py-1.5 rounded-full bg-primary text-black font-semibold text-xs">全部</button>
<button class="whitespace-nowrap px-4 py-1.5 rounded-full bg-card-dark border border-gray-800 text-gray-300 font-medium text-xs hover:border-gray-600 transition-colors">项目结构判断</button>
<button class="whitespace-nowrap px-4 py-1.5 rounded-full bg-card-dark border border-gray-800 text-gray-300 font-medium text-xs hover:border-gray-600 transition-colors">产品架构</button>
<button class="whitespace-nowrap px-4 py-1.5 rounded-full bg-card-dark border border-gray-800 text-gray-300 font-medium text-xs hover:border-gray-600 transition-colors">BP梳理</button>
</div>
</div>
<main class="px-4 pb-8 space-y-4 pt-4 safe-area-bottom">
<div class="flex justify-between items-end mb-2">
<h2 class="text-base font-bold text-gray-200">推荐导师</h2>
<a class="text-xs text-primary font-medium hover:opacity-80 flex items-center" href="#">查看全部 <span class="material-symbols-outlined text-[16px] ml-0.5">chevron_right</span></a>
</div>
<div class="bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-800 relative overflow-hidden group hover:border-gray-700 transition-all">
<div class="flex gap-4">
<div class="flex-shrink-0 relative">
<img alt="Mentor Profile" class="w-14 h-14 rounded-full object-cover border border-gray-700 shadow-md" src="https://lh3.googleusercontent.com/aida-public/AB6AXuB8f2js-gZTCIf5XMw7hgLz3DoqdqAR5N8rUvWrDHBs_4YlILiWPBK1kEQFcMNQZ47g9PT70QUwm2URAAOhr4tUQv_ckcnlsgorXbxw7wehqOJ7e5FUp2zcLvuVBj0WE_nKVNHiv5-Gpvz6qDJnDwL3_JnV4TzY1x1Mz-0lGUqkYWldPfLllwdrpgthJ2hGbaNDIXYbkSrDL5nJhWNrHIB0tVNHzuOLI3-JEY1jATxRTrjhZn4c0fAFRXYIPgjD3PNW0cOfi0GwXRA"/>
</div>
<div class="flex-1">
<div class="flex justify-between items-start">
<div>
<h3 class="text-base font-bold text-white leading-tight">卡若</h3>
<p class="text-xs text-subtext-dark mt-1">结构判断型咨询 · Decision &gt; Execution</p>
</div>
</div>
<div class="flex flex-wrap gap-2 mt-3">
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">项目结构判断</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">风险止损</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">人×项目匹配</span>
</div>
<div class="flex justify-between items-center mt-4 border-t border-gray-800 pt-3">
<div class="flex items-baseline gap-1">
<span class="text-lg font-bold text-primary">¥980</span>
<span class="text-xs font-normal text-gray-500">起 / 单次咨询</span>
</div>
<button class="bg-white text-black px-4 py-1.5 rounded-full text-xs font-bold hover:opacity-90 transition-opacity">预约</button>
</div>
</div>
</div>
</div>
<div class="bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-800 relative overflow-hidden group hover:border-gray-700 transition-all">
<div class="flex gap-4">
<div class="flex-shrink-0 relative">
<img alt="Mentor Profile" class="w-14 h-14 rounded-full object-cover border border-gray-700 shadow-md" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCHmRfkFR5OClBawJnpt7pU_9LygmMV2dM8eqjbmJourDUDo4WOoMhXVw7B1EPq40Qp5ENcR0Wkz3T4DgS8Wmvzz739wvo2VVn98H8vjJYqWW1WWzkXo3LiSWy78IhoE3nt0uIxFvx8BiBohPcIlqCSr6orLvUdA781O75du-RfI7a_gPWsSt29awOyyVO5RDpXQ9vhqdulaZA5LBlKa0_ZzaFCGq5eqiKV8WXTRUnNJunGsou7HEs31q8RncKHqyhPhqqGu1Un9qk"/>
</div>
<div class="flex-1">
<div class="flex justify-between items-start">
<div>
<h3 class="text-base font-bold text-white leading-tight">林晓雯</h3>
<p class="text-xs text-subtext-dark mt-1">资深产品经理 · 10年互联网经验</p>
</div>
</div>
<div class="flex flex-wrap gap-2 mt-3">
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">产品架构</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">MVP落地</span>
</div>
<div class="flex justify-between items-center mt-4 border-t border-gray-800 pt-3">
<div class="flex items-baseline gap-1">
<span class="text-lg font-bold text-primary">¥1,200</span>
<span class="text-xs font-normal text-gray-500">起 / 单次咨询</span>
</div>
<button class="bg-white text-black px-4 py-1.5 rounded-full text-xs font-bold hover:opacity-90 transition-opacity">预约</button>
</div>
</div>
</div>
</div>
<div class="bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-800 relative overflow-hidden group hover:border-gray-700 transition-all">
<div class="flex gap-4">
<div class="flex-shrink-0 relative">
<img alt="Mentor Profile" class="w-14 h-14 rounded-full object-cover border border-gray-700 shadow-md" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAxI32u0vnwLDLLpyu7oE9BwN_vgFHbOARwqczhJZ8YacePvj_rSo7yD2DCv1eKyh6eR21QQtCYhccxg1VT-VXpG4LwuXNGFjcN_Q6UMf44_7cjVTGlo0ccINiljCY7UNybGtL99zlNj6bqhI27XFT7BDbBq0EDOhCoBuQI3jUzc_ge-GcQb7EdfXXx0wVm4B715gQo67--eH1_cyHoHOSaFYHqaUffpmWEiyL7qseQ9m_nlPawgzN_vi0fEZG_PWLXK84bKcxyxJg"/>
</div>
<div class="flex-1">
<div class="flex justify-between items-start">
<div>
<h3 class="text-base font-bold text-white leading-tight">张伟</h3>
<p class="text-xs text-subtext-dark mt-1">连续创业者 · 融资顾问</p>
</div>
</div>
<div class="flex flex-wrap gap-2 mt-3">
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">BP梳理</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">股权设计</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">路演辅导</span>
</div>
<div class="flex justify-between items-center mt-4 border-t border-gray-800 pt-3">
<div class="flex items-baseline gap-1">
<span class="text-lg font-bold text-primary">¥2,500</span>
<span class="text-xs font-normal text-gray-500">起 / 单次咨询</span>
</div>
<button class="bg-white text-black px-4 py-1.5 rounded-full text-xs font-bold hover:opacity-90 transition-opacity">预约</button>
</div>
</div>
</div>
</div>
<div class="bg-card-dark rounded-2xl p-4 shadow-sm border border-gray-800 relative overflow-hidden group hover:border-gray-700 transition-all">
<div class="flex gap-4">
<div class="flex-shrink-0 relative">
<img alt="Mentor Profile" class="w-14 h-14 rounded-full object-cover border border-gray-700 shadow-md" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCGZYHocKCph6QWMbGW_jomr8o5Nb4IkALRSDYXilrMR6RZaVREGYZrQul5r2ILFWGycCwzAEILQltE4HrkmTohyETRNu-D-fHv-UvP8KxnyCzm8KjCMiMkhWeFsdWqoIQpxoaBXAkfRoF3M4n2IddKUo9k1EyUaNPzhnTtqCQwYO6UKHp7AyJeoI0-Y17vbwTvp2wFjHBhGfBSCMqbPzoKyGJYxo0MNBTrHVWnbJsv7NfGzENkg01G2SAlSXrGG691wAVnDfQwH3E"/>
</div>
<div class="flex-1">
<div class="flex justify-between items-start">
<div>
<h3 class="text-base font-bold text-white leading-tight">Sarah Chen</h3>
<p class="text-xs text-subtext-dark mt-1">跨国企业HRD · 职业规划</p>
</div>
</div>
<div class="flex flex-wrap gap-2 mt-3">
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">职业转型</span>
<span class="px-2 py-1 rounded-md text-[10px] font-medium bg-tag-bg text-tag-text border border-gray-700/50">面试辅导</span>
</div>
<div class="flex justify-between items-center mt-4 border-t border-gray-800 pt-3">
<div class="flex items-baseline gap-1">
<span class="text-lg font-bold text-primary">¥600</span>
<span class="text-xs font-normal text-gray-500">起 / 单次咨询</span>
</div>
<button class="bg-white text-black px-4 py-1.5 rounded-full text-xs font-bold hover:opacity-90 transition-opacity">预约</button>
</div>
</div>
</div>
</div>
</main>
<script>
document.documentElement.classList.add('dark');
</script>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

View File

@@ -1,269 +0,0 @@
<!DOCTYPE html><html class="dark" lang="zh-CN"><head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Clean Home with Contact Author Link</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet">
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5", // Teal accent
"background-dark": "#000000", // Pure black background
"card-dark": "#1C1C1E", // Dark gray card background
"text-primary": "#FFFFFF",
"text-secondary": "#A0AEC0",
"accent-orange": "#F6AD55",
},
fontFamily: {
sans: ['"Noto Sans SC"', 'sans-serif'],
},
borderRadius: {
DEFAULT: "0.75rem",
xl: "1rem",
'2xl': "1.5rem",
}
},
},
};
</script>
<style type="text/tailwindcss">
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
body {
min-height: max(884px, 100dvh);
background-color: #000000;
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-text-primary font-sans antialiased">
<div class="max-w-md mx-auto min-h-screen relative pb-24">
<header class="pt-6 px-4 pb-2 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-primary rounded-xl flex items-center justify-center text-black font-bold text-lg shadow-lg shadow-primary/20" style="">
S
</div>
<div>
<div class="flex items-center gap-2">
<h1 class="text-xl font-bold text-white" style="">Soul创业派对</h1>
<div class="flex items-center bg-card-dark rounded-full pr-2 pl-0.5 py-0.5 border border-gray-800">
<img alt="Author" class="w-5 h-5 rounded-full object-cover mr-1.5" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDt0LPVk4rpoGURC14aewQDlOdPLCY-WBgu-orJ7EKJzZsYaiK2eSxQEL4l36LMFzK-fXWmWyX-xfhB4NQqEOhNM1Sc0tLIJmyRFNSgeitHN4Do7Beo9rKT-kQPmKSZGq6ae4XzpkJGoq9ymqfPqgqgpgSDicCKajZrR-MHh3Xz8NvKAlVKsRWo0HKv79WIo8muyOqz7C1j1X_bb3JJm3dKGi2Ls2UBiRvTj2W5F7vejrW3iHsazchJaXEa9tzgARzYKLUICz7nY0k" style="">
<button class="text-[10px] font-bold text-primary active:scale-95 transition-transform" style="">
点击链接卡若</button>
</div>
</div>
<p class="text-xs text-text-secondary mt-0.5" style="">来自派对房的真实故事</p>
</div>
</div>
<div class="bg-card-dark px-2 py-1 rounded-md text-xs font-medium text-primary" style="">
73章
</div>
</header>
<div class="px-4 mt-4">
<div class="relative">
<span class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="material-symbols-outlined text-gray-500 text-sm" style="">search</span>
</span>
<input class="block w-full pl-10 pr-3 py-3 rounded-xl bg-card-dark border-none text-sm placeholder-gray-500 text-white focus:ring-1 focus:ring-primary transition-all" placeholder="搜索章节标题或内容..." type="text">
</div>
</div>
<div class="px-4 mt-6">
<div class="bg-gradient-to-br from-[#1a2e2c] to-black rounded-2xl p-6 relative overflow-hidden border border-gray-800">
<div class="absolute -right-10 -top-10 w-40 h-40 bg-primary/5 rounded-full blur-3xl"></div>
<span class="inline-block bg-primary text-black text-[10px] font-bold px-2 py-1 rounded-md mb-3" style="">最新更新</span>
<h2 class="text-2xl font-bold text-white mb-1" style="">流量红利的终局</h2>
<p class="text-text-secondary text-sm mb-6" style="">第五篇 真实的社会</p>
<a class="inline-flex items-center text-primary font-medium text-sm hover:text-white transition-colors" href="#" style="">
开始阅读 <span class="material-symbols-outlined text-sm ml-1" style="">arrow_forward</span>
</a>
</div>
</div>
<div class="px-4 mt-4">
<div class="bg-card-dark rounded-2xl p-4 shadow-sm">
<div class="flex justify-between items-center mb-3">
<h3 class="font-bold text-sm text-white" style="">我的阅读</h3>
<span class="text-xs text-text-secondary" style="">2/73章</span>
</div>
<div class="w-full bg-black h-1.5 rounded-full mb-4 overflow-hidden">
<div class="bg-primary h-1.5 rounded-full" style="width: 3%"></div>
</div>
<div class="grid grid-cols-4 gap-2 text-center">
<div>
<div class="text-lg font-bold text-white" style="">2</div>
<div class="text-xs text-text-secondary" style="">已读</div>
</div>
<div>
<div class="text-lg font-bold text-white" style="">71</div>
<div class="text-xs text-text-secondary" style="">待读</div>
</div>
<div>
<div class="text-lg font-bold text-white" style="">5</div>
<div class="text-xs text-text-secondary" style="">篇章</div>
</div>
<div>
<div class="text-lg font-bold text-white" style="">11</div>
<div class="text-xs text-text-secondary" style="">章节</div>
</div>
</div>
</div>
</div>
<div class="mt-8">
<div class="flex justify-between items-center px-4 mb-3">
<h3 class="text-lg font-bold text-white" style="">超级个体</h3>
<a class="text-primary text-xs flex items-center" href="#" style="">查看全部 <span class="material-symbols-outlined text-[12px] ml-0.5" style="">chevron_right</span></a>
</div>
<div class="flex overflow-x-auto px-4 gap-4 hide-scrollbar pb-2">
<div class="flex flex-col items-center min-w-[70px]">
<div class="w-14 h-14 rounded-full bg-card-dark flex items-center justify-center mb-2 overflow-hidden border border-gray-800">
<span class="material-symbols-outlined text-gray-500 text-3xl" style="">person</span>
</div>
<span class="text-[10px] text-center text-text-secondary w-full truncate" style="">微信用户RBEY</span>
</div>
<div class="flex flex-col items-center min-w-[70px]">
<div class="w-14 h-14 rounded-full bg-teal-900/20 flex items-center justify-center mb-2 overflow-hidden border border-gray-800">
<span class="material-symbols-outlined text-primary/40 text-3xl" style="">person</span>
</div>
<span class="text-[10px] text-center text-text-secondary w-full truncate" style="">微信用户HciA</span>
</div>
<div class="flex flex-col items-center min-w-[70px]">
<div class="w-14 h-14 rounded-full bg-amber-500/10 text-amber-500 font-bold flex items-center justify-center mb-2 border border-amber-500/20 shadow-md" style="">
</div>
<span class="text-[10px] text-center text-text-secondary w-full truncate" style="">好版登小程序顾问</span>
</div>
<div class="flex flex-col items-center min-w-[70px]">
<div class="w-14 h-14 rounded-full bg-card-dark flex items-center justify-center mb-2 overflow-hidden border border-gray-800">
<img alt="Profile of user" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDt0LPVk4rpoGURC14aewQDlOdPLCY-WBgu-orJ7EKJzZsYaiK2eSxQEL4l36LMFzK-fXWmWyX-xfhB4NQqEOhNM1Sc0tLIJmyRFNSgeitHN4Do7Beo9rKT-kQPmKSZGq6ae4XzpkJGoq9ymqfPqgqgpgSDicCKajZrR-MHh3Xz8NvKAlVKsRWo0HKv79WIo8muyOqz7C1j1X_bb3JJm3dKGi2Ls2UBiRvTj2W5F7vejrW3iHsazchJaXEa9tzgARzYKLUICz7nY0k" style="">
</div>
<span class="text-[10px] text-center text-text-secondary w-full truncate" style="">乘风</span>
</div>
</div>
</div>
<div class="mt-6 px-4">
<div class="flex justify-between items-center mb-3">
<h3 class="text-lg font-bold text-white" style="">精选推荐</h3>
<a class="text-primary text-xs flex items-center" href="#" style="">查看全部 <span class="material-symbols-outlined text-[12px] ml-0.5" style="">chevron_right</span></a>
</div>
<div class="space-y-3">
<div class="bg-card-dark p-4 rounded-2xl flex items-center justify-between group active:scale-[0.98] transition-all">
<div class="flex-1 pr-4">
<div class="flex items-center gap-2 mb-1">
<span class="text-primary text-sm font-bold" style="">1.1</span>
<span class="bg-accent-orange/10 text-accent-orange text-[10px] font-bold px-1.5 py-0.5 rounded" style="">热门</span>
</div>
<h4 class="font-medium text-sm text-white line-clamp-1" style="">荷包:电动车出租的被动收入模式</h4>
<p class="text-xs text-text-secondary mt-1" style="">第一篇 真实的人</p>
</div>
<span class="material-symbols-outlined text-gray-600 group-hover:text-primary transition-colors text-lg" style="">chevron_right</span>
</div>
<div class="bg-card-dark p-4 rounded-2xl flex items-center justify-between group active:scale-[0.98] transition-all">
<div class="flex-1 pr-4">
<div class="flex items-center gap-2 mb-1">
<span class="text-primary text-sm font-bold" style="">1.2</span>
<span class="bg-primary/10 text-primary text-[10px] font-bold px-1.5 py-0.5 rounded" style="">推荐</span>
</div>
<h4 class="font-medium text-sm text-white line-clamp-1" style="">老墨:资源整合高手的社交方法</h4>
<p class="text-xs text-text-secondary mt-1" style="">第一篇 真实的人</p>
</div>
<span class="material-symbols-outlined text-gray-600 group-hover:text-primary transition-colors text-lg" style="">chevron_right</span>
</div>
<div class="bg-card-dark p-4 rounded-2xl flex items-center justify-between group active:scale-[0.98] transition-all">
<div class="flex-1 pr-4">
<div class="flex items-center gap-2 mb-1">
<span class="text-primary text-sm font-bold" style="">9.17</span>
<span class="bg-primary/10 text-primary text-[10px] font-bold px-1.5 py-0.5 rounded" style="">精选</span>
</div>
<h4 class="font-medium text-sm text-white line-clamp-1" style="">第104场 | 婚恋、AI客服与一个微信</h4>
<p class="text-xs text-text-secondary mt-1" style="">第四篇 真实的赚钱</p>
</div>
<span class="material-symbols-outlined text-gray-600 group-hover:text-primary transition-colors text-lg" style="">chevron_right</span>
</div>
</div>
</div>
<div class="mt-8 px-4 pb-28">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold text-white" style="">最新新增</h3>
<span class="bg-accent-orange text-black text-[10px] font-bold px-2 py-0.5 rounded-full shadow-lg shadow-accent-orange/20" style="">+10</span>
</div>
<div class="relative pl-3 space-y-6 before:content-[''] before:absolute before:left-3 before:top-2 before:bottom-2 before:w-[1px] before:bg-gray-800">
<div class="relative pl-6 flex items-start group">
<div class="absolute left-[-4.5px] top-1.5 w-2.5 h-2.5 rounded-full bg-primary ring-4 ring-black"></div>
<div class="flex-1 pb-4 border-b border-gray-900">
<div class="flex justify-between items-start mb-1">
<div class="flex items-center gap-2">
<span class="bg-accent-orange/10 text-accent-orange text-[9px] px-1 rounded font-bold border border-accent-orange/20" style="">NEW</span>
<h4 class="text-sm font-medium text-white line-clamp-1 group-hover:text-primary transition-colors" style="">流量红利的终局</h4>
</div>
<div class="flex flex-col items-end pl-2">
<span class="text-accent-orange font-bold text-sm" style="">¥1</span>
<span class="text-[10px] text-text-secondary" style="">2/24</span>
</div>
</div>
<p class="text-xs text-text-secondary line-clamp-1" style="">深入分析当前市场的流量趋势与未来走向...</p>
</div>
</div>
<div class="relative pl-6 flex items-start group">
<div class="absolute left-[-4.5px] top-1.5 w-2.5 h-2.5 rounded-full bg-gray-800 ring-4 ring-black group-hover:bg-primary transition-colors"></div>
<div class="flex-1 pb-4 border-b border-gray-900">
<div class="flex justify-between items-start mb-1">
<div class="flex items-center gap-2">
<span class="bg-accent-orange/10 text-accent-orange text-[9px] px-1 rounded font-bold border border-accent-orange/20" style="">NEW</span>
<h4 class="text-sm font-medium text-white line-clamp-1 group-hover:text-primary transition-colors" style="">9.18 第105场 | 创业社群</h4>
</div>
<div class="flex flex-col items-end pl-2">
<span class="text-accent-orange font-bold text-sm" style="">¥1</span>
<span class="text-[10px] text-text-secondary" style="">2/24</span>
</div>
</div>
<p class="text-xs text-text-secondary line-clamp-1" style="">社群运营的核心逻辑与直播实战技巧分享...</p>
</div>
</div>
</div>
</div>
<div class="fixed bottom-6 left-4 right-4 bg-card-dark/90 backdrop-blur-md rounded-full shadow-2xl z-50 px-6 py-3 border border-gray-800">
<div class="flex justify-between items-center relative">
<button class="flex flex-col items-center gap-1 text-primary" style="">
<span class="material-symbols-outlined text-2xl font-variation-fill-1" style="">home</span>
<span class="text-[10px] font-medium" style="">首页</span>
</button>
<button class="flex flex-col items-center gap-1 text-gray-500 hover:text-primary transition-colors" style="">
<span class="material-symbols-outlined text-2xl" style="">list</span>
<span class="text-[10px] font-medium" style="">目录</span>
</button>
<div class="relative -top-8 group">
<button class="w-14 h-14 bg-primary rounded-full flex items-center justify-center shadow-lg shadow-primary/20 transform transition-transform active:scale-95" style="">
<span class="material-symbols-outlined text-black text-2xl font-bold" style="">groups</span>
</button>
<span class="absolute -bottom-6 w-full text-center text-[10px] font-medium text-gray-500" style="">找伙伴</span>
</div>
<button class="flex flex-col items-center gap-1 text-gray-500 hover:text-primary transition-colors" style="">
<span class="material-symbols-outlined text-2xl" style="">person</span>
<span class="text-[10px] font-medium" style="">我的</span>
</button>
<div class="w-2"></div>
</div>
</div>
</div>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -1,183 +0,0 @@
<!DOCTYPE html><html class="dark" lang="zh-CN"><head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>Premium Membership Landing</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet">
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#FFBD2E",
teal: {
accent: "#4FD1C5",
},
background: {
light: "#F8FAFC",
dark: "#000000",
},
card: {
dark: "#121212",
}
},
fontFamily: {
display: ["Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "sans-serif"],
},
borderRadius: {
DEFAULT: "12px",
'xl': "16px",
'2xl': "24px",
},
},
},
};
</script>
<style type="text/tailwindcss">
:root {
--gold-gradient: linear-gradient(135deg, #FFD700 0%, #FFB000 100%);
--footer-bg: #000000;
}
body {
font-family: 'Inter', "PingFang SC", sans-serif;
-webkit-tap-highlight-color: transparent;
background-color: #000000;
}
.premium-card-glow {
background: linear-gradient(135deg, #132F2C 0%, #0D0D0D 100%);
position: relative;
overflow: hidden;
}
.premium-card-glow::after {
content: '';
position: absolute;
top: -50%;
right: -20%;
width: 80%;
height: 150%;
background: radial-gradient(circle, rgba(79, 209, 197, 0.12) 0%, transparent 70%);
transform: rotate(-15deg);
}
.gold-btn-gradient {
background: var(--gold-gradient);
}
.benefit-card {
background: #141414;
border: 1px solid rgba(255, 255, 255, 0.05);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-slate-100 min-h-screen flex flex-col">
<nav class="fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-5 py-3 bg-background-dark/90 backdrop-blur-md border-b border-white/5">
<button class="flex items-center justify-center w-8 h-8 rounded-full bg-zinc-900 text-white" style="">
<span class="material-symbols-outlined text-xl" style="">chevron_left</span>
</button>
<h1 class="text-sm font-semibold tracking-tight" style="">卡若创业派对</h1>
<div class="flex items-center bg-zinc-900 px-3 py-1 rounded-full border border-white/5">
<span class="material-symbols-outlined text-sm mr-2" style="">more_horiz</span>
<div class="w-[1px] h-3 bg-zinc-700 mr-2"></div>
<span class="material-symbols-outlined text-sm" style="">radio_button_checked</span>
</div>
</nav>
<main class="pt-20 pb-40 px-4 max-w-lg mx-auto w-full space-y-8">
<section class="premium-card-glow rounded-2xl border border-zinc-800/50 p-6 shadow-2xl relative">
<div class="inline-flex items-center px-2 py-0.5 rounded bg-teal-accent/10 border border-teal-accent/20 mb-4">
<span class="text-[9px] font-bold text-teal-accent tracking-widest uppercase" style="">卡若创业派对</span>
</div>
<div class="space-y-1">
<h2 class="text-2xl font-bold leading-tight" style="">加入卡若的</h2>
<h2 class="text-2xl font-bold leading-tight text-primary" style="">创业派对 会员</h2>
</div>
<p class="mt-3 text-zinc-400 text-xs font-medium tracking-wide" style="">一次加入 尊享终身陪伴与成长</p>
</section>
<div class="grid grid-cols-2 gap-4">
<section class="space-y-3">
<div class="flex items-center gap-1.5 px-1 mb-2">
<span class="w-1 h-3 bg-teal-accent rounded-full"></span>
<h3 class="text-teal-accent font-bold text-xs tracking-wider uppercase" style="">内容权益</h3>
</div>
<div class="space-y-2">
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-teal-accent text-lg" style="">auto_stories</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">解锁全部章节</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">365天全案精读</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-teal-accent text-lg" style="">database</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">案例库</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">100+创业实战案例</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-teal-accent text-lg" style="">psychology</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">智能纪要</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">AI每日精华推送</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-teal-accent text-lg" style="">folder_open</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">会议纪要库</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">往期完整沉淀</p>
</div>
</div>
</div>
</section>
<section class="space-y-3">
<div class="flex items-center gap-1.5 px-1 mb-2">
<span class="w-1 h-3 bg-primary rounded-full"></span>
<h3 class="text-primary font-bold text-xs tracking-wider uppercase" style="">社交权益</h3>
</div>
<div class="space-y-2">
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-primary text-lg" style="">group_add</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">匹配创业伙伴</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">精准人脉匹配</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-primary text-lg" style="">leaderboard</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">创业老板排行</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">项目曝光展示</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-primary text-lg" style="">hub</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">链接资源</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">深度私域资源池</p>
</div>
</div>
<div class="benefit-card p-3 rounded-xl flex flex-col gap-2">
<span class="material-symbols-outlined text-primary text-lg" style="">verified</span>
<div>
<h4 class="font-bold text-[13px] text-white" style="">专属VIP标识</h4>
<p class="text-[10px] text-zinc-500 mt-0.5 leading-tight" style="">金色尊享光圈</p>
</div>
</div>
</div>
</section>
</div>
</main>
<footer class="fixed bottom-0 left-0 right-0 z-50 bg-black/95 backdrop-blur-sm border-t border-white/5 pb-[calc(1.5rem+env(safe-area-inset-bottom))] px-5 pt-5">
<div class="max-w-lg mx-auto">
<button class="w-full gold-btn-gradient hover:brightness-110 active:scale-[0.98] transition-all py-4 rounded-full text-black font-bold text-base shadow-[0_8px_30px_rgb(255,188,46,0.15)]" style="">
¥1980/年 加入创业派对
</button>
</div>
</footer>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

View File

@@ -1,221 +0,0 @@
<html lang="zh-CN"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Updated Community Profile Screen</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#4FD1C5",
"background-light": "#F3F4F6",
"background-dark": "#121212",
"card-light": "#FFFFFF",
"card-dark": "#1A1A1A",
"card-inner": "#252525",
"text-light": "#1F2937",
"text-dark": "#E5E7EB",
"secondary-light": "#9CA3AF",
"secondary-dark": "#6B7280",
"vip-gold": "#C8A146",
},
fontFamily: {
display: ["Inter", "system-ui", "-apple-system", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.75rem",
xl: "1rem",
'2xl': "1.5rem",
}
},
},
};
</script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap" rel="stylesheet"/>
<style type="text/tailwindcss">
body {
font-family: 'Inter', sans-serif;
min-height: max(884px, 100dvh);
background-color: #121212;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.avatar-glow {
box-shadow: 0 0 12px rgba(200, 161, 70, 0.4);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background-dark text-text-dark antialiased min-h-screen flex flex-col relative pb-24">
<div class="px-6 py-3 flex justify-between items-center text-sm font-medium z-10 sticky top-0 bg-background-dark bg-opacity-90 backdrop-blur-sm">
<span class="text-white">9:41</span>
<div class="flex items-center space-x-2">
<span class="material-symbols-outlined text-[18px] text-white">signal_cellular_alt</span>
<span class="material-symbols-outlined text-[18px] text-white">wifi</span>
<span class="material-symbols-outlined text-[18px] text-white">battery_full</span>
</div>
</div>
<header class="px-5 pt-2 pb-6 flex flex-col relative">
<div class="flex justify-between items-center mb-6">
<h1 class="text-xl font-bold text-[#4FD1C5]">我的</h1>
<div class="flex items-center">
<button class="p-2 rounded-full hover:bg-gray-800 transition">
<span class="material-symbols-outlined text-gray-300">settings</span>
</button>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="relative">
<div class="w-[72px] h-[72px] rounded-full bg-[#1C2524] overflow-hidden border-[2.5px] border-[#C8A146] avatar-glow flex items-center justify-center">
<img alt="User Avatar" class="w-full h-full object-cover rounded-full" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBo2PHq2d-8F1vMun8MHr81-HxHRvAL3uAlT1_pEGiX6oUcRR_1hgwzwmN4LBMwu3808aUN6wHKR6Ks2JsjXY5xnUSldONlmVaC_2H9sHl_ZZO_tz0gTqfqsTWb82LeQFet6BO5g4MCXjE22pK0Kxvhl5xYrvLBbh1YzHl9ZH2FcpUmCcq7P0FhGS5WkbeojWMT0uEdndmeSBkgTmLwjXuJk1KRUMn3UYd1h5eC9QjoScBWwCFl_5zRVnWeqOGXwHxziIbrzizqrqY"/>
</div>
<div class="absolute -bottom-1 -right-0.5 bg-gradient-to-r from-[#E6B84D] to-[#D4A017] rounded-[4px] px-1.5 py-0.5 flex items-center justify-center shadow-lg">
<span class="text-[9px] font-black text-black leading-none italic">VIP</span>
</div>
</div>
<div class="flex-1">
<div class="flex items-center gap-3">
<h2 class="text-[28px] font-bold text-white tracking-wide">卡若</h2>
<div class="flex gap-1.5 ml-1">
<span class="text-[10px] text-[#C8A146] border border-[#C8A146] rounded-[4px] px-1.5 py-0.5 bg-[#C8A146]/10">会员</span>
<span class="text-[10px] text-[#C8A146] border border-[#C8A146] rounded-[4px] px-1.5 py-0.5 bg-[#C8A146]/10">匹配</span>
<span class="text-[10px] text-[#C8A146] border border-[#C8A146] rounded-[4px] px-1.5 py-0.5 bg-[#C8A146]/10">排行</span>
</div>
</div>
<div class="mt-2 text-sm text-gray-500 font-medium">
ID: ogpTW5Wbbo...6EGc-g
</div>
<div class="mt-1 text-[11px] text-gray-500">
会员到期时间2025-12-31
</div>
</div>
</div>
</header>
<main class="flex-1 px-4 space-y-4">
<div class="bg-card-dark rounded-xl p-5 shadow-sm border border-gray-800/50">
<div class="flex items-center gap-2 mb-4">
<span class="material-symbols-outlined text-primary">account_balance_wallet</span>
<h3 class="font-bold text-white">分享收益</h3>
</div>
<div class="grid grid-cols-3 gap-2 text-center divide-x divide-gray-800">
<div class="flex flex-col items-center">
<span class="text-xl font-bold text-primary">0</span>
<span class="text-xs text-secondary-dark mt-1">推荐好友</span>
</div>
<div class="flex flex-col items-center">
<span class="text-xl font-bold text-primary">0.00</span>
<span class="text-xs text-secondary-dark mt-1">我的收益</span>
</div>
<div class="flex flex-col items-center">
<span class="text-xl font-bold text-primary">--</span>
<span class="text-xs text-secondary-dark mt-1">可提现金额</span>
</div>
</div>
</div>
<div class="bg-card-dark rounded-xl p-5 shadow-sm border border-gray-800/50">
<div class="flex items-center gap-2 mb-4">
<span class="material-symbols-outlined text-primary">visibility</span>
<h3 class="font-bold text-white">阅读统计</h3>
</div>
<div class="grid grid-cols-3 gap-3">
<div class="bg-card-inner rounded-lg p-3 flex flex-col items-center justify-center aspect-[4/3]">
<span class="material-symbols-outlined text-primary mb-2 text-2xl">menu_book</span>
<span class="text-xl font-bold text-white">2</span>
<span class="text-[10px] text-gray-500 mt-1">已读章节</span>
</div>
<div class="bg-card-inner rounded-lg p-3 flex flex-col items-center justify-center aspect-[4/3]">
<span class="material-symbols-outlined text-primary mb-2 text-2xl">timer</span>
<span class="text-xl font-bold text-white">97</span>
<span class="text-[10px] text-gray-500 mt-1">阅读分钟</span>
</div>
<div class="bg-card-inner rounded-lg p-3 flex flex-col items-center justify-center aspect-[4/3]">
<span class="material-symbols-outlined text-primary mb-2 text-2xl">group</span>
<span class="text-xl font-bold text-white">0</span>
<span class="text-[10px] text-gray-500 mt-1">匹配伙伴</span>
</div>
</div>
</div>
<div class="bg-card-dark rounded-xl p-5 shadow-sm border border-gray-800/50">
<div class="flex items-center gap-2 mb-4">
<span class="material-symbols-outlined text-primary">history_edu</span>
<h3 class="font-bold text-white">最近阅读</h3>
</div>
<div class="space-y-3">
<div class="flex items-center justify-between p-3 rounded-lg bg-card-inner">
<div class="flex items-center gap-3 overflow-hidden">
<span class="text-gray-500 font-mono text-sm">1</span>
<span class="text-sm text-gray-200 truncate">章节 preface</span>
</div>
<a class="text-xs text-primary font-medium hover:underline shrink-0 ml-2" href="#">继续阅读</a>
</div>
<div class="flex items-center justify-between p-3 rounded-lg bg-card-inner">
<div class="flex items-center gap-3 overflow-hidden">
<span class="text-gray-500 font-mono text-sm">2</span>
<span class="text-sm text-gray-200 truncate">章节 1.1</span>
</div>
<a class="text-xs text-primary font-medium hover:underline shrink-0 ml-2" href="#">继续阅读</a>
</div>
</div>
</div>
<div class="bg-card-dark rounded-xl overflow-hidden shadow-sm border border-gray-800/50 mb-6">
<a class="flex items-center justify-between p-4 hover:bg-card-inner transition-colors border-b border-gray-800" href="#">
<div class="flex items-center gap-3">
<div class="w-6 h-6 rounded-full bg-teal-900/30 flex items-center justify-center">
<span class="material-symbols-outlined text-teal-400 text-sm">inventory_2</span>
</div>
<span class="text-sm text-gray-200 font-medium">我的订单</span>
</div>
<span class="material-symbols-outlined text-gray-400 text-sm">chevron_right</span>
</a>
<a class="flex items-center justify-between p-4 hover:bg-card-inner transition-colors border-gray-100 last:border-0" href="#">
<div class="flex items-center gap-3">
<div class="w-6 h-6 rounded-full bg-blue-900/30 flex items-center justify-center">
<span class="material-symbols-outlined text-blue-400 text-sm">info</span>
</div>
<span class="text-sm text-gray-200 font-medium">关于作者</span>
</div>
<span class="material-symbols-outlined text-gray-400 text-sm">chevron_right</span>
</a>
</div>
</main>
<nav class="fixed bottom-0 left-0 right-0 bg-card-dark border-t border-gray-800 pb-safe pt-2 px-6 flex justify-between items-end z-50 h-[84px]">
<a class="flex flex-col items-center space-y-1 w-16 mb-2 group" href="#">
<span class="material-symbols-outlined text-gray-500 group-hover:text-primary transition-colors text-2xl">home</span>
<span class="text-[10px] text-gray-500 group-hover:text-primary font-medium">首页</span>
</a>
<a class="flex flex-col items-center space-y-1 w-16 mb-2 group" href="#">
<span class="material-symbols-outlined text-gray-500 group-hover:text-primary transition-colors text-2xl">list</span>
<span class="text-[10px] text-gray-500 group-hover:text-primary font-medium">目录</span>
</a>
<a class="relative -top-6 flex flex-col items-center group" href="#">
<div class="w-14 h-14 rounded-full bg-primary shadow-lg shadow-primary/30 flex items-center justify-center transform group-hover:scale-105 transition-transform border-4 border-background-dark">
<span class="material-symbols-outlined text-white text-3xl">people</span>
</div>
<span class="text-[10px] text-primary font-medium mt-1">找伙伴</span>
</a>
<a class="flex flex-col items-center space-y-1 w-16 mb-2 group" href="#">
<span class="material-symbols-outlined text-primary transition-colors text-2xl">person</span>
<span class="text-[10px] text-primary font-medium">我的</span>
</a>
</nav>
<div class="h-24"></div>
<script>
document.documentElement.classList.add('dark');
</script>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

413
开发文档/api_v1.md Normal file
View File

@@ -0,0 +1,413 @@
# 对外获客线索上报接口文档V1
## 一、接口概述
- **接口名称**:对外获客线索上报接口
- **接口用途**:供第三方系统向【存客宝】上报客户线索(手机号 / 微信号等),用于后续的跟进、标签管理和画像分析。
- **接口协议**HTTP
- **请求方式**`POST`
- **请求地址** `https://ckbapi.quwanzhi.com/v1/api/scenarios`
> 具体 URL 以实际环境配置为准。
- **数据格式**
- 推荐:`application/json`
- 兼容:`application/x-www-form-urlencoded`
- **字符编码**`UTF-8`
---
## 二、鉴权与签名
### 2.1 必填鉴权字段
| 字段名 | 类型 | 必填 | 说明 |
|-------------|--------|------|---------------------------------------|
| `apiKey` | string | 是 | 分配给第三方的接口密钥(每个任务唯一)|
| `sign` | string | 是 | 签名值 |
| `timestamp` | int | 是 | 秒级时间戳(与服务器时间差不超过 5 分钟) |
### 2.2 时间戳校验
服务器会校验 `timestamp` 是否在当前时间前后 **5 分钟** 内:
- 通过条件:`|server_time - timestamp| <= 300`
- 超出范围则返回:`请求已过期`
### 2.3 签名生成规则
接口采用自定义签名机制。**签名字段为 `sign`,生成步骤如下:**
假设本次请求的所有参数为 `params`,其中包括业务参数 + `apiKey` + `timestamp` + `sign` + 可能存在的 `portrait` 对象。
#### 第一步:移除特定字段
`params` 中移除以下字段:
- `sign` —— 自身不参与签名
- `apiKey` —— 不参与参数拼接,仅在最后一步参与二次 MD5
- `portrait` —— 整个画像对象不参与签名(即使内部还有子字段)
> 说明:`portrait` 通常是一个 JSON 对象,字段较多,为避免签名实现复杂且双方难以对齐,统一不参与签名。
#### 第二步:移除空值字段
从剩余参数中,移除值为:
- `null`
- 空字符串 `''`
的字段,这些字段不参与签名。
#### 第三步:按参数名升序排序
对剩余参数按**参数名(键名)升序排序**,排序规则为标准的 ASCII 升序:
```text
例如: name, phone, source, timestamp
```
#### 第四步:拼接参数值
将排序后的参数 **只取“值”**,按顺序直接拼接为一个字符串,中间不加任何分隔符:
- 示例:
排序后参数为:
```text
name = 张三
phone = 13800000000
source = 微信广告
timestamp = 1710000000
```
则拼接:
```text
stringToSign = "张三13800000000微信广告1710000000"
```
#### 第五步:第一次 MD5
对上一步拼接得到的字符串做一次 MD5
\[
\text{firstMd5} = \text{MD5}(\text{stringToSign})
\]
#### 第六步:拼接 apiKey 再次 MD5
将第一步的结果与 `apiKey` 直接拼接,再做一次 MD5得到最终签名值
\[
\text{sign} = \text{MD5}(\text{firstMd5} + \text{apiKey})
\]
#### 第七步:放入请求
将第六步得到的 `sign` 填入请求参数中的 `sign` 字段即可。
> 建议:
> - 使用小写 MD5 字符串(双方约定统一即可)。
> - 请确保参与签名的参数与最终请求发送的参数一致(包括是否传空值)。
### 2.4 签名示例PHP 伪代码)
```php
$params = [
'apiKey' => 'YOUR_API_KEY',
'timestamp' => '1710000000',
'phone' => '13800000000',
'name' => '张三',
'source' => '微信广告',
'remark' => '通过H5落地页留资',
// 'portrait' => [...], // 如有画像,这里会存在,但不参与签名
// 'sign' => '待生成',
];
// 1. 去掉 sign、apiKey、portrait
unset($params['sign'], $params['apiKey'], $params['portrait']);
// 2. 去掉空值
$params = array_filter($params, function($value) {
return !is_null($value) && $value !== '';
});
// 3. 按键名升序排序
ksort($params);
// 4. 拼接参数值
$stringToSign = implode('', array_values($params));
// 5. 第一次 MD5
$firstMd5 = md5($stringToSign);
// 6. 第二次 MD5拼接 apiKey
$apiKey = 'YOUR_API_KEY';
$sign = md5($firstMd5 . $apiKey);
// 将 $sign 作为字段发送
$params['sign'] = $sign;
```
---
## 三、请求参数说明
### 3.1 主标识字段(至少传一个)
| 字段名 | 类型 | 必填 | 说明 |
|-----------|--------|------|-------------------------------------------|
| `wechatId`| string | 否 | 微信号,存在时优先作为主标识 |
| `phone` | string | 否 | 手机号,当 `wechatId` 为空时用作主标识 |
### 3.2 基础信息字段
| 字段名 | 类型 | 必填 | 说明 |
|------------|--------|------|-------------------------|
| `name` | string | 否 | 客户姓名 |
| `source` | string | 否 | 线索来源描述,如“百度推广”、“抖音直播间” |
| `remark` | string | 否 | 备注信息 |
| `tags` | string | 否 | 逗号分隔的“微信标签”,如:`"高意向,电商,女装"` |
| `siteTags` | string | 否 | 逗号分隔的“站内标签”,用于站内进一步细分 |
### 3.3 用户画像字段 `portrait`(可选)
`portrait` 为一个对象JSON用于记录用户的行为画像数据。
#### 3.3.1 基本示例
```json
"portrait": {
"type": 1,
"source": 1,
"sourceData": {
"age": 28,
"gender": "female",
"city": "上海",
"productId": "P12345",
"pageUrl": "https://example.com/product/123"
},
"remark": "画像-基础属性",
"uniqueId": "user_13800000000_20250301_001"
}
```
#### 3.3.2 字段详细说明
| 字段名 | 类型 | 必填 | 说明 |
|-----------------------|--------|------|----------------------------------------|
| `portrait.type` | int | 否 | 画像类型,枚举值:<br>0-浏览<br>1-点击<br>2-下单/购买<br>3-注册<br>4-互动<br>默认值0 |
| `portrait.source` | int | 否 | 画像来源,枚举值:<br>0-本站<br>1-老油条<br>2-老坑爹<br>默认值0 |
| `portrait.sourceData` | object | 否 | 画像明细数据(键值对,会存储为 JSON 格式)<br>可包含任意业务相关的键值对年龄、性别、城市、商品ID、页面URL等 |
| `portrait.remark` | string | 否 | 画像备注信息最大长度100字符 |
| `portrait.uniqueId` | string | 否 | 画像去重用唯一 ID<br>用于防止重复记录,相同 `uniqueId` 的画像数据在半小时内会被合并统计count字段累加<br>建议格式:`{来源标识}_{用户标识}_{时间戳}_{序号}` |
#### 3.3.3 画像类型type说明
| 值 | 类型 | 说明 | 适用场景 |
|---|------|------|---------|
| 0 | 浏览 | 用户浏览了页面或内容 | 页面访问、商品浏览、文章阅读等 |
| 1 | 点击 | 用户点击了某个元素 | 按钮点击、链接点击、广告点击等 |
| 2 | 下单/购买 | 用户完成了购买行为 | 订单提交、支付完成等 |
| 3 | 注册 | 用户完成了注册 | 账号注册、会员注册等 |
| 4 | 互动 | 用户进行了互动行为 | 点赞、评论、分享、咨询等 |
#### 3.3.4 画像来源source说明
| 值 | 来源 | 说明 |
|---|------|------|
| 0 | 本站 | 来自本站的数据 |
| 1 | 老油条 | 来自"老油条"系统的数据 |
| 2 | 老坑爹 | 来自"老坑爹"系统的数据 |
#### 3.3.5 sourceData 数据格式说明
`sourceData` 是一个 JSON 对象,可以包含任意业务相关的键值对。常见字段示例:
```json
{
"age": 28,
"gender": "female",
"city": "上海",
"province": "上海市",
"productId": "P12345",
"productName": "商品名称",
"category": "女装",
"price": 299.00,
"pageUrl": "https://example.com/product/123",
"referrer": "https://www.baidu.com",
"device": "mobile",
"browser": "WeChat"
}
```
> **注意**
> - `sourceData` 中的数据类型可以是字符串、数字、布尔值等
> - 嵌套对象会被序列化为 JSON 字符串存储
> - 建议根据实际业务需求定义字段结构
#### 3.3.6 uniqueId 去重机制说明
- **作用**:防止重复记录相同的画像数据
- **规则**:相同 `uniqueId` 的画像数据在 **半小时内** 会被合并统计,`count` 字段会自动累加
- **建议格式**`{来源标识}_{用户标识}_{时间戳}_{序号}`
- 示例:`site_13800000000_1710000000_001`
- 示例:`wechat_wxid_abc123_1710000000_001`
- **注意事项**
- 如果不传 `uniqueId`,系统会为每条画像数据创建新记录
- 如果需要在半小时内多次统计同一行为,应使用相同的 `uniqueId`
- 如果需要在半小时后重新统计,应使用不同的 `uniqueId`(建议修改时间戳部分)
> **重要提示**`portrait` **整体不参与签名计算**,但会参与业务处理。系统会根据 `uniqueId` 自动处理去重和统计。
---
## 四、请求示例
### 4.1 JSON 请求示例(无画像)
```json
{
"apiKey": "YOUR_API_KEY",
"timestamp": 1710000000,
"phone": "13800000000",
"name": "张三",
"source": "微信广告",
"remark": "通过H5落地页留资",
"tags": "高意向,电商",
"siteTags": "新客,女装",
"sign": "根据签名规则生成的MD5字符串"
}
```
### 4.2 JSON 请求示例(带微信号与画像)
```json
{
"apiKey": "YOUR_API_KEY",
"timestamp": 1710000000,
"wechatId": "wxid_abcdefg123",
"phone": "13800000001",
"name": "李四",
"source": "小程序落地页",
"remark": "点击【立即咨询】按钮",
"tags": "中意向,直播",
"siteTags": "复购,高客单",
"portrait": {
"type": 1,
"source": 0,
"sourceData": {
"age": 28,
"gender": "female",
"city": "上海",
"pageUrl": "https://example.com/product/123",
"productId": "P12345"
},
"remark": "画像-点击行为",
"uniqueId": "site_13800000001_1710000000_001"
},
"sign": "根据签名规则生成的MD5字符串"
}
```
### 4.3 JSON 请求示例(多种画像类型)
#### 4.3.1 浏览行为画像
```json
{
"apiKey": "YOUR_API_KEY",
"timestamp": 1710000000,
"phone": "13800000002",
"name": "王五",
"source": "百度推广",
"portrait": {
"type": 0,
"source": 0,
"sourceData": {
"pageUrl": "https://example.com/product/456",
"productName": "商品名称",
"category": "女装",
"stayTime": 120,
"device": "mobile"
},
"remark": "商品浏览",
"uniqueId": "site_13800000002_1710000000_001"
},
"sign": "根据签名规则生成的MD5字符串"
}
```
```
---
## 五、响应说明
### 5.1 成功响应
**1新增线索成功**
```json
{
"code": 200,
"message": "新增成功",
"data": "13800000000"
}
```
**2线索已存在**
```json
{
"code": 200,
"message": "已存在",
"data": "13800000000"
}
```
> `data` 字段返回本次线索的主标识 `wechatId` 或 `phone`。
### 5.2 常见错误响应
```json
{ "code": 400, "message": "apiKey不能为空", "data": null }
{ "code": 400, "message": "sign不能为空", "data": null }
{ "code": 400, "message": "timestamp不能为空", "data": null }
{ "code": 400, "message": "请求已过期", "data": null }
{ "code": 401, "message": "无效的apiKey", "data": null }
{ "code": 401, "message": "签名验证失败", "data": null }
{ "code": 500, "message": "系统错误: 具体错误信息", "data": null }
```
---
## 六、常见问题FAQ
### Q1: 如果同一个用户多次上报相同的行为,会如何处理?
**A**: 如果使用相同的 `uniqueId`,系统会在半小时内合并统计,`count` 字段会累加。如果使用不同的 `uniqueId`,会创建多条记录。
### Q2: portrait 字段是否必须传递?
**A**: 不是必须的。`portrait` 字段是可选的,只有在需要记录用户画像数据时才传递。
### Q3: sourceData 中可以存储哪些类型的数据?
**A**: `sourceData` 是一个 JSON 对象,可以存储任意键值对。支持字符串、数字、布尔值等基本类型,嵌套对象会被序列化为 JSON 字符串。
### Q4: uniqueId 的作用是什么?
**A**: `uniqueId` 用于防止重复记录。相同 `uniqueId` 的画像数据在半小时内会被合并统计,避免重复数据。
### Q5: 画像数据如何与用户关联?
**A**: 系统会根据请求中的 `wechatId` 或 `phone` 自动匹配 `traffic_pool` 表中的用户,并将画像数据关联到对应的 `trafficPoolId`。
---