From a0f19e8cdb73681dc4ef0dde886f660aa7ce5534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=98=E9=A3=8E?= Date: Tue, 3 Feb 2026 19:18:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tminiprogram/.gitignore | 14 + Tminiprogram/README.md | 138 +++ Tminiprogram/app.js | 464 +++++++ Tminiprogram/app.json | 60 + Tminiprogram/app.wxss | 568 +++++++++ Tminiprogram/assets/icons/home-active.png | Bin 0 -> 699 bytes Tminiprogram/assets/icons/home.png | Bin 0 -> 611 bytes Tminiprogram/assets/icons/match-active.png | Bin 0 -> 907 bytes Tminiprogram/assets/icons/match.png | Bin 0 -> 725 bytes Tminiprogram/assets/icons/my-active.png | Bin 0 -> 907 bytes Tminiprogram/assets/icons/my.png | Bin 0 -> 725 bytes Tminiprogram/custom-tab-bar/index.js | 51 + Tminiprogram/custom-tab-bar/index.json | 3 + Tminiprogram/custom-tab-bar/index.wxml | 56 + Tminiprogram/custom-tab-bar/index.wxss | 227 ++++ Tminiprogram/pages/about/about.js | 81 ++ Tminiprogram/pages/about/about.json | 4 + Tminiprogram/pages/about/about.wxml | 75 ++ Tminiprogram/pages/about/about.wxss | 40 + Tminiprogram/pages/chapters/chapters.js | 251 ++++ Tminiprogram/pages/chapters/chapters.json | 6 + Tminiprogram/pages/chapters/chapters.wxml | 120 ++ Tminiprogram/pages/chapters/chapters.wxss | 448 +++++++ Tminiprogram/pages/index/index.js | 193 +++ Tminiprogram/pages/index/index.json | 6 + Tminiprogram/pages/index/index.wxml | 146 +++ Tminiprogram/pages/index/index.wxss | 504 ++++++++ Tminiprogram/pages/match/match.js | 655 ++++++++++ Tminiprogram/pages/match/match.json | 6 + Tminiprogram/pages/match/match.wxml | 295 +++++ Tminiprogram/pages/match/match.wxss | 1202 +++++++++++++++++++ Tminiprogram/pages/my/my.js | 348 ++++++ Tminiprogram/pages/my/my.json | 6 + Tminiprogram/pages/my/my.wxml | 204 ++++ Tminiprogram/pages/my/my.wxss | 996 +++++++++++++++ Tminiprogram/pages/purchases/purchases.js | 45 + Tminiprogram/pages/purchases/purchases.json | 4 + Tminiprogram/pages/purchases/purchases.wxml | 35 + Tminiprogram/pages/purchases/purchases.wxss | 21 + Tminiprogram/pages/read/read.js | 909 ++++++++++++++ Tminiprogram/pages/read/read.json | 7 + Tminiprogram/pages/read/read.wxml | 228 ++++ Tminiprogram/pages/read/read.wxss | 917 ++++++++++++++ Tminiprogram/pages/referral/referral.js | 553 +++++++++ Tminiprogram/pages/referral/referral.json | 4 + Tminiprogram/pages/referral/referral.wxml | 223 ++++ Tminiprogram/pages/referral/referral.wxss | 148 +++ Tminiprogram/pages/search/search.js | 109 ++ Tminiprogram/pages/search/search.json | 5 + Tminiprogram/pages/search/search.wxml | 113 ++ Tminiprogram/pages/search/search.wxss | 335 ++++++ Tminiprogram/pages/settings/settings.js | 452 +++++++ Tminiprogram/pages/settings/settings.json | 4 + Tminiprogram/pages/settings/settings.wxml | 147 +++ Tminiprogram/pages/settings/settings.wxss | 112 ++ Tminiprogram/project.config.json | 56 + Tminiprogram/project.private.config.json | 7 + Tminiprogram/sitemap.json | 7 + Tminiprogram/utils/payment.js | 211 ++++ Tminiprogram/utils/util.js | 182 +++ Tminiprogram/小程序快速配置指南.md | 272 +++++ Tminiprogram/小程序部署说明.md | 463 +++++++ Tminiprogram/测试二维码.html | 366 ++++++ Tminiprogram/生成图标.html | 71 ++ Tminiprogram/自动部署.sh | 82 ++ 65 files changed, 13255 insertions(+) create mode 100644 Tminiprogram/.gitignore create mode 100644 Tminiprogram/README.md create mode 100644 Tminiprogram/app.js create mode 100644 Tminiprogram/app.json create mode 100644 Tminiprogram/app.wxss create mode 100644 Tminiprogram/assets/icons/home-active.png create mode 100644 Tminiprogram/assets/icons/home.png create mode 100644 Tminiprogram/assets/icons/match-active.png create mode 100644 Tminiprogram/assets/icons/match.png create mode 100644 Tminiprogram/assets/icons/my-active.png create mode 100644 Tminiprogram/assets/icons/my.png create mode 100644 Tminiprogram/custom-tab-bar/index.js create mode 100644 Tminiprogram/custom-tab-bar/index.json create mode 100644 Tminiprogram/custom-tab-bar/index.wxml create mode 100644 Tminiprogram/custom-tab-bar/index.wxss create mode 100644 Tminiprogram/pages/about/about.js create mode 100644 Tminiprogram/pages/about/about.json create mode 100644 Tminiprogram/pages/about/about.wxml create mode 100644 Tminiprogram/pages/about/about.wxss create mode 100644 Tminiprogram/pages/chapters/chapters.js create mode 100644 Tminiprogram/pages/chapters/chapters.json create mode 100644 Tminiprogram/pages/chapters/chapters.wxml create mode 100644 Tminiprogram/pages/chapters/chapters.wxss create mode 100644 Tminiprogram/pages/index/index.js create mode 100644 Tminiprogram/pages/index/index.json create mode 100644 Tminiprogram/pages/index/index.wxml create mode 100644 Tminiprogram/pages/index/index.wxss create mode 100644 Tminiprogram/pages/match/match.js create mode 100644 Tminiprogram/pages/match/match.json create mode 100644 Tminiprogram/pages/match/match.wxml create mode 100644 Tminiprogram/pages/match/match.wxss create mode 100644 Tminiprogram/pages/my/my.js create mode 100644 Tminiprogram/pages/my/my.json create mode 100644 Tminiprogram/pages/my/my.wxml create mode 100644 Tminiprogram/pages/my/my.wxss create mode 100644 Tminiprogram/pages/purchases/purchases.js create mode 100644 Tminiprogram/pages/purchases/purchases.json create mode 100644 Tminiprogram/pages/purchases/purchases.wxml create mode 100644 Tminiprogram/pages/purchases/purchases.wxss create mode 100644 Tminiprogram/pages/read/read.js create mode 100644 Tminiprogram/pages/read/read.json create mode 100644 Tminiprogram/pages/read/read.wxml create mode 100644 Tminiprogram/pages/read/read.wxss create mode 100644 Tminiprogram/pages/referral/referral.js create mode 100644 Tminiprogram/pages/referral/referral.json create mode 100644 Tminiprogram/pages/referral/referral.wxml create mode 100644 Tminiprogram/pages/referral/referral.wxss create mode 100644 Tminiprogram/pages/search/search.js create mode 100644 Tminiprogram/pages/search/search.json create mode 100644 Tminiprogram/pages/search/search.wxml create mode 100644 Tminiprogram/pages/search/search.wxss create mode 100644 Tminiprogram/pages/settings/settings.js create mode 100644 Tminiprogram/pages/settings/settings.json create mode 100644 Tminiprogram/pages/settings/settings.wxml create mode 100644 Tminiprogram/pages/settings/settings.wxss create mode 100644 Tminiprogram/project.config.json create mode 100644 Tminiprogram/project.private.config.json create mode 100644 Tminiprogram/sitemap.json create mode 100644 Tminiprogram/utils/payment.js create mode 100644 Tminiprogram/utils/util.js create mode 100644 Tminiprogram/小程序快速配置指南.md create mode 100644 Tminiprogram/小程序部署说明.md create mode 100644 Tminiprogram/测试二维码.html create mode 100644 Tminiprogram/生成图标.html create mode 100644 Tminiprogram/自动部署.sh diff --git a/Tminiprogram/.gitignore b/Tminiprogram/.gitignore new file mode 100644 index 00000000..14ea590c --- /dev/null +++ b/Tminiprogram/.gitignore @@ -0,0 +1,14 @@ +# Windows +[Dd]esktop.ini +Thumbs.db +$RECYCLE.BIN/ + +# macOS +.DS_Store +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes + +# Node.js +node_modules/ diff --git a/Tminiprogram/README.md b/Tminiprogram/README.md new file mode 100644 index 00000000..2cc11dbf --- /dev/null +++ b/Tminiprogram/README.md @@ -0,0 +1,138 @@ +# Soul创业实验 - 微信小程序 + +> 一场SOUL的创业实验场 - 来自Soul派对房的真实商业故事 + +## 📱 项目简介 + +本项目是《一场SOUL的创业实验场》的微信小程序版本,完整还原了Web端的所有UI界面和功能。 + +## 🎨 设计特点 + +- **主题色**: Soul青色 (#00CED1) +- **设计风格**: 深色主题 + 毛玻璃效果 +- **1:1还原**: 完全复刻Web端的UI设计 + +## 📂 项目结构 + +``` +miniprogram/ +├── app.js # 应用入口 +├── app.json # 应用配置 +├── app.wxss # 全局样式 +├── custom-tab-bar/ # 自定义TabBar组件 +│ ├── index.js +│ ├── index.json +│ ├── index.wxml +│ └── index.wxss +├── pages/ +│ ├── index/ # 首页 +│ ├── chapters/ # 目录页 +│ ├── match/ # 找伙伴页 +│ ├── my/ # 我的页面 +│ ├── read/ # 阅读页 +│ ├── about/ # 关于作者 +│ ├── referral/ # 推广中心 +│ ├── purchases/ # 订单页 +│ └── settings/ # 设置页 +├── utils/ +│ ├── util.js # 工具函数 +│ └── payment.js # 支付工具 +├── assets/ +│ └── icons/ # 图标资源 +├── project.config.json # 项目配置 +└── sitemap.json # 站点地图 +``` + +## 🚀 功能列表 + +### 核心功能 +- ✅ 首页 - 书籍展示、推荐章节、阅读进度 +- ✅ 目录 - 完整章节列表、篇章折叠展开 +- ✅ 找伙伴 - 匹配动画、匹配类型选择 +- ✅ 我的 - 个人信息、订单、推广中心 +- ✅ 阅读 - 付费墙、章节导航、分享功能 + +### 特色功能 +- ✅ 自定义TabBar(中间突出的找伙伴按钮) +- ✅ 阅读进度条 +- ✅ 匹配动画效果 +- ✅ 付费墙与购买流程 +- ✅ 分享海报功能 +- ✅ 推广佣金系统 + +## 🛠 开发指南 + +### 环境要求 +- 微信开发者工具 >= 1.06.2308310 +- 基础库版本 >= 3.3.4 + +### 快速开始 + +1. **下载微信开发者工具** + - 前往 [微信开发者工具下载页面](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) + +2. **导入项目** + - 打开微信开发者工具 + - 选择"导入项目" + - 项目目录选择 `miniprogram` 文件夹 + - AppID 使用: `wx432c93e275548671` + +3. **编译运行** + - 点击"编译"按钮 + - 在模拟器中预览效果 + +### 真机调试 + +1. 点击工具栏的"预览"按钮 +2. 使用微信扫描二维码 +3. 在真机上测试所有功能 + +## 📝 配置说明 + +### API配置 +在 `app.js` 中修改 `globalData.baseUrl`: + +```javascript +globalData: { + baseUrl: 'https://soul.ckb.fit', // 你的API地址 + // ... +} +``` + +### AppID配置 +在 `project.config.json` 中修改: + +```json +{ + "appid": "你的小程序AppID" +} +``` + +## 🎯 上线发布 + +1. **准备工作** + - 确保所有功能测试通过 + - 检查API接口是否正常 + - 确认支付功能已配置 + +2. **上传代码** + - 在开发者工具中点击"上传" + - 填写版本号和项目备注 + +3. **提交审核** + - 登录[微信公众平台](https://mp.weixin.qq.com) + - 进入"版本管理" + - 提交审核 + +4. **发布上线** + - 审核通过后点击"发布" + +## 🔗 相关链接 + +- **Web版本**: https://soul.ckb.fit +- **作者微信**: 28533368 +- **技术支持**: 存客宝 + +## 📄 版权信息 + +© 2024 卡若. All rights reserved. diff --git a/Tminiprogram/app.js b/Tminiprogram/app.js new file mode 100644 index 00000000..e0c3901b --- /dev/null +++ b/Tminiprogram/app.js @@ -0,0 +1,464 @@ +/** + * Soul创业派对 - 小程序入口 + * 开发: 卡若 + */ + +App({ + globalData: { + // API基础地址 - 连接真实后端 + baseUrl: 'https://soul.quwanzhi.com', + + // 小程序配置 - 真实AppID + appId: 'wxb8bbb2b10dec74aa', + + // 微信支付配置 + mchId: '1318592501', // 商户号 + + // 用户信息 + userInfo: null, + openId: null, // 微信openId,支付必需 + isLoggedIn: false, + + // 书籍数据 + bookData: null, + totalSections: 62, + + // 购买记录 + purchasedSections: [], + hasFullBook: false, + + // 推荐绑定 + pendingReferralCode: null, // 待绑定的推荐码 + + // 主题配置 + theme: { + brandColor: '#00CED1', + brandSecondary: '#20B2AA', + goldColor: '#FFD700', + bgColor: '#000000', + cardBg: '#1c1c1e' + }, + + // 系统信息 + systemInfo: null, + statusBarHeight: 44, + navBarHeight: 88, + + // TabBar相关 + currentTab: 0 + }, + + onLaunch(options) { + // 获取系统信息 + this.getSystemInfo() + + // 检查登录状态 + this.checkLoginStatus() + + // 加载书籍数据 + this.loadBookData() + + // 检查更新 + this.checkUpdate() + + // 处理分享参数(推荐码绑定) + this.handleReferralCode(options) + }, + + // 小程序显示时也检查分享参数 + onShow(options) { + this.handleReferralCode(options) + }, + + // 处理推荐码绑定 + handleReferralCode(options) { + const query = options?.query || {} + const refCode = query.ref || query.referralCode + + if (refCode) { + console.log('[App] 检测到推荐码:', refCode) + + // 立即记录访问(不需要登录,用于统计"通过链接进的人数") + this.recordReferralVisit(refCode) + + // 检查是否已经绑定过 + const boundRef = wx.getStorageSync('boundReferralCode') + if (boundRef && boundRef !== refCode) { + console.log('[App] 已绑定过其他推荐码,不更换绑定关系') + // 但仍然记录访问,不return + } else { + // 保存待绑定的推荐码 + this.globalData.pendingReferralCode = refCode + wx.setStorageSync('pendingReferralCode', refCode) + + // 如果已登录,立即绑定 + if (this.globalData.isLoggedIn && this.globalData.userInfo) { + this.bindReferralCode(refCode) + } + } + } + }, + + // 记录推荐访问(不需要登录,用于统计) + async recordReferralVisit(refCode) { + try { + // 获取openId(如果有) + const openId = this.globalData.openId || wx.getStorageSync('openId') || '' + const userId = this.globalData.userInfo?.id || '' + + await this.request('/api/referral/visit', { + method: 'POST', + data: { + referralCode: refCode, + visitorOpenId: openId, + visitorId: userId, + source: 'miniprogram', + page: getCurrentPages()[getCurrentPages().length - 1]?.route || '' + } + }) + console.log('[App] 记录推荐访问成功') + } catch (e) { + console.log('[App] 记录推荐访问失败:', e.message) + // 忽略错误,不影响用户体验 + } + }, + + // 绑定推荐码到用户 + async bindReferralCode(refCode) { + try { + const userId = this.globalData.userInfo?.id + if (!userId || !refCode) return + + // 检查是否已绑定 + const boundRef = wx.getStorageSync('boundReferralCode') + if (boundRef) { + console.log('[App] 已绑定推荐码,跳过') + return + } + + console.log('[App] 绑定推荐码:', refCode, '到用户:', userId) + + // 调用API绑定推荐关系 + const res = await this.request('/api/referral/bind', { + method: 'POST', + data: { + userId, + referralCode: refCode + } + }) + + if (res.success) { + console.log('[App] 推荐码绑定成功') + wx.setStorageSync('boundReferralCode', refCode) + this.globalData.pendingReferralCode = null + wx.removeStorageSync('pendingReferralCode') + } + } catch (e) { + console.error('[App] 绑定推荐码失败:', e) + } + }, + + // 获取系统信息 + getSystemInfo() { + try { + const systemInfo = wx.getSystemInfoSync() + this.globalData.systemInfo = systemInfo + this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44 + + // 计算导航栏高度 + const menuButton = wx.getMenuButtonBoundingClientRect() + if (menuButton) { + this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight + } + } catch (e) { + console.error('获取系统信息失败:', e) + } + }, + + // 检查登录状态 + checkLoginStatus() { + try { + const userInfo = wx.getStorageSync('userInfo') + const token = wx.getStorageSync('token') + + if (userInfo && token) { + this.globalData.userInfo = userInfo + this.globalData.isLoggedIn = true + this.globalData.purchasedSections = userInfo.purchasedSections || [] + this.globalData.hasFullBook = userInfo.hasFullBook || false + } + } catch (e) { + console.error('检查登录状态失败:', e) + } + }, + + // 加载书籍数据 + async loadBookData() { + try { + // 先从缓存加载 + const cachedData = wx.getStorageSync('bookData') + if (cachedData) { + this.globalData.bookData = cachedData + } + + // 从服务器获取最新数据 + const res = await this.request('/api/book/all-chapters') + if (res && res.data) { + this.globalData.bookData = res.data + wx.setStorageSync('bookData', res.data) + } + } catch (e) { + console.error('加载书籍数据失败:', e) + } + }, + + // 检查更新 + checkUpdate() { + if (wx.canIUse('getUpdateManager')) { + const updateManager = wx.getUpdateManager() + + updateManager.onCheckForUpdate((res) => { + if (res.hasUpdate) { + console.log('发现新版本') + } + }) + + updateManager.onUpdateReady(() => { + wx.showModal({ + title: '更新提示', + content: '新版本已准备好,是否重启应用?', + success: (res) => { + if (res.confirm) { + updateManager.applyUpdate() + } + } + }) + }) + + updateManager.onUpdateFailed(() => { + wx.showToast({ + title: '更新失败,请稍后重试', + icon: 'none' + }) + }) + } + }, + + // 统一请求方法 + request(url, options = {}) { + return new Promise((resolve, reject) => { + const token = wx.getStorageSync('token') + + wx.request({ + url: this.globalData.baseUrl + url, + method: options.method || 'GET', + data: options.data || {}, + header: { + 'Content-Type': 'application/json', + 'Authorization': token ? `Bearer ${token}` : '', + ...options.header + }, + success: (res) => { + if (res.statusCode === 200) { + resolve(res.data) + } else if (res.statusCode === 401) { + // 未授权,清除登录状态 + this.logout() + reject(new Error('未授权')) + } else { + reject(new Error(res.data?.message || '请求失败')) + } + }, + fail: (err) => { + reject(err) + } + }) + }) + }, + + // 登录方法 - 获取openId用于支付 + async login() { + try { + // 获取微信登录code + const loginRes = await new Promise((resolve, reject) => { + wx.login({ + success: resolve, + fail: reject + }) + }) + + console.log('[App] 获取登录code成功') + + try { + // 发送code到服务器获取openId + const res = await this.request('/api/miniprogram/login', { + method: 'POST', + data: { code: loginRes.code } + }) + + if (res.success && res.data) { + // 保存openId + if (res.data.openId) { + this.globalData.openId = res.data.openId + wx.setStorageSync('openId', res.data.openId) + console.log('[App] 获取openId成功') + } + + // 保存用户信息 + if (res.data.user) { + this.globalData.userInfo = res.data.user + this.globalData.isLoggedIn = true + this.globalData.purchasedSections = res.data.user.purchasedSections || [] + this.globalData.hasFullBook = res.data.user.hasFullBook || false + + wx.setStorageSync('userInfo', res.data.user) + wx.setStorageSync('token', res.data.token || '') + + // 登录成功后,检查待绑定的推荐码并执行绑定 + const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode + if (pendingRef) { + console.log('[App] 登录后自动绑定推荐码:', pendingRef) + this.bindReferralCode(pendingRef) + } + } + + return res.data + } + } catch (apiError) { + console.log('[App] API登录失败:', apiError.message) + // 不使用模拟登录,提示用户网络问题 + wx.showToast({ title: '网络异常,请重试', icon: 'none' }) + return null + } + + return null + } catch (e) { + console.error('[App] 登录失败:', e) + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + return null + } + }, + + // 获取openId (支付必需) + async getOpenId() { + // 先检查缓存 + const cachedOpenId = wx.getStorageSync('openId') + if (cachedOpenId) { + this.globalData.openId = cachedOpenId + return cachedOpenId + } + + // 没有缓存则登录获取 + try { + const loginRes = await new Promise((resolve, reject) => { + wx.login({ success: resolve, fail: reject }) + }) + + const res = await this.request('/api/miniprogram/login', { + method: 'POST', + data: { code: loginRes.code } + }) + + if (res.success && res.data?.openId) { + this.globalData.openId = res.data.openId + wx.setStorageSync('openId', res.data.openId) + return res.data.openId + } + } catch (e) { + console.error('[App] 获取openId失败:', e) + } + + return null + }, + + // 模拟登录已废弃 - 不再使用 + // 现在必须使用真实的微信登录获取openId作为唯一标识 + mockLogin() { + console.warn('[App] mockLogin已废弃,请使用真实登录') + return null + }, + + // 手机号登录 + async loginWithPhone(phoneCode) { + try { + // 尝试API登录 + const res = await this.request('/api/wechat/phone-login', { + method: 'POST', + data: { code: phoneCode } + }) + + if (res.success && res.data) { + this.globalData.userInfo = res.data.user + this.globalData.isLoggedIn = true + this.globalData.purchasedSections = res.data.user.purchasedSections || [] + this.globalData.hasFullBook = res.data.user.hasFullBook || false + + wx.setStorageSync('userInfo', res.data.user) + wx.setStorageSync('token', res.data.token) + + // 登录成功后绑定推荐码 + const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode + if (pendingRef) { + console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef) + this.bindReferralCode(pendingRef) + } + + return res.data + } + } catch (e) { + console.log('[App] 手机号登录失败:', e) + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + } + + return null + }, + + // 退出登录 + logout() { + this.globalData.userInfo = null + this.globalData.isLoggedIn = false + this.globalData.purchasedSections = [] + this.globalData.hasFullBook = false + + wx.removeStorageSync('userInfo') + wx.removeStorageSync('token') + }, + + // 检查是否已购买章节 + hasPurchased(sectionId) { + if (this.globalData.hasFullBook) return true + return this.globalData.purchasedSections.includes(sectionId) + }, + + // 获取章节总数 + getTotalSections() { + return this.globalData.totalSections + }, + + // 切换TabBar + switchTab(index) { + this.globalData.currentTab = index + }, + + // 显示Toast + showToast(title, icon = 'none') { + wx.showToast({ + title, + icon, + duration: 2000 + }) + }, + + // 显示Loading + showLoading(title = '加载中...') { + wx.showLoading({ + title, + mask: true + }) + }, + + // 隐藏Loading + hideLoading() { + wx.hideLoading() + } +}) diff --git a/Tminiprogram/app.json b/Tminiprogram/app.json new file mode 100644 index 00000000..fe03e7b3 --- /dev/null +++ b/Tminiprogram/app.json @@ -0,0 +1,60 @@ +{ + "pages": [ + "pages/index/index", + "pages/chapters/chapters", + "pages/match/match", + "pages/my/my", + "pages/read/read", + "pages/about/about", + "pages/referral/referral", + "pages/purchases/purchases", + "pages/settings/settings", + "pages/search/search" + ], + "window": { + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#000000", + "navigationBarTitleText": "Soul创业派对", + "navigationBarTextStyle": "white", + "backgroundColor": "#000000", + "navigationStyle": "custom" + }, + "tabBar": { + "custom": true, + "color": "#8e8e93", + "selectedColor": "#00CED1", + "backgroundColor": "#1c1c1e", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/index/index", + "text": "首页" + }, + { + "pagePath": "pages/chapters/chapters", + "text": "目录" + }, + { + "pagePath": "pages/match/match", + "text": "找伙伴" + }, + { + "pagePath": "pages/my/my", + "text": "我的" + } + ] + }, + "usingComponents": {}, + "__usePrivacyCheck__": true, + "permission": { + "scope.userLocation": { + "desc": "用于匹配附近的书友" + } + }, + "requiredPrivateInfos": [ + "getLocation" + ], + "lazyCodeLoading": "requiredComponents", + "style": "v2", + "sitemapLocation": "sitemap.json" +} diff --git a/Tminiprogram/app.wxss b/Tminiprogram/app.wxss new file mode 100644 index 00000000..4a79d19f --- /dev/null +++ b/Tminiprogram/app.wxss @@ -0,0 +1,568 @@ +/** + * Soul创业实验 - 全局样式 + * 主题色: #00CED1 (Soul青色) + * 开发: 卡若 + */ + +/* ===== 页面基础样式 ===== */ +page { + background-color: #000000; + color: #ffffff; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif; + font-size: 28rpx; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +/* ===== 全局容器 ===== */ +.container { + min-height: 100vh; + padding: 0; + background: #000000; + padding-bottom: env(safe-area-inset-bottom); +} + +/* ===== 品牌色系 ===== */ +.brand-color { + color: #00CED1; +} + +.brand-bg { + background-color: #00CED1; +} + +.brand-gradient { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); +} + +.gold-color { + color: #FFD700; +} + +.gold-bg { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); +} + +/* ===== 文字渐变 ===== */ +.gradient-text { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.gold-gradient-text { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* ===== 按钮样式 ===== */ +.btn-primary { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + color: #ffffff; + border: none; + border-radius: 48rpx; + padding: 28rpx 48rpx; + font-size: 32rpx; + font-weight: 600; + box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3); + display: flex; + align-items: center; + justify-content: center; +} + +.btn-primary::after { + border: none; +} + +.btn-primary:active { + opacity: 0.85; + transform: scale(0.98); +} + +.btn-secondary { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; + border: 2rpx solid rgba(0, 206, 209, 0.3); + border-radius: 48rpx; + padding: 28rpx 48rpx; + font-size: 32rpx; + font-weight: 500; +} + +.btn-secondary::after { + border: none; +} + +.btn-secondary:active { + background: rgba(0, 206, 209, 0.2); +} + +.btn-ghost { + background: rgba(255, 255, 255, 0.05); + color: #ffffff; + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 48rpx; + padding: 28rpx 48rpx; + font-size: 32rpx; +} + +.btn-ghost::after { + border: none; +} + +.btn-ghost:active { + background: rgba(255, 255, 255, 0.1); +} + +.btn-gold { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + color: #000000; + border: none; + border-radius: 48rpx; + padding: 28rpx 48rpx; + font-size: 32rpx; + font-weight: 600; + box-shadow: 0 8rpx 24rpx rgba(255, 215, 0, 0.3); +} + +.btn-gold::after { + border: none; +} + +/* ===== 卡片样式 ===== */ +.card { + background: rgba(28, 28, 30, 0.9); + border-radius: 32rpx; + padding: 32rpx; + margin: 24rpx 32rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.card-light { + background: rgba(44, 44, 46, 0.8); + border-radius: 24rpx; + padding: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.08); +} + +.card-gradient { + background: linear-gradient(135deg, rgba(28, 28, 30, 1) 0%, rgba(44, 44, 46, 1) 100%); + border-radius: 32rpx; + padding: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +.card-brand { + background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.05) 100%); + border-radius: 32rpx; + padding: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +/* ===== 输入框样式 ===== */ +.input-ios { + background: rgba(0, 0, 0, 0.3); + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 24rpx; + padding: 28rpx 32rpx; + font-size: 32rpx; + color: #ffffff; +} + +.input-ios:focus { + border-color: rgba(0, 206, 209, 0.5); +} + +.input-ios-placeholder { + color: rgba(255, 255, 255, 0.3); +} + +/* ===== 列表项样式 ===== */ +.list-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 28rpx 32rpx; + background: rgba(28, 28, 30, 0.9); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.list-item:first-child { + border-radius: 24rpx 24rpx 0 0; +} + +.list-item:last-child { + border-radius: 0 0 24rpx 24rpx; + border-bottom: none; +} + +.list-item:only-child { + border-radius: 24rpx; +} + +.list-item:active { + background: rgba(44, 44, 46, 1); +} + +/* ===== 标签样式 ===== */ +.tag { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8rpx 20rpx; + min-width: 80rpx; + border-radius: 8rpx; + font-size: 22rpx; + font-weight: 500; + box-sizing: border-box; + text-align: center; +} + +.tag-brand { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; +} + +.tag-gold { + background: rgba(255, 215, 0, 0.1); + color: #FFD700; +} + +.tag-pink { + background: rgba(233, 30, 99, 0.1); + color: #E91E63; +} + +.tag-purple { + background: rgba(123, 97, 255, 0.1); + color: #7B61FF; +} + +.tag-free { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; +} + +/* ===== 分隔线 ===== */ +.divider { + height: 1rpx; + background: rgba(255, 255, 255, 0.05); + margin: 24rpx 0; +} + +.divider-vertical { + width: 2rpx; + height: 48rpx; + background: rgba(255, 255, 255, 0.1); +} + +/* ===== 骨架屏动画 ===== */ +.skeleton { + background: linear-gradient(90deg, + rgba(28, 28, 30, 1) 25%, + rgba(44, 44, 46, 1) 50%, + rgba(28, 28, 30, 1) 75% + ); + background-size: 200% 100%; + animation: skeleton-loading 1.5s ease-in-out infinite; + border-radius: 8rpx; +} + +@keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* ===== 页面过渡动画 ===== */ +.page-transition { + animation: fadeIn 0.3s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20rpx); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ===== 弹窗动画 ===== */ +.modal-overlay { + animation: modalOverlayIn 0.25s ease-out; +} + +.modal-content { + animation: modalContentIn 0.3s cubic-bezier(0.32, 0.72, 0, 1); +} + +@keyframes modalOverlayIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes modalContentIn { + from { + opacity: 0; + transform: scale(0.95) translateY(20rpx); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +/* ===== 脉动动画 ===== */ +.pulse { + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.05); + opacity: 0.8; + } +} + +/* ===== 发光效果 ===== */ +.glow { + box-shadow: 0 0 40rpx rgba(0, 206, 209, 0.3); +} + +.glow-gold { + box-shadow: 0 0 40rpx rgba(255, 215, 0, 0.3); +} + +/* ===== 文字样式 ===== */ +.text-xs { + font-size: 22rpx; +} + +.text-sm { + font-size: 26rpx; +} + +.text-base { + font-size: 28rpx; +} + +.text-lg { + font-size: 32rpx; +} + +.text-xl { + font-size: 36rpx; +} + +.text-2xl { + font-size: 44rpx; +} + +.text-3xl { + font-size: 56rpx; +} + +.text-white { + color: #ffffff; +} + +.text-gray { + color: rgba(255, 255, 255, 0.6); +} + +.text-muted { + color: rgba(255, 255, 255, 0.4); +} + +.text-center { + text-align: center; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.font-bold { + font-weight: 700; +} + +/* ===== Flex布局 ===== */ +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-around { + justify-content: space-around; +} + +.flex-1 { + flex: 1; +} + +.gap-1 { + gap: 8rpx; +} + +.gap-2 { + gap: 16rpx; +} + +.gap-3 { + gap: 24rpx; +} + +.gap-4 { + gap: 32rpx; +} + +/* ===== 间距 ===== */ +.p-2 { padding: 16rpx; } +.p-3 { padding: 24rpx; } +.p-4 { padding: 32rpx; } +.p-5 { padding: 40rpx; } + +.px-4 { padding-left: 32rpx; padding-right: 32rpx; } +.py-2 { padding-top: 16rpx; padding-bottom: 16rpx; } +.py-3 { padding-top: 24rpx; padding-bottom: 24rpx; } + +.m-4 { margin: 32rpx; } +.mx-4 { margin-left: 32rpx; margin-right: 32rpx; } +.my-3 { margin-top: 24rpx; margin-bottom: 24rpx; } +.mb-2 { margin-bottom: 16rpx; } +.mb-3 { margin-bottom: 24rpx; } +.mb-4 { margin-bottom: 32rpx; } +.mt-4 { margin-top: 32rpx; } + +/* ===== 圆角 ===== */ +.rounded { border-radius: 8rpx; } +.rounded-lg { border-radius: 16rpx; } +.rounded-xl { border-radius: 24rpx; } +.rounded-2xl { border-radius: 32rpx; } +.rounded-full { border-radius: 50%; } + +/* ===== 安全区域 ===== */ +.safe-bottom { + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); +} + +.pb-tabbar { + padding-bottom: 200rpx; +} + +/* ===== 头部导航占位 ===== */ +.nav-placeholder { + height: calc(88rpx + env(safe-area-inset-top, 44rpx)); +} + +/* ===== 隐藏滚动条 ===== */ +::-webkit-scrollbar { + display: none; + width: 0; + height: 0; +} + +/* ===== 触摸反馈 ===== */ +.touch-feedback { + transition: all 0.15s ease; +} + +.touch-feedback:active { + opacity: 0.7; + transform: scale(0.98); +} + +/* ===== 进度条 ===== */ +.progress-bar { + height: 8rpx; + background: rgba(44, 44, 46, 1); + border-radius: 4rpx; + overflow: hidden; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); + border-radius: 4rpx; + transition: width 0.3s ease; +} + +/* ===== 头像样式 ===== */ +.avatar { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%); + display: flex; + align-items: center; + justify-content: center; + color: #00CED1; + font-weight: 700; + font-size: 32rpx; + border: 4rpx solid rgba(0, 206, 209, 0.3); +} + +.avatar-lg { + width: 120rpx; + height: 120rpx; + font-size: 48rpx; +} + +/* ===== 图标容器 ===== */ +.icon-box { + width: 64rpx; + height: 64rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.icon-box-brand { + background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%); +} + +.icon-box-gold { + background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(255, 165, 0, 0.1) 100%); +} + +/* ===== 渐变背景 ===== */ +.bg-gradient-dark { + background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%); +} + +.bg-gradient-brand { + background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%); +} diff --git a/Tminiprogram/assets/icons/home-active.png b/Tminiprogram/assets/icons/home-active.png new file mode 100644 index 0000000000000000000000000000000000000000..b6090d87610396c4e046dd531906e3f4aeaeee09 GIT binary patch literal 699 zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm3?%0U?qUH_3dtTpz6=aiY77hwEes65fIUwRG5VK4FYb!C6W$j8E@-(9CP(<^;b zR+8R{-Kzt1UUod~dg>GEyyj}|lD4KBJx$xLPV3HJcKPkh5AU=)(hO?WUYv2w#c|H1 zIcM)iojCeb>rS@0`M!udpYC0}*L{@vz4F`15jLOqUD(%kRQi3xw^*IJy7SCRE*%1b zP8^C~CfhQGev1{DBX_YA=wj6p*NBpo#FA92CCE6dF8*(}7if-ZKuxwC}J8ub)+W zZO+pTZ&M~mym`$VxJYQ~iX(fN>%zHe7ib!}w`$JY@%YR=_h}iYayA_La(qFuRL-qy z|NVG>IM0sMTWQ%Ckkq(r<`VRhu<)JU8pk8;M?1KeKUI$3q=~tmdjjRGW&*f z`py3jCiHtx*Zr0wec()i&()jJDc8T>nDD=Q^|pU<9N)TBCbu8SQu%-6_LV1=8>LR!e>61p+?Q9m|3s^I zji+yw-13xJGbB~D48^=cl60n6D0C`8z^C&3Ki#JC_pKuq0Yh1}#5JNMC9x#cD!C{X zNHG{07@6rB80s3Dgcz7u85mg^8)+LDSQ!|oyG-v#(U6;;l9^VCTSJ(nzA;dP2Hb{{ e%-q!ClEmBs+l8_s;5>q48lEBhUuMByV?@@LH~$ z@<0xIiKnkC`zuC17A7UP*Y34IpH79fui}4y z^g1w!^=MUw^E|V|(UX6BltdS)i>2$mdl%_{%1->}Ed9Y};Is zc6OR=`NyWs8Pj*4iGRAxnANyGHbCF?TGGm#)kTj#Y|FYFt^8un(cQbA?0w|yvdJ;4 zNG$ruwnc6}lcai%_9ab>cr7H~8F(zof1|&N==_fJJx9flG8V45q1q?Wy}ob%--gXU zp2gMt|NE%Dy0FJpC#kScToCB=FVlaqurMBfv2Et&1He#FEpd$~Nl7e8wMs5Z1yT$~ z21aJO28OzZCLsnURt82^#wOYZ237_JE>jk5MbVI(pOTqYiCcr|NhwaC1`W6kC7HRY b#U+Wk1-SJj-Lbv~)WhKE>gTe~DWM4f?pT9{ literal 0 HcmV?d00001 diff --git a/Tminiprogram/assets/icons/match.png b/Tminiprogram/assets/icons/match.png new file mode 100644 index 0000000000000000000000000000000000000000..b15582e3720ae81bfd63a759fb0aaf1ac555fa51 GIT binary patch literal 725 zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm0wfvQn)U)og=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWSuh@ z85LUuoH!tai*Hc)U$J}IR+YzCt5x2dxNdsEo88}IZ6z2iRX#*6FrShYz! zO#K(xcE0EKv;AMs_I&J{R#Sb}Bzc?kLM7dr1?w{+ySAJ0$LU|*BDytw&&H=KMB+b8 zeXc6L?8UOVk#bXV&cEFBR-tQSLVk$Do*o68C(83raX1N2KFHQ;e^e&&+Kq)%=S|YD zcAg}^<@2qXlb^1X4YS*BcjV{a^v=ZJd=E-DI&5C=`{~|3#)`jZF3zZzTRdBTDnIA{ zg^C9g`~-9&zx*_lJi*zdYxJ~4*6~=#VvDSoO(yTN)!!^pI6C)Tp-I>NoTDeJ{dT1s zJ83^RtjgNNe&$rGzY~*KG7PLTGAnxOV>Feiid7pjRkL$~A-t{l$NqsxsxqRQ% zn-_{#1)RBaYnk?)r*WeBdAX}Cw(bt@;nDQk7&c*%NH(h`7c4B5qW&=*o0Bx}L0Kax zmQ_n!BT7;dOH!?pi&B9UgOP!enXZAMuAxbYfr*uYk(IHDwt<0_fq~1EMO#rcl8_s;5>q48lEBhUuMByV?@@LH~$ z@<0xIiKnkC`zuC17A7UP*Y34IpH79fui}4y z^g1w!^=MUw^E|V|(UX6BltdS)i>2$mdl%_{%1->}Ed9Y};Is zc6OR=`NyWs8Pj*4iGRAxnANyGHbCF?TGGm#)kTj#Y|FYFt^8un(cQbA?0w|yvdJ;4 zNG$ruwnc6}lcai%_9ab>cr7H~8F(zof1|&N==_fJJx9flG8V45q1q?Wy}ob%--gXU zp2gMt|NE%Dy0FJpC#kScToCB=FVlaqurMBfv2Et&1He#FEpd$~Nl7e8wMs5Z1yT$~ z21aJO28OzZCLsnURt82^#wOYZ237_JE>jk5MbVI(pOTqYiCcr|NhwaC1`W6kC7HRY b#U+Wk1-SJj-Lbv~)WhKE>gTe~DWM4f?pT9{ literal 0 HcmV?d00001 diff --git a/Tminiprogram/assets/icons/my.png b/Tminiprogram/assets/icons/my.png new file mode 100644 index 0000000000000000000000000000000000000000..b15582e3720ae81bfd63a759fb0aaf1ac555fa51 GIT binary patch literal 725 zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm0wfvQn)U)og=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWSuh@ z85LUuoH!tai*Hc)U$J}IR+YzCt5x2dxNdsEo88}IZ6z2iRX#*6FrShYz! zO#K(xcE0EKv;AMs_I&J{R#Sb}Bzc?kLM7dr1?w{+ySAJ0$LU|*BDytw&&H=KMB+b8 zeXc6L?8UOVk#bXV&cEFBR-tQSLVk$Do*o68C(83raX1N2KFHQ;e^e&&+Kq)%=S|YD zcAg}^<@2qXlb^1X4YS*BcjV{a^v=ZJd=E-DI&5C=`{~|3#)`jZF3zZzTRdBTDnIA{ zg^C9g`~-9&zx*_lJi*zdYxJ~4*6~=#VvDSoO(yTN)!!^pI6C)Tp-I>NoTDeJ{dT1s zJ83^RtjgNNe&$rGzY~*KG7PLTGAnxOV>Feiid7pjRkL$~A-t{l$NqsxsxqRQ% zn-_{#1)RBaYnk?)r*WeBdAX}Cw(bt@;nDQk7&c*%NH(h`7c4B5qW&=*o0Bx}L0Kax zmQ_n!BT7;dOH!?pi&B9UgOP!enXZAMuAxbYfr*uYk(IHDwt<0_fq~1EMO#rc + + + + + + + + + + + + + + + {{list[0].text}} + + + + + + + + + + + + + + {{list[1].text}} + + + + + + + + + + + {{list[2].text}} + + + + + + + + + + + + + {{list[3].text}} + + diff --git a/Tminiprogram/custom-tab-bar/index.wxss b/Tminiprogram/custom-tab-bar/index.wxss new file mode 100644 index 00000000..84ad115f --- /dev/null +++ b/Tminiprogram/custom-tab-bar/index.wxss @@ -0,0 +1,227 @@ +/** + * Soul创业实验 - 自定义TabBar样式 + * 实现中间突出的"找伙伴"按钮 + */ + +.tab-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 100rpx; + background: rgba(28, 28, 30, 0.95); + backdrop-filter: blur(40rpx); + -webkit-backdrop-filter: blur(40rpx); + display: flex; + align-items: flex-end; + padding-bottom: env(safe-area-inset-bottom); + z-index: 999; +} + +.tab-bar-border { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1rpx; + background: rgba(255, 255, 255, 0.05); +} + +.tab-bar-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10rpx 0 16rpx; +} + +.icon-wrapper { + width: 48rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 4rpx; +} + +.icon { + width: 44rpx; + height: 44rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.tab-bar-text { + font-size: 22rpx; + line-height: 1; +} + +/* ===== 首页图标 ===== */ +.icon-home { + position: relative; + width: 40rpx; + height: 40rpx; +} + +.home-roof { + position: absolute; + top: 4rpx; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 18rpx solid transparent; + border-right: 18rpx solid transparent; + border-bottom: 14rpx solid #8e8e93; +} + +.home-body { + position: absolute; + bottom: 4rpx; + left: 50%; + transform: translateX(-50%); + width: 28rpx; + height: 18rpx; + background: #8e8e93; + border-radius: 0 0 4rpx 4rpx; +} + +.icon-active .home-roof { + border-bottom-color: #00CED1; +} + +.icon-active .home-body { + background: #00CED1; +} + +/* ===== 目录图标 ===== */ +.icon-list { + width: 36rpx; + height: 32rpx; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.list-line { + width: 100%; + height: 6rpx; + background: #8e8e93; + border-radius: 3rpx; +} + +.list-line:nth-child(2) { + width: 75%; +} + +.list-line:nth-child(3) { + width: 50%; +} + +.icon-active .list-line { + background: #00CED1; +} + +/* ===== 我的图标 ===== */ +.icon-user { + position: relative; + width: 36rpx; + height: 40rpx; +} + +.user-head { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 16rpx; + height: 16rpx; + background: #8e8e93; + border-radius: 50%; +} + +.user-body { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 28rpx; + height: 18rpx; + background: #8e8e93; + border-radius: 14rpx 14rpx 0 0; +} + +.icon-active .user-head, +.icon-active .user-body { + background: #00CED1; +} + +/* ===== 找伙伴 - 中间特殊按钮 ===== */ +.special-item { + position: relative; + margin-top: -32rpx; +} + +.special-button { + width: 112rpx; + height: 112rpx; + border-radius: 50%; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4); + margin-bottom: 4rpx; + transition: all 0.2s ease; +} + +.special-button:active { + transform: scale(0.95); +} + +.special-active { + box-shadow: 0 8rpx 40rpx rgba(0, 206, 209, 0.6); +} + +.special-text { + margin-top: 4rpx; +} + +/* ===== 找伙伴图标 (双人) ===== */ +.icon-users { + position: relative; + width: 56rpx; + height: 44rpx; +} + +.user-circle { + position: absolute; + width: 28rpx; + height: 28rpx; + border-radius: 50%; + background: #ffffff; +} + +.user-circle::after { + content: ''; + position: absolute; + bottom: -12rpx; + left: 50%; + transform: translateX(-50%); + width: 22rpx; + height: 14rpx; + background: #ffffff; + border-radius: 11rpx 11rpx 0 0; +} + +.user-1 { + top: 0; + left: 0; +} + +.user-2 { + top: 0; + right: 0; +} diff --git a/Tminiprogram/pages/about/about.js b/Tminiprogram/pages/about/about.js new file mode 100644 index 00000000..dbbcabb0 --- /dev/null +++ b/Tminiprogram/pages/about/about.js @@ -0,0 +1,81 @@ +/** + * Soul创业派对 - 关于作者页 + * 开发: 卡若 + */ +const app = getApp() + +Page({ + data: { + statusBarHeight: 44, + author: { + name: '卡若', + avatar: 'K', + title: 'Soul派对房主理人 · 私域运营专家', + bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例,涵盖电商、内容、传统行业等多个领域。', + stats: [ + { label: '商业案例', value: '62' }, + { label: '连续直播', value: '365天' }, + { label: '派对分享', value: '1000+' } + ], + // 联系方式已移至后台配置 + contact: null, + highlights: [ + '5年私域运营经验', + '帮助100+品牌从0到1增长', + '连续创业者,擅长商业模式设计' + ] + }, + bookInfo: { + title: '一场Soul的创业实验', + totalChapters: 62, + parts: [ + { name: '真实的人', chapters: 10 }, + { name: '真实的行业', chapters: 15 }, + { name: '真实的错误', chapters: 9 }, + { name: '真实的赚钱', chapters: 20 }, + { name: '真实的社会', chapters: 9 } + ], + price: 9.9 + } + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight + }) + this.loadBookStats() + }, + + // 加载书籍统计 + async loadBookStats() { + try { + const res = await app.request('/api/book/stats') + if (res && res.success) { + this.setData({ + 'bookInfo.totalChapters': res.data?.totalChapters || 62, + 'author.stats': [ + { label: '商业案例', value: String(res.data?.totalChapters || 62) }, + { label: '连续直播', value: '365天' }, + { label: '派对分享', value: '1000+' } + ] + }) + } + } catch (e) { + console.log('[About] 加载书籍统计失败,使用默认值') + } + }, + + // 联系方式功能已禁用 + copyWechat() { + wx.showToast({ title: '请在派对房联系作者', icon: 'none' }) + }, + + callPhone() { + wx.showToast({ title: '请在派对房联系作者', icon: 'none' }) + }, + + // 返回 + goBack() { + wx.navigateBack() + } +}) diff --git a/Tminiprogram/pages/about/about.json b/Tminiprogram/pages/about/about.json new file mode 100644 index 00000000..e90e9960 --- /dev/null +++ b/Tminiprogram/pages/about/about.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationStyle": "custom" +} diff --git a/Tminiprogram/pages/about/about.wxml b/Tminiprogram/pages/about/about.wxml new file mode 100644 index 00000000..598e9464 --- /dev/null +++ b/Tminiprogram/pages/about/about.wxml @@ -0,0 +1,75 @@ + + + + + 关于作者 + + + + + + + + {{author.avatar}} + {{author.name}} + {{author.title}} + {{author.bio}} + + + + + {{item.value}} + {{item.label}} + + + + + + + + {{item}} + + + + + + + 📚 {{bookInfo.title}} + + + {{bookInfo.totalChapters}} + 篇章节 + + + 5 + 大篇章 + + + ¥{{bookInfo.price}} + 全书价格 + + + + + {{item.name}} + {{item.chapters}}节 + + + + + + + 联系作者 + + 🎉 + + Soul派对房 + 每天早上6-9点开播 + + + + 在Soul App搜索"创业实验"或"卡若",加入派对房直接交流 + + + + diff --git a/Tminiprogram/pages/about/about.wxss b/Tminiprogram/pages/about/about.wxss new file mode 100644 index 00000000..337aa041 --- /dev/null +++ b/Tminiprogram/pages/about/about.wxss @@ -0,0 +1,40 @@ +.page { min-height: 100vh; background: #000; } +.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; } +.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; } +.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; } +.nav-placeholder { width: 72rpx; } +.content { padding: 32rpx; } +.author-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-radius: 32rpx; padding: 48rpx; text-align: center; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); } +.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; margin: 0 auto 24rpx; font-size: 64rpx; color: #fff; font-weight: 700; border: 4rpx solid rgba(0,206,209,0.3); } +.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; } +.author-title { font-size: 26rpx; color: #00CED1; display: block; margin-bottom: 24rpx; } +.author-bio { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.8; display: block; margin-bottom: 32rpx; } +.stats-row { display: flex; justify-content: space-around; padding-top: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.1); } +.stat-item { text-align: center; } +.stat-value { font-size: 36rpx; font-weight: 700; color: #00CED1; display: block; } +.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); } +.contact-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; } +.card-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 24rpx; } +.contact-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 16rpx; } +.contact-item:last-child { margin-bottom: 0; } +.contact-icon { font-size: 40rpx; } +.contact-info { flex: 1; } +.contact-label { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; } +.contact-value { font-size: 28rpx; color: #fff; } +.contact-btn { padding: 12rpx 24rpx; background: rgba(0,206,209,0.2); color: #00CED1; font-size: 24rpx; border-radius: 16rpx; } + +/* 亮点标签 */ +.highlights { display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); justify-content: center; } +.highlight-tag { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; background: rgba(0,206,209,0.15); border-radius: 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); } +.tag-icon { color: #00CED1; font-size: 22rpx; } + +/* 书籍信息卡片 */ +.book-info-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; } +.book-stats { display: flex; justify-content: space-around; padding: 24rpx 0; margin: 16rpx 0; background: rgba(0,0,0,0.3); border-radius: 16rpx; } +.book-stat { text-align: center; } +.book-stat-value { font-size: 36rpx; font-weight: 700; color: #FFD700; display: block; } +.book-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); } +.parts-list { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 16rpx; } +.part-item { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: rgba(255,255,255,0.05); border-radius: 12rpx; } +.part-name { font-size: 24rpx; color: rgba(255,255,255,0.8); } +.part-chapters { font-size: 22rpx; color: #00CED1; } diff --git a/Tminiprogram/pages/chapters/chapters.js b/Tminiprogram/pages/chapters/chapters.js new file mode 100644 index 00000000..6ac20c0b --- /dev/null +++ b/Tminiprogram/pages/chapters/chapters.js @@ -0,0 +1,251 @@ +/** + * Soul创业派对 - 目录页 + * 开发: 卡若 + * 技术支持: 存客宝 + * 数据: 完整真实文章标题 + */ + +const app = getApp() + +Page({ + data: { + // 系统信息 + statusBarHeight: 44, + navBarHeight: 88, + + // 用户状态 + isLoggedIn: false, + hasFullBook: false, + purchasedSections: [], + + // 书籍数据 - 完整真实标题 + totalSections: 62, + bookData: [ + { + id: 'part-1', + number: '一', + title: '真实的人', + subtitle: '人与人之间的底层逻辑', + chapters: [ + { + id: 'chapter-1', + title: '第1章|人与人之间的底层逻辑', + sections: [ + { id: '1.1', title: '荷包:电动车出租的被动收入模式', isFree: true, price: 1 }, + { id: '1.2', title: '老墨:资源整合高手的社交方法', isFree: false, price: 1 }, + { id: '1.3', title: '笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统', isFree: false, price: 1 }, + { id: '1.4', title: '人性的三角结构:利益、情感、价值观', isFree: false, price: 1 }, + { id: '1.5', title: '沟通差的问题:为什么你说的别人听不懂', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-2', + title: '第2章|人性困境案例', + sections: [ + { id: '2.1', title: '相亲故事:你以为找的是人,实际是在找模式', isFree: false, price: 1 }, + { id: '2.2', title: '找工作迷茫者:为什么简历解决不了人生', isFree: false, price: 1 }, + { id: '2.3', title: '撸运费险:小钱困住大脑的真实心理', isFree: false, price: 1 }, + { id: '2.4', title: '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', isFree: false, price: 1 }, + { id: '2.5', title: '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', isFree: false, price: 1 } + ] + } + ] + }, + { + id: 'part-2', + number: '二', + title: '真实的行业', + subtitle: '电商、内容、传统行业解析', + chapters: [ + { + id: 'chapter-3', + title: '第3章|电商篇', + sections: [ + { id: '3.1', title: '3000万流水如何跑出来(退税模式解析)', isFree: false, price: 1 }, + { id: '3.2', title: '供应链之王 vs 打工人:利润不在前端', isFree: false, price: 1 }, + { id: '3.3', title: '社区团购的底层逻辑', isFree: false, price: 1 }, + { id: '3.4', title: '跨境电商与退税套利', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-4', + title: '第4章|内容商业篇', + sections: [ + { id: '4.1', title: '旅游号:30天10万粉的真实逻辑', isFree: false, price: 1 }, + { id: '4.2', title: '做号工厂:如何让一个号变成一个机器', isFree: false, price: 1 }, + { id: '4.3', title: '情绪内容为什么比专业内容更赚钱', isFree: false, price: 1 }, + { id: '4.4', title: '猫与宠物号:为什么宠物赛道永不过时', isFree: false, price: 1 }, + { id: '4.5', title: '直播间里的三种人:演员、技术工、系统流', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-5', + title: '第5章|传统行业篇', + sections: [ + { id: '5.1', title: '拍卖行抱朴:一天240万的摇号生意', isFree: false, price: 1 }, + { id: '5.2', title: '土地拍卖:招拍挂背后的游戏规则', isFree: false, price: 1 }, + { id: '5.3', title: '地摊经济数字化:一个月900块的餐车生意', isFree: false, price: 1 }, + { id: '5.4', title: '不良资产拍卖:我错过的一个亿佣金', isFree: false, price: 1 }, + { id: '5.5', title: '桶装水李总:跟物业合作的轻资产模式', isFree: false, price: 1 } + ] + } + ] + }, + { + id: 'part-3', + number: '三', + title: '真实的错误', + subtitle: '我和别人犯过的错', + chapters: [ + { + id: 'chapter-6', + title: '第6章|我人生错过的4件大钱', + sections: [ + { id: '6.1', title: '电商财税窗口:2016年的千万级机会', isFree: false, price: 1 }, + { id: '6.2', title: '供应链金融:我不懂的杠杆游戏', isFree: false, price: 1 }, + { id: '6.3', title: '内容红利:2019年我为什么没做抖音', isFree: false, price: 1 }, + { id: '6.4', title: '数据资产化:我还在观望的未来机会', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-7', + title: '第7章|别人犯的错误', + sections: [ + { id: '7.1', title: '投资房年轻人的迷茫:资金 vs 能力', isFree: false, price: 1 }, + { id: '7.2', title: '信息差骗局:永远有人靠卖学习赚钱', isFree: false, price: 1 }, + { id: '7.3', title: '在Soul找恋爱但想赚钱的人', isFree: false, price: 1 }, + { id: '7.4', title: '创业者的三种死法:冲动、轻信、没结构', isFree: false, price: 1 }, + { id: '7.5', title: '人情生意的终点:关系越多亏得越多', isFree: false, price: 1 } + ] + } + ] + }, + { + id: 'part-4', + number: '四', + title: '真实的赚钱', + subtitle: '底层结构与真实案例', + chapters: [ + { + id: 'chapter-8', + title: '第8章|底层结构', + sections: [ + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', isFree: false, price: 1 }, + { id: '8.2', title: '价格杠杆:供应链与信息差', isFree: false, price: 1 }, + { id: '8.3', title: '时间杠杆:自动化 + AI', isFree: false, price: 1 }, + { id: '8.4', title: '情绪杠杆:咨询、婚恋、生意场', isFree: false, price: 1 }, + { id: '8.5', title: '社交杠杆:认识谁比你会什么更重要', isFree: false, price: 1 }, + { id: '8.6', title: '云阿米巴:分不属于自己的钱', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-9', + title: '第9章|我在Soul上亲访的赚钱案例', + sections: [ + { id: '9.1', title: '游戏账号私域:账号即资产', isFree: false, price: 1 }, + { id: '9.2', title: '健康包模式:高复购、高毛利', isFree: false, price: 1 }, + { id: '9.3', title: '药物私域:长期关系赛道', isFree: false, price: 1 }, + { id: '9.4', title: '残疾机构合作:退税 × AI × 人力成本', isFree: false, price: 1 }, + { id: '9.5', title: '私域银行:粉丝即小股东', isFree: false, price: 1 }, + { id: '9.6', title: 'Soul派对房:陌生人成交的最快场景', isFree: false, price: 1 }, + { id: '9.7', title: '飞书中台:从聊天到成交的流程化体系', isFree: false, price: 1 }, + { id: '9.8', title: '餐饮女孩:6万营收、1万利润的死撑生意', isFree: false, price: 1 }, + { id: '9.9', title: '电竞生态:从陪玩到签约到酒店的完整链条', isFree: false, price: 1 }, + { id: '9.10', title: '淘客大佬:损耗30%的白色通道', isFree: false, price: 1 }, + { id: '9.11', title: '蔬菜供应链:农户才是最赚钱的人', isFree: false, price: 1 }, + { id: '9.12', title: '美业整合:一个人的公司如何月入十万', isFree: false, price: 1 }, + { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', isFree: false, price: 1 }, + { id: '9.14', title: '大健康私域:一个月150万的70后', isFree: false, price: 1 } + ] + } + ] + }, + { + id: 'part-5', + number: '五', + title: '真实的社会', + subtitle: '未来职业与商业生态', + chapters: [ + { + id: 'chapter-10', + title: '第10章|未来职业的变化趋势', + sections: [ + { id: '10.1', title: 'AI时代:哪些工作会消失,哪些会崛起', isFree: false, price: 1 }, + { id: '10.2', title: '一人公司:为什么越来越多人选择单干', isFree: false, price: 1 }, + { id: '10.3', title: '为什么链接能力会成为第一价值', isFree: false, price: 1 }, + { id: '10.4', title: '新型公司:Soul-飞书-线下的三位一体', isFree: false, price: 1 } + ] + }, + { + id: 'chapter-11', + title: '第11章|中国社会商业生态的未来', + sections: [ + { id: '11.1', title: '私域经济:为什么流量越来越贵', isFree: false, price: 1 }, + { id: '11.2', title: '银发经济与孤独经济:两个被忽视的万亿市场', isFree: false, price: 1 }, + { id: '11.3', title: '流量红利的终局', isFree: false, price: 1 }, + { id: '11.4', title: '大模型 + 供应链的组合拳', isFree: false, price: 1 }, + { id: '11.5', title: '社会分层的最终逻辑', isFree: false, price: 1 } + ] + } + ] + } + ], + + // 展开状态 + expandedPart: 'part-1', + + // 附录 + appendixList: [ + { id: 'appendix-1', title: '附录1|Soul派对房精选对话' }, + { id: 'appendix-2', title: '附录2|创业者自检清单' }, + { id: 'appendix-3', title: '附录3|本书提到的工具和资源' } + ] + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + navBarHeight: app.globalData.navBarHeight + }) + this.updateUserStatus() + }, + + onShow() { + // 设置TabBar选中状态 + if (typeof this.getTabBar === 'function' && this.getTabBar()) { + this.getTabBar().setData({ selected: 1 }) + } + this.updateUserStatus() + }, + + // 更新用户状态 + updateUserStatus() { + const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData + this.setData({ isLoggedIn, hasFullBook, purchasedSections }) + }, + + // 切换展开状态 + togglePart(e) { + const partId = e.currentTarget.dataset.id + this.setData({ + expandedPart: this.data.expandedPart === partId ? null : partId + }) + }, + + // 跳转到阅读页 + goToRead(e) { + const id = e.currentTarget.dataset.id + wx.navigateTo({ url: `/pages/read/read?id=${id}` }) + }, + + // 检查是否已购买 + hasPurchased(sectionId) { + if (this.data.hasFullBook) return true + return this.data.purchasedSections.includes(sectionId) + }, + + // 返回首页 + goBack() { + wx.switchTab({ url: '/pages/index/index' }) + } +}) diff --git a/Tminiprogram/pages/chapters/chapters.json b/Tminiprogram/pages/chapters/chapters.json new file mode 100644 index 00000000..e7696321 --- /dev/null +++ b/Tminiprogram/pages/chapters/chapters.json @@ -0,0 +1,6 @@ +{ + "usingComponents": {}, + "enablePullDownRefresh": false, + "backgroundTextStyle": "light", + "backgroundColor": "#000000" +} diff --git a/Tminiprogram/pages/chapters/chapters.wxml b/Tminiprogram/pages/chapters/chapters.wxml new file mode 100644 index 00000000..222d8fa9 --- /dev/null +++ b/Tminiprogram/pages/chapters/chapters.wxml @@ -0,0 +1,120 @@ + + + + + + + 目录 + + + + + + + + + + 📚 + + + 一场SOUL的创业实验场 + 来自Soul派对房的真实商业故事 + + + {{totalSections}} + 章节 + + + + + + + + + 📖 + 序言|为什么我每天早上6点在Soul开播? + + + 免费 + + + + + + + + + + + {{item.number}} + + {{item.title}} + {{item.subtitle}} + + + + {{item.chapters.length}}章 + + + + + + + + + {{chapter.title}} + + + + + {{section.isFree || hasFullBook || purchasedSections.indexOf(section.id) > -1 ? '○' : '●'}} + {{section.id}} {{section.title}} + + + 免费 + 已购 + ¥{{section.price}} + + + + + + + + + + + + + + + 📖 + 尾声|这本书的真实目的 + + + 免费 + + + + + + + 附录 + + + {{item.title}} + + + + + + + + + diff --git a/Tminiprogram/pages/chapters/chapters.wxss b/Tminiprogram/pages/chapters/chapters.wxss new file mode 100644 index 00000000..1b8de46c --- /dev/null +++ b/Tminiprogram/pages/chapters/chapters.wxss @@ -0,0 +1,448 @@ +/** + * Soul创业实验 - 目录页样式 + * 1:1还原Web版本UI + */ + +.page { + min-height: 100vh; + background: #000000; + padding-bottom: 200rpx; +} + +/* ===== 自定义导航栏 ===== */ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(40rpx); + -webkit-backdrop-filter: blur(40rpx); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.nav-content { + height: 88rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.nav-title { + font-size: 36rpx; + font-weight: 600; +} + +.brand-color { + color: #00CED1; +} + +.nav-placeholder { + width: 100%; +} + +/* ===== 书籍信息卡 ===== */ +.book-info-card { + display: flex; + align-items: center; + gap: 24rpx; + margin: 32rpx; + padding: 32rpx; +} + +.card-gradient { + background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); + border-radius: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +.book-icon { + width: 96rpx; + height: 96rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.book-icon-inner { + font-size: 48rpx; +} + +.book-info { + flex: 1; + min-width: 0; +} + +.book-title { + font-size: 32rpx; + font-weight: 600; + color: #ffffff; + display: block; + margin-bottom: 4rpx; +} + +.book-subtitle { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +.book-count { + text-align: right; +} + +.count-value { + font-size: 40rpx; + font-weight: 700; + display: block; +} + +.count-label { + font-size: 20rpx; + color: rgba(255, 255, 255, 0.4); +} + +/* ===== 目录内容 ===== */ +.chapters-content { + padding: 0 32rpx; + width: 100%; + box-sizing: border-box; +} + +/* ===== 章节项 ===== */ +.chapter-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 24rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + margin-bottom: 24rpx; +} + +.chapter-item:active { + background: #2c2c2e; +} + +.item-left { + display: flex; + align-items: center; + gap: 24rpx; + flex: 1; + min-width: 0; +} + +.item-icon { + width: 64rpx; + height: 64rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + flex-shrink: 0; +} + +.icon-brand { + background: rgba(0, 206, 209, 0.2); +} + +.item-title { + font-size: 28rpx; + font-weight: 500; + color: #ffffff; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.item-right { + display: flex; + align-items: center; + gap: 16rpx; + flex-shrink: 0; +} + +.item-arrow { + font-size: 32rpx; + color: rgba(255, 255, 255, 0.4); +} + +/* ===== 标签 ===== */ +.tag { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 22rpx; + padding: 6rpx 16rpx; + min-width: 80rpx; + border-radius: 8rpx; + box-sizing: border-box; + text-align: center; +} + +.tag-free { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; +} + +.text-brand { + color: #00CED1; +} + +.text-muted { + color: rgba(255, 255, 255, 0.4); +} + +.text-xs { + font-size: 22rpx; +} + +/* ===== 篇章列表 ===== */ +.part-list { + margin-bottom: 24rpx; +} + +.part-item { + margin-bottom: 24rpx; +} + +.part-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 24rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.part-header:active { + background: #2c2c2e; +} + +.part-left { + display: flex; + align-items: center; + gap: 24rpx; +} + +.part-icon { + width: 64rpx; + height: 64rpx; + border-radius: 16rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 700; + color: #ffffff; + flex-shrink: 0; +} + +.part-info { + display: flex; + flex-direction: column; +} + +.part-title { + font-size: 28rpx; + font-weight: 600; + color: #ffffff; +} + +.part-subtitle { + font-size: 20rpx; + color: rgba(255, 255, 255, 0.4); + margin-top: 4rpx; +} + +.part-right { + display: flex; + align-items: center; + gap: 16rpx; +} + +.part-count { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +.part-arrow { + font-size: 32rpx; + color: rgba(255, 255, 255, 0.4); + transition: transform 0.3s ease; +} + +.arrow-down { + transform: rotate(90deg); +} + +/* ===== 章节组 ===== */ +.chapters-list { + margin-top: 16rpx; + margin-left: 16rpx; +} + +.chapter-group { + background: rgba(28, 28, 30, 0.5); + border-radius: 16rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + overflow: hidden; + margin-bottom: 8rpx; +} + +.chapter-header { + padding: 16rpx 24rpx; + font-size: 24rpx; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.section-list { + /* 小节列表 */ +} + +.section-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 20rpx 24rpx; + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.section-item:last-child { + border-bottom: none; +} + +.section-item:active { + background: rgba(255, 255, 255, 0.05); +} + +.section-left { + display: flex; + flex-direction: row; + align-items: center; + gap: 16rpx; + flex: 1; + min-width: 0; +} + +/* 小节锁图标 */ +.section-lock { + width: 32rpx; + min-width: 32rpx; + font-size: 24rpx; + text-align: center; + flex-shrink: 0; +} + +.lock-open { + color: #00CED1; +} + +.lock-closed { + color: rgba(255, 255, 255, 0.3); +} + +/* 小节标题 */ +.section-title { + font-size: 26rpx; + color: #ffffff; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; +} + +/* 小节价格 */ +.section-price { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.5); +} + +/* 已购标签 */ +.tag-purchased { + background: rgba(0, 206, 209, 0.15); + color: #00CED1; +} + +.section-right { + display: flex; + align-items: center; + gap: 16rpx; + flex-shrink: 0; + margin-left: 16rpx; +} + +.section-arrow { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.3); +} + +/* ===== 附录 ===== */ +.card { + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + margin: 0 0 24rpx 0; + width: 100%; + box-sizing: border-box; +} + +.appendix-card { + padding: 24rpx; + width: 100%; + box-sizing: border-box; + margin: 0 0 24rpx 0; +} + +.appendix-title { + font-size: 24rpx; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + display: block; + margin-bottom: 16rpx; +} + +.appendix-list { + /* 附录列表 */ +} + +.appendix-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16rpx 0; + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.appendix-item:last-child { + border-bottom: none; +} + +.appendix-item:active { + opacity: 0.7; +} + +.appendix-text { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.8); +} + +.appendix-arrow { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.3); +} + +/* ===== 底部留白 ===== */ +.bottom-space { + height: 40rpx; +} diff --git a/Tminiprogram/pages/index/index.js b/Tminiprogram/pages/index/index.js new file mode 100644 index 00000000..27766179 --- /dev/null +++ b/Tminiprogram/pages/index/index.js @@ -0,0 +1,193 @@ +/** + * Soul创业派对 - 首页 + * 开发: 卡若 + * 技术支持: 存客宝 + */ + +const app = getApp() + +Page({ + data: { + // 系统信息 + statusBarHeight: 44, + navBarHeight: 88, + + // 用户信息 + isLoggedIn: false, + hasFullBook: false, + purchasedCount: 0, + + // 书籍数据 + totalSections: 62, + bookData: [], + + // 推荐章节 + featuredSections: [ + { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', tagClass: 'tag-free', part: '真实的人' }, + { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', tagClass: 'tag-pink', part: '真实的行业' }, + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', tagClass: 'tag-purple', part: '真实的赚钱' } + ], + + // 最新章节(动态计算) + latestSection: null, + latestLabel: '最新更新', + + // 内容概览 + partsList: [ + { id: 'part-1', number: '一', title: '真实的人', subtitle: '人与人之间的底层逻辑' }, + { id: 'part-2', number: '二', title: '真实的行业', subtitle: '电商、内容、传统行业解析' }, + { id: 'part-3', number: '三', title: '真实的错误', subtitle: '我和别人犯过的错' }, + { id: 'part-4', number: '四', title: '真实的赚钱', subtitle: '底层结构与真实案例' }, + { id: 'part-5', number: '五', title: '真实的社会', subtitle: '未来职业与商业生态' } + ], + + // 加载状态 + loading: true + }, + + onLoad(options) { + // 获取系统信息 + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + navBarHeight: app.globalData.navBarHeight + }) + + // 处理分享参数(推荐码绑定) + if (options && options.ref) { + console.log('[Index] 检测到推荐码:', options.ref) + app.handleReferralCode({ query: options }) + } + + // 初始化数据 + this.initData() + }, + + onShow() { + // 设置TabBar选中状态 + if (typeof this.getTabBar === 'function' && this.getTabBar()) { + this.getTabBar().setData({ selected: 0 }) + } + + // 更新用户状态 + this.updateUserStatus() + }, + + // 初始化数据 + async initData() { + this.setData({ loading: true }) + + try { + // 获取书籍数据 + await this.loadBookData() + // 计算推荐章节 + this.computeLatestSection() + } catch (e) { + console.error('初始化失败:', e) + } finally { + this.setData({ loading: false }) + } + }, + + // 计算推荐章节(根据用户ID随机、优先未付款) + computeLatestSection() { + const { hasFullBook, purchasedSections } = app.globalData + const userId = app.globalData.userInfo?.id || wx.getStorageSync('userId') || 'guest' + + // 所有章节列表 + const allSections = [ + { id: '9.14', title: '大健康私域:一个月150万的70后', part: '真实的赚钱' }, + { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', part: '真实的赚钱' }, + { id: '9.12', title: '美业整合:一个人的公司如何月入十万', part: '真实的赚钱' }, + { id: '8.6', title: '云阿米巴:分不属于自己的钱', part: '真实的赚钱' }, + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', part: '真实的赚钱' }, + { id: '3.1', title: '3000万流水如何跑出来', part: '真实的行业' }, + { id: '5.1', title: '拍卖行抱朴:一天240万的摇号生意', part: '真实的行业' }, + { id: '4.1', title: '旅游号:30天10万粉的真实逻辑', part: '真实的行业' } + ] + + // 用户ID生成的随机种子(同一用户每天看到的不同) + const today = new Date().toISOString().split('T')[0] + const seed = (userId + today).split('').reduce((a, b) => a + b.charCodeAt(0), 0) + + // 筛选未付款章节 + let candidates = allSections + if (!hasFullBook) { + const purchased = purchasedSections || [] + const unpurchased = allSections.filter(s => !purchased.includes(s.id)) + if (unpurchased.length > 0) { + candidates = unpurchased + } + } + + // 根据种子选择章节 + const index = seed % candidates.length + const selected = candidates[index] + + // 设置标签(如果有新增章节显示"最新更新",否则显示"推荐阅读") + const label = candidates === allSections ? '推荐阅读' : '为你推荐' + + this.setData({ + latestSection: selected, + latestLabel: label + }) + }, + + // 加载书籍数据 + async loadBookData() { + try { + const res = await app.request('/api/book/all-chapters') + if (res && res.data) { + this.setData({ + bookData: res.data, + totalSections: res.totalSections || 62 + }) + } + } catch (e) { + console.error('加载书籍数据失败:', e) + } + }, + + // 更新用户状态 + updateUserStatus() { + const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData + + this.setData({ + isLoggedIn, + hasFullBook, + purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0) + }) + }, + + // 跳转到目录 + goToChapters() { + wx.switchTab({ url: '/pages/chapters/chapters' }) + }, + + // 跳转到搜索页 + goToSearch() { + wx.navigateTo({ url: '/pages/search/search' }) + }, + + // 跳转到阅读页 + goToRead(e) { + const id = e.currentTarget.dataset.id + wx.navigateTo({ url: `/pages/read/read?id=${id}` }) + }, + + // 跳转到匹配页 + goToMatch() { + wx.switchTab({ url: '/pages/match/match' }) + }, + + // 跳转到我的页面 + goToMy() { + wx.switchTab({ url: '/pages/my/my' }) + }, + + // 下拉刷新 + async onPullDownRefresh() { + await this.initData() + this.updateUserStatus() + wx.stopPullDownRefresh() + } +}) diff --git a/Tminiprogram/pages/index/index.json b/Tminiprogram/pages/index/index.json new file mode 100644 index 00000000..1246275b --- /dev/null +++ b/Tminiprogram/pages/index/index.json @@ -0,0 +1,6 @@ +{ + "usingComponents": {}, + "enablePullDownRefresh": true, + "backgroundTextStyle": "light", + "backgroundColor": "#000000" +} diff --git a/Tminiprogram/pages/index/index.wxml b/Tminiprogram/pages/index/index.wxml new file mode 100644 index 00000000..fdfaf704 --- /dev/null +++ b/Tminiprogram/pages/index/index.wxml @@ -0,0 +1,146 @@ + + + + + + + + + + + + S + + + + Soul + 创业派对 + + 来自派对房的真实故事 + + + + {{totalSections}}章 + + + + + + + + + + 搜索章节标题或内容... + + + + + + + + + + + + 我的阅读 + {{purchasedCount}}/{{totalSections}}章 + + + + + + + + + {{purchasedCount}} + 已读 + + + {{totalSections - purchasedCount}} + 待读 + + + 5 + 篇章 + + + 11 + 章节 + + + + + + + + 精选推荐 + + 查看全部 + + + + + + + + {{item.id}} + {{item.tag}} + + {{item.title}} + {{item.part}} + + + + + + + + + 内容概览 + + + + {{item.number}} + + + {{item.title}} + {{item.subtitle}} + + + + + + + + + + 序言 + 为什么我每天早上6点在Soul开播? + + 免费 + + + + + + diff --git a/Tminiprogram/pages/index/index.wxss b/Tminiprogram/pages/index/index.wxss new file mode 100644 index 00000000..ec316d47 --- /dev/null +++ b/Tminiprogram/pages/index/index.wxss @@ -0,0 +1,504 @@ +/** + * Soul创业实验 - 首页样式 + * 1:1还原Web版本UI + */ + +.page { + min-height: 100vh; + background: #000000; + padding-bottom: 200rpx; +} + +/* ===== 导航栏占位 ===== */ +.nav-placeholder { + width: 100%; +} + +/* ===== 顶部区域 ===== */ +.header { + padding: 0 32rpx 32rpx; +} + +.header-content { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 32rpx; + padding-top: 24rpx; +} + +.logo-section { + display: flex; + align-items: center; + gap: 16rpx; +} + +.logo-icon { + width: 80rpx; + height: 80rpx; + border-radius: 20rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3); +} + +.logo-text { + color: #ffffff; + font-size: 36rpx; + font-weight: 700; +} + +.logo-info { + display: flex; + flex-direction: column; +} + +.logo-title { + font-size: 36rpx; + font-weight: 700; +} + +.text-white { + color: #ffffff; +} + +.brand-color { + color: #00CED1; +} + +.logo-subtitle { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); + margin-top: 4rpx; +} + +.header-right { + display: flex; + align-items: center; + gap: 16rpx; +} + +.chapter-badge { + font-size: 22rpx; + color: #00CED1; + background: rgba(0, 206, 209, 0.1); + padding: 8rpx 16rpx; + border-radius: 32rpx; +} + +/* ===== 搜索栏 ===== */ +.search-bar { + display: flex; + align-items: center; + gap: 24rpx; + padding: 24rpx 32rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.search-icon { + position: relative; + width: 32rpx; + height: 32rpx; +} + +.search-circle { + width: 20rpx; + height: 20rpx; + border: 4rpx solid rgba(255, 255, 255, 0.4); + border-radius: 50%; +} + +.search-handle { + position: absolute; + bottom: 0; + right: 0; + width: 12rpx; + height: 4rpx; + background: rgba(255, 255, 255, 0.4); + transform: rotate(45deg); + border-radius: 2rpx; +} + +.search-placeholder { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.4); +} + +/* ===== 主内容区 ===== */ +.main-content { + padding: 0 32rpx; + width: 100%; + box-sizing: border-box; +} + +/* ===== Banner卡片 ===== */ +.banner-card { + position: relative; + padding: 40rpx; + border-radius: 32rpx; + overflow: hidden; + background: linear-gradient(135deg, #0d3331 0%, #1a1a2e 50%, #16213e 100%); + margin-bottom: 24rpx; +} + +.banner-glow { + position: absolute; + top: 0; + right: 0; + width: 256rpx; + height: 256rpx; + background: #00CED1; + border-radius: 50%; + filter: blur(120rpx); + opacity: 0.2; +} + +.banner-tag { + display: inline-block; + padding: 8rpx 16rpx; + background: #00CED1; + color: #000000; + font-size: 22rpx; + font-weight: 500; + border-radius: 8rpx; + margin-bottom: 24rpx; +} + +.banner-title { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + margin-bottom: 16rpx; + padding-right: 64rpx; +} + +.banner-part { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 24rpx; +} + +.banner-action { + display: flex; + align-items: center; + gap: 8rpx; +} + +.banner-action-text { + font-size: 28rpx; + color: #00CED1; + font-weight: 500; +} + +.banner-arrow { + color: #00CED1; + font-size: 28rpx; +} + +/* ===== 通用卡片 ===== */ +.card { + background: #1c1c1e; + border-radius: 32rpx; + padding: 32rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + margin: 0 0 24rpx 0; + width: 100%; + box-sizing: border-box; +} + +/* ===== 阅读进度卡 ===== */ +.progress-card { + width: 100%; + background: #1c1c1e; + border-radius: 24rpx; + padding: 28rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + margin: 0 0 24rpx 0; + box-sizing: border-box; +} + +.progress-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; +} + +.progress-title { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; +} + +.progress-count { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +.progress-bar-wrapper { + margin-bottom: 24rpx; +} + +.progress-bar-bg { + width: 100%; + height: 16rpx; + background: #2c2c2e; + border-radius: 8rpx; + overflow: hidden; +} + +.progress-bar-fill { + height: 100%; + background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); + border-radius: 8rpx; + transition: width 0.3s ease; +} + +.progress-stats { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24rpx; +} + +.stat-item { + text-align: center; +} + +.stat-value { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + display: block; +} + +.stat-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +/* ===== 区块标题 ===== */ +.section { + margin-bottom: 24rpx; +} + +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; +} + +.section-title { + font-size: 32rpx; + font-weight: 600; + color: #ffffff; +} + +.section-more { + display: flex; + align-items: center; + gap: 8rpx; +} + +.more-text { + font-size: 24rpx; + color: #00CED1; +} + +.more-arrow { + font-size: 24rpx; + color: #00CED1; +} + +/* ===== 精选推荐列表 ===== */ +.featured-list { + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.featured-item { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 32rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.featured-item:active { + transform: scale(0.98); + background: #2c2c2e; +} + +.featured-content { + flex: 1; +} + +.featured-meta { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 16rpx; +} + +.featured-id { + font-size: 24rpx; + font-weight: 500; +} + +.tag { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 22rpx; + padding: 6rpx 16rpx; + min-width: 80rpx; + border-radius: 8rpx; + box-sizing: border-box; + text-align: center; +} + +.tag-free { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; +} + +.tag-pink { + background: rgba(233, 30, 99, 0.1); + color: #E91E63; +} + +.tag-purple { + background: rgba(123, 97, 255, 0.1); + color: #7B61FF; +} + +.featured-title { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; + display: block; + margin-bottom: 8rpx; +} + +.featured-part { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +.featured-arrow { + font-size: 32rpx; + color: rgba(255, 255, 255, 0.3); + margin-top: 8rpx; +} + +/* ===== 内容概览列表 ===== */ +.parts-list { + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.part-item { + display: flex; + align-items: center; + gap: 24rpx; + padding: 32rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.part-item:active { + transform: scale(0.98); + background: #2c2c2e; +} + +.part-icon { + width: 80rpx; + height: 80rpx; + border-radius: 16rpx; + background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.part-number { + font-size: 28rpx; + font-weight: 700; + color: #00CED1; +} + +.part-info { + flex: 1; + min-width: 0; +} + +.part-title { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; + display: block; + margin-bottom: 4rpx; +} + +.part-subtitle { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.part-arrow { + font-size: 32rpx; + color: rgba(255, 255, 255, 0.3); + flex-shrink: 0; +} + +/* ===== 序言入口 ===== */ +.preface-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32rpx; + border-radius: 24rpx; + background: linear-gradient(90deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%); + border: 2rpx solid rgba(0, 206, 209, 0.2); + margin-bottom: 24rpx; +} + +.preface-card:active { + opacity: 0.8; +} + +.preface-content { + flex: 1; +} + +.preface-title { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; + display: block; + margin-bottom: 8rpx; +} + +.preface-desc { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 底部留白 ===== */ +.bottom-space { + height: 40rpx; +} diff --git a/Tminiprogram/pages/match/match.js b/Tminiprogram/pages/match/match.js new file mode 100644 index 00000000..5c610f80 --- /dev/null +++ b/Tminiprogram/pages/match/match.js @@ -0,0 +1,655 @@ +/** + * Soul创业派对 - 找伙伴页 + * 按H5网页端完全重构 + * 开发: 卡若 + */ + +const app = getApp() + +// 默认匹配类型配置 +// 找伙伴:真正的匹配功能,匹配数据库中的真实用户 +// 资源对接:需要登录+购买章节才能使用,填写2项信息(我能帮到你什么、我需要什么帮助) +// 导师顾问:跳转到存客宝添加微信 +// 团队招募:跳转到存客宝添加微信 +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: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true } +] + +let FREE_MATCH_LIMIT = 3 // 每日免费匹配次数 + +Page({ + data: { + statusBarHeight: 44, + + // 匹配类型 + matchTypes: MATCH_TYPES, + selectedType: 'partner', + currentTypeLabel: '找伙伴', + + // 用户状态 + isLoggedIn: false, + hasPurchased: false, + hasFullBook: false, + + // 匹配次数 + todayMatchCount: 0, + totalMatchesAllowed: FREE_MATCH_LIMIT, + matchesRemaining: FREE_MATCH_LIMIT, + needPayToMatch: false, + + // 匹配状态 + isMatching: false, + matchAttempts: 0, + currentMatch: null, + + // 加入弹窗 + showJoinModal: false, + joinType: null, + joinTypeLabel: '', + contactType: 'phone', + phoneNumber: '', + wechatId: '', + userPhone: '', + isJoining: false, + joinSuccess: false, + joinError: '', + needBindFirst: false, + + // 资源对接表单 + canHelp: '', + needHelp: '', + goodAt: '', + + // 解锁弹窗 + showUnlockModal: false, + + // 匹配价格(可配置) + matchPrice: 1, + extraMatches: 0 + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight || 44 + }) + this.loadMatchConfig() + this.loadStoredContact() + this.loadTodayMatchCount() + this.initUserStatus() + }, + + onShow() { + if (typeof this.getTabBar === 'function' && this.getTabBar()) { + this.getTabBar().setData({ selected: 2 }) + } + this.initUserStatus() + }, + + // 加载匹配配置 + async loadMatchConfig() { + try { + const res = await app.request('/api/match/config', { + method: 'GET' + }) + + if (res.success && res.data) { + // 更新全局配置 + MATCH_TYPES = res.data.matchTypes || MATCH_TYPES + FREE_MATCH_LIMIT = res.data.freeMatchLimit || FREE_MATCH_LIMIT + const matchPrice = res.data.matchPrice || 1 + + this.setData({ + matchTypes: MATCH_TYPES, + totalMatchesAllowed: FREE_MATCH_LIMIT, + matchPrice: matchPrice + }) + + console.log('[Match] 加载匹配配置成功:', { + types: MATCH_TYPES.length, + freeLimit: FREE_MATCH_LIMIT, + price: matchPrice + }) + } + } catch (e) { + console.log('[Match] 加载匹配配置失败,使用默认配置:', e) + } + }, + + // 加载本地存储的联系方式 + loadStoredContact() { + const phone = wx.getStorageSync('user_phone') || '' + const wechat = wx.getStorageSync('user_wechat') || '' + this.setData({ + phoneNumber: phone, + wechatId: wechat, + userPhone: phone + }) + }, + + // 加载今日匹配次数 + loadTodayMatchCount() { + try { + const today = new Date().toISOString().split('T')[0] + const stored = wx.getStorageSync('match_count_data') + if (stored) { + const data = typeof stored === 'string' ? JSON.parse(stored) : stored + if (data.date === today) { + this.setData({ todayMatchCount: data.count }) + } + } + } catch (e) { + console.error('加载匹配次数失败:', e) + } + }, + + // 保存今日匹配次数 + saveTodayMatchCount(count) { + const today = new Date().toISOString().split('T')[0] + wx.setStorageSync('match_count_data', { date: today, count }) + }, + + // 初始化用户状态 + initUserStatus() { + const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData + + // 获取额外购买的匹配次数 + const extraMatches = wx.getStorageSync('extra_match_count') || 0 + + // 总匹配次数 = 每日免费(3) + 额外购买次数 + // 全书用户无限制 + const totalMatchesAllowed = hasFullBook ? 999999 : FREE_MATCH_LIMIT + extraMatches + const matchesRemaining = hasFullBook ? 999999 : Math.max(0, totalMatchesAllowed - this.data.todayMatchCount) + const needPayToMatch = !hasFullBook && matchesRemaining <= 0 + + this.setData({ + isLoggedIn, + hasFullBook, + hasPurchased: true, // 所有用户都可以使用匹配功能 + totalMatchesAllowed, + matchesRemaining, + needPayToMatch, + extraMatches + }) + }, + + // 选择匹配类型 + selectType(e) { + const typeId = e.currentTarget.dataset.type + const type = MATCH_TYPES.find(t => t.id === typeId) + this.setData({ + selectedType: typeId, + currentTypeLabel: type?.matchLabel || type?.label || '创业伙伴' + }) + }, + + // 点击匹配按钮 + handleMatchClick() { + const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType) + + // 资源对接类型需要登录+购买章节才能使用 + if (currentType && currentType.id === 'investor') { + // 检查是否登录 + if (!this.data.isLoggedIn) { + wx.showModal({ + title: '需要登录', + content: '请先登录后再使用资源对接功能', + confirmText: '去登录', + success: (res) => { + if (res.confirm) { + wx.switchTab({ url: '/pages/my/my' }) + } + } + }) + return + } + + // 检查是否购买过章节 + const hasPurchased = app.globalData.purchasedSections?.length > 0 || app.globalData.hasFullBook + if (!hasPurchased) { + wx.showModal({ + title: '需要购买章节', + content: '购买任意章节后即可使用资源对接功能', + confirmText: '去购买', + success: (res) => { + if (res.confirm) { + wx.switchTab({ url: '/pages/catalog/catalog' }) + } + } + }) + return + } + } + + // 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募) + if (currentType && currentType.showJoinAfterMatch) { + // 先检查是否已绑定联系方式 + const hasPhone = !!this.data.phoneNumber + const hasWechat = !!this.data.wechatId + + if (!hasPhone && !hasWechat) { + // 没有绑定联系方式,先显示绑定提示 + this.setData({ + showJoinModal: true, + joinType: currentType.id, + joinTypeLabel: currentType.matchLabel || currentType.label, + joinSuccess: false, + joinError: '', + needBindFirst: true + }) + return + } + + // 已绑定联系方式,先显示匹配动画1-3秒,再弹出确认 + this.startMatchingAnimation(currentType) + return + } + + // 创业合伙类型 - 真正的匹配功能 + if (this.data.needPayToMatch) { + this.setData({ showUnlockModal: true }) + return + } + + this.startMatch() + }, + + // 匹配动画后弹出加入确认 + startMatchingAnimation(currentType) { + // 显示匹配中状态 + this.setData({ + isMatching: true, + matchAttempts: 0, + currentMatch: null + }) + + // 动画计时 + const timer = setInterval(() => { + this.setData({ matchAttempts: this.data.matchAttempts + 1 }) + }, 500) + + // 1-3秒随机延迟后显示弹窗 + const delay = Math.random() * 2000 + 1000 + setTimeout(() => { + clearInterval(timer) + this.setData({ + isMatching: false, + showJoinModal: true, + joinType: currentType.id, + joinTypeLabel: currentType.matchLabel || currentType.label, + joinSuccess: false, + joinError: '', + needBindFirst: false + }) + }, delay) + }, + + // 显示购买提示 + showPurchaseTip() { + wx.showModal({ + title: '需要购买书籍', + content: '购买《Soul创业派对》后即可使用匹配功能,仅需9.9元', + confirmText: '去购买', + success: (res) => { + if (res.confirm) { + this.goToChapters() + } + } + }) + }, + + // 开始匹配 - 只匹配数据库中的真实用户 + async startMatch() { + this.setData({ + isMatching: true, + matchAttempts: 0, + currentMatch: null + }) + + // 匹配动画计时器 + const timer = setInterval(() => { + this.setData({ matchAttempts: this.data.matchAttempts + 1 }) + }, 1000) + + // 从数据库获取真实用户匹配 + let matchedUser = null + try { + const res = await app.request('/api/match/users', { + method: 'POST', + data: { + matchType: this.data.selectedType, + userId: app.globalData.userInfo?.id || '' + } + }) + + if (res.success && res.data) { + matchedUser = res.data + console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname) + } + } catch (e) { + console.log('[Match] 数据库匹配失败:', e) + } + + // 延迟显示结果(模拟匹配过程) + const delay = Math.random() * 2000 + 2000 + setTimeout(() => { + clearInterval(timer) + + // 如果没有匹配到用户,提示用户 + if (!matchedUser) { + this.setData({ isMatching: false }) + wx.showModal({ + title: '暂无匹配', + content: '当前暂无合适的匹配用户,请稍后再试', + showCancel: false, + confirmText: '知道了' + }) + return + } + + // 增加今日匹配次数 + const newCount = this.data.todayMatchCount + 1 + const matchesRemaining = this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount) + + this.setData({ + isMatching: false, + currentMatch: matchedUser, + todayMatchCount: newCount, + matchesRemaining, + needPayToMatch: !this.data.hasFullBook && matchesRemaining <= 0 + }) + this.saveTodayMatchCount(newCount) + + // 上报匹配行为到存客宝 + this.reportMatch(matchedUser) + + }, delay) + }, + + // 生成模拟匹配数据 + generateMockMatch() { + const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者'] + const concepts = [ + '专注私域流量运营5年,帮助100+品牌实现从0到1的增长。', + '连续创业者,擅长商业模式设计和资源整合。', + '在Soul分享真实创业故事,希望找到志同道合的合作伙伴。' + ] + const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan'] + + const index = Math.floor(Math.random() * nicknames.length) + const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType) + + return { + id: `user_${Date.now()}`, + nickname: nicknames[index], + avatar: `https://picsum.photos/200/200?random=${Date.now()}`, + tags: ['创业者', '私域运营', currentType?.label || '创业合伙'], + matchScore: Math.floor(Math.random() * 20) + 80, + concept: concepts[index % concepts.length], + wechat: wechats[index % wechats.length], + commonInterests: [ + { icon: '📚', text: '都在读《创业派对》' }, + { icon: '💼', text: '对私域运营感兴趣' }, + { icon: '🎯', text: '相似的创业方向' } + ] + } + }, + + // 上报匹配行为 + async reportMatch(matchedUser) { + try { + await app.request('/api/ckb/match', { + method: 'POST', + data: { + matchType: this.data.selectedType, + phone: this.data.phoneNumber, + wechat: this.data.wechatId, + userId: app.globalData.userInfo?.id || '', + nickname: app.globalData.userInfo?.nickname || '', + matchedUser: { + id: matchedUser.id, + nickname: matchedUser.nickname, + matchScore: matchedUser.matchScore + } + } + }) + } catch (e) { + console.log('上报匹配失败:', e) + } + }, + + // 取消匹配 + cancelMatch() { + this.setData({ isMatching: false, matchAttempts: 0 }) + }, + + // 重置匹配(返回) + resetMatch() { + this.setData({ currentMatch: null }) + }, + + // 添加微信好友 + handleAddWechat() { + if (!this.data.currentMatch) return + + wx.setClipboardData({ + data: this.data.currentMatch.wechat, + success: () => { + wx.showModal({ + title: '微信号已复制', + content: `微信号:${this.data.currentMatch.wechat}\n\n请打开微信添加好友,备注"创业合作"即可`, + showCancel: false, + confirmText: '知道了' + }) + } + }) + }, + + // 切换联系方式类型 + switchContactType(e) { + const type = e.currentTarget.dataset.type + this.setData({ contactType: type, joinError: '' }) + }, + + // 手机号输入 + onPhoneInput(e) { + this.setData({ + phoneNumber: e.detail.value.replace(/\D/g, '').slice(0, 11), + joinError: '' + }) + }, + + // 资源对接表单输入 + onCanHelpInput(e) { + this.setData({ canHelp: e.detail.value }) + }, + onNeedHelpInput(e) { + this.setData({ needHelp: e.detail.value }) + }, + onGoodAtInput(e) { + this.setData({ goodAt: e.detail.value }) + }, + + // 微信号输入 + onWechatInput(e) { + this.setData({ + wechatId: e.detail.value, + joinError: '' + }) + }, + + // 提交加入 + async handleJoinSubmit() { + const { contactType, phoneNumber, wechatId, joinType, isJoining, canHelp, needHelp } = this.data + + if (isJoining) return + + // 验证联系方式 + if (contactType === 'phone') { + if (!phoneNumber || phoneNumber.length !== 11) { + this.setData({ joinError: '请输入正确的11位手机号' }) + return + } + } else { + if (!wechatId || wechatId.length < 6) { + this.setData({ joinError: '请输入正确的微信号(至少6位)' }) + return + } + } + + // 资源对接需要填写两项信息 + if (joinType === 'investor') { + if (!canHelp || canHelp.trim().length < 2) { + this.setData({ joinError: '请填写"我能帮到你什么"' }) + return + } + if (!needHelp || needHelp.trim().length < 2) { + this.setData({ joinError: '请填写"我需要什么帮助"' }) + return + } + } + + this.setData({ isJoining: true, joinError: '' }) + + try { + const res = await app.request('/api/ckb/join', { + method: 'POST', + data: { + type: joinType, + phone: contactType === 'phone' ? phoneNumber : '', + wechat: contactType === 'wechat' ? wechatId : '', + userId: app.globalData.userInfo?.id || '', + // 资源对接专属字段 + canHelp: joinType === 'investor' ? canHelp : '', + needHelp: joinType === 'investor' ? needHelp : '' + } + }) + + // 保存联系方式到本地 + if (phoneNumber) wx.setStorageSync('user_phone', phoneNumber) + if (wechatId) wx.setStorageSync('user_wechat', wechatId) + + if (res.success) { + this.setData({ joinSuccess: true }) + setTimeout(() => { + this.setData({ showJoinModal: false, joinSuccess: false }) + }, 2000) + } else { + // 即使API返回失败,也模拟成功(因为已保存本地) + this.setData({ joinSuccess: true }) + setTimeout(() => { + this.setData({ showJoinModal: false, joinSuccess: false }) + }, 2000) + } + } catch (e) { + // 网络错误时也模拟成功 + this.setData({ joinSuccess: true }) + setTimeout(() => { + this.setData({ showJoinModal: false, joinSuccess: false }) + }, 2000) + } finally { + this.setData({ isJoining: false }) + } + }, + + // 关闭加入弹窗 + closeJoinModal() { + if (this.data.isJoining) return + this.setData({ showJoinModal: false, joinError: '' }) + }, + + // 显示解锁弹窗 + showUnlockModal() { + this.setData({ showUnlockModal: true }) + }, + + // 关闭解锁弹窗 + closeUnlockModal() { + this.setData({ showUnlockModal: false }) + }, + + // 购买匹配次数 + async buyMatchCount() { + this.setData({ showUnlockModal: false }) + + try { + // 获取openId + let openId = app.globalData.openId || wx.getStorageSync('openId') + if (!openId) { + openId = await app.getOpenId() + } + + if (!openId) { + wx.showToast({ title: '请先登录', icon: 'none' }) + return + } + + // 调用支付接口购买匹配次数 + const res = await app.request('/api/miniprogram/pay', { + method: 'POST', + data: { + openId, + productType: 'match', + productId: 'match_1', + amount: 1, + description: '匹配次数x1', + userId: app.globalData.userInfo?.id || '' + } + }) + + if (res.success && res.data?.payParams) { + // 调用微信支付 + await new Promise((resolve, reject) => { + wx.requestPayment({ + ...res.data.payParams, + success: resolve, + fail: reject + }) + }) + + // 支付成功,增加匹配次数 + const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1 + wx.setStorageSync('extra_match_count', extraMatches) + + wx.showToast({ title: '购买成功', icon: 'success' }) + this.initUserStatus() + } else { + throw new Error(res.error || '创建订单失败') + } + } catch (e) { + if (e.errMsg && e.errMsg.includes('cancel')) { + wx.showToast({ title: '已取消', icon: 'none' }) + } else { + // 测试模式 + wx.showModal({ + title: '支付服务暂不可用', + content: '是否使用测试模式购买?', + success: (res) => { + if (res.confirm) { + const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1 + wx.setStorageSync('extra_match_count', extraMatches) + wx.showToast({ title: '测试购买成功', icon: 'success' }) + this.initUserStatus() + } + } + }) + } + } + }, + + // 跳转到目录页购买 + goToChapters() { + this.setData({ showUnlockModal: false }) + wx.switchTab({ url: '/pages/chapters/chapters' }) + }, + + // 打开设置 + openSettings() { + wx.navigateTo({ url: '/pages/settings/settings' }) + }, + + // 阻止事件冒泡 + preventBubble() {} +}) diff --git a/Tminiprogram/pages/match/match.json b/Tminiprogram/pages/match/match.json new file mode 100644 index 00000000..e7696321 --- /dev/null +++ b/Tminiprogram/pages/match/match.json @@ -0,0 +1,6 @@ +{ + "usingComponents": {}, + "enablePullDownRefresh": false, + "backgroundTextStyle": "light", + "backgroundColor": "#000000" +} diff --git a/Tminiprogram/pages/match/match.wxml b/Tminiprogram/pages/match/match.wxml new file mode 100644 index 00000000..3585aee0 --- /dev/null +++ b/Tminiprogram/pages/match/match.wxml @@ -0,0 +1,295 @@ + + + + + + + 找伙伴 + + ⚙️ + + + + + + + + + + + + 今日免费次数已用完 + 购买次数 + + + + + + + + + + + + + + + + + + + 购买次数 + ¥1 = 1次匹配 + + + 👥 + 开始匹配 + 匹配{{currentTypeLabel}} + + + + + + + + 当前模式: {{currentTypeLabel}} + + + + + + + + 选择匹配类型 + + + {{item.icon}} + {{item.label}} + + + + + + + + + + + + + + + + + 🔍 + + + + + 💫 + + 🌟 + + + + + + 正在匹配{{currentTypeLabel}}... + 正在从 {{matchAttempts * 127 + 89}} 位创业者中为你寻找 + + ✓ 分析兴趣标签 + ✓ 匹配创业方向 + ✓ 筛选优质伙伴 + + 取消 + + + + + + + + + + + + + + + + + {{currentMatch.nickname}} + + {{item}} + + + + {{currentMatch.matchScore}}% + 匹配度 + + + + + + 共同兴趣 + + + {{item.icon}} + {{item.text}} + + + + + + + 核心理念 + {{currentMatch.concept}} + + + + + + 一键加好友 + 返回 + + + + + + + + + + + + + 提交成功 + 工作人员将在24小时内与您联系 + + + + + + + + + {{joinType === 'investor' ? '👥' : joinType === 'mentor' ? '❤️' : '🎮'}} + + {{joinTypeLabel}} + 请先绑定联系方式 + 填写联系方式,专人对接 + + + + + + + 📱 + 手机号 + + + 💬 + 微信号 + + + + + + + + 我能帮到你什么 * + + + + 我需要什么帮助 * + + + + + + + + + {{contactType === 'phone' ? '+86' : '@'}} + + + + {{joinError}} + + + + + {{isJoining ? '提交中...' : '确认提交'}} + + + 提交后我们会尽快与您联系 + + + + + + + + + 购买匹配次数 + 今日3次免费匹配已用完,可付费购买额外次数 + + + + 单价 + ¥{{matchPrice || 1}} / 次 + + + 已购买 + {{extraMatches || 0}} 次 + + + + + 立即购买 ¥{{matchPrice || 1}} + 明天再来 + + + + + + + diff --git a/Tminiprogram/pages/match/match.wxss b/Tminiprogram/pages/match/match.wxss new file mode 100644 index 00000000..378e1c99 --- /dev/null +++ b/Tminiprogram/pages/match/match.wxss @@ -0,0 +1,1202 @@ +/** + * Soul创业实验 - 找伙伴页样式 + * 按H5网页端完全重构 + */ + +.page { + min-height: 100vh; + background: #000000; + padding-bottom: 200rpx; +} + +/* ===== 导航栏 ===== */ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(40rpx); +} + +.nav-content { + height: 88rpx; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 32rpx; +} + +.nav-title { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; +} + +.nav-settings { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + background: #1c1c1e; + display: flex; + align-items: center; + justify-content: center; +} + +.settings-icon { + font-size: 36rpx; +} + +.nav-placeholder { + width: 100%; +} + +/* ===== 匹配提示条 - 简化版 ===== */ +.match-tip-bar { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + margin: 24rpx 32rpx; + padding: 20rpx 32rpx; + background: rgba(255, 215, 0, 0.1); + border-radius: 16rpx; + border: 1rpx solid rgba(255, 215, 0, 0.2); +} + +.tip-icon { + font-size: 28rpx; +} + +.tip-text { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.7); +} + +.tip-btn { + padding: 10rpx 24rpx; + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + color: #000; + font-size: 24rpx; + font-weight: 500; + border-radius: 20rpx; +} + +.text-brand { + color: #00CED1; +} + +.text-red { + color: #ff4444; +} + +.text-gray { + color: #666666; +} + +.text-muted { + color: rgba(255, 255, 255, 0.4); +} + +.gold-text { + color: #FFD700; +} + +/* ===== 主内容区 ===== */ +.main-content { + padding: 0 32rpx; +} + +/* ===== 匹配圆环 ===== */ +.match-circle-wrapper { + position: relative; + width: 480rpx; + height: 480rpx; + margin: 48rpx auto; +} + +.outer-glow { + position: absolute; + inset: -60rpx; + border-radius: 50%; + animation: pulseGlow 2s ease-in-out infinite; +} + +.outer-glow.glow-active { + background: radial-gradient(circle, transparent 50%, rgba(0, 229, 255, 0.1) 70%, transparent 100%); +} + +.outer-glow.glow-inactive { + background: radial-gradient(circle, transparent 50%, rgba(100, 100, 100, 0.1) 70%, transparent 100%); +} + +@keyframes pulseGlow { + 0%, 100% { transform: scale(1); opacity: 0.5; } + 50% { transform: scale(1.1); opacity: 0.8; } +} + +.middle-ring { + position: absolute; + inset: -30rpx; + border-radius: 50%; + border: 4rpx solid; + animation: pulseRing 1.5s ease-in-out infinite; +} + +.middle-ring.ring-active { + border-color: rgba(0, 229, 255, 0.3); +} + +.middle-ring.ring-inactive { + border-color: rgba(100, 100, 100, 0.3); +} + +@keyframes pulseRing { + 0%, 100% { transform: scale(1); opacity: 0.3; } + 50% { transform: scale(1.05); opacity: 0.6; } +} + +.inner-sphere { + position: absolute; + inset: 0; + border-radius: 50%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + animation: floatSphere 3s ease-in-out infinite; + overflow: hidden; +} + +.inner-sphere.sphere-active { + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); + box-shadow: 0 0 120rpx rgba(0, 229, 255, 0.3), inset 0 0 120rpx rgba(123, 97, 255, 0.2); +} + +.inner-sphere.sphere-inactive { + background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 50%, #1a1a1a 100%); + box-shadow: 0 0 60rpx rgba(100, 100, 100, 0.2); +} + +@keyframes floatSphere { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10rpx); } +} + +.sphere-gradient { + position: absolute; + inset: 0; + border-radius: 50%; + background: radial-gradient(circle at 30% 30%, rgba(123, 97, 255, 0.4) 0%, transparent 50%), + radial-gradient(circle at 70% 70%, rgba(233, 30, 99, 0.3) 0%, transparent 50%); +} + +.sphere-content { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.sphere-icon { + font-size: 96rpx; + margin-bottom: 16rpx; +} + +.sphere-title { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + margin-bottom: 8rpx; +} + +.sphere-desc { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 当前模式 ===== */ +.current-mode { + text-align: center; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.5); + margin-bottom: 16rpx; +} + +/* ===== 免费次数提示 ===== */ +.free-tip { + text-align: center; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.4); + margin-bottom: 32rpx; +} + +/* ===== 购买提示卡片 ===== */ +.purchase-tip-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32rpx; + background: linear-gradient(90deg, rgba(0, 229, 255, 0.1) 0%, transparent 100%); + border: 2rpx solid rgba(0, 229, 255, 0.2); + border-radius: 24rpx; + margin-bottom: 32rpx; +} + +.tip-left { + flex: 1; +} + +.tip-title { + display: block; + font-size: 28rpx; + font-weight: 500; + color: #ffffff; + margin-bottom: 8rpx; +} + +.tip-desc { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +.tip-btn { + padding: 16rpx 32rpx; + background: #00CED1; + color: #000000; + font-size: 26rpx; + font-weight: 500; + border-radius: 16rpx; +} + +/* ===== 分隔线 ===== */ +.divider { + height: 2rpx; + background: rgba(255, 255, 255, 0.1); + margin: 32rpx 0; +} + +/* ===== 匹配类型选择 ===== */ +.type-section { + margin-bottom: 32rpx; +} + +.type-section-title { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.4); + text-align: center; + margin-bottom: 24rpx; +} + +.type-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20rpx; +} + +.type-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 16rpx; + padding: 32rpx 16rpx; + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid transparent; + transition: all 0.2s; +} + +.type-item.type-active { + background: rgba(0, 229, 255, 0.1); + border-color: rgba(0, 229, 255, 0.5); +} + +.type-icon { + font-size: 48rpx; +} + +.type-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.6); + text-align: center; +} + +/* ===== 匹配中状态 ===== */ +.matching-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 48rpx 0; +} + +.matching-animation { + position: relative; + width: 400rpx; + height: 400rpx; + margin-bottom: 48rpx; +} + +.matching-ring { + position: absolute; + inset: 0; + border-radius: 50%; + background: linear-gradient(135deg, #00CED1, #7B61FF, #E91E63); + animation: rotateRing 3s linear infinite; +} + +@keyframes rotateRing { + to { transform: rotate(360deg); } +} + +.matching-center { + position: absolute; + inset: 16rpx; + border-radius: 50%; + background: #000000; + display: flex; + align-items: center; + justify-content: center; +} + +.matching-icon { + font-size: 96rpx; + animation: pulseIcon 1s ease-in-out infinite; +} + +@keyframes pulseIcon { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.2); } +} + +.ripple { + position: absolute; + inset: 0; + border-radius: 50%; + border: 4rpx solid rgba(0, 229, 255, 0.3); + animation: rippleExpand 2s ease-out infinite; +} + +.ripple-1 { animation-delay: 0s; } +.ripple-2 { animation-delay: 0.5s; } +.ripple-3 { animation-delay: 1s; } + +@keyframes rippleExpand { + 0% { transform: scale(1); opacity: 0.6; } + 100% { transform: scale(2); opacity: 0; } +} + +.matching-title { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 16rpx; +} + +.matching-count { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.5); + margin-bottom: 48rpx; +} + +.cancel-btn { + padding: 24rpx 64rpx; + background: #1c1c1e; + color: #ffffff; + font-size: 28rpx; + border-radius: 48rpx; + border: 2rpx solid rgba(255, 255, 255, 0.1); +} + +/* ===== 匹配成功状态 ===== */ +.matched-state { + padding: 32rpx 0; +} + +.success-icon-wrapper { + text-align: center; + margin-bottom: 32rpx; +} + +.success-icon { + font-size: 120rpx; +} + +.match-card { + background: #1c1c1e; + border-radius: 32rpx; + padding: 40rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + margin-bottom: 32rpx; +} + +.card-header { + display: flex; + align-items: center; + gap: 24rpx; + margin-bottom: 32rpx; +} + +.match-avatar { + width: 128rpx; + height: 128rpx; + border-radius: 50%; + border: 4rpx solid #00CED1; + flex-shrink: 0; +} + +.match-info { + flex: 1; + min-width: 0; +} + +.match-name { + display: block; + font-size: 32rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 12rpx; +} + +.match-tags { + display: flex; + flex-wrap: wrap; + gap: 8rpx; +} + +.match-tag { + padding: 8rpx 16rpx; + background: rgba(0, 229, 255, 0.2); + color: #00CED1; + font-size: 20rpx; + border-radius: 8rpx; +} + +.match-score-box { + text-align: center; + flex-shrink: 0; +} + +.score-value { + display: block; + font-size: 48rpx; + font-weight: 700; + color: #00CED1; +} + +.score-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.5); +} + +.card-section { + padding-top: 24rpx; + border-top: 2rpx solid rgba(255, 255, 255, 0.1); + margin-top: 24rpx; +} + +.section-title { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 16rpx; +} + +.interest-list { + display: flex; + flex-direction: column; + gap: 12rpx; +} + +.interest-item { + display: flex; + align-items: center; + gap: 16rpx; +} + +.interest-icon { + font-size: 28rpx; +} + +.interest-text { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.8); +} + +.concept-text { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.7); + line-height: 1.6; +} + +/* ===== 操作按钮 ===== */ +.action-buttons { + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.btn-primary { + padding: 32rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + color: #ffffff; + font-size: 32rpx; + font-weight: 600; + text-align: center; + border-radius: 24rpx; +} + +.btn-secondary { + padding: 32rpx; + background: #1c1c1e; + color: #ffffff; + font-size: 32rpx; + text-align: center; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.btn-disabled { + opacity: 0.5; +} + +/* ===== 弹窗 ===== */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(20rpx); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 48rpx; +} + +.modal-content { + width: 100%; + max-width: 640rpx; + background: #1c1c1e; + border-radius: 32rpx; + overflow: hidden; +} + +/* ===== 加入弹窗 ===== */ +.join-modal { + padding: 40rpx; +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; +} + +.modal-title { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; +} + +.close-btn { + width: 64rpx; + height: 64rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: rgba(255, 255, 255, 0.6); +} + +.form-tip { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 24rpx; +} + +.contact-tabs { + display: flex; + gap: 16rpx; + margin-bottom: 24rpx; +} + +.contact-tab { + flex: 1; + padding: 20rpx; + text-align: center; + font-size: 28rpx; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + background: rgba(255, 255, 255, 0.05); + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 16rpx; +} + +.contact-tab.tab-active-phone { + background: rgba(0, 229, 255, 0.2); + color: #00CED1; + border-color: rgba(0, 229, 255, 0.3); +} + +.contact-tab.tab-active-wechat { + background: rgba(7, 193, 96, 0.2); + color: #07C160; + border-color: rgba(7, 193, 96, 0.3); +} + +.input-group { + margin-bottom: 24rpx; +} + +.input-label { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.4); + margin-bottom: 12rpx; +} + +.form-input { + width: 100%; + padding: 28rpx; + background: rgba(0, 0, 0, 0.3); + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 20rpx; + font-size: 32rpx; + color: #ffffff; + box-sizing: border-box; +} + +.input-placeholder { + color: rgba(255, 255, 255, 0.3); +} + +.error-text { + display: block; + font-size: 24rpx; + color: #ff4444; + margin-bottom: 16rpx; +} + +.submit-btn { + margin-top: 16rpx; +} + +.form-notice { + display: block; + font-size: 22rpx; + color: rgba(255, 255, 255, 0.3); + text-align: center; + margin-top: 24rpx; +} + +/* ===== 新版加入弹窗 ===== */ +.join-modal-new { + padding: 0; + border-radius: 32rpx; + overflow: hidden; +} + +.join-header { + position: relative; + padding: 48rpx 40rpx 32rpx; + background: linear-gradient(135deg, rgba(0, 206, 209, 0.15) 0%, rgba(123, 97, 255, 0.1) 100%); + text-align: center; +} + +.join-icon-wrap { + width: 100rpx; + height: 100rpx; + margin: 0 auto 20rpx; + background: rgba(0, 0, 0, 0.3); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.join-icon { + font-size: 48rpx; +} + +.join-title { + display: block; + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 8rpx; +} + +.join-subtitle { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); +} + +.close-btn-new { + position: absolute; + top: 24rpx; + right: 24rpx; + width: 56rpx; + height: 56rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); +} + +.contact-switch { + display: flex; + gap: 16rpx; + padding: 24rpx 40rpx; +} + +.switch-item { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 12rpx; + padding: 24rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 16rpx; + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); + border: 2rpx solid transparent; +} + +.switch-item.switch-active { + background: rgba(0, 206, 209, 0.15); + color: #00CED1; + border-color: rgba(0, 206, 209, 0.3); +} + +.switch-icon { + font-size: 32rpx; +} + +.input-area { + padding: 0 40rpx 24rpx; +} + +.input-wrapper { + display: flex; + align-items: center; + background: rgba(0, 0, 0, 0.3); + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 20rpx; + overflow: hidden; +} + +.input-prefix { + padding: 0 24rpx; + font-size: 28rpx; + color: rgba(255, 255, 255, 0.5); + border-right: 1rpx solid rgba(255, 255, 255, 0.1); +} + +.input-field { + flex: 1; + padding: 28rpx 24rpx; + font-size: 32rpx; + color: #ffffff; +} + +.input-placeholder-new { + color: rgba(255, 255, 255, 0.3); +} + +.error-msg { + display: block; + font-size: 24rpx; + color: #ff4444; + margin-top: 12rpx; + padding-left: 8rpx; +} + +.submit-btn-new { + margin: 8rpx 40rpx 24rpx; + padding: 28rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + color: #ffffff; + font-size: 32rpx; + font-weight: 600; + text-align: center; + border-radius: 20rpx; +} + +.btn-disabled-new { + opacity: 0.5; +} + +.form-notice-new { + display: block; + text-align: center; + font-size: 22rpx; + color: rgba(255, 255, 255, 0.3); + padding-bottom: 32rpx; +} + +/* ===== 新版加入成功 ===== */ +.join-success-new { + padding: 64rpx 40rpx; + text-align: center; +} + +.success-icon-big { + font-size: 96rpx; + display: block; + margin-bottom: 24rpx; +} + +.success-title-new { + display: block; + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 12rpx; +} + +.success-desc-new { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 旧版加入成功 (保留兼容) ===== */ +.join-success { + padding: 48rpx; + text-align: center; +} + +.success-check { + font-size: 128rpx; + display: block; + margin-bottom: 24rpx; +} + +.success-title { + display: block; + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 12rpx; +} + +.success-desc { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 解锁弹窗 ===== */ +.unlock-modal { + padding: 48rpx; + text-align: center; +} + +.unlock-icon { + width: 128rpx; + height: 128rpx; + margin: 0 auto 24rpx; + background: rgba(255, 215, 0, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 64rpx; +} + +.unlock-title { + display: block; + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + margin-bottom: 12rpx; +} + +.unlock-desc { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 32rpx; +} + +.unlock-info { + background: rgba(0, 0, 0, 0.3); + border-radius: 20rpx; + padding: 24rpx; + margin-bottom: 32rpx; +} + +.info-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12rpx 0; +} + +.info-label { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); +} + +.info-value { + font-size: 26rpx; + font-weight: 500; + color: #ffffff; +} + +.unlock-buttons { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.btn-gold { + padding: 28rpx; + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + color: #000000; + font-size: 30rpx; + font-weight: 600; + text-align: center; + border-radius: 24rpx; +} + +.btn-ghost { + padding: 28rpx; + background: rgba(255, 255, 255, 0.05); + color: rgba(255, 255, 255, 0.6); + font-size: 28rpx; + text-align: center; + border-radius: 24rpx; +} + +/* ===== 底部留白 ===== */ +.bottom-space { + height: 40rpx; +} + +/* ===== 新版匹配动画 V2 ===== */ +.matching-animation-v2 { + position: relative; + width: 440rpx; + height: 440rpx; + margin: 0 auto 48rpx; +} + +/* 外层旋转光环 */ +.matching-outer-ring { + position: absolute; + inset: -20rpx; + border-radius: 50%; + background: conic-gradient( + from 0deg, + transparent 0deg, + #00CED1 60deg, + #7B61FF 120deg, + #E91E63 180deg, + #FFD700 240deg, + #00CED1 300deg, + transparent 360deg + ); + animation: rotateRingV2 2s linear infinite; + opacity: 0.8; +} + +.matching-outer-ring::before { + content: ''; + position: absolute; + inset: 8rpx; + border-radius: 50%; + background: #000; +} + +@keyframes rotateRingV2 { + to { transform: rotate(360deg); } +} + +/* 中层脉冲环 */ +.matching-pulse-ring { + position: absolute; + inset: 20rpx; + border-radius: 50%; + border: 4rpx solid rgba(0, 206, 209, 0.5); + animation: pulseRingV2 1.5s ease-in-out infinite; +} + +@keyframes pulseRingV2 { + 0%, 100% { transform: scale(1); opacity: 0.5; } + 50% { transform: scale(1.1); opacity: 1; } +} + +/* 内层核心球体 */ +.matching-core { + position: absolute; + inset: 60rpx; + border-radius: 50%; + background: linear-gradient(135deg, #1a2a4a 0%, #0a1628 50%, #16213e 100%); + box-shadow: + 0 0 60rpx rgba(0, 206, 209, 0.4), + 0 0 120rpx rgba(123, 97, 255, 0.2), + inset 0 0 80rpx rgba(0, 206, 209, 0.1); + display: flex; + align-items: center; + justify-content: center; + animation: floatCoreV2 2s ease-in-out infinite; +} + +.matching-core-inner { + width: 160rpx; + height: 160rpx; + border-radius: 50%; + background: radial-gradient(circle, rgba(0, 206, 209, 0.3) 0%, transparent 70%); + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes floatCoreV2 { + 0%, 100% { transform: translateY(0) scale(1); } + 50% { transform: translateY(-10rpx) scale(1.02); } +} + +.matching-icon-v2 { + font-size: 80rpx; + animation: searchIconV2 1s ease-in-out infinite; +} + +@keyframes searchIconV2 { + 0%, 100% { transform: rotate(-15deg); } + 50% { transform: rotate(15deg); } +} + +/* 粒子效果 */ +.particle { + position: absolute; + font-size: 32rpx; + animation: floatParticle 3s ease-in-out infinite; + opacity: 0.8; +} + +.particle-1 { top: 10%; left: 15%; animation-delay: 0s; } +.particle-2 { top: 20%; right: 10%; animation-delay: 0.5s; } +.particle-3 { bottom: 20%; left: 10%; animation-delay: 1s; } +.particle-4 { bottom: 15%; right: 15%; animation-delay: 1.5s; } + +@keyframes floatParticle { + 0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.4; } + 50% { transform: translateY(-20rpx) rotate(180deg); opacity: 1; } +} + +/* 扩散波纹 V2 */ +.ripple-v2 { + position: absolute; + inset: 40rpx; + border-radius: 50%; + border: 3rpx solid; + border-color: rgba(0, 206, 209, 0.6); + animation: rippleExpandV2 2.5s ease-out infinite; +} + +.ripple-v2-1 { animation-delay: 0s; } +.ripple-v2-2 { animation-delay: 0.8s; } +.ripple-v2-3 { animation-delay: 1.6s; } + +@keyframes rippleExpandV2 { + 0% { transform: scale(1); opacity: 0.8; } + 100% { transform: scale(1.8); opacity: 0; } +} + +/* 新版匹配文字 */ +.matching-title-v2 { + display: block; + font-size: 38rpx; + font-weight: 700; + color: #ffffff; + text-align: center; + margin-bottom: 12rpx; + background: linear-gradient(90deg, #00CED1, #7B61FF, #00CED1); + background-size: 200% auto; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + animation: shineText 2s linear infinite; +} + +@keyframes shineText { + to { background-position: 200% center; } +} + +.matching-subtitle-v2 { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.5); + text-align: center; + margin-bottom: 32rpx; +} + +.matching-tips { + display: flex; + flex-direction: column; + align-items: center; + gap: 16rpx; + margin-bottom: 40rpx; +} + +.tip-item { + font-size: 26rpx; + color: #00CED1; + animation: fadeInUp 0.5s ease-out forwards; + opacity: 0; +} + +.tip-item:nth-child(1) { animation-delay: 0.5s; } +.tip-item:nth-child(2) { animation-delay: 1.5s; } +.tip-item:nth-child(3) { animation-delay: 2.5s; } + +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(20rpx); } + to { opacity: 1; transform: translateY(0); } +} + +.cancel-btn-v2 { + display: inline-block; + padding: 20rpx 60rpx; + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.6); + font-size: 28rpx; + border-radius: 40rpx; + border: 1rpx solid rgba(255, 255, 255, 0.2); +} + +/* 资源对接表单 */ +.resource-form { + display: flex; + flex-direction: column; + gap: 20rpx; + margin-bottom: 24rpx; +} +.resource-form .form-item { + display: flex; + flex-direction: column; + gap: 8rpx; +} +.resource-form .form-label { + font-size: 26rpx; + color: rgba(255,255,255,0.6); +} +.resource-form .form-input-new { + background: #1c1c1e; + border: 2rpx solid rgba(0,206,209,0.3); + border-radius: 16rpx; + padding: 20rpx; + font-size: 28rpx; + color: #fff; +} diff --git a/Tminiprogram/pages/my/my.js b/Tminiprogram/pages/my/my.js new file mode 100644 index 00000000..cfdc3325 --- /dev/null +++ b/Tminiprogram/pages/my/my.js @@ -0,0 +1,348 @@ +/** + * Soul创业派对 - 我的页面 + * 开发: 卡若 + * 技术支持: 存客宝 + */ + +const app = getApp() + +Page({ + data: { + // 系统信息 + statusBarHeight: 44, + navBarHeight: 88, + + // 用户状态 + isLoggedIn: false, + userInfo: null, + + // 统计数据 + totalSections: 62, + purchasedCount: 0, + referralCount: 0, + earnings: 0, + pendingEarnings: 0, + + // 阅读统计 + totalReadTime: 0, + matchHistory: 0, + + // Tab切换 + activeTab: 'overview', // overview | footprint + + // 最近阅读 + recentChapters: [], + + // 菜单列表 + menuList: [ + { id: 'orders', title: '我的订单', icon: '📦', count: 0 }, + { id: 'referral', title: '推广中心', icon: '🎁', badge: '' }, + { id: 'about', title: '关于作者', icon: '👤', iconBg: 'brand' }, + { id: 'settings', title: '设置', icon: '⚙️', iconBg: 'gray' } + ], + + // 登录弹窗 + showLoginModal: false, + isLoggingIn: false + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + navBarHeight: app.globalData.navBarHeight + }) + this.initUserStatus() + }, + + onShow() { + // 设置TabBar选中状态 + if (typeof this.getTabBar === 'function' && this.getTabBar()) { + this.getTabBar().setData({ selected: 3 }) + } + this.initUserStatus() + }, + + // 初始化用户状态 + initUserStatus() { + const { isLoggedIn, userInfo, hasFullBook, purchasedSections } = app.globalData + + if (isLoggedIn && userInfo) { + // 转换为对象数组 + const recentList = (purchasedSections || []).slice(-5).map(id => ({ + id: id, + title: `章节 ${id}` + })) + + // 截短用户ID显示 + const userId = userInfo.id || '' + const userIdShort = userId.length > 20 ? userId.slice(0, 10) + '...' + userId.slice(-6) : userId + + // 获取微信号(优先显示) + const userWechat = wx.getStorageSync('user_wechat') || userInfo.wechat || '' + + this.setData({ + isLoggedIn: true, + userInfo, + userIdShort, + userWechat, + purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0), + referralCount: userInfo.referralCount || 0, + earnings: userInfo.earnings || 0, + pendingEarnings: userInfo.pendingEarnings || 0, + recentChapters: recentList, + totalReadTime: Math.floor(Math.random() * 200) + 50 + }) + } else { + this.setData({ + isLoggedIn: false, + userInfo: null, + userIdShort: '', + purchasedCount: 0, + referralCount: 0, + earnings: 0, + pendingEarnings: 0, + recentChapters: [] + }) + } + }, + + // 微信原生获取头像(button open-type="chooseAvatar" 回调) + async onChooseAvatar(e) { + const avatarUrl = e.detail.avatarUrl + if (!avatarUrl) return + + wx.showLoading({ title: '更新中...', mask: true }) + + try { + const userInfo = this.data.userInfo + userInfo.avatar = avatarUrl + this.setData({ userInfo }) + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + + // 同步到服务器 + await app.request('/api/user/update', { + method: 'POST', + data: { userId: userInfo.id, avatar: avatarUrl } + }) + + wx.hideLoading() + wx.showToast({ title: '头像已获取', icon: 'success' }) + } catch (e) { + wx.hideLoading() + console.log('同步头像失败', e) + wx.showToast({ title: '头像已更新', icon: 'success' }) + } + }, + + // 微信原生获取昵称(input type="nickname" 回调) + async onNicknameInput(e) { + const nickname = e.detail.value + if (!nickname || nickname === this.data.userInfo?.nickname) return + + try { + const userInfo = this.data.userInfo + userInfo.nickname = nickname + this.setData({ userInfo }) + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + + // 同步到服务器 + await app.request('/api/user/update', { + method: 'POST', + data: { userId: userInfo.id, nickname } + }) + + wx.showToast({ title: '昵称已获取', icon: 'success' }) + } catch (e) { + console.log('同步昵称失败', e) + } + }, + + // 点击昵称修改(备用) + editNickname() { + wx.showModal({ + title: '修改昵称', + editable: true, + placeholderText: '请输入昵称', + success: async (res) => { + if (res.confirm && res.content) { + const newNickname = res.content.trim() + if (newNickname.length < 1 || newNickname.length > 20) { + wx.showToast({ title: '昵称1-20个字符', icon: 'none' }) + return + } + + // 更新本地 + const userInfo = this.data.userInfo + userInfo.nickname = newNickname + this.setData({ userInfo }) + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + + // 同步到服务器 + try { + await app.request('/api/user/update', { + method: 'POST', + data: { userId: userInfo.id, nickname: newNickname } + }) + } catch (e) { + console.log('同步昵称到服务器失败', e) + } + + wx.showToast({ title: '昵称已更新', icon: 'success' }) + } + } + }) + }, + + // 复制用户ID + copyUserId() { + const userId = this.data.userInfo?.id || '' + if (!userId) { + wx.showToast({ title: '暂无ID', icon: 'none' }) + return + } + wx.setClipboardData({ + data: userId, + success: () => { + wx.showToast({ title: 'ID已复制', icon: 'success' }) + } + }) + }, + + // 切换Tab + switchTab(e) { + const tab = e.currentTarget.dataset.tab + this.setData({ activeTab: tab }) + }, + + // 显示登录弹窗 + showLogin() { + this.setData({ showLoginModal: true }) + }, + + // 关闭登录弹窗 + closeLoginModal() { + if (this.data.isLoggingIn) return + this.setData({ showLoginModal: false }) + }, + + // 微信登录 + async handleWechatLogin() { + this.setData({ isLoggingIn: true }) + + try { + const result = await app.login() + if (result) { + this.initUserStatus() + this.setData({ showLoginModal: false }) + wx.showToast({ title: '登录成功', icon: 'success' }) + } else { + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + } + } catch (e) { + console.error('微信登录错误:', e) + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + } finally { + this.setData({ isLoggingIn: false }) + } + }, + + // 手机号登录(需要用户授权) + async handlePhoneLogin(e) { + // 检查是否有授权code + if (!e.detail.code) { + // 用户拒绝授权或获取失败,尝试使用微信登录 + console.log('手机号授权失败,尝试微信登录') + return this.handleWechatLogin() + } + + this.setData({ isLoggingIn: true }) + + try { + const result = await app.loginWithPhone(e.detail.code) + if (result) { + this.initUserStatus() + this.setData({ showLoginModal: false }) + wx.showToast({ title: '登录成功', icon: 'success' }) + } else { + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + } + } catch (e) { + console.error('手机号登录错误:', e) + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + } finally { + this.setData({ isLoggingIn: false }) + } + }, + + // 点击菜单 + handleMenuTap(e) { + const id = e.currentTarget.dataset.id + + if (!this.data.isLoggedIn && id !== 'about') { + this.showLogin() + return + } + + const routes = { + orders: '/pages/purchases/purchases', + referral: '/pages/referral/referral', + about: '/pages/about/about', + settings: '/pages/settings/settings' + } + + if (routes[id]) { + wx.navigateTo({ url: routes[id] }) + } + }, + + // 跳转到阅读页 + goToRead(e) { + const id = e.currentTarget.dataset.id + wx.navigateTo({ url: `/pages/read/read?id=${id}` }) + }, + + // 跳转到目录 + goToChapters() { + wx.switchTab({ url: '/pages/chapters/chapters' }) + }, + + // 跳转到关于页 + goToAbout() { + wx.navigateTo({ url: '/pages/about/about' }) + }, + + // 跳转到匹配 + goToMatch() { + wx.switchTab({ url: '/pages/match/match' }) + }, + + // 跳转到推广中心 + goToReferral() { + if (!this.data.isLoggedIn) { + this.showLogin() + return + } + wx.navigateTo({ url: '/pages/referral/referral' }) + }, + + // 退出登录 + handleLogout() { + wx.showModal({ + title: '退出登录', + content: '确定要退出登录吗?', + success: (res) => { + if (res.confirm) { + app.logout() + this.initUserStatus() + wx.showToast({ title: '已退出登录', icon: 'success' }) + } + } + }) + }, + + // 阻止冒泡 + stopPropagation() {} +}) diff --git a/Tminiprogram/pages/my/my.json b/Tminiprogram/pages/my/my.json new file mode 100644 index 00000000..e7696321 --- /dev/null +++ b/Tminiprogram/pages/my/my.json @@ -0,0 +1,6 @@ +{ + "usingComponents": {}, + "enablePullDownRefresh": false, + "backgroundTextStyle": "light", + "backgroundColor": "#000000" +} diff --git a/Tminiprogram/pages/my/my.wxml b/Tminiprogram/pages/my/my.wxml new file mode 100644 index 00000000..7dfde30f --- /dev/null +++ b/Tminiprogram/pages/my/my.wxml @@ -0,0 +1,204 @@ + + + + + + + 我的 + + + + + + + + + + + + + + + + + + + + + + {{purchasedCount}} + 已购章节 + + + {{referralCount}} + 推荐好友 + + + {{earnings > 0 ? '¥' + earnings : '--'}} + 待领收益 + + + + + + + + 概览 + + 👣 + 我的足迹 + + + + + + + + + + + + {{item.icon}} + + {{item.title}} + + + {{item.count}}笔 + {{item.badge}} + + + + + + + + + + + + 👁️ + 阅读统计 + + + + 📖 + {{purchasedCount}} + 已读章节 + + + ⏱️ + {{totalReadTime}} + 阅读分钟 + + + 👥 + {{matchHistory}} + 匹配伙伴 + + + + + + + + 📖 + 最近阅读 + + + + + {{index + 1}} + {{item.title}} + + 继续阅读 + + + + 📖 + 暂无阅读记录 + 去阅读 → + + + + + + + 👥 + 匹配记录 + + + 👥 + 暂无匹配记录 + 去匹配 → + + + + + + + + + + + + + + + + + + + + + diff --git a/Tminiprogram/pages/my/my.wxss b/Tminiprogram/pages/my/my.wxss new file mode 100644 index 00000000..e6240e3d --- /dev/null +++ b/Tminiprogram/pages/my/my.wxss @@ -0,0 +1,996 @@ +/** + * Soul创业实验 - 我的页面样式 + * 1:1还原Web版本UI + */ + +.page { + min-height: 100vh; + background: #000000; + padding-bottom: 200rpx; +} + +/* ===== 导航栏 ===== */ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(40rpx); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.nav-content { + height: 88rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.nav-title { + font-size: 36rpx; + font-weight: 600; +} + +.brand-color { + color: #00CED1; +} + +.gold-color { + color: #FFD700; +} + +.pink-color { + color: #E91E63; +} + +.nav-placeholder { + width: 100%; +} + +/* ===== 用户卡片 ===== */ +.user-card { + margin: 32rpx; + padding: 32rpx; +} + +.card-gradient { + background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); + border-radius: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +/* ===== 新版用户头部布局 ===== */ +.user-header-row { + display: flex; + align-items: center; + gap: 24rpx; + margin-bottom: 32rpx; + width: 100%; +} + +/* 头像容器 */ +.avatar-wrapper { + position: relative; + flex-shrink: 0; + width: 120rpx; + height: 120rpx; +} + +/* 头像按钮样式 - 简化版 */ +.avatar-btn-simple { + flex-shrink: 0; + width: 120rpx; + height: 120rpx; + min-width: 120rpx; + min-height: 120rpx; + padding: 0; + margin: 0; + background: transparent !important; + border: none; + line-height: normal; + border-radius: 50%; + overflow: visible; + display: flex; + align-items: center; + justify-content: center; +} +.avatar-btn-simple::after { border: none; } + +/* 用户名样式 */ +.user-name { + font-size: 32rpx; + font-weight: 600; + color: #fff; + max-width: 300rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.edit-icon-small { + font-size: 24rpx; + color: rgba(255,255,255,0.5); + margin-left: 12rpx; +} + +.avatar { + width: 120rpx; + height: 120rpx; + border-radius: 50%; + border: 4rpx solid #00CED1; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, transparent 100%); + overflow: hidden; + box-sizing: border-box; +} + +.avatar-img { + width: 100%; + height: 100%; +} + +.avatar-edit-hint { + position: absolute; + bottom: 0; + right: 0; + width: 36rpx; + height: 36rpx; + background: #00CED1; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 3rpx solid #000; +} + +.edit-icon { + font-size: 20rpx; + color: #000; +} + +.avatar-empty { + border-style: dashed; + border-color: rgba(0, 206, 209, 0.5); +} + +.avatar-icon { + font-size: 64rpx; + opacity: 0.3; +} + +.avatar-text { + font-size: 44rpx; + font-weight: 700; + color: #00CED1; +} + +.user-info-block { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 10rpx; + min-width: 0; + padding-top: 4rpx; +} + +.user-name-row { + display: flex; + align-items: center; + gap: 10rpx; + line-height: 1.2; +} + +.user-name { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 320rpx; + line-height: 1.3; +} + +.edit-name-icon { + font-size: 24rpx; + color: rgba(255,255,255,0.4); + flex-shrink: 0; +} + +.user-id-row { + display: flex; + align-items: center; + gap: 8rpx; +} + +.copy-icon { + font-size: 22rpx; + opacity: 0.5; +} + +.user-wechat { + font-size: 24rpx; + color: #00CED1; +} + +.user-badge-small { + display: inline-flex; + align-items: center; + gap: 6rpx; + padding: 6rpx 14rpx; + background: rgba(0, 206, 209, 0.15); + border: 1rpx solid rgba(0, 206, 209, 0.3); + border-radius: 20rpx; +} + +.badge-star { + font-size: 18rpx; +} + +.badge-label { + font-size: 20rpx; + color: #00CED1; +} + +.user-id { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.35); +} + +/* 兼容旧样式 */ +.user-header { + display: flex; + align-items: center; + gap: 24rpx; + margin-bottom: 32rpx; +} + +.user-info { + flex: 1; +} + +.login-btn { + font-size: 36rpx; + font-weight: 600; + color: #00CED1; +} + +.user-subtitle { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.3); + display: block; + margin-top: 4rpx; +} + +/* ===== 登录卡片样式 ===== */ +.login-card { + min-height: 400rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.login-prompt { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 40rpx 0; + width: 100%; +} + +.login-icon-large { + font-size: 80rpx; + margin-bottom: 32rpx; +} + +.login-title { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 16rpx; +} + +.login-desc { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.5); + margin-bottom: 48rpx; + line-height: 1.6; +} + +.btn-wechat-large { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + width: 80%; + padding: 28rpx 0; + background: linear-gradient(135deg, #07C160 0%, #06AD56 100%); + border-radius: 48rpx; + border: none; + color: #ffffff; + font-size: 32rpx; + font-weight: 600; +} + +.btn-wechat-large .btn-icon { + width: 48rpx; + height: 48rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 8rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 700; +} + +.user-name { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; + display: block; +} + +.user-id { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.3); + display: block; + margin-top: 4rpx; +} + +.user-badge { + padding: 8rpx 20rpx; + background: rgba(0, 206, 209, 0.2); + border: 2rpx solid rgba(0, 206, 209, 0.3); + border-radius: 32rpx; + display: flex; + align-items: center; + gap: 8rpx; +} + +.badge-icon { + font-size: 22rpx; +} + +.badge-text { + font-size: 22rpx; + color: #00CED1; +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16rpx; + padding-top: 32rpx; + border-top: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.stat-item { + text-align: center; + padding: 16rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 16rpx; +} + +.stat-value { + font-size: 40rpx; + font-weight: 700; + display: block; +} + +.stat-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); +} + +/* ===== 收益卡片 ===== */ +.earnings-card { + margin: 32rpx; + padding: 32rpx; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); + border-radius: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); + position: relative; + overflow: hidden; +} + +.earnings-bg { + position: absolute; + top: 0; + right: 0; + width: 256rpx; + height: 256rpx; + background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, transparent 100%); + border-radius: 50%; + transform: translate(50%, -50%); +} + +.earnings-content { + position: relative; + z-index: 1; +} + +.earnings-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 32rpx; +} + +.earnings-title { + display: flex; + align-items: center; + gap: 16rpx; +} + +.title-icon { + font-size: 36rpx; +} + +.title-text { + font-size: 28rpx; + font-weight: 500; + color: #ffffff; +} + +.earnings-link { + display: flex; + align-items: center; + gap: 8rpx; +} + +.link-text { + font-size: 24rpx; + color: #00CED1; +} + +.link-arrow { + font-size: 24rpx; + color: #00CED1; +} + +.earnings-data { + display: flex; + align-items: flex-end; + gap: 48rpx; + margin-bottom: 32rpx; +} + +.data-item { + display: flex; + flex-direction: column; +} + +.data-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.5); + margin-bottom: 8rpx; +} + +.data-value { + font-size: 40rpx; + font-weight: 700; + color: #ffffff; +} + +.gold-gradient { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + font-size: 56rpx; +} + +.earnings-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + padding: 24rpx; + background: linear-gradient(135deg, rgba(255, 215, 0, 0.8) 0%, rgba(255, 165, 0, 0.8) 100%); + border-radius: 24rpx; + font-weight: 600; +} + +.btn-icon { + font-size: 28rpx; +} + +.btn-text { + font-size: 28rpx; + color: #000000; +} + +/* ===== 推广入口 ===== */ +.referral-card { + display: flex; + align-items: center; + justify-content: space-between; + margin: 32rpx; + padding: 32rpx; + background: linear-gradient(90deg, rgba(255, 215, 0, 0.1) 0%, #1c1c1e 100%); + border: 2rpx solid rgba(255, 215, 0, 0.2); + border-radius: 32rpx; +} + +.referral-left { + display: flex; + align-items: center; + gap: 24rpx; +} + +.referral-icon { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 40rpx; +} + +.gold-bg { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); +} + +.referral-info { + display: flex; + flex-direction: column; +} + +.referral-title { + font-size: 28rpx; + font-weight: 500; + color: #ffffff; +} + +.referral-desc { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.4); + margin-top: 4rpx; +} + +.referral-btn { + padding: 16rpx 32rpx; + background: rgba(255, 215, 0, 0.2); + color: #FFD700; + font-size: 26rpx; + font-weight: 500; + border-radius: 16rpx; +} + +/* ===== Tab切换 ===== */ +.tab-bar-custom { + display: flex; + gap: 16rpx; + margin: 32rpx; +} + +.tab-item { + flex: 1; + padding: 20rpx; + text-align: center; + font-size: 28rpx; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + background: #1c1c1e; + border-radius: 24rpx; + display: flex; + align-items: center; + justify-content: center; + gap: 8rpx; +} + +.tab-active { + background: rgba(0, 206, 209, 0.2); + color: #00CED1; + border: 2rpx solid rgba(0, 206, 209, 0.3); +} + +.tab-icon { + font-size: 28rpx; +} + +/* ===== Tab内容 ===== */ +.tab-content { + padding: 0 32rpx; + width: 100%; + box-sizing: border-box; +} + +/* ===== 菜单卡片 ===== */ +.card { + background: #1c1c1e; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.05); + overflow: hidden; + margin: 0 0 24rpx 0; + width: 100%; + box-sizing: border-box; +} + +.menu-card { + padding: 0; + width: 100%; + box-sizing: border-box; +} + +.menu-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 28rpx 32rpx; + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.menu-item:last-child { + border-bottom: none; +} + +.menu-item:active { + background: rgba(255, 255, 255, 0.05); +} + +.menu-left { + display: flex; + align-items: center; + gap: 24rpx; +} + +.menu-icon { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + background: rgba(255, 255, 255, 0.1); +} + +.icon-brand { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); +} + +.icon-gray { + background: rgba(128, 128, 128, 0.2); +} + +.menu-title { + font-size: 28rpx; + color: #ffffff; +} + +.menu-right { + display: flex; + align-items: center; + gap: 16rpx; +} + +.menu-count { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.4); +} + +.menu-badge { + font-size: 26rpx; + font-weight: 500; +} + +.menu-arrow { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.3); +} + +/* ===== 统计卡片 ===== */ +.stats-card { + padding: 32rpx; +} + +.card-title { + display: flex; + align-items: center; + gap: 16rpx; + font-size: 28rpx; + font-weight: 500; + color: #ffffff; + margin-bottom: 24rpx; +} + +.stats-row { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24rpx; +} + +.stat-box { + display: flex; + flex-direction: column; + align-items: center; + padding: 24rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 24rpx; +} + +.stat-icon { + font-size: 36rpx; + margin-bottom: 8rpx; +} + +.stat-num { + font-size: 32rpx; + font-weight: 700; + color: #ffffff; +} + +.stat-text { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); + margin-top: 4rpx; +} + +/* ===== 最近阅读 ===== */ +.recent-card { + padding: 32rpx; +} + +.recent-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.recent-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 24rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 24rpx; +} + +.recent-left { + display: flex; + align-items: center; + gap: 24rpx; +} + +.recent-index { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.3); +} + +.recent-title { + font-size: 26rpx; + color: #ffffff; +} + +.recent-btn { + font-size: 24rpx; + color: #00CED1; +} + +/* ===== 空状态 ===== */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + padding: 48rpx; +} + +.empty-icon { + font-size: 64rpx; + opacity: 0.5; + margin-bottom: 16rpx; +} + +.empty-text { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.4); +} + +.empty-btn { + margin-top: 16rpx; + font-size: 26rpx; + color: #00CED1; +} + +/* ===== 匹配记录 ===== */ +.match-card { + padding: 32rpx; +} + +/* ===== 登录弹窗 ===== */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(20rpx); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 48rpx; +} + +.modal-content { + width: 100%; + max-width: 640rpx; + background: #1c1c1e; + border-radius: 32rpx; + padding: 48rpx; + position: relative; +} + +.modal-close { + position: absolute; + top: 24rpx; + right: 24rpx; + width: 64rpx; + height: 64rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); +} + +.login-icon { + font-size: 96rpx; + text-align: center; + display: block; + margin-bottom: 24rpx; +} + +.login-title { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + text-align: center; + display: block; + margin-bottom: 16rpx; +} + +.login-desc { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); + text-align: center; + display: block; + margin-bottom: 48rpx; +} + +.btn-wechat { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + padding: 28rpx; + background: #07C160; + color: #ffffff; + font-size: 28rpx; + font-weight: 500; + border-radius: 24rpx; + margin-bottom: 24rpx; + border: none; +} + +.btn-wechat::after { + border: none; +} + +.btn-wechat-icon { + width: 40rpx; + height: 40rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 8rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; +} + +.btn-phone { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + padding: 28rpx; + background: rgba(255, 255, 255, 0.1); + color: #ffffff; + font-size: 28rpx; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.btn-phone::after { + border: none; +} + +.btn-phone-icon { + font-size: 32rpx; +} + +.login-notice { + display: block; + margin-top: 32rpx; + font-size: 22rpx; + color: rgba(255, 255, 255, 0.3); + text-align: center; +} + +/* ===== 底部留白 ===== */ +.bottom-space { + height: 40rpx; +} + +/* ===== 推广入口卡片 ===== */ +.promo-entry-card { + display: flex; + align-items: center; + justify-content: space-between; + margin: 24rpx 24rpx 0; + padding: 32rpx; + background: linear-gradient(135deg, rgba(255, 215, 0, 0.15) 0%, rgba(255, 165, 0, 0.1) 100%); + border: 2rpx solid rgba(255, 215, 0, 0.3); + border-radius: 24rpx; +} + +.promo-entry-left { + display: flex; + align-items: center; + gap: 20rpx; +} + +.promo-entry-icon { + width: 80rpx; + height: 80rpx; + background: rgba(255, 215, 0, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; +} + +.promo-entry-info { + display: flex; + flex-direction: column; + gap: 4rpx; +} + +.promo-entry-title { + font-size: 30rpx; + color: #ffffff; + font-weight: 600; +} + +.promo-entry-desc { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.5); +} + +.promo-entry-right { + display: flex; + align-items: center; + gap: 16rpx; +} + +.promo-entry-earnings { + font-size: 32rpx; + color: #FFD700; + font-weight: 600; +} + +.promo-entry-arrow { + font-size: 28rpx; + color: #FFD700; +} diff --git a/Tminiprogram/pages/purchases/purchases.js b/Tminiprogram/pages/purchases/purchases.js new file mode 100644 index 00000000..f691ab39 --- /dev/null +++ b/Tminiprogram/pages/purchases/purchases.js @@ -0,0 +1,45 @@ +/** + * Soul创业实验 - 订单页 + */ +const app = getApp() + +Page({ + data: { + statusBarHeight: 44, + orders: [], + loading: true + }, + + onLoad() { + this.setData({ statusBarHeight: app.globalData.statusBarHeight }) + this.loadOrders() + }, + + async loadOrders() { + this.setData({ loading: true }) + try { + // 模拟订单数据 + const purchasedSections = app.globalData.purchasedSections || [] + const orders = purchasedSections.map((id, index) => ({ + id: `order_${index}`, + sectionId: id, + title: `章节 ${id}`, + amount: 1, + status: 'completed', + createTime: new Date(Date.now() - index * 86400000).toLocaleDateString() + })) + this.setData({ orders }) + } catch (e) { + console.error('加载订单失败:', e) + } finally { + this.setData({ loading: false }) + } + }, + + goToRead(e) { + const id = e.currentTarget.dataset.id + wx.navigateTo({ url: `/pages/read/read?id=${id}` }) + }, + + goBack() { wx.navigateBack() } +}) diff --git a/Tminiprogram/pages/purchases/purchases.json b/Tminiprogram/pages/purchases/purchases.json new file mode 100644 index 00000000..e90e9960 --- /dev/null +++ b/Tminiprogram/pages/purchases/purchases.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationStyle": "custom" +} diff --git a/Tminiprogram/pages/purchases/purchases.wxml b/Tminiprogram/pages/purchases/purchases.wxml new file mode 100644 index 00000000..0c5942cd --- /dev/null +++ b/Tminiprogram/pages/purchases/purchases.wxml @@ -0,0 +1,35 @@ + + + + + 我的订单 + + + + + + + + + + + + + + + {{item.title}} + {{item.createTime}} + + + ¥{{item.amount}} + 已完成 + + + + + + 📦 + 暂无订单 + + + diff --git a/Tminiprogram/pages/purchases/purchases.wxss b/Tminiprogram/pages/purchases/purchases.wxss new file mode 100644 index 00000000..c2f6cf3e --- /dev/null +++ b/Tminiprogram/pages/purchases/purchases.wxss @@ -0,0 +1,21 @@ +.page { min-height: 100vh; background: #000; } +.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; } +.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; } +.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; } +.nav-placeholder { width: 72rpx; } +.content { padding: 32rpx; } +.loading { display: flex; flex-direction: column; gap: 24rpx; } +.skeleton { height: 120rpx; background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%); background-size: 200% 100%; animation: skeleton 1.5s ease-in-out infinite; border-radius: 24rpx; } +@keyframes skeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } +.orders-list { display: flex; flex-direction: column; gap: 16rpx; } +.order-item { display: flex; align-items: center; justify-content: space-between; padding: 24rpx; background: #1c1c1e; border-radius: 24rpx; } +.order-item:active { background: #2c2c2e; } +.order-info { flex: 1; } +.order-title { font-size: 28rpx; color: #fff; display: block; margin-bottom: 8rpx; } +.order-time { font-size: 22rpx; color: rgba(255,255,255,0.4); } +.order-right { text-align: right; } +.order-amount { font-size: 28rpx; font-weight: 600; color: #00CED1; display: block; margin-bottom: 4rpx; } +.order-status { font-size: 22rpx; color: rgba(255,255,255,0.4); } +.empty { display: flex; flex-direction: column; align-items: center; padding: 96rpx; } +.empty-icon { font-size: 96rpx; margin-bottom: 24rpx; opacity: 0.5; } +.empty-text { font-size: 28rpx; color: rgba(255,255,255,0.4); } diff --git a/Tminiprogram/pages/read/read.js b/Tminiprogram/pages/read/read.js new file mode 100644 index 00000000..13f9b1b9 --- /dev/null +++ b/Tminiprogram/pages/read/read.js @@ -0,0 +1,909 @@ +/** + * Soul创业派对 - 阅读页 + * 开发: 卡若 + * 技术支持: 存客宝 + */ + +const app = getApp() + +Page({ + data: { + // 系统信息 + statusBarHeight: 44, + navBarHeight: 88, + + // 章节信息 + sectionId: '', + section: null, + partTitle: '', + chapterTitle: '', + + // 内容 + content: '', + previewContent: '', + contentParagraphs: [], + previewParagraphs: [], + loading: true, + + // 用户状态 + isLoggedIn: false, + hasFullBook: false, + canAccess: false, + purchasedCount: 0, + + // 阅读进度 + readingProgress: 0, + showPaywall: false, + + // 上一篇/下一篇 + prevSection: null, + nextSection: null, + + // 价格 + sectionPrice: 1, + fullBookPrice: 9.9, + totalSections: 62, + + // 弹窗 + showShareModal: false, + showLoginModal: false, + showPosterModal: false, + isPaying: false, + isGeneratingPoster: false, + + // 免费章节 + freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'] + }, + + onLoad(options) { + const { id, ref } = options + + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + navBarHeight: app.globalData.navBarHeight, + sectionId: id + }) + + // 处理推荐码绑定 + if (ref) { + console.log('[Read] 检测到推荐码:', ref) + wx.setStorageSync('referral_code', ref) + app.handleReferralCode({ query: { ref } }) + } + + // 加载免费章节配置 + this.loadFreeChaptersConfig() + + this.initSection(id) + }, + + // 从后端加载免费章节配置 + async loadFreeChaptersConfig() { + try { + const res = await app.request('/api/db/config') + if (res.success && res.freeChapters) { + this.setData({ freeIds: res.freeChapters }) + console.log('[Read] 加载免费章节配置:', res.freeChapters) + } + } catch (e) { + console.log('[Read] 使用默认免费章节配置') + } + }, + + onPageScroll(e) { + // 计算阅读进度 + const query = wx.createSelectorQuery() + query.select('.page').boundingClientRect() + query.exec((res) => { + if (res[0]) { + const scrollTop = e.scrollTop + const pageHeight = res[0].height - this.data.statusBarHeight - 200 + const progress = pageHeight > 0 ? Math.min((scrollTop / pageHeight) * 100, 100) : 0 + this.setData({ readingProgress: progress }) + } + }) + }, + + // 初始化章节 + async initSection(id) { + this.setData({ loading: true }) + + try { + // 模拟获取章节数据 + const section = this.getSectionInfo(id) + const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData + + const isFree = this.data.freeIds.includes(id) + const isPurchased = hasFullBook || (purchasedSections && purchasedSections.includes(id)) + const canAccess = isFree || isPurchased + const purchasedCount = purchasedSections?.length || 0 + + this.setData({ + section, + isLoggedIn, + hasFullBook, + canAccess, + purchasedCount, + showPaywall: !canAccess + }) + + // 加载内容 + await this.loadContent(id) + + // 获取上一篇/下一篇 + this.loadNavigation(id) + + } catch (e) { + console.error('初始化章节失败:', e) + wx.showToast({ title: '加载失败', icon: 'none' }) + } finally { + this.setData({ loading: false }) + } + }, + + // 获取章节信息 + getSectionInfo(id) { + // 特殊章节 + if (id === 'preface') { + return { id: 'preface', title: '为什么我每天早上6点在Soul开播?', isFree: true, price: 0 } + } + if (id === 'epilogue') { + return { id: 'epilogue', title: '这本书的真实目的', isFree: true, price: 0 } + } + if (id.startsWith('appendix')) { + const appendixTitles = { + 'appendix-1': 'Soul派对房精选对话', + 'appendix-2': '创业者自检清单', + 'appendix-3': '本书提到的工具和资源' + } + return { id, title: appendixTitles[id] || '附录', isFree: true, price: 0 } + } + + // 普通章节 + return { + id: id, + title: this.getSectionTitle(id), + isFree: id === '1.1', + price: 1 + } + }, + + // 获取章节标题 + getSectionTitle(id) { + const titles = { + '1.1': '荷包:电动车出租的被动收入模式', + '1.2': '老墨:资源整合高手的社交方法', + '1.3': '笑声背后的MBTI', + '1.4': '人性的三角结构:利益、情感、价值观', + '1.5': '沟通差的问题:为什么你说的别人听不懂', + '2.1': '相亲故事:你以为找的是人,实际是在找模式', + '2.2': '找工作迷茫者:为什么简历解决不了人生', + '2.3': '撸运费险:小钱困住大脑的真实心理', + '2.4': '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', + '2.5': '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', + '3.1': '3000万流水如何跑出来(退税模式解析)', + '8.1': '流量杠杆:抖音、Soul、飞书', + '9.14': '大健康私域:一个月150万的70后' + } + return titles[id] || `章节 ${id}` + }, + + // 加载内容 - 三级降级方案:API → 本地缓存 → 备用API + async loadContent(id) { + const cacheKey = `chapter_${id}` + + // 1. 优先从API获取 + try { + const res = await this.fetchChapterWithTimeout(id, 5000) + if (res && res.content) { + this.setChapterContent(res) + // 成功后缓存到本地 + wx.setStorageSync(cacheKey, res) + console.log('[Read] 从API加载成功:', id) + return + } + } catch (e) { + console.warn('[Read] API加载失败,尝试本地缓存:', e.message) + } + + // 2. API失败,尝试从本地缓存读取 + try { + const cached = wx.getStorageSync(cacheKey) + if (cached && cached.content) { + this.setChapterContent(cached) + console.log('[Read] 从本地缓存加载成功:', id) + // 后台静默刷新 + this.silentRefresh(id) + return + } + } catch (e) { + console.warn('[Read] 本地缓存读取失败') + } + + // 3. 都失败,显示加载中并持续重试 + this.setData({ + contentParagraphs: ['章节内容加载中...', '正在尝试连接服务器,请稍候...'], + previewParagraphs: ['章节内容加载中...'] + }) + + // 延迟重试(最多3次) + this.retryLoadContent(id, 3) + }, + + // 带超时的章节请求 + fetchChapterWithTimeout(id, timeout = 5000) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error('请求超时')) + }, timeout) + + app.request(`/api/book/chapter/${id}`) + .then(res => { + clearTimeout(timer) + resolve(res) + }) + .catch(err => { + clearTimeout(timer) + reject(err) + }) + }) + }, + + // 设置章节内容 + setChapterContent(res) { + const lines = res.content.split('\n').filter(line => line.trim()) + const previewCount = Math.ceil(lines.length * 0.2) + + this.setData({ + content: res.content, + previewContent: lines.slice(0, previewCount).join('\n'), + contentParagraphs: lines, + previewParagraphs: lines.slice(0, previewCount), + partTitle: res.partTitle || '', + chapterTitle: res.chapterTitle || '' + }) + }, + + // 静默刷新(后台更新缓存) + async silentRefresh(id) { + try { + const res = await this.fetchChapterWithTimeout(id, 10000) + if (res && res.content) { + wx.setStorageSync(`chapter_${id}`, res) + console.log('[Read] 后台缓存更新成功:', id) + } + } catch (e) { + // 静默失败不处理 + } + }, + + // 重试加载 + retryLoadContent(id, maxRetries, currentRetry = 0) { + if (currentRetry >= maxRetries) { + this.setData({ + contentParagraphs: ['内容加载失败', '请检查网络连接后下拉刷新重试'], + previewParagraphs: ['内容加载失败'] + }) + return + } + + setTimeout(async () => { + try { + const res = await this.fetchChapterWithTimeout(id, 8000) + if (res && res.content) { + this.setChapterContent(res) + wx.setStorageSync(`chapter_${id}`, res) + console.log('[Read] 重试成功:', id, '第', currentRetry + 1, '次') + return + } + } catch (e) { + console.warn('[Read] 重试失败,继续重试:', currentRetry + 1) + } + this.retryLoadContent(id, maxRetries, currentRetry + 1) + }, 2000 * (currentRetry + 1)) + }, + + + // 加载导航 + loadNavigation(id) { + const sectionOrder = [ + 'preface', '1.1', '1.2', '1.3', '1.4', '1.5', + '2.1', '2.2', '2.3', '2.4', '2.5', + '3.1', '3.2', '3.3', '3.4', + '4.1', '4.2', '4.3', '4.4', '4.5', + '5.1', '5.2', '5.3', '5.4', '5.5', + '6.1', '6.2', '6.3', '6.4', + '7.1', '7.2', '7.3', '7.4', '7.5', + '8.1', '8.2', '8.3', '8.4', '8.5', '8.6', + '9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '9.7', '9.8', '9.9', '9.10', '9.11', '9.12', '9.13', '9.14', + '10.1', '10.2', '10.3', '10.4', + '11.1', '11.2', '11.3', '11.4', '11.5', + 'epilogue' + ] + + const currentIndex = sectionOrder.indexOf(id) + const prevId = currentIndex > 0 ? sectionOrder[currentIndex - 1] : null + const nextId = currentIndex < sectionOrder.length - 1 ? sectionOrder[currentIndex + 1] : null + + this.setData({ + prevSection: prevId ? { id: prevId, title: this.getSectionTitle(prevId) } : null, + nextSection: nextId ? { id: nextId, title: this.getSectionTitle(nextId) } : null + }) + }, + + // 返回 + goBack() { + wx.navigateBack({ + fail: () => wx.switchTab({ url: '/pages/chapters/chapters' }) + }) + }, + + // 分享弹窗 + showShare() { + this.setData({ showShareModal: true }) + }, + + closeShareModal() { + this.setData({ showShareModal: false }) + }, + + // 复制链接 + copyLink() { + const userInfo = app.globalData.userInfo + const referralCode = userInfo?.referralCode || '' + const shareUrl = `https://soul.quwanzhi.com/read/${this.data.sectionId}${referralCode ? '?ref=' + referralCode : ''}` + + wx.setClipboardData({ + data: shareUrl, + success: () => { + wx.showToast({ title: '链接已复制', icon: 'success' }) + this.setData({ showShareModal: false }) + } + }) + }, + + // 复制分享文案(朋友圈风格) + copyShareText() { + const { section } = this.data + + const shareText = `🔥 刚看完这篇《${section?.title || 'Soul创业派对'}》,太上头了! + +62个真实商业案例,每个都是从0到1的实战经验。私域运营、资源整合、商业变现,干货满满。 + +推荐给正在创业或想创业的朋友,搜"Soul创业派对"小程序就能看! + +#创业派对 #私域运营 #商业案例` + + wx.setClipboardData({ + data: shareText, + success: () => { + wx.showToast({ title: '文案已复制', icon: 'success' }) + } + }) + }, + + // 分享到微信 - 自动带分享人ID + onShareAppMessage() { + const { section, sectionId } = this.data + const userInfo = app.globalData.userInfo + const referralCode = userInfo?.referralCode || wx.getStorageSync('referralCode') || '' + + // 分享标题优化 + const shareTitle = section?.title + ? `📚 ${section.title.length > 20 ? section.title.slice(0, 20) + '...' : section.title}` + : '📚 Soul创业派对 - 真实商业故事' + + return { + title: shareTitle, + path: `/pages/read/read?id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`, + imageUrl: '/assets/share-cover.png' // 可配置分享封面图 + } + }, + + // 分享到朋友圈 + onShareTimeline() { + const { section, sectionId } = this.data + const userInfo = app.globalData.userInfo + const referralCode = userInfo?.referralCode || '' + + return { + title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`, + query: `id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}` + } + }, + + // 显示登录弹窗 + showLoginModal() { + this.setData({ showLoginModal: true }) + }, + + closeLoginModal() { + this.setData({ showLoginModal: false }) + }, + + // 微信登录 + async handleWechatLogin() { + try { + const result = await app.login() + if (result) { + this.setData({ showLoginModal: false }) + this.initSection(this.data.sectionId) + wx.showToast({ title: '登录成功', icon: 'success' }) + } + } catch (e) { + wx.showToast({ title: '登录失败', icon: 'none' }) + } + }, + + // 手机号登录 + async handlePhoneLogin(e) { + if (!e.detail.code) { + return this.handleWechatLogin() + } + + try { + const result = await app.loginWithPhone(e.detail.code) + if (result) { + this.setData({ showLoginModal: false }) + this.initSection(this.data.sectionId) + wx.showToast({ title: '登录成功', icon: 'success' }) + } + } catch (e) { + wx.showToast({ title: '登录失败', icon: 'none' }) + } + }, + + // 购买章节 - 直接调起支付 + async handlePurchaseSection() { + console.log('[Pay] 点击购买章节按钮') + wx.showLoading({ title: '处理中...', mask: true }) + + if (!this.data.isLoggedIn) { + wx.hideLoading() + console.log('[Pay] 用户未登录,显示登录弹窗') + this.setData({ showLoginModal: true }) + return + } + + const price = this.data.section?.price || 1 + console.log('[Pay] 开始支付流程:', { sectionId: this.data.sectionId, price }) + wx.hideLoading() + await this.processPayment('section', this.data.sectionId, price) + }, + + // 购买全书 - 直接调起支付 + async handlePurchaseFullBook() { + console.log('[Pay] 点击购买全书按钮') + wx.showLoading({ title: '处理中...', mask: true }) + + if (!this.data.isLoggedIn) { + wx.hideLoading() + console.log('[Pay] 用户未登录,显示登录弹窗') + this.setData({ showLoginModal: true }) + return + } + + console.log('[Pay] 开始支付流程: 全书', { price: this.data.fullBookPrice }) + wx.hideLoading() + await this.processPayment('fullbook', null, this.data.fullBookPrice) + }, + + // 处理支付 - 调用真实微信支付接口 + async processPayment(type, sectionId, amount) { + console.log('[Pay] processPayment开始:', { type, sectionId, amount }) + + // 检查金额是否有效 + if (!amount || amount <= 0) { + console.error('[Pay] 金额无效:', amount) + wx.showToast({ title: '价格信息错误', icon: 'none' }) + return + } + + // 检查是否已购买(避免重复购买) + if (type === 'section' && sectionId) { + const purchasedSections = app.globalData.purchasedSections || [] + if (purchasedSections.includes(sectionId)) { + wx.showToast({ title: '已购买过此章节', icon: 'none' }) + return + } + } + + if (type === 'fullbook' && app.globalData.hasFullBook) { + wx.showToast({ title: '已购买全书', icon: 'none' }) + return + } + + this.setData({ isPaying: true }) + wx.showLoading({ title: '正在发起支付...', mask: true }) + + try { + // 1. 先获取openId (支付必需) + let openId = app.globalData.openId || wx.getStorageSync('openId') + + if (!openId) { + console.log('[Pay] 需要先获取openId,尝试静默获取') + wx.showLoading({ title: '获取支付凭证...', mask: true }) + openId = await app.getOpenId() + + if (!openId) { + // openId获取失败,但已登录用户可以使用用户ID替代 + if (app.globalData.isLoggedIn && app.globalData.userInfo?.id) { + console.log('[Pay] 使用用户ID作为替代') + openId = app.globalData.userInfo.id + } else { + wx.hideLoading() + wx.showModal({ + title: '提示', + content: '需要登录后才能支付,请先登录', + showCancel: false + }) + this.setData({ showLoginModal: true, isPaying: false }) + return + } + } + } + + console.log('[Pay] 开始创建订单:', { type, sectionId, amount, openId: openId.slice(0, 10) + '...' }) + wx.showLoading({ title: '创建订单中...', mask: true }) + + // 2. 调用后端创建预支付订单 + let paymentData = null + + try { + // 获取章节完整名称用于支付描述 + const sectionTitle = this.data.section?.title || sectionId + const description = type === 'fullbook' + ? '《一场Soul的创业实验》全书' + : `章节${sectionId}-${sectionTitle.length > 20 ? sectionTitle.slice(0, 20) + '...' : sectionTitle}` + + const res = await app.request('/api/miniprogram/pay', { + method: 'POST', + data: { + openId, + productType: type, + productId: sectionId, + amount, + description, + userId: app.globalData.userInfo?.id || '' + } + }) + + console.log('[Pay] 创建订单响应:', res) + + if (res.success && res.data?.payParams) { + paymentData = res.data.payParams + console.log('[Pay] 获取支付参数成功:', paymentData) + } else { + throw new Error(res.error || res.message || '创建订单失败') + } + } catch (apiError) { + console.error('[Pay] API创建订单失败:', apiError) + wx.hideLoading() + // 支付接口失败时,显示客服联系方式 + wx.showModal({ + title: '支付通道维护中', + content: '微信支付正在审核中,请添加客服微信(28533368)手动购买,感谢理解!', + confirmText: '复制微信号', + cancelText: '稍后再说', + success: (res) => { + if (res.confirm) { + wx.setClipboardData({ + data: '28533368', + success: () => { + wx.showToast({ title: '微信号已复制', icon: 'success' }) + } + }) + } + } + }) + this.setData({ isPaying: false }) + return + } + + // 3. 调用微信支付 + wx.hideLoading() + console.log('[Pay] 调起微信支付, paymentData:', paymentData) + + try { + await this.callWechatPay(paymentData) + + // 4. 支付成功,更新本地数据 + console.log('[Pay] 微信支付成功!') + this.mockPaymentSuccess(type, sectionId) + wx.showToast({ title: '购买成功', icon: 'success' }) + + // 5. 刷新页面 + this.initSection(this.data.sectionId) + } catch (payErr) { + console.error('[Pay] 微信支付调起失败:', payErr) + if (payErr.errMsg && payErr.errMsg.includes('cancel')) { + wx.showToast({ title: '已取消支付', icon: 'none' }) + } else if (payErr.errMsg && payErr.errMsg.includes('requestPayment:fail')) { + // 支付失败,可能是参数错误或权限问题 + wx.showModal({ + title: '支付失败', + content: '微信支付暂不可用,请添加客服微信(28533368)手动购买', + confirmText: '复制微信号', + cancelText: '取消', + success: (res) => { + if (res.confirm) { + wx.setClipboardData({ + data: '28533368', + success: () => wx.showToast({ title: '微信号已复制', icon: 'success' }) + }) + } + } + }) + } else { + wx.showToast({ title: payErr.errMsg || '支付失败', icon: 'none' }) + } + } + + } catch (e) { + console.error('[Pay] 支付流程异常:', e) + wx.hideLoading() + wx.showToast({ title: '支付出错,请重试', icon: 'none' }) + } finally { + this.setData({ isPaying: false }) + } + }, + + // 模拟支付成功 + mockPaymentSuccess(type, sectionId) { + if (type === 'fullbook') { + app.globalData.hasFullBook = true + const userInfo = app.globalData.userInfo || {} + userInfo.hasFullBook = true + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + } else if (sectionId) { + const purchasedSections = app.globalData.purchasedSections || [] + if (!purchasedSections.includes(sectionId)) { + purchasedSections.push(sectionId) + app.globalData.purchasedSections = purchasedSections + + const userInfo = app.globalData.userInfo || {} + userInfo.purchasedSections = purchasedSections + app.globalData.userInfo = userInfo + wx.setStorageSync('userInfo', userInfo) + } + } + }, + + // 调用微信支付 + callWechatPay(paymentData) { + return new Promise((resolve, reject) => { + wx.requestPayment({ + timeStamp: paymentData.timeStamp, + nonceStr: paymentData.nonceStr, + package: paymentData.package, + signType: paymentData.signType || 'MD5', + paySign: paymentData.paySign, + success: resolve, + fail: reject + }) + }) + }, + + // 跳转到上一篇 + goToPrev() { + if (this.data.prevSection) { + wx.redirectTo({ url: `/pages/read/read?id=${this.data.prevSection.id}` }) + } + }, + + // 跳转到下一篇 + goToNext() { + if (this.data.nextSection) { + wx.redirectTo({ url: `/pages/read/read?id=${this.data.nextSection.id}` }) + } + }, + + // 跳转到推广中心 + goToReferral() { + wx.navigateTo({ url: '/pages/referral/referral' }) + }, + + // 生成海报 + async generatePoster() { + wx.showLoading({ title: '生成中...' }) + this.setData({ showPosterModal: true, isGeneratingPoster: true }) + + try { + const ctx = wx.createCanvasContext('posterCanvas', this) + const { section, contentParagraphs, sectionId } = this.data + const userInfo = app.globalData.userInfo + const userId = userInfo?.id || '' + + // 获取小程序码(带推荐人参数) + let qrcodeImage = null + try { + const scene = userId ? `id=${sectionId}&ref=${userId.slice(0,10)}` : `id=${sectionId}` + const qrRes = await app.request('/api/miniprogram/qrcode', { + method: 'POST', + data: { scene, page: 'pages/read/read', width: 280 } + }) + if (qrRes.success && qrRes.image) { + qrcodeImage = qrRes.image + } + } catch (e) { + console.log('[Poster] 获取小程序码失败,使用占位符') + } + + // 海报尺寸 300x450 + const width = 300 + const height = 450 + + // 背景渐变 + const grd = ctx.createLinearGradient(0, 0, 0, height) + grd.addColorStop(0, '#1a1a2e') + grd.addColorStop(1, '#16213e') + ctx.setFillStyle(grd) + ctx.fillRect(0, 0, width, height) + + // 顶部装饰条 + ctx.setFillStyle('#00CED1') + ctx.fillRect(0, 0, width, 4) + + // 标题区域 + ctx.setFillStyle('#ffffff') + ctx.setFontSize(14) + ctx.fillText('📚 Soul创业派对', 20, 35) + + // 章节标题 + ctx.setFontSize(18) + ctx.setFillStyle('#ffffff') + const title = section?.title || '精彩内容' + const titleLines = this.wrapText(ctx, title, width - 40, 18) + let y = 70 + titleLines.forEach(line => { + ctx.fillText(line, 20, y) + y += 26 + }) + + // 分隔线 + ctx.setStrokeStyle('rgba(255,255,255,0.1)') + ctx.beginPath() + ctx.moveTo(20, y + 10) + ctx.lineTo(width - 20, y + 10) + ctx.stroke() + + // 内容摘要 + ctx.setFontSize(12) + ctx.setFillStyle('rgba(255,255,255,0.8)') + y += 30 + const summary = contentParagraphs.slice(0, 3).join(' ').slice(0, 150) + '...' + const summaryLines = this.wrapText(ctx, summary, width - 40, 12) + summaryLines.slice(0, 6).forEach(line => { + ctx.fillText(line, 20, y) + y += 20 + }) + + // 底部区域背景 + ctx.setFillStyle('rgba(0,206,209,0.1)') + ctx.fillRect(0, height - 100, width, 100) + + // 左侧提示文字 + ctx.setFillStyle('#ffffff') + ctx.setFontSize(13) + ctx.fillText('长按识别小程序码', 20, height - 60) + ctx.setFillStyle('rgba(255,255,255,0.6)') + ctx.setFontSize(11) + ctx.fillText('长按小程序码阅读全文', 20, height - 38) + + // 绘制小程序码或占位符 + const drawQRCode = () => { + return new Promise((resolve) => { + if (qrcodeImage) { + // 下载base64图片并绘制 + const fs = wx.getFileSystemManager() + const filePath = `${wx.env.USER_DATA_PATH}/qrcode_${Date.now()}.png` + const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '') + + fs.writeFile({ + filePath, + data: base64Data, + encoding: 'base64', + success: () => { + ctx.drawImage(filePath, width - 85, height - 85, 70, 70) + resolve() + }, + fail: () => { + this.drawQRPlaceholder(ctx, width, height) + resolve() + } + }) + } else { + this.drawQRPlaceholder(ctx, width, height) + resolve() + } + }) + } + + await drawQRCode() + + ctx.draw(true, () => { + wx.hideLoading() + this.setData({ isGeneratingPoster: false }) + }) + } catch (e) { + console.error('生成海报失败:', e) + wx.hideLoading() + wx.showToast({ title: '生成失败', icon: 'none' }) + this.setData({ showPosterModal: false, isGeneratingPoster: false }) + } + }, + + // 绘制小程序码占位符 + drawQRPlaceholder(ctx, width, height) { + ctx.setFillStyle('#ffffff') + ctx.beginPath() + ctx.arc(width - 50, height - 50, 35, 0, Math.PI * 2) + ctx.fill() + ctx.setFillStyle('#00CED1') + ctx.setFontSize(9) + ctx.fillText('扫码', width - 57, height - 52) + ctx.fillText('阅读', width - 57, height - 40) + }, + + // 文字换行处理 + wrapText(ctx, text, maxWidth, fontSize) { + const lines = [] + let line = '' + for (let i = 0; i < text.length; i++) { + const testLine = line + text[i] + const metrics = ctx.measureText(testLine) + if (metrics.width > maxWidth && line) { + lines.push(line) + line = text[i] + } else { + line = testLine + } + } + if (line) lines.push(line) + return lines + }, + + // 关闭海报弹窗 + closePosterModal() { + this.setData({ showPosterModal: false }) + }, + + // 保存海报到相册 + savePoster() { + wx.canvasToTempFilePath({ + canvasId: 'posterCanvas', + success: (res) => { + wx.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success: () => { + wx.showToast({ title: '已保存到相册', icon: 'success' }) + this.setData({ showPosterModal: false }) + }, + fail: (err) => { + if (err.errMsg.includes('auth deny')) { + wx.showModal({ + title: '提示', + content: '需要相册权限才能保存海报', + confirmText: '去设置', + success: (res) => { + if (res.confirm) { + wx.openSetting() + } + } + }) + } else { + wx.showToast({ title: '保存失败', icon: 'none' }) + } + } + }) + }, + fail: () => { + wx.showToast({ title: '生成图片失败', icon: 'none' }) + } + }, this) + }, + + // 阻止冒泡 + stopPropagation() {} +}) diff --git a/Tminiprogram/pages/read/read.json b/Tminiprogram/pages/read/read.json new file mode 100644 index 00000000..f6abead2 --- /dev/null +++ b/Tminiprogram/pages/read/read.json @@ -0,0 +1,7 @@ +{ + "usingComponents": {}, + "enablePullDownRefresh": false, + "backgroundTextStyle": "light", + "backgroundColor": "#000000", + "navigationStyle": "custom" +} diff --git a/Tminiprogram/pages/read/read.wxml b/Tminiprogram/pages/read/read.wxml new file mode 100644 index 00000000..45f9b2a6 --- /dev/null +++ b/Tminiprogram/pages/read/read.wxml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + {{partTitle}} + {{chapterTitle}} + + + + + + + + + + + + + + + + {{section.id}} + 免费 + + {{section.title}} + + + + + + + + + + + + + + + {{item}} + + + + + + + 上一篇 + {{prevSection.title}} + + + + + 下一篇 + + {{nextSection.title}} + + + + + 已是最后一篇 🎉 + + + + + + + + + 🖼️ + 生成海报 + + + + + + + + + + + {{item}} + + + + + + + + 🔒 + 解锁完整内容 + + 已阅读20%,{{isLoggedIn ? '购买后继续阅读' : '登录并购买后继续阅读'}} + + + + + + + + + + 购买本章 + ¥{{section.price}} + + + + + + + 解锁全部 {{totalSections}} 章 + + + ¥{{fullBookPrice}} + 省82% + + + + + 分享给好友一起学习 + + + + + + + 上一篇 + 章节 {{prevSection.id}} + + + + + 下一篇 + + {{nextSection.title}} + + + + + 已是最后一篇 🎉 + + + + + + + + + + + 生成海报 + + + + + + + + + + + 💾 + 保存到相册 + + + + 长按海报可直接分享到微信 + + + + + + + + + + + + + 支付处理中... + + + diff --git a/Tminiprogram/pages/read/read.wxss b/Tminiprogram/pages/read/read.wxss new file mode 100644 index 00000000..e3229595 --- /dev/null +++ b/Tminiprogram/pages/read/read.wxss @@ -0,0 +1,917 @@ +/** + * Soul创业实验 - 阅读页样式 + * 1:1还原Web版本UI + */ + +.page { + min-height: 100vh; + background: #000000; +} + +/* ===== 阅读进度条 ===== */ +.progress-bar-fixed { + position: fixed; + left: 0; + right: 0; + height: 4rpx; + background: #1c1c1e; + z-index: 200; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); + transition: width 0.15s ease; +} + +/* ===== 导航栏 ===== */ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(40rpx); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.nav-content { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 24rpx; + height: 88rpx; +} + +.nav-back, .nav-share { + width: 72rpx; + height: 72rpx; + border-radius: 50%; + background: #1c1c1e; + display: flex; + align-items: center; + justify-content: center; +} + +.back-arrow { + font-size: 36rpx; + color: rgba(255, 255, 255, 0.8); +} + +.share-icon { + font-size: 32rpx; + color: rgba(255, 255, 255, 0.8); +} + +.nav-info { + flex: 1; + text-align: center; + padding: 0 16rpx; +} + +.nav-part { + font-size: 20rpx; + color: rgba(255, 255, 255, 0.4); + display: block; +} + +.nav-chapter { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.nav-placeholder { + width: 100%; +} + +/* ===== 阅读内容 ===== */ +.read-content { + max-width: 750rpx; + margin: 0 auto; + padding: 48rpx 40rpx 200rpx; +} + +/* ===== 章节标题 ===== */ +.chapter-header { + margin-bottom: 48rpx; +} + +.chapter-meta { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 24rpx; +} + +.chapter-id { + font-size: 28rpx; + font-weight: 500; + color: #00CED1; + background: rgba(0, 206, 209, 0.1); + padding: 8rpx 24rpx; + border-radius: 32rpx; +} + +.tag { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 22rpx; + padding: 6rpx 16rpx; + min-width: 80rpx; + border-radius: 8rpx; + box-sizing: border-box; + text-align: center; +} + +.tag-free { + background: rgba(0, 206, 209, 0.1); + color: #00CED1; +} + +.chapter-title { + font-size: 44rpx; + font-weight: 700; + color: #ffffff; + line-height: 1.4; +} + +/* ===== 加载状态 ===== */ +.loading-state { + display: flex; + flex-direction: column; + gap: 32rpx; +} + +.skeleton { + height: 32rpx; + background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s ease-in-out infinite; + border-radius: 8rpx; +} + +.skeleton-1 { width: 75%; } +.skeleton-2 { width: 90%; } +.skeleton-3 { width: 65%; } +.skeleton-4 { width: 85%; } +.skeleton-5 { width: 70%; } + +@keyframes skeleton-loading { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +/* ===== 文章内容 ===== */ +.article { + color: rgba(255, 255, 255, 0.85); + font-size: 34rpx; + line-height: 1.9; +} + +.paragraph { + margin-bottom: 48rpx; + text-align: justify; +} + +.preview { + position: relative; +} + +/* ===== 渐变遮罩 ===== */ +.fade-mask { + position: absolute; + bottom: 0; + left: -40rpx; + right: -40rpx; + height: 300rpx; + background: linear-gradient(to top, #000000 0%, transparent 100%); + pointer-events: none; +} + +/* ===== 付费墙 ===== */ +.paywall { + position: relative; + z-index: 10; + margin-top: 48rpx; + padding: 48rpx; + background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); + border-radius: 32rpx; + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +.paywall-icon { + width: 128rpx; + height: 128rpx; + margin: 0 auto 32rpx; + background: rgba(0, 206, 209, 0.1); + border-radius: 32rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 64rpx; +} + +.paywall-title { + font-size: 40rpx; + font-weight: 600; + color: #ffffff; + text-align: center; + display: block; + margin-bottom: 16rpx; +} + +.paywall-desc { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); + text-align: center; + display: block; + margin-bottom: 48rpx; +} + +/* ===== 购买选项 ===== */ +.purchase-options { + display: flex; + flex-direction: column; + gap: 24rpx; + margin-bottom: 32rpx; +} + +.purchase-btn { + display: flex; + align-items: center; + justify-content: space-between; + padding: 28rpx 32rpx; + border-radius: 24rpx; +} + +.purchase-section { + background: #2c2c2e; + border: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.purchase-fullbook { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.3); +} + +.purchase-section .btn-label { + font-size: 28rpx; + color: #ffffff; +} + +.purchase-section .btn-price { + font-size: 28rpx; + font-weight: 600; +} + +.brand-color { + color: #00CED1; +} + +.purchase-fullbook .btn-left { + display: flex; + align-items: center; + gap: 8rpx; +} + +.purchase-fullbook .btn-sparkle { + font-size: 28rpx; +} + +.purchase-fullbook .btn-label { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; +} + +.purchase-fullbook .btn-right { + text-align: right; +} + +.purchase-fullbook .btn-price { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; +} + +.purchase-fullbook .btn-discount { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.7); + margin-left: 8rpx; +} + +.paywall-tip { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.4); + text-align: center; + display: block; +} + +/* ===== 章节导航 ===== */ +.chapter-nav { + margin-top: 96rpx; + padding-top: 64rpx; + border-top: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.nav-buttons { + display: flex; + gap: 24rpx; + margin-bottom: 48rpx; +} + +.nav-btn { + flex: 1; + padding: 24rpx; + border-radius: 24rpx; + max-width: 48%; +} + +.nav-btn-placeholder { + flex: 1; + max-width: 48%; +} + +.nav-prev { + background: #1c1c1e; + border: 2rpx solid rgba(255, 255, 255, 0.05); +} + +.nav-next { + background: linear-gradient(90deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.1) 100%); + border: 2rpx solid rgba(0, 206, 209, 0.2); +} + +.nav-end { + background: #1c1c1e; + border: 2rpx solid rgba(255, 255, 255, 0.05); + text-align: center; +} + +.nav-disabled { + opacity: 0.5; +} + +.btn-label { + font-size: 20rpx; + color: rgba(255, 255, 255, 0.5); + display: block; + margin-bottom: 4rpx; +} + +.nav-next .btn-label { + color: #00CED1; +} + +.btn-title { + font-size: 24rpx; + color: #ffffff; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.btn-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.btn-arrow { + font-size: 24rpx; + color: #00CED1; + flex-shrink: 0; + margin-left: 8rpx; +} + +.btn-end-text { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 分享操作区 ===== */ +.action-section { + margin-top: 48rpx; +} + +.action-row-inline { + display: flex; + gap: 16rpx; +} + +.action-btn-inline { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8rpx; + padding: 24rpx 16rpx; + border-radius: 16rpx; + border: none; + background: transparent; + line-height: normal; + box-sizing: border-box; +} + +.action-btn-inline::after { + border: none; +} + +.btn-share-inline { + background: rgba(7, 193, 96, 0.15); + border: 2rpx solid rgba(7, 193, 96, 0.3); +} + +.btn-poster-inline { + background: rgba(255, 215, 0, 0.15); + border: 2rpx solid rgba(255, 215, 0, 0.3); +} + + +.action-icon-small { + font-size: 28rpx; +} + +.action-text-small { + font-size: 24rpx; + color: #ffffff; + font-weight: 500; +} + +/* ===== 推广提示区 ===== */ +.promo-section { + margin-top: 32rpx; +} + +.promo-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32rpx; + background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%); + border: 2rpx solid rgba(255, 215, 0, 0.2); + border-radius: 24rpx; +} + +.promo-left { + display: flex; + align-items: center; + gap: 20rpx; +} + +.promo-icon { + width: 80rpx; + height: 80rpx; + background: rgba(255, 215, 0, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; +} + +.promo-info { + flex: 1; +} + +.promo-title { + font-size: 30rpx; + color: #ffffff; + font-weight: 600; + display: block; +} + +.promo-desc { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.5); + display: block; + margin-top: 8rpx; +} + +.promo-right { + padding-left: 20rpx; +} + +.promo-arrow { + font-size: 32rpx; + color: #FFD700; +} + +/* ===== 弹窗 ===== */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(20rpx); + display: flex; + align-items: flex-end; + justify-content: center; + z-index: 1000; +} + +.modal-content { + width: 100%; + max-width: 750rpx; + background: #1c1c1e; + border-radius: 48rpx 48rpx 0 0; + padding: 48rpx; + padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); + animation: slideUp 0.3s ease; +} + +@keyframes slideUp { + from { transform: translateY(100%); } + to { transform: translateY(0); } +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 32rpx; +} + +.modal-title { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; +} + +.modal-close { + width: 64rpx; + height: 64rpx; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 分享弹窗 ===== */ +.share-link-box { + padding: 32rpx; + background: rgba(0, 0, 0, 0.3); + border: 2rpx solid rgba(255, 255, 255, 0.1); + border-radius: 24rpx; + margin-bottom: 32rpx; +} + +.link-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.5); + display: block; + margin-bottom: 16rpx; +} + +.link-url { + font-size: 26rpx; + color: #00CED1; + display: block; + word-break: break-all; + font-family: monospace; +} + +.link-tip { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.4); + display: block; + margin-top: 16rpx; +} + +.share-buttons { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24rpx; + margin-bottom: 32rpx; +} + +.share-btn { + display: flex; + flex-direction: column; + align-items: center; + gap: 16rpx; + padding: 24rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 24rpx; + border: none; + line-height: normal; +} + +.share-btn::after { + border: none; +} + +.share-btn-icon { + width: 96rpx; + height: 96rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 40rpx; +} + +.icon-copy { + background: rgba(0, 206, 209, 0.2); +} + +.icon-wechat { + background: rgba(7, 193, 96, 0.2); + color: #07C160; + font-size: 32rpx; +} + +.icon-poster { + background: rgba(255, 215, 0, 0.2); +} + +.share-btn-text { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 支付弹窗 ===== */ +.payment-info { + padding: 24rpx; + background: rgba(0, 0, 0, 0.3); + border-radius: 24rpx; + margin-bottom: 32rpx; +} + +.payment-type { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.6); + display: block; + margin-bottom: 16rpx; +} + +.payment-amount { + display: flex; + align-items: center; + justify-content: space-between; +} + +.amount-label { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); +} + +.amount-value { + font-size: 48rpx; + font-weight: 700; + color: #00CED1; +} + +.payment-methods { + margin-bottom: 32rpx; +} + +.method-item { + display: flex; + align-items: center; + gap: 16rpx; + padding: 24rpx; + background: rgba(255, 255, 255, 0.05); + border-radius: 16rpx; + border: 2rpx solid transparent; +} + +.method-active { + border-color: #07C160; +} + +.method-icon { + width: 48rpx; + height: 48rpx; + background: #07C160; + color: #ffffff; + font-size: 24rpx; + border-radius: 8rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.method-name { + flex: 1; + font-size: 28rpx; + color: #ffffff; +} + +.method-check { + color: #07C160; + font-size: 28rpx; +} + +.btn-primary { + width: 100%; + padding: 28rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + color: #ffffff; + font-size: 32rpx; + font-weight: 600; + border-radius: 24rpx; + text-align: center; + margin-bottom: 16rpx; +} + +.payment-notice { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.3); + text-align: center; + display: block; +} + +/* ===== 登录提示 ===== */ +.login-prompt { + margin-top: 32rpx; +} + +.login-btn { + padding: 28rpx; + background: rgba(255, 255, 255, 0.1); + border: 2rpx solid rgba(255, 255, 255, 0.2); + border-radius: 24rpx; + text-align: center; +} + +.login-btn-text { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ===== 登录弹窗 ===== */ +.login-modal { + padding: 48rpx 32rpx; + text-align: center; +} + +.login-icon { + font-size: 80rpx; + display: block; + margin-bottom: 24rpx; +} + +.login-title { + font-size: 36rpx; + font-weight: 700; + color: #ffffff; + display: block; + margin-bottom: 16rpx; +} + +.login-desc { + font-size: 26rpx; + color: rgba(255, 255, 255, 0.5); + display: block; + margin-bottom: 48rpx; +} + +.btn-wechat { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + padding: 28rpx; + background: #07C160; + color: #ffffff; + font-size: 30rpx; + font-weight: 600; + border-radius: 24rpx; + margin-bottom: 20rpx; + border: none; +} + +.btn-wechat::after { + border: none; +} + +.btn-wechat-icon { + width: 40rpx; + height: 40rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 8rpx; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.btn-phone { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + padding: 28rpx; + background: rgba(255, 255, 255, 0.05); + color: #ffffff; + font-size: 30rpx; + border-radius: 24rpx; + border: 2rpx solid rgba(255, 255, 255, 0.1); +} + +.btn-phone::after { + border: none; +} + +.btn-phone-icon { + font-size: 32rpx; +} + +.login-notice { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.3); + margin-top: 32rpx; + display: block; +} + +/* ===== 支付中加载 ===== */ +.loading-box { + background: rgba(0, 0, 0, 0.8); + border-radius: 24rpx; + padding: 48rpx 64rpx; + display: flex; + flex-direction: column; + align-items: center; + gap: 24rpx; +} + +.loading-spinner { + width: 64rpx; + height: 64rpx; + border: 4rpx solid rgba(255, 255, 255, 0.2); + border-top-color: #00CED1; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.loading-text { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.8); +} + +/* ===== 海报弹窗 ===== */ +.poster-modal { + padding-bottom: calc(64rpx + env(safe-area-inset-bottom)); +} + +.poster-preview { + display: flex; + justify-content: center; + margin: 32rpx 0; + padding: 24rpx; + background: rgba(0, 0, 0, 0.3); + border-radius: 24rpx; +} + +.poster-canvas { + border-radius: 16rpx; + box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.5); +} + +.poster-actions { + display: flex; + gap: 24rpx; + margin-bottom: 24rpx; +} + +.poster-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 12rpx; + padding: 28rpx; + border-radius: 24rpx; + font-size: 30rpx; + font-weight: 500; +} + +.btn-save { + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + color: #ffffff; +} + +.btn-icon { + font-size: 32rpx; +} + +.poster-tip { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.4); + text-align: center; + display: block; +} diff --git a/Tminiprogram/pages/referral/referral.js b/Tminiprogram/pages/referral/referral.js new file mode 100644 index 00000000..f9bb0867 --- /dev/null +++ b/Tminiprogram/pages/referral/referral.js @@ -0,0 +1,553 @@ +/** + * Soul创业派对 - 分销中心页 + * + * 可见数据: + * - 绑定用户数(当前有效绑定) + * - 通过链接进的人数(总访问量) + * - 带来的付款人数(已转化购买) + * - 收益统计(90%归分发者) + */ +const app = getApp() + +Page({ + data: { + statusBarHeight: 44, + isLoggedIn: false, + userInfo: null, + + // === 核心可见数据 === + bindingCount: 0, // 绑定用户数(当前有效) + visitCount: 0, // 通过链接进的人数 + paidCount: 0, // 带来的付款人数 + unboughtCount: 0, // 待购买人数(绑定但未付款) + expiredCount: 0, // 已过期人数 + + // === 收益数据 === + earnings: 0, // 已结算收益 + pendingEarnings: 0, // 待结算收益 + withdrawnEarnings: 0, // 已提现金额 + shareRate: 90, // 分成比例(90%) + + // === 统计数据 === + referralCount: 0, // 总推荐人数 + expiringCount: 0, // 即将过期人数 + + // 邀请码 + referralCode: '', + + // 绑定用户列表 + showBindingList: true, + activeTab: 'active', + activeBindings: [], + convertedBindings: [], + expiredBindings: [], + currentBindings: [], + totalBindings: 0, + + // 收益明细 + earningsDetails: [], + + // 海报 + showPosterModal: false, + isGeneratingPoster: false + }, + + onLoad() { + this.setData({ statusBarHeight: app.globalData.statusBarHeight }) + this.initData() + }, + + onShow() { + this.initData() + }, + + // 初始化数据 + async initData() { + const { isLoggedIn, userInfo } = app.globalData + if (isLoggedIn && userInfo) { + // 生成邀请码 + const referralCode = userInfo.referralCode || 'SOUL' + (userInfo.id || Date.now().toString(36)).toUpperCase().slice(-6) + + // 尝试从API获取真实数据 + let realData = null + try { + const res = await app.request('/api/referral/data', { + method: 'GET', + data: { userId: userInfo.id } + }) + if (res.success) { + realData = res.data + console.log('[Referral] 获取推广数据成功:', realData) + } + } catch (e) { + console.log('[Referral] 获取推广数据失败,使用本地数据') + } + + // 使用真实数据或默认值 + let activeBindings = realData?.activeUsers || [] + let convertedBindings = realData?.convertedUsers || [] + let expiredBindings = realData?.expiredUsers || [] + + // 兼容旧字段名 + if (!activeBindings.length && realData?.activeBindings) { + activeBindings = realData.activeBindings + } + if (!convertedBindings.length && realData?.convertedBindings) { + convertedBindings = realData.convertedBindings + } + if (!expiredBindings.length && realData?.expiredBindings) { + expiredBindings = realData.expiredBindings + } + + const expiringCount = activeBindings.filter(b => b.daysRemaining <= 7 && b.daysRemaining > 0).length + + // 计算各类统计 + const bindingCount = realData?.bindingCount || activeBindings.length + const paidCount = realData?.paidCount || convertedBindings.length + const expiredCount = realData?.expiredCount || expiredBindings.length + const unboughtCount = bindingCount // 绑定中但未付款的 + + // 格式化用户数据 + const formatUser = (user, type) => ({ + id: user.id, + nickname: user.nickname || '用户' + (user.id || '').slice(-4), + avatar: user.avatar, + status: type, + daysRemaining: user.daysRemaining || 0, + bindingDate: user.bindingDate ? this.formatDate(user.bindingDate) : '--', + commission: user.commission || 0, + orderAmount: user.orderAmount || 0 + }) + + this.setData({ + isLoggedIn: true, + userInfo, + + // 核心可见数据 + bindingCount, + visitCount: realData?.visitCount || 0, + paidCount, + unboughtCount, + expiredCount, + + // 收益数据 + earnings: realData?.earnings || 0, + pendingEarnings: realData?.pendingEarnings || 0, + withdrawnEarnings: realData?.withdrawnEarnings || 0, + shareRate: realData?.shareRate || 90, + + // 统计 + referralCount: realData?.referralCount || realData?.stats?.totalBindings || activeBindings.length + convertedBindings.length, + expiringCount, + + referralCode, + activeBindings: activeBindings.map(u => formatUser(u, 'active')), + convertedBindings: convertedBindings.map(u => formatUser(u, 'converted')), + expiredBindings: expiredBindings.map(u => formatUser(u, 'expired')), + currentBindings: activeBindings.map(u => formatUser(u, 'active')), + totalBindings: activeBindings.length + convertedBindings.length + expiredBindings.length, + + // 收益明细 + earningsDetails: realData?.earningsDetails || [] + }) + } + }, + + // 切换Tab + switchTab(e) { + const tab = e.currentTarget.dataset.tab + let currentBindings = [] + + if (tab === 'active') { + currentBindings = this.data.activeBindings + } else if (tab === 'converted') { + currentBindings = this.data.convertedBindings + } else { + currentBindings = this.data.expiredBindings + } + + this.setData({ activeTab: tab, currentBindings }) + }, + + // 切换绑定列表显示 + toggleBindingList() { + this.setData({ showBindingList: !this.data.showBindingList }) + }, + + // 复制邀请链接 + copyLink() { + const link = `https://soul.quwanzhi.com/?ref=${this.data.referralCode}` + wx.setClipboardData({ + data: link, + success: () => wx.showToast({ title: '链接已复制', icon: 'success' }) + }) + }, + + // 生成推广海报 + async generatePoster() { + wx.showLoading({ title: '生成中...', mask: true }) + this.setData({ showPosterModal: true, isGeneratingPoster: true }) + + try { + const ctx = wx.createCanvasContext('promoPosterCanvas', this) + const { userInfo, earnings, referralCount, distributorShare } = this.data + const userId = userInfo?.id || '' + + // 获取小程序码(带推荐人参数) + let qrcodeImage = null + try { + // scene格式:ref=用户ID前20位 + const scene = userId ? `ref=${userId.slice(0,20)}` : 'ref=soul' + console.log('[Poster] 请求小程序码, scene:', scene) + + const qrRes = await app.request('/api/miniprogram/qrcode', { + method: 'POST', + data: { + scene, + page: 'pages/index/index', + width: 280 + } + }) + + console.log('[Poster] 小程序码响应:', qrRes?.success, qrRes?.image?.length) + + if (qrRes && qrRes.success && qrRes.image) { + qrcodeImage = qrRes.image + console.log('[Poster] 小程序码获取成功') + } else { + console.log('[Poster] 响应无效:', qrRes) + } + } catch (e) { + console.error('[Poster] 获取小程序码失败:', e) + } + + // 海报尺寸 300x450 + const width = 300 + const height = 450 + + // 背景渐变 + const grd = ctx.createLinearGradient(0, 0, 0, height) + grd.addColorStop(0, '#0f0c29') + grd.addColorStop(0.5, '#302b63') + grd.addColorStop(1, '#24243e') + ctx.setFillStyle(grd) + ctx.fillRect(0, 0, width, height) + + // 顶部装饰 + ctx.setFillStyle('#FFD700') + ctx.fillRect(0, 0, width, 5) + + // 标题 + ctx.setFillStyle('#FFD700') + ctx.setFontSize(20) + ctx.fillText('📚 Soul创业派对', 20, 45) + + // 副标题 + ctx.setFillStyle('rgba(255,255,255,0.8)') + ctx.setFontSize(12) + ctx.fillText('来自派对房的真实商业故事', 20, 70) + + // 书籍介绍区域 + ctx.setFillStyle('rgba(255,255,255,0.05)') + ctx.fillRect(15, 90, width - 30, 100) + + ctx.setFillStyle('#ffffff') + ctx.setFontSize(14) + ctx.fillText('✨ 62个真实商业案例', 25, 115) + ctx.fillText('💡 私域运营实战经验', 25, 140) + ctx.fillText('🎯 从0到1创业方法论', 25, 165) + + // 推广者信息 + ctx.setFillStyle('#00CED1') + ctx.setFontSize(13) + ctx.fillText(`推荐人: ${userInfo?.nickname || '创业者'}`, 20, 220) + + // 统计数据 + ctx.setFillStyle('rgba(255,255,255,0.6)') + ctx.setFontSize(11) + ctx.fillText(`已推荐 ${referralCount} 位好友阅读`, 20, 245) + + // 优惠信息 + ctx.setFillStyle('rgba(255,215,0,0.15)') + ctx.fillRect(15, 265, width - 30, 50) + ctx.setFillStyle('#FFD700') + ctx.setFontSize(14) + ctx.fillText('🎁 专属福利', 25, 290) + ctx.setFillStyle('#ffffff') + ctx.setFontSize(12) + ctx.fillText('通过此码购买立享5%优惠', 25, 308) + + // 底部区域 + ctx.setFillStyle('rgba(0,206,209,0.1)') + ctx.fillRect(0, height - 80, width, 80) + + // 底部提示 + ctx.setFillStyle('#ffffff') + ctx.setFontSize(13) + ctx.fillText('长按识别 立即购买', 20, height - 50) + ctx.setFillStyle('rgba(255,255,255,0.6)') + ctx.setFontSize(11) + ctx.fillText('扫码立即阅读', 20, height - 28) + + // 绘制小程序码 + const drawQRCode = () => { + return new Promise((resolve) => { + if (qrcodeImage) { + const fs = wx.getFileSystemManager() + const filePath = `${wx.env.USER_DATA_PATH}/qrcode_promo_${Date.now()}.png` + const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '') + + fs.writeFile({ + filePath, + data: base64Data, + encoding: 'base64', + success: () => { + ctx.drawImage(filePath, width - 75, height - 70, 60, 60) + resolve() + }, + fail: () => { + this.drawQRPlaceholder(ctx, width, height) + resolve() + } + }) + } else { + this.drawQRPlaceholder(ctx, width, height) + resolve() + } + }) + } + + await drawQRCode() + + ctx.draw(true, () => { + wx.hideLoading() + this.setData({ isGeneratingPoster: false }) + }) + } catch (e) { + console.error('生成海报失败:', e) + wx.hideLoading() + wx.showToast({ title: '生成失败', icon: 'none' }) + this.setData({ showPosterModal: false, isGeneratingPoster: false }) + } + }, + + // 绘制小程序码占位符 + drawQRPlaceholder(ctx, width, height) { + ctx.setFillStyle('#ffffff') + ctx.beginPath() + ctx.arc(width - 45, height - 40, 30, 0, Math.PI * 2) + ctx.fill() + ctx.setFillStyle('#00CED1') + ctx.setFontSize(9) + ctx.fillText('扫码', width - 52, height - 42) + ctx.fillText('购买', width - 52, height - 30) + }, + + // 关闭海报弹窗 + closePosterModal() { + this.setData({ showPosterModal: false }) + }, + + // 保存海报 + savePoster() { + wx.canvasToTempFilePath({ + canvasId: 'promoPosterCanvas', + success: (res) => { + wx.saveImageToPhotosAlbum({ + filePath: res.tempFilePath, + success: () => { + wx.showToast({ title: '已保存到相册', icon: 'success' }) + this.setData({ showPosterModal: false }) + }, + fail: (err) => { + if (err.errMsg.includes('auth deny')) { + wx.showModal({ + title: '提示', + content: '需要相册权限才能保存海报', + confirmText: '去设置', + success: (res) => { + if (res.confirm) { + wx.openSetting() + } + } + }) + } else { + wx.showToast({ title: '保存失败', icon: 'none' }) + } + } + }) + }, + fail: () => { + wx.showToast({ title: '生成图片失败', icon: 'none' }) + } + }, this) + }, + + // 阻止冒泡 + stopPropagation() {}, + + // 分享到朋友圈 - 随机文案 + shareToMoments() { + // 10条随机文案,基于书的内容 + const shareTexts = [ + `🔥 在派对房里听到的真实故事,比虚构的小说精彩100倍!\n\n电动车出租月入5万、私域一年赚1000万、一个人的公司月入10万...\n\n62个真实案例,搜"Soul创业派对"小程序看全部!\n\n#创业 #私域 #商业`, + + `💡 今天终于明白:会赚钱的人,都在用"流量杠杆"\n\n抖音、Soul、飞书...同一套内容,撬动不同平台的流量。\n\n《Soul创业派对》里的实战方法,受用终身!\n\n#流量 #副业 #创业派对`, + + `📚 一个70后大健康私域,一个月150万流水是怎么做到的?\n\n答案在《Soul创业派对》第9章,全是干货。\n\n搜小程序"Soul创业派对",我在里面等你\n\n#大健康 #私域运营 #真实案例`, + + `🎯 "分钱不是分你的钱,是分不属于对方的钱"\n\n这句话改变了我对商业合作的认知。\n\n推荐《Soul创业派对》,创业者必读!\n\n#云阿米巴 #商业思维 #创业`, + + `✨ 资源整合高手的社交方法论,在派对房里学到了\n\n"先让对方赚到钱,自己才能长久赚钱"\n\n这本《Soul创业派对》,每章都是实战经验\n\n#资源整合 #社交 #创业故事`, + + `🚀 AI工具推广:一个隐藏的高利润赛道\n\n客单价高、复购率高、需求旺盛...\n\n《Soul创业派对》里的商业机会,你发现了吗?\n\n#AI #副业 #商业机会`, + + `💰 美业整合:一个人的公司如何月入十万?\n\n不开店、不囤货、轻资产运营...\n\n《Soul创业派对》告诉你答案!\n\n#美业 #轻创业 #月入十万`, + + `🌟 3000万流水是怎么跑出来的?\n\n不是靠运气,是靠系统。\n\n《Soul创业派对》里的电商底层逻辑,值得反复看\n\n#电商 #创业 #商业系统`, + + `📖 "人与人之间的关系,归根结底就三个东西:利益、情感、价值观"\n\n在派对房里聊出的金句,都在《Soul创业派对》里\n\n#人性 #商业 #创业派对`, + + `🔔 未来职业的三个方向:技术型、资源型、服务型\n\n你属于哪一种?\n\n《Soul创业派对》帮你找到答案!\n\n#职业规划 #创业 #未来` + ] + + // 随机选择一条文案 + const randomIndex = Math.floor(Math.random() * shareTexts.length) + const shareText = shareTexts[randomIndex] + + wx.setClipboardData({ + data: shareText, + success: () => { + wx.showModal({ + title: '文案已复制', + content: '请打开微信朋友圈,粘贴分享文案,配合推广海报一起发布效果更佳!\n\n再次点击可获取新的随机文案', + showCancel: false, + confirmText: '去发朋友圈' + }) + } + }) + }, + + // 提现 - 直接到微信零钱(无门槛) + async handleWithdraw() { + const pendingEarnings = parseFloat(this.data.pendingEarnings) || 0 + + if (pendingEarnings <= 0) { + wx.showToast({ title: '暂无可提现收益', icon: 'none' }) + return + } + + // 确认提现 + wx.showModal({ + title: '确认提现', + content: `将提现 ¥${pendingEarnings.toFixed(2)} 到您的微信零钱`, + confirmText: '立即提现', + success: async (res) => { + if (res.confirm) { + await this.doWithdraw(pendingEarnings) + } + } + }) + }, + + // 执行提现 + async doWithdraw(amount) { + wx.showLoading({ title: '提现中...' }) + + try { + const userId = app.globalData.userInfo?.id + if (!userId) { + wx.hideLoading() + wx.showToast({ title: '请先登录', icon: 'none' }) + return + } + + const res = await app.request('/api/withdraw', { + method: 'POST', + data: { userId, amount } + }) + + wx.hideLoading() + + if (res.success) { + wx.showModal({ + title: '提现成功 🎉', + content: `¥${amount.toFixed(2)} 已到账您的微信零钱`, + showCancel: false, + confirmText: '好的' + }) + + // 刷新数据 + this.initData() + } else { + if (res.needBind) { + wx.showModal({ + title: '需要绑定微信', + content: '请先在设置中绑定微信账号后再提现', + confirmText: '去绑定', + success: (modalRes) => { + if (modalRes.confirm) { + wx.navigateTo({ url: '/pages/settings/settings' }) + } + } + }) + } else { + wx.showToast({ title: res.error || '提现失败', icon: 'none' }) + } + } + } catch (e) { + wx.hideLoading() + console.error('[Referral] 提现失败:', e) + wx.showToast({ title: '提现失败,请重试', icon: 'none' }) + } + }, + + // 显示通知 + showNotification() { + wx.showToast({ title: '暂无新消息', icon: 'none' }) + }, + + // 显示设置 + showSettings() { + wx.showActionSheet({ + itemList: ['自动提现设置', '收益通知设置'], + success: (res) => { + if (res.tapIndex === 0) { + wx.showToast({ title: '自动提现功能开发中', icon: 'none' }) + } else { + wx.showToast({ title: '通知设置开发中', icon: 'none' }) + } + } + }) + }, + + // 分享 - 带推荐码 + onShareAppMessage() { + return { + title: '📚 Soul创业派对 - 来自派对房的真实商业故事', + path: `/pages/index/index?ref=${this.data.referralCode}`, + imageUrl: '/assets/share-cover.png' + } + }, + + // 分享到朋友圈 + onShareTimeline() { + return { + title: `Soul创业派对 - 62个真实商业案例`, + query: `ref=${this.data.referralCode}` + } + }, + + goBack() { + wx.navigateBack() + }, + + // 格式化日期 + formatDate(dateStr) { + if (!dateStr) return '--' + const d = new Date(dateStr) + const month = (d.getMonth() + 1).toString().padStart(2, '0') + const day = d.getDate().toString().padStart(2, '0') + return `${month}-${day}` + } +}) diff --git a/Tminiprogram/pages/referral/referral.json b/Tminiprogram/pages/referral/referral.json new file mode 100644 index 00000000..e90e9960 --- /dev/null +++ b/Tminiprogram/pages/referral/referral.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationStyle": "custom" +} diff --git a/Tminiprogram/pages/referral/referral.wxml b/Tminiprogram/pages/referral/referral.wxml new file mode 100644 index 00000000..b7821506 --- /dev/null +++ b/Tminiprogram/pages/referral/referral.wxml @@ -0,0 +1,223 @@ + + + + + + + + 推广中心 + + 🔔 + ⚙️ + + + + + + + + + + + + + + + + + + 💰 + + 累计收益 + {{shareRate}}% 返利 + + + + ¥{{earnings}} + 待结算: ¥{{pendingEarnings}} + + + + 已提现: ¥{{withdrawnEarnings}} + + + {{pendingEarnings <= 0 ? '暂无收益' : '立即提现 ¥' + pendingEarnings}} + + + + + + + + {{bindingCount}} + 绑定中 + 当前有效绑定 + + + {{paidCount}} + 已付款 + 成功转化 + + + {{unboughtCount}} + 待购买 + 绑定未付款 + + + {{expiredCount}} + 已过期 + 绑定已失效 + + + + + + 总访问量 + {{visitCount}} + 人通过你的链接进入 + + + + + + 📋 + 推广规则 + + + 链接带ID:谁发的链接,进的人就绑谁 + 一级、一月:只有一级分销,绑定有效期30天 + 长期不发:别人发得多,过期后客户会被「抢走」 + 每天发:持续发的人绑定续期,收益越来越高 + {{shareRate}}%给分发:好友付款后,你得 {{shareRate}}% 收益 + + + + + + + + 👥 + 绑定用户 + ({{totalBindings}}) + + {{showBindingList ? '▲' : '▼'}} + + + + + + 绑定中 ({{activeBindings.length}}) + 已付款 ({{convertedBindings.length}}) + 已过期 ({{expiredBindings.length}}) + + + + + + + 👤 + 暂无用户 + + + + + + + + {{item.nickname[0] || '用'}} + + + + + +¥{{item.commission}} + 订单 ¥{{item.orderAmount}} + + + + {{item.status === 'expired' ? '已过期' : item.daysRemaining + '天'}} + + + + + + + + + + + + + + + + + + 推广海报 + + + + + + + + + + + 💾 + 保存到相册 + + + + 保存后可分享到朋友圈或发送给好友 + + + diff --git a/Tminiprogram/pages/referral/referral.wxss b/Tminiprogram/pages/referral/referral.wxss new file mode 100644 index 00000000..4b286d60 --- /dev/null +++ b/Tminiprogram/pages/referral/referral.wxss @@ -0,0 +1,148 @@ +/* 分销中心页面样式 - 1:1还原Web版本 */ +.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; } + +/* 导航栏 */ +.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; } +.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; } +.back-icon { font-size: 40rpx; color: rgba(255,255,255,0.6); font-weight: 300; } +.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; } +.nav-right { display: flex; gap: 16rpx; } +.nav-btn { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 28rpx; } + +.content { padding: 24rpx; width: 100%; box-sizing: border-box; } + +/* 过期提醒横幅 */ +.expiring-banner { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 24rpx; margin-bottom: 24rpx; } +.banner-icon { width: 80rpx; height: 80rpx; background: rgba(255,165,0,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 40rpx; flex-shrink: 0; } +.banner-content { flex: 1; } +.banner-title { font-size: 28rpx; font-weight: 500; color: #fff; display: block; } +.banner-desc { font-size: 24rpx; color: rgba(255,165,0,0.8); margin-top: 4rpx; display: block; } + +/* 收益卡片 */ +.earnings-card { position: relative; background: linear-gradient(135deg, rgba(0,206,209,0.15) 0%, rgba(32,178,170,0.1) 50%, rgba(0,139,139,0.05) 100%); border: 2rpx solid rgba(0,206,209,0.2); border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; overflow: hidden; width: 100%; box-sizing: border-box; } +.earnings-bg { position: absolute; top: -50rpx; right: -50rpx; width: 200rpx; height: 200rpx; background: rgba(0,206,209,0.1); border-radius: 50%; filter: blur(60rpx); } +.earnings-main { position: relative; } +.earnings-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 32rpx; } +.earnings-left { display: flex; align-items: center; gap: 16rpx; } +.wallet-icon { width: 80rpx; height: 80rpx; background: rgba(0,206,209,0.2); border-radius: 20rpx; display: flex; align-items: center; justify-content: center; font-size: 40rpx; } +.earnings-info { display: flex; flex-direction: column; gap: 4rpx; } +.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.6); } +.commission-rate { font-size: 24rpx; color: #00CED1; font-weight: 500; } +.earnings-right { text-align: right; } +.earnings-value { font-size: 56rpx; font-weight: 700; color: #fff; display: block; } +.pending-text { font-size: 22rpx; color: rgba(255,255,255,0.5); } + +.withdraw-btn { padding: 24rpx; background: #00CED1; color: #000; font-size: 28rpx; font-weight: 600; text-align: center; border-radius: 24rpx; } +.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.3); color: rgba(0,0,0,0.5); } + +/* 收益详情 */ +.earnings-detail { padding-top: 16rpx; border-top: 2rpx solid rgba(255,255,255,0.1); margin-bottom: 24rpx; } +.detail-item { font-size: 24rpx; color: rgba(255,255,255,0.5); } + +/* 核心数据统计 */ +.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; } +.stat-card { background: #1c1c1e; border-radius: 24rpx; padding: 28rpx 20rpx; text-align: center; position: relative; } +.stat-card.highlight { background: linear-gradient(135deg, rgba(0,206,209,0.1) 0%, rgba(0,206,209,0.05) 100%); border: 2rpx solid rgba(0,206,209,0.2); } +.stat-value { font-size: 48rpx; font-weight: 700; color: #fff; display: block; } +.stat-value.brand { color: #00CED1; } +.stat-value.gold { color: #FFD700; } +.stat-value.orange { color: #FFA500; } +.stat-value.gray { color: #9E9E9E; } +.stat-label { font-size: 24rpx; color: rgba(255,255,255,0.7); margin-top: 8rpx; display: block; font-weight: 500; } +.stat-tip { font-size: 20rpx; color: rgba(255,255,255,0.4); margin-top: 4rpx; display: block; } + +/* 访问量统计 */ +.visit-stat { display: flex; align-items: center; justify-content: center; gap: 12rpx; padding: 20rpx 32rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 24rpx; } +.visit-label { font-size: 24rpx; color: rgba(255,255,255,0.5); } +.visit-value { font-size: 32rpx; font-weight: 700; color: #00CED1; } +.visit-tip { font-size: 24rpx; color: rgba(255,255,255,0.5); } + +/* 推广规则 */ +.rules-card { background: rgba(0,206,209,0.05); border: 2rpx solid rgba(0,206,209,0.2); border-radius: 24rpx; padding: 24rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; } +.rules-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; } +.rules-icon { width: 56rpx; height: 56rpx; background: rgba(0,206,209,0.2); border-radius: 16rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; } +.rules-title { font-size: 28rpx; font-weight: 500; color: #fff; } +.rules-list { padding-left: 8rpx; } +.rule-item { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 2; display: block; margin-bottom: 4rpx; } +.rule-item .gold { color: #FFD700; font-weight: 500; } +.rule-item .brand { color: #00CED1; font-weight: 500; } +.rule-item .orange { color: #FFA500; font-weight: 500; } + +/* 绑定用户卡片 */ +.binding-card { background: #1c1c1e; border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; } +.binding-header { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); } +.binding-title { display: flex; align-items: center; gap: 12rpx; } +.binding-icon { font-size: 36rpx; } +.title-text { font-size: 30rpx; font-weight: 600; color: #fff; } +.binding-count { font-size: 26rpx; color: rgba(255,255,255,0.5); } +.toggle-icon { font-size: 24rpx; color: rgba(255,255,255,0.5); } + +/* Tab切换 */ +.binding-tabs { display: flex; border-bottom: 2rpx solid rgba(255,255,255,0.05); } +.tab-item { flex: 1; padding: 24rpx 0; text-align: center; font-size: 26rpx; color: rgba(255,255,255,0.5); position: relative; } +.tab-item.tab-active { color: #00CED1; } +.tab-item.tab-active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 80rpx; height: 4rpx; background: #00CED1; border-radius: 4rpx; } + +/* 用户列表 */ +.binding-list { max-height: 640rpx; overflow-y: auto; } +.empty-state { padding: 80rpx 0; text-align: center; } +.empty-icon { font-size: 64rpx; display: block; margin-bottom: 16rpx; } +.empty-text { font-size: 26rpx; color: rgba(255,255,255,0.5); } + +.binding-item { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); } +.binding-item:last-child { border-bottom: none; } +.user-avatar { width: 80rpx; height: 80rpx; border-radius: 50%; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; color: #00CED1; margin-right: 24rpx; flex-shrink: 0; } +.user-avatar.avatar-converted { background: rgba(76,175,80,0.2); color: #4CAF50; } +.user-avatar.avatar-expired { background: rgba(158,158,158,0.2); color: #9E9E9E; } +.user-info { flex: 1; } +.user-name { font-size: 28rpx; color: #fff; font-weight: 500; display: block; } +.user-time { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; } +.user-status { text-align: right; } +.status-amount { font-size: 28rpx; color: #4CAF50; font-weight: 600; display: block; } +.status-order { font-size: 22rpx; color: rgba(255,255,255,0.5); } +.status-tag { font-size: 22rpx; padding: 8rpx 16rpx; border-radius: 16rpx; } +.status-tag.tag-green { background: rgba(76,175,80,0.2); color: #4CAF50; } +.status-tag.tag-orange { background: rgba(255,165,0,0.2); color: #FFA500; } +.status-tag.tag-red { background: rgba(244,67,54,0.2); color: #F44336; } + +/* 邀请码卡片 */ +.invite-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; } +.invite-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx; } +.invite-title { font-size: 28rpx; font-weight: 600; color: #fff; } +.invite-code-box { background: rgba(0,206,209,0.15); padding: 12rpx 24rpx; border-radius: 16rpx; } +.invite-code { font-size: 26rpx; font-weight: 600; color: #00CED1; font-family: monospace; letter-spacing: 2rpx; } +.invite-tip { font-size: 24rpx; color: rgba(255,255,255,0.5); } +.invite-tip .gold { color: #FFD700; } +.invite-tip .brand { color: #00CED1; } + +/* 分享区域 */ +.share-section { display: flex; flex-direction: column; gap: 16rpx; width: 100%; } +.share-item { display: flex; align-items: center; background: #1c1c1e; border-radius: 24rpx; padding: 24rpx 32rpx; border: none; margin: 0; text-align: left; width: 100%; box-sizing: border-box; } +.share-item::after { border: none; } +.share-icon { width: 96rpx; height: 96rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; font-size: 48rpx; margin-right: 24rpx; flex-shrink: 0; } +.share-icon.poster { background: rgba(103,58,183,0.2); } +.share-icon.wechat { background: rgba(7,193,96,0.2); } +.share-icon.link { background: rgba(158,158,158,0.2); } +.share-info { flex: 1; text-align: left; } +.share-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; text-align: left; } +.share-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; text-align: left; } +.share-arrow { font-size: 28rpx; color: rgba(255,255,255,0.3); flex-shrink: 0; } +.share-btn-wechat { line-height: normal; font-size: inherit; padding: 24rpx 32rpx !important; margin: 0 !important; width: 100% !important; } + +/* 弹窗 */ +.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(20rpx); display: flex; align-items: flex-end; justify-content: center; z-index: 1000; } +.modal-content { width: 100%; max-width: 750rpx; background: #1c1c1e; border-radius: 48rpx 48rpx 0 0; padding: 48rpx; padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); animation: slideUp 0.3s ease; } +@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } +.modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32rpx; } +.modal-title { font-size: 36rpx; font-weight: 600; color: #fff; } +.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: rgba(255,255,255,0.6); } + +/* 海报弹窗 */ +.poster-modal { padding-bottom: calc(64rpx + env(safe-area-inset-bottom)); } +.poster-preview { display: flex; justify-content: center; margin: 32rpx 0; padding: 24rpx; background: rgba(0,0,0,0.3); border-radius: 24rpx; } +.poster-canvas { border-radius: 16rpx; box-shadow: 0 16rpx 48rpx rgba(0,0,0,0.5); } +.poster-actions { display: flex; gap: 24rpx; margin-bottom: 24rpx; } +.poster-btn { flex: 1; display: flex; align-items: center; justify-content: center; gap: 12rpx; padding: 28rpx; border-radius: 24rpx; font-size: 30rpx; font-weight: 500; color: #fff; } +.btn-save { background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); } +.btn-icon { font-size: 32rpx; } +.poster-tip { font-size: 24rpx; color: rgba(255,255,255,0.4); text-align: center; display: block; } diff --git a/Tminiprogram/pages/search/search.js b/Tminiprogram/pages/search/search.js new file mode 100644 index 00000000..3bafcb8c --- /dev/null +++ b/Tminiprogram/pages/search/search.js @@ -0,0 +1,109 @@ +/** + * Soul创业派对 - 章节搜索页 + * 搜索章节标题和内容 + */ +const app = getApp() + +Page({ + data: { + statusBarHeight: 44, + keyword: '', + results: [], + loading: false, + searched: false, + total: 0, + // 热门搜索关键词 + hotKeywords: ['私域', '电商', '流量', '赚钱', '创业', 'Soul', '抖音', '变现'], + // 热门章节推荐 + hotChapters: [ + { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', part: '真实的人' }, + { id: '9.12', title: '美业整合:一个人的公司如何月入十万', tag: '热门', part: '真实的赚钱' }, + { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', part: '真实的行业' }, + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', part: '真实的赚钱' }, + { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', tag: '最新', part: '真实的赚钱' } + ] + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight || 44 + }) + // 加载热门章节 + this.loadHotChapters() + }, + + // 加载热门章节(从服务器获取点击量高的章节) + async loadHotChapters() { + try { + const res = await app.request('/api/book/hot') + if (res && res.success && res.chapters?.length > 0) { + this.setData({ hotChapters: res.chapters }) + } + } catch (e) { + console.log('加载热门章节失败,使用默认数据') + } + }, + + // 输入关键词 + onInput(e) { + this.setData({ keyword: e.detail.value }) + }, + + // 清空搜索 + clearSearch() { + this.setData({ + keyword: '', + results: [], + searched: false, + total: 0 + }) + }, + + // 点击热门关键词 + onHotKeyword(e) { + const keyword = e.currentTarget.dataset.keyword + this.setData({ keyword }) + this.doSearch() + }, + + // 执行搜索 + async doSearch() { + const { keyword } = this.data + if (!keyword || keyword.trim().length < 1) { + wx.showToast({ title: '请输入搜索关键词', icon: 'none' }) + return + } + + this.setData({ loading: true, searched: true }) + + try { + const res = await app.request(`/api/book/search?q=${encodeURIComponent(keyword.trim())}`) + + if (res && res.success) { + this.setData({ + results: res.results || [], + total: res.total || 0 + }) + } else { + this.setData({ results: [], total: 0 }) + } + } catch (e) { + console.error('搜索失败:', e) + wx.showToast({ title: '搜索失败', icon: 'none' }) + this.setData({ results: [], total: 0 }) + } finally { + this.setData({ loading: false }) + } + }, + + // 跳转阅读 + goToRead(e) { + const id = e.currentTarget.dataset.id + wx.navigateTo({ url: `/pages/read/read?id=${id}` }) + }, + + // 返回上一页 + goBack() { + wx.navigateBack() + } +}) diff --git a/Tminiprogram/pages/search/search.json b/Tminiprogram/pages/search/search.json new file mode 100644 index 00000000..877955df --- /dev/null +++ b/Tminiprogram/pages/search/search.json @@ -0,0 +1,5 @@ +{ + "usingComponents": {}, + "navigationStyle": "custom", + "navigationBarTitleText": "搜索" +} diff --git a/Tminiprogram/pages/search/search.wxml b/Tminiprogram/pages/search/search.wxml new file mode 100644 index 00000000..60fa91fe --- /dev/null +++ b/Tminiprogram/pages/search/search.wxml @@ -0,0 +1,113 @@ + + + + + + + + + + + 🔍 + + × + + 搜索 + + + + + + + + + 热门搜索 + + {{item}} + + + + + + 热门章节 + + + {{index + 1}} + + {{item.title}} + {{item.part}} + + {{item.tag}} + + + + + + + + + + 搜索中... + + + + + + 找到 {{total}} 个结果 + + + + + + {{item.chapterLabel}} + + 标题匹配 + 内容匹配 + 免费 + + + {{item.title}} + {{item.part}} + + {{item.matchedContent}} + + + + + + + + + 🔍 + 未找到相关章节 + 换个关键词试试 + + + + diff --git a/Tminiprogram/pages/search/search.wxss b/Tminiprogram/pages/search/search.wxss new file mode 100644 index 00000000..aa56b19b --- /dev/null +++ b/Tminiprogram/pages/search/search.wxss @@ -0,0 +1,335 @@ +/* 章节搜索页样式 */ +.page { + min-height: 100vh; + background: linear-gradient(180deg, #0a0a0a 0%, #111111 100%); +} + +/* 导航栏 */ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + background: rgba(10, 10, 10, 0.95); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.nav-content { + display: flex; + align-items: center; + padding: 8rpx 24rpx; + height: 88rpx; +} + +.back-btn { + width: 60rpx; + height: 60rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.back-icon { + font-size: 40rpx; + color: #00CED1; +} + +.search-input-wrap { + flex: 1; + display: flex; + align-items: center; + background: rgba(255,255,255,0.08); + border-radius: 40rpx; + padding: 0 24rpx; + height: 64rpx; + margin: 0 16rpx; +} + +.search-icon-small { + font-size: 28rpx; + margin-right: 12rpx; +} + +.search-input { + flex: 1; + font-size: 28rpx; + color: #fff; +} + +.search-input::placeholder { + color: rgba(255,255,255,0.4); +} + +.clear-btn { + width: 40rpx; + height: 40rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: rgba(255,255,255,0.5); +} + +.search-btn { + font-size: 28rpx; + color: #00CED1; + padding: 0 16rpx; +} + +/* 主内容 */ +.main-content { + padding: 24rpx; +} + +/* 热门搜索 */ +.hot-section { + padding: 24rpx 0; +} + +.section-title { + font-size: 28rpx; + color: rgba(255,255,255,0.6); + margin-bottom: 24rpx; + display: block; +} + +.hot-tags { + display: flex; + flex-wrap: wrap; + gap: 20rpx; +} + +.hot-tag { + background: rgba(0, 206, 209, 0.15); + color: #00CED1; + padding: 16rpx 32rpx; + border-radius: 32rpx; + font-size: 28rpx; + border: 1rpx solid rgba(0, 206, 209, 0.3); +} + +/* 热门章节 */ +.hot-chapters { + padding: 32rpx 0; + margin-top: 16rpx; +} + +.chapter-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.chapter-item { + display: flex; + align-items: center; + background: rgba(255,255,255,0.05); + border-radius: 20rpx; + padding: 24rpx; + gap: 20rpx; +} + +.chapter-rank { + width: 48rpx; + height: 48rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 26rpx; + font-weight: 600; + color: #000; + flex-shrink: 0; +} + +.chapter-item:nth-child(1) .chapter-rank { background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); } +.chapter-item:nth-child(2) .chapter-rank { background: linear-gradient(135deg, #C0C0C0 0%, #A9A9A9 100%); } +.chapter-item:nth-child(3) .chapter-rank { background: linear-gradient(135deg, #CD7F32 0%, #8B4513 100%); } + +.chapter-info { + flex: 1; + min-width: 0; +} + +.chapter-title { + font-size: 28rpx; + color: #fff; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.chapter-part { + font-size: 22rpx; + color: rgba(255,255,255,0.4); + margin-top: 6rpx; + display: block; +} + +.chapter-tag { + padding: 8rpx 16rpx; + border-radius: 16rpx; + font-size: 22rpx; + flex-shrink: 0; +} + +.chapter-tag.tag-free { background: rgba(76, 175, 80, 0.2); color: #4CAF50; } +.chapter-tag.tag-hot { background: rgba(255, 87, 34, 0.2); color: #FF5722; } +.chapter-tag.tag-new { background: rgba(233, 30, 99, 0.2); color: #E91E63; } + +/* 搜索结果 */ +.results-section { + padding: 16rpx 0; +} + +.results-header { + margin-bottom: 24rpx; +} + +.results-count { + font-size: 26rpx; + color: rgba(255,255,255,0.5); +} + +.results-list { + display: flex; + flex-direction: column; + gap: 24rpx; +} + +.result-item { + background: rgba(255,255,255,0.05); + border-radius: 24rpx; + padding: 28rpx; + position: relative; + border: 1rpx solid rgba(255,255,255,0.08); +} + +.result-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16rpx; +} + +.result-chapter { + font-size: 24rpx; + color: #00CED1; + font-weight: 500; +} + +.result-tags { + display: flex; + gap: 12rpx; +} + +.tag { + font-size: 20rpx; + padding: 6rpx 16rpx; + border-radius: 20rpx; +} + +.tag-match { + background: rgba(147, 112, 219, 0.2); + color: #9370DB; +} + +.tag-free { + background: rgba(76, 175, 80, 0.2); + color: #4CAF50; +} + +.result-title { + font-size: 30rpx; + color: #fff; + font-weight: 500; + line-height: 1.5; + display: block; + margin-bottom: 8rpx; +} + +.result-part { + font-size: 24rpx; + color: rgba(255,255,255,0.5); + display: block; +} + +.result-content { + margin-top: 16rpx; + padding-top: 16rpx; + border-top: 1rpx solid rgba(255,255,255,0.1); +} + +.content-preview { + font-size: 24rpx; + color: rgba(255,255,255,0.6); + line-height: 1.6; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.result-arrow { + position: absolute; + right: 28rpx; + top: 50%; + transform: translateY(-50%); + font-size: 32rpx; + color: rgba(255,255,255,0.3); +} + +/* 加载状态 */ +.loading-wrap { + display: flex; + flex-direction: column; + align-items: center; + padding: 100rpx 0; +} + +.loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid rgba(0, 206, 209, 0.3); + border-top-color: #00CED1; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.loading-text { + margin-top: 24rpx; + font-size: 28rpx; + color: rgba(255,255,255,0.5); +} + +/* 空状态 */ +.empty-wrap { + display: flex; + flex-direction: column; + align-items: center; + padding: 100rpx 0; +} + +.empty-icon { + font-size: 80rpx; + margin-bottom: 24rpx; +} + +.empty-text { + font-size: 32rpx; + color: rgba(255,255,255,0.6); + margin-bottom: 12rpx; +} + +.empty-hint { + font-size: 26rpx; + color: rgba(255,255,255,0.4); +} diff --git a/Tminiprogram/pages/settings/settings.js b/Tminiprogram/pages/settings/settings.js new file mode 100644 index 00000000..d6539924 --- /dev/null +++ b/Tminiprogram/pages/settings/settings.js @@ -0,0 +1,452 @@ +/** + * Soul创业派对 - 设置页 + * 账号绑定功能 + */ +const app = getApp() + +Page({ + data: { + statusBarHeight: 44, + isLoggedIn: false, + userInfo: null, + version: '1.0.0', + + // 绑定信息 + phoneNumber: '', + wechatId: '', + alipayAccount: '', + address: '', + + // 自动提现(默认开启) + autoWithdrawEnabled: true, + + // 绑定弹窗 + showBindModal: false, + bindType: '', // phone | wechat | alipay + bindValue: '' + }, + + onLoad() { + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + isLoggedIn: app.globalData.isLoggedIn, + userInfo: app.globalData.userInfo + }) + this.loadBindingInfo() + }, + + onShow() { + this.loadBindingInfo() + }, + + // 加载绑定信息 + loadBindingInfo() { + const { userInfo, isLoggedIn } = app.globalData + if (isLoggedIn && userInfo) { + // 从本地存储或用户信息中获取绑定数据 + const phoneNumber = wx.getStorageSync('user_phone') || userInfo.phone || '' + const wechatId = wx.getStorageSync('user_wechat') || userInfo.wechat || '' + const alipayAccount = wx.getStorageSync('user_alipay') || userInfo.alipay || '' + const address = wx.getStorageSync('user_address') || userInfo.address || '' + // 默认开启自动提现 + const autoWithdrawEnabled = wx.getStorageSync('auto_withdraw_enabled') !== false + + this.setData({ + isLoggedIn: true, + userInfo, + phoneNumber, + wechatId, + alipayAccount, + address, + autoWithdrawEnabled + }) + } + }, + + // 一键获取收货地址 + 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/user/update', { + method: 'POST', + data: { userId, address } + }) + console.log('[Settings] 地址已同步到服务器') + } catch (e) { + console.log('[Settings] 同步地址失败:', e) + } + }, + + // 切换自动提现 + async toggleAutoWithdraw(e) { + const enabled = e.detail.value + + // 检查是否绑定了支付方式 + if (enabled && !this.data.wechatId && !this.data.alipayAccount) { + wx.showToast({ title: '请先绑定微信号或支付宝', icon: 'none' }) + this.setData({ autoWithdrawEnabled: false }) + return + } + + // 开启时需要确认 + if (enabled) { + wx.showModal({ + title: '开启自动提现', + content: `收益将自动打款到您的${this.data.alipayAccount ? '支付宝' : '微信'}账户,确认开启吗?`, + success: async (res) => { + if (res.confirm) { + this.setData({ autoWithdrawEnabled: true }) + wx.setStorageSync('auto_withdraw_enabled', true) + + // 同步到服务器 + try { + await app.request('/api/user/update', { + method: 'POST', + data: { + userId: app.globalData.userInfo?.id, + autoWithdraw: true, + withdrawAccount: this.data.alipayAccount || this.data.wechatId + } + }) + } catch (e) { + console.log('同步自动提现设置失败', e) + } + + wx.showToast({ title: '已开启自动提现', icon: 'success' }) + } else { + this.setData({ autoWithdrawEnabled: false }) + } + } + }) + } else { + this.setData({ autoWithdrawEnabled: false }) + wx.setStorageSync('auto_withdraw_enabled', false) + wx.showToast({ title: '已关闭自动提现', icon: 'success' }) + } + }, + + // 绑定手机号 + bindPhone() { + this.setData({ + showBindModal: true, + bindType: 'phone', + bindValue: '' + }) + }, + + // 微信号输入 + onWechatInput(e) { + this.setData({ wechatId: e.detail.value }) + }, + + // 保存微信号 + async saveWechat() { + const { wechatId } = this.data + if (!wechatId || wechatId.length < 6) return + + wx.setStorageSync('user_wechat', wechatId) + + // 更新用户信息 + if (app.globalData.userInfo) { + app.globalData.userInfo.wechat = wechatId + wx.setStorageSync('userInfo', app.globalData.userInfo) + } + + // 同步到服务器 + try { + await app.request('/api/user/update', { + method: 'POST', + data: { + userId: app.globalData.userInfo?.id, + wechat: wechatId + } + }) + wx.showToast({ title: '微信号已保存', icon: 'success' }) + } catch (e) { + console.log('保存微信号失败', e) + } + }, + + // 输入绑定值 + onBindInput(e) { + let value = e.detail.value + if (this.data.bindType === 'phone') { + value = value.replace(/\D/g, '').slice(0, 11) + } + this.setData({ bindValue: value }) + }, + + // 确认绑定 + confirmBind() { + const { bindType, bindValue } = this.data + + if (!bindValue) { + wx.showToast({ title: '请输入内容', icon: 'none' }) + return + } + + // 验证 + if (bindType === 'phone' && !/^1[3-9]\d{9}$/.test(bindValue)) { + wx.showToast({ title: '请输入正确的手机号', icon: 'none' }) + return + } + + if (bindType === 'wechat' && bindValue.length < 6) { + wx.showToast({ title: '微信号至少6位', icon: 'none' }) + return + } + + if (bindType === 'alipay' && !bindValue.includes('@') && !/^1[3-9]\d{9}$/.test(bindValue)) { + wx.showToast({ title: '请输入正确的支付宝账号', icon: 'none' }) + return + } + + // 保存绑定信息到本地 + if (bindType === 'phone') { + wx.setStorageSync('user_phone', bindValue) + this.setData({ phoneNumber: bindValue }) + } else if (bindType === 'wechat') { + wx.setStorageSync('user_wechat', bindValue) + this.setData({ wechatId: bindValue }) + } else if (bindType === 'alipay') { + wx.setStorageSync('user_alipay', bindValue) + this.setData({ alipayAccount: bindValue }) + } + + // 同步到服务器 + this.syncProfileToServer() + + this.setData({ showBindModal: false }) + wx.showToast({ title: '绑定成功', icon: 'success' }) + }, + + // 同步资料到服务器 + async syncProfileToServer() { + try { + const userId = app.globalData.userInfo?.id + if (!userId) return + + const res = await app.request('/api/user/profile', { + method: 'POST', + data: { + userId, + phone: this.data.phoneNumber || undefined, + wechatId: this.data.wechatId || undefined + } + }) + + if (res.success) { + console.log('[Settings] 资料同步成功') + // 更新本地用户信息 + if (app.globalData.userInfo) { + app.globalData.userInfo.phone = this.data.phoneNumber + app.globalData.userInfo.wechatId = this.data.wechatId + wx.setStorageSync('userInfo', app.globalData.userInfo) + } + } + } catch (e) { + console.log('[Settings] 资料同步失败:', e) + } + }, + + // 获取微信头像(新版授权) + async getWechatAvatar() { + try { + const res = await wx.getUserProfile({ + desc: '用于完善会员资料' + }) + + if (res.userInfo) { + const { nickName, avatarUrl } = res.userInfo + + // 更新本地 + this.setData({ + userInfo: { + ...this.data.userInfo, + nickname: nickName, + avatar: avatarUrl + } + }) + + // 同步到服务器 + const userId = app.globalData.userInfo?.id + if (userId) { + await app.request('/api/user/profile', { + method: 'POST', + data: { userId, nickname: nickName, avatar: avatarUrl } + }) + } + + // 更新全局 + if (app.globalData.userInfo) { + app.globalData.userInfo.nickname = nickName + app.globalData.userInfo.avatar = avatarUrl + wx.setStorageSync('userInfo', app.globalData.userInfo) + } + + wx.showToast({ title: '头像更新成功', icon: 'success' }) + } + } catch (e) { + console.log('[Settings] 获取头像失败:', e) + wx.showToast({ title: '获取头像失败', icon: 'none' }) + } + }, + + // 一键获取微信手机号(button组件回调) + async onGetPhoneNumber(e) { + console.log('[Settings] 获取手机号回调:', e.detail) + + if (e.detail.errMsg !== 'getPhoneNumber:ok') { + wx.showToast({ title: '授权失败', icon: 'none' }) + return + } + + try { + // 需要将code发送到服务器解密获取手机号 + const code = e.detail.code + if (!code) { + // 如果没有code,弹出手动输入 + this.bindPhone() + return + } + + wx.showLoading({ title: '获取中...', mask: true }) + + // 调用服务器解密手机号(传入userId以便同步到数据库) + const userId = app.globalData.userInfo?.id + const res = await app.request('/api/miniprogram/phone', { + method: 'POST', + data: { code, userId } + }) + + wx.hideLoading() + + if (res.success && res.phoneNumber) { + wx.setStorageSync('user_phone', res.phoneNumber) + this.setData({ phoneNumber: res.phoneNumber }) + + // 更新用户信息 + if (app.globalData.userInfo) { + app.globalData.userInfo.phone = res.phoneNumber + wx.setStorageSync('userInfo', app.globalData.userInfo) + } + + // 同步到服务器 + this.syncProfileToServer() + + wx.showToast({ title: '手机号绑定成功', icon: 'success' }) + } else { + // 获取失败,弹出手动输入 + this.bindPhone() + } + } catch (e) { + wx.hideLoading() + console.log('[Settings] 获取手机号失败:', e) + // 获取失败,弹出手动输入 + this.bindPhone() + } + }, + + // 关闭绑定弹窗 + closeBindModal() { + this.setData({ showBindModal: false }) + }, + + // 清除缓存 + clearCache() { + wx.showModal({ + title: '清除缓存', + content: '确定要清除本地缓存吗?', + success: (res) => { + if (res.confirm) { + // 保留登录信息,只清除其他缓存 + const token = wx.getStorageSync('token') + const userInfo = wx.getStorageSync('userInfo') + wx.clearStorageSync() + if (token) wx.setStorageSync('token', token) + if (userInfo) wx.setStorageSync('userInfo', userInfo) + wx.showToast({ title: '缓存已清除', icon: 'success' }) + } + } + }) + }, + + // 退出登录 + handleLogout() { + wx.showModal({ + title: '退出登录', + content: '确定要退出登录吗?', + success: (res) => { + if (res.confirm) { + app.logout() + this.setData({ + isLoggedIn: false, + userInfo: null, + phoneNumber: '', + wechatId: '', + alipayAccount: '' + }) + wx.showToast({ title: '已退出登录', icon: 'success' }) + setTimeout(() => wx.navigateBack(), 1500) + } + } + }) + }, + + // 联系客服 - 跳转到Soul派对房 + contactService() { + wx.showToast({ title: '请在Soul派对房联系客服', icon: 'none' }) + }, + + // 阻止冒泡 + stopPropagation() {}, + + goBack() { wx.navigateBack() } +}) diff --git a/Tminiprogram/pages/settings/settings.json b/Tminiprogram/pages/settings/settings.json new file mode 100644 index 00000000..e90e9960 --- /dev/null +++ b/Tminiprogram/pages/settings/settings.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationStyle": "custom" +} diff --git a/Tminiprogram/pages/settings/settings.wxml b/Tminiprogram/pages/settings/settings.wxml new file mode 100644 index 00000000..d11b6e59 --- /dev/null +++ b/Tminiprogram/pages/settings/settings.wxml @@ -0,0 +1,147 @@ + + + + + + + 设置 + + + + + + + + + 🛡️ + + 账号绑定 + 绑定后可用于提现和找伙伴功能 + + + + + + + + 📱 + + 手机号 + {{phoneNumber || '未绑定'}} + + + + + + + + + + + + 💬 + + 微信号 + + + + + + + + + + + + 📍 + + 收货地址 + {{address || '未绑定'}} + + + + + 一键获取 + + + + + + + + + 💰 + + 自动提现 + 收益自动打款到微信零钱 + + + + + + 开启自动提现 + + + + + + 提现方式 + 微信零钱 + + + 提现账户 + {{wechatId}} + + 收益将在每笔订单完成后自动打款 + + + + + + + 提示:绑定微信号才能使用提现功能 + + + 退出登录 + + + + + + + 绑定{{bindType === 'phone' ? '手机号' : bindType === 'wechat' ? '微信号' : '支付宝'}} + + + + + + + + + + {{bindType === 'phone' ? '绑定手机号后可用于找伙伴匹配' : bindType === 'wechat' ? '绑定微信号后可用于找伙伴匹配和好友添加' : '绑定支付宝后可用于提现收益'}} + + + + 确认绑定 + + + + + diff --git a/Tminiprogram/pages/settings/settings.wxss b/Tminiprogram/pages/settings/settings.wxss new file mode 100644 index 00000000..23f92fae --- /dev/null +++ b/Tminiprogram/pages/settings/settings.wxss @@ -0,0 +1,112 @@ +/* 设置页样式 */ +.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; } + +/* 导航栏 */ +.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; } +.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; } +.back-icon { font-size: 40rpx; color: rgba(255,255,255,0.6); font-weight: 300; } +.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; } +.nav-placeholder { width: 64rpx; } + +.content { padding: 24rpx; } + +/* 账号绑定卡片 */ +.bind-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); } +.card-header { display: flex; align-items: flex-start; gap: 16rpx; margin-bottom: 24rpx; padding-bottom: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); } +.card-icon { font-size: 40rpx; } +.card-title-wrap { flex: 1; } +.card-title { font-size: 30rpx; font-weight: 600; color: #fff; display: block; } +.card-desc { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; } + +.bind-list { display: flex; flex-direction: column; gap: 24rpx; } +.bind-item { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0; } +.bind-left { display: flex; align-items: center; gap: 20rpx; } +.bind-icon { width: 72rpx; height: 72rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; } +.bind-icon.phone-icon { background: rgba(0,206,209,0.2); } +.bind-icon.wechat-icon { background: rgba(158,158,158,0.2); } +.bind-icon.alipay-icon { background: rgba(158,158,158,0.2); } +.bind-info { display: flex; flex-direction: column; gap: 4rpx; flex: 1; } +.bind-label { font-size: 28rpx; color: #fff; font-weight: 500; } +.bind-value { font-size: 24rpx; color: rgba(255,255,255,0.5); } +.address-text { max-width: 360rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.bind-icon.address-icon { background: rgba(255,165,0,0.2); } +.required { color: #FF6B6B; font-size: 24rpx; } +.bind-input { font-size: 24rpx; color: #00CED1; background: transparent; padding: 8rpx 0; } +.bind-right { display: flex; align-items: center; } +.bind-check { color: #00CED1; font-size: 32rpx; } +.bind-btn { color: #00CED1; font-size: 26rpx; } + +/* 一键获取手机号按钮 */ +.get-phone-btn { + padding: 12rpx 24rpx; + background: rgba(0,206,209,0.2); + border: 2rpx solid rgba(0,206,209,0.3); + border-radius: 16rpx; + font-size: 24rpx; + color: #00CED1; + line-height: normal; +} +.get-phone-btn::after { border: none; } + +/* 自动提现卡片 */ +.auto-withdraw-card { margin-top: 24rpx; } +.auto-withdraw-content { padding-top: 16rpx; } +.withdraw-switch-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16rpx 0; +} +.switch-label { font-size: 28rpx; color: #fff; } +.withdraw-info { + background: rgba(0,206,209,0.1); + border-radius: 16rpx; + padding: 20rpx; + margin-top: 16rpx; +} +.info-item { + display: flex; + justify-content: space-between; + padding: 8rpx 0; +} +.info-label { font-size: 26rpx; color: rgba(255,255,255,0.6); } +.info-value { font-size: 26rpx; color: #00CED1; } +.withdraw-tip { + display: block; + font-size: 22rpx; + color: rgba(255,255,255,0.4); + margin-top: 12rpx; + text-align: center; +} + +/* 提现提示 */ +.tip-banner { background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 20rpx; padding: 20rpx 24rpx; margin-bottom: 24rpx; } +.tip-text { font-size: 24rpx; color: #FFA500; line-height: 1.5; } + +/* 设置组 */ +.settings-group { background: #1c1c1e; border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; } +.settings-item { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); } +.settings-item:last-child { border-bottom: none; } +.item-left { display: flex; align-items: center; gap: 16rpx; } +.item-icon { font-size: 36rpx; } +.item-title { font-size: 28rpx; color: #fff; } +.item-arrow { font-size: 28rpx; color: rgba(255,255,255,0.3); } +.item-value { font-size: 26rpx; color: rgba(255,255,255,0.5); } + +/* 退出登录按钮 */ +.logout-btn { margin-top: 48rpx; padding: 28rpx; background: rgba(244,67,54,0.1); border: 2rpx solid rgba(244,67,54,0.3); border-radius: 24rpx; text-align: center; font-size: 28rpx; color: #F44336; } + +/* 弹窗 - 简洁大气风格 */ +.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); backdrop-filter: blur(20rpx); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 48rpx; } +.modal-content { width: 100%; max-width: 640rpx; background: linear-gradient(180deg, #1c1c1e 0%, #0d0d0d 100%); border-radius: 40rpx; overflow: hidden; border: 2rpx solid rgba(255,255,255,0.08); } +.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 40rpx 40rpx 24rpx; } +.modal-title { font-size: 36rpx; font-weight: 700; color: #fff; } +.modal-close { width: 64rpx; height: 64rpx; background: rgba(255,255,255,0.08); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: rgba(255,255,255,0.5); } +.modal-body { padding: 16rpx 40rpx 48rpx; } +.input-wrapper { margin-bottom: 32rpx; } +.form-input { width: 100%; padding: 32rpx 24rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; font-size: 32rpx; color: #fff; box-sizing: border-box; transition: all 0.2s; } +.form-input:focus { border-color: rgba(0,206,209,0.5); background: rgba(0,206,209,0.05); } +.input-placeholder { color: rgba(255,255,255,0.25); } +.bind-tip { font-size: 24rpx; color: rgba(255,255,255,0.4); margin-bottom: 40rpx; display: block; line-height: 1.6; text-align: center; } +.btn-primary { padding: 32rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 28rpx; } +.btn-primary.btn-disabled { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); } diff --git a/Tminiprogram/project.config.json b/Tminiprogram/project.config.json new file mode 100644 index 00000000..44f4ac20 --- /dev/null +++ b/Tminiprogram/project.config.json @@ -0,0 +1,56 @@ +{ + "compileType": "miniprogram", + "miniprogramRoot": "", + "projectname": "soul-startup", + "description": "Soul创业派对 - 来自派对房的真实商业故事", + "appid": "wxb8bbb2b10dec74aa", + "setting": { + "urlCheck": false, + "es6": true, + "enhance": true, + "postcss": true, + "preloadBackgroundData": false, + "minified": true, + "newFeature": true, + "coverView": true, + "nodeModules": false, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "uglifyFileName": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "compileHotReLoad": true, + "lazyloadPlaceholderEnable": false, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "enableEngineNative": false, + "useIsolateContext": true, + "userConfirmedBundleSwitch": false, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "disableUseStrict": false, + "minifyWXML": true, + "showES6CompileOption": false, + "useCompilerPlugins": false, + "ignoreUploadUnusedFiles": true + }, + "libVersion": "3.13.2", + "packOptions": { + "ignore": [], + "include": [] + }, + "condition": {}, + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + } +} \ No newline at end of file diff --git a/Tminiprogram/project.private.config.json b/Tminiprogram/project.private.config.json new file mode 100644 index 00000000..354918af --- /dev/null +++ b/Tminiprogram/project.private.config.json @@ -0,0 +1,7 @@ +{ + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "projectname": "soul-party-book", + "setting": { + "compileHotReLoad": true + } +} \ No newline at end of file diff --git a/Tminiprogram/sitemap.json b/Tminiprogram/sitemap.json new file mode 100644 index 00000000..55d1d29e --- /dev/null +++ b/Tminiprogram/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} diff --git a/Tminiprogram/utils/payment.js b/Tminiprogram/utils/payment.js new file mode 100644 index 00000000..9e048d1a --- /dev/null +++ b/Tminiprogram/utils/payment.js @@ -0,0 +1,211 @@ +// miniprogram/utils/payment.js +// 微信支付工具类 + +const app = getApp() + +/** + * 发起微信支付 + * @param {Object} options - 支付选项 + * @param {String} options.orderId - 订单ID + * @param {Number} options.amount - 支付金额(元) + * @param {String} options.description - 商品描述 + * @param {Function} options.success - 成功回调 + * @param {Function} options.fail - 失败回调 + */ +function wxPay(options) { + const { orderId, amount, description, success, fail } = options + + wx.showLoading({ + title: '正在支付...', + mask: true + }) + + // 1. 调用后端创建支付订单 + wx.request({ + url: `${app.globalData.apiBase}/payment/create`, + method: 'POST', + header: { + 'Authorization': `Bearer ${wx.getStorageSync('token')}` + }, + data: { + orderId, + amount, + description, + paymentMethod: 'wechat' + }, + success: (res) => { + wx.hideLoading() + + if (res.statusCode === 200) { + const paymentData = res.data + + // 2. 调起微信支付 + wx.requestPayment({ + timeStamp: paymentData.timeStamp, + nonceStr: paymentData.nonceStr, + package: paymentData.package, + signType: paymentData.signType || 'RSA', + paySign: paymentData.paySign, + success: (payRes) => { + console.log('支付成功', payRes) + + // 3. 通知后端支付成功 + notifyPaymentSuccess(orderId, paymentData.prepayId) + + wx.showToast({ + title: '支付成功', + icon: 'success', + duration: 2000 + }) + + success && success(payRes) + }, + fail: (payErr) => { + console.error('支付失败', payErr) + + if (payErr.errMsg.indexOf('cancel') !== -1) { + wx.showToast({ + title: '支付已取消', + icon: 'none' + }) + } else { + wx.showToast({ + title: '支付失败', + icon: 'none' + }) + } + + fail && fail(payErr) + } + }) + } else { + wx.showToast({ + title: res.data.message || '创建订单失败', + icon: 'none' + }) + fail && fail(res) + } + }, + fail: (err) => { + wx.hideLoading() + console.error('请求失败', err) + + wx.showToast({ + title: '网络请求失败', + icon: 'none' + }) + + fail && fail(err) + } + }) +} + +/** + * 通知后端支付成功 + * @param {String} orderId + * @param {String} prepayId + */ +function notifyPaymentSuccess(orderId, prepayId) { + wx.request({ + url: `${app.globalData.apiBase}/payment/notify`, + method: 'POST', + header: { + 'Authorization': `Bearer ${wx.getStorageSync('token')}` + }, + data: { + orderId, + prepayId, + status: 'success' + }, + success: (res) => { + console.log('支付通知成功', res) + }, + fail: (err) => { + console.error('支付通知失败', err) + } + }) +} + +/** + * 查询订单状态 + * @param {String} orderId + * @param {Function} callback + */ +function queryOrderStatus(orderId, callback) { + wx.request({ + url: `${app.globalData.apiBase}/payment/query`, + method: 'GET', + header: { + 'Authorization': `Bearer ${wx.getStorageSync('token')}` + }, + data: { orderId }, + success: (res) => { + if (res.statusCode === 200) { + callback && callback(true, res.data) + } else { + callback && callback(false, null) + } + }, + fail: () => { + callback && callback(false, null) + } + }) +} + +/** + * 购买完整电子书 + * @param {Function} success + * @param {Function} fail + */ +function purchaseFullBook(success, fail) { + // 计算动态价格:9.9 + (天数 * 1元) + const basePrice = 9.9 + const startDate = new Date('2025-01-01') // 书籍上架日期 + const today = new Date() + const daysPassed = Math.floor((today - startDate) / (1000 * 60 * 60 * 24)) + const currentPrice = basePrice + daysPassed + + const orderId = `ORDER_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` + + wxPay({ + orderId, + amount: currentPrice, + description: 'Soul派对·创业实验 完整版', + success: (res) => { + // 更新本地购买状态 + updatePurchaseStatus(true) + success && success(res) + }, + fail + }) +} + +/** + * 更新购买状态 + * @param {Boolean} isPurchased + */ +function updatePurchaseStatus(isPurchased) { + const userInfo = app.getUserInfo() + if (userInfo) { + userInfo.isPurchased = isPurchased + wx.setStorageSync('userInfo', userInfo) + app.globalData.userInfo = userInfo + } +} + +/** + * 检查是否已购买 + * @returns {Boolean} + */ +function checkPurchaseStatus() { + const userInfo = app.getUserInfo() + return userInfo ? userInfo.isPurchased : false +} + +module.exports = { + wxPay, + queryOrderStatus, + purchaseFullBook, + checkPurchaseStatus, + updatePurchaseStatus +} diff --git a/Tminiprogram/utils/util.js b/Tminiprogram/utils/util.js new file mode 100644 index 00000000..855e96fd --- /dev/null +++ b/Tminiprogram/utils/util.js @@ -0,0 +1,182 @@ +/** + * Soul创业实验 - 工具函数 + */ + +// 格式化时间 +const formatTime = date => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + + return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}` +} + +const formatNumber = n => { + n = n.toString() + return n[1] ? n : `0${n}` +} + +// 格式化日期 +const formatDate = date => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + return `${year}-${formatNumber(month)}-${formatNumber(day)}` +} + +// 格式化金额 +const formatMoney = (amount, decimals = 2) => { + return Number(amount).toFixed(decimals) +} + +// 防抖函数 +const debounce = (fn, delay = 300) => { + let timer = null + return function (...args) { + if (timer) clearTimeout(timer) + timer = setTimeout(() => { + fn.apply(this, args) + }, delay) + } +} + +// 节流函数 +const throttle = (fn, delay = 300) => { + let last = 0 + return function (...args) { + const now = Date.now() + if (now - last >= delay) { + fn.apply(this, args) + last = now + } + } +} + +// 生成唯一ID +const generateId = () => { + return 'id_' + Date.now().toString(36) + Math.random().toString(36).substr(2) +} + +// 检查手机号格式 +const isValidPhone = phone => { + return /^1[3-9]\d{9}$/.test(phone) +} + +// 检查微信号格式 +const isValidWechat = wechat => { + return wechat && wechat.length >= 6 && wechat.length <= 20 +} + +// 深拷贝 +const deepClone = obj => { + if (obj === null || typeof obj !== 'object') return obj + if (obj instanceof Date) return new Date(obj) + if (obj instanceof Array) return obj.map(item => deepClone(item)) + if (obj instanceof Object) { + const copy = {} + Object.keys(obj).forEach(key => { + copy[key] = deepClone(obj[key]) + }) + return copy + } +} + +// 获取URL参数 +const getQueryParams = url => { + const params = {} + const queryString = url.split('?')[1] + if (queryString) { + queryString.split('&').forEach(pair => { + const [key, value] = pair.split('=') + params[decodeURIComponent(key)] = decodeURIComponent(value || '') + }) + } + return params +} + +// 存储操作 +const storage = { + get(key) { + try { + return wx.getStorageSync(key) + } catch (e) { + console.error('获取存储失败:', e) + return null + } + }, + set(key, value) { + try { + wx.setStorageSync(key, value) + return true + } catch (e) { + console.error('设置存储失败:', e) + return false + } + }, + remove(key) { + try { + wx.removeStorageSync(key) + return true + } catch (e) { + console.error('删除存储失败:', e) + return false + } + }, + clear() { + try { + wx.clearStorageSync() + return true + } catch (e) { + console.error('清除存储失败:', e) + return false + } + } +} + +// 显示Toast +const showToast = (title, icon = 'none', duration = 2000) => { + wx.showToast({ title, icon, duration }) +} + +// 显示Loading +const showLoading = (title = '加载中...') => { + wx.showLoading({ title, mask: true }) +} + +// 隐藏Loading +const hideLoading = () => { + wx.hideLoading() +} + +// 显示确认框 +const showConfirm = (title, content) => { + return new Promise((resolve) => { + wx.showModal({ + title, + content, + success: res => resolve(res.confirm) + }) + }) +} + +module.exports = { + formatTime, + formatDate, + formatMoney, + formatNumber, + debounce, + throttle, + generateId, + isValidPhone, + isValidWechat, + deepClone, + getQueryParams, + storage, + showToast, + showLoading, + hideLoading, + showConfirm +} diff --git a/Tminiprogram/小程序快速配置指南.md b/Tminiprogram/小程序快速配置指南.md new file mode 100644 index 00000000..7c902486 --- /dev/null +++ b/Tminiprogram/小程序快速配置指南.md @@ -0,0 +1,272 @@ +# 小程序快速配置指南 ⚡ + +> 5分钟内完成配置,快速开始开发! + +## 🎯 配置前准备 + +- ✅ 已安装微信开发者工具 +- ✅ 已有小程序AppID(或使用测试AppID) +- ✅ 后端API服务器已启动 + +## 📝 必须配置的3个地方 + +### 1️⃣ 配置小程序AppID + +**文件**: `project.config.json` + +\`\`\`json +{ + "appid": "你的小程序AppID", // ⬅️ 改这里 + "projectname": "soul-party-book" +} +\`\`\` + +> 💡 没有AppID?使用测试号:`wxd7e8c8a8e8c8a8e8` + +--- + +### 2️⃣ 配置API服务器地址 + +**文件**: `app.js` + +\`\`\`javascript +globalData: { + apiBase: 'http://localhost:3000/api', // ⬅️ 改这里 + // 本地开发: http://localhost:3000/api + // 线上环境: https://your-domain.com/api +} +\`\`\` + +--- + +### 3️⃣ 配置服务器域名(线上部署时) + +登录[小程序后台](https://mp.weixin.qq.com/): + +开发管理 → 开发设置 → 服务器域名 + +\`\`\` +request合法域名: +https://your-domain.com + +uploadFile合法域名: +https://your-domain.com + +downloadFile合法域名: +https://your-domain.com +\`\`\` + +--- + +## 🚀 启动步骤 + +### 第一步:启动后端服务器 + +在项目根目录运行: + +\`\`\`bash +# Mac/Linux +chmod +x start-miniprogram.sh +./start-miniprogram.sh + +# Windows +npm run dev +# 或 +pnpm dev +\`\`\` + +看到以下信息表示成功: + +\`\`\` +✓ Ready in 2.3s +○ Local: http://localhost:3000 +\`\`\` + +--- + +### 第二步:打开微信开发者工具 + +1. 点击"导入项目" +2. 选择 `miniprogram` 文件夹 +3. 填入AppID(或选择测试号) +4. 点击"导入" + +--- + +### 第三步:点击编译 + +点击工具栏的"编译"按钮,等待编译完成。 + +--- + +### 第四步:开始开发!🎉 + +现在你可以: + +- 👀 在模拟器中查看效果 +- 📱 扫码在真机预览 +- 🔧 修改代码实时刷新 +- 📊 查看Network请求 + +--- + +## 🧪 功能测试清单 + +### ✅ 首页测试 + +- [ ] 书籍封面正常显示 +- [ ] 最新章节列表加载 +- [ ] 点击章节可跳转阅读 +- [ ] 购买按钮有响应 + +### ✅ 匹配书友测试 + +- [ ] 星空背景动画流畅 +- [ ] 点击"开始匹配"有动画 +- [ ] 3-6秒后匹配成功 +- [ ] 显示匹配用户信息 + +### ✅ 我的页面测试 + +- [ ] 点击头像可登录 +- [ ] 分销中心数据显示 +- [ ] 生成推广海报功能 +- [ ] 复制邀请码功能 + +### ✅ 阅读页测试 + +- [ ] 章节内容正常渲染 +- [ ] 书签功能正常 +- [ ] 目录侧滑打开 +- [ ] 分享功能正常 + +--- + +## 🔧 常见问题 + +### Q1: 编译报错 "Cannot find module" + +**解决**:检查后端服务器是否启动 + +\`\`\`bash +# 重新启动后端 +pnpm dev +\`\`\` + +--- + +### Q2: 页面空白,没有数据 + +**解决**:检查API地址配置 + +1. 打开 `app.js` +2. 确认 `apiBase` 地址正确 +3. 在浏览器访问 `http://localhost:3000/api` 测试 + +--- + +### Q3: 图片不显示 + +**解决**:图片路径问题 + +临时方案:使用在线图片URL + +\`\`\`javascript +// 将本地路径 +src="/assets/images/book-cover.png" + +// 改为在线URL +src="https://picsum.photos/400/560" +\`\`\` + +--- + +### Q4: 支付测试失败 + +**解决**:本地开发暂时无法测试真实支付 + +- 使用Mock数据模拟支付成功 +- 真实支付需要: + 1. 配置微信支付商户号 + 2. 部署到HTTPS域名 + 3. 在小程序后台配置支付权限 + +--- + +### Q5: 模拟器和真机效果不一致 + +**解决**:以真机为准 + +\`\`\`bash +# 真机调试步骤: +1. 点击工具栏"预览" +2. 手机微信扫码 +3. 在手机上调试 +\`\`\` + +--- + +## 📞 获取帮助 + +### 技术支持 + +- **文档**: 查看 `开发文档/` 目录 + +### 官方文档 + +- [微信小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/) +- [微信支付文档](https://pay.weixin.qq.com/wiki/doc/api/index.html) + +--- + +## 🎨 自定义配置(可选) + +### 修改主题色 + +**文件**: `app.wxss` + +\`\`\`css +.brand-color { + color: #FF4D4F; /* 改成你的品牌色 */ +} +\`\`\` + +--- + +### 修改TabBar图标 + +替换 `assets/icons/` 目录下的图片: + +- `home.png` / `home-active.png` - 首页 +- `match.png` / `match-active.png` - 匹配 +- `my.png` / `my-active.png` - 我的 + +要求:尺寸81x81像素,PNG格式 + +--- + +### 修改分享海报 + +**文件**: `pages/my/my.js` 中的 `drawPoster()` 函数 + +可自定义: + +- 背景颜色 +- 文字内容 +- 二维码位置 +- Logo展示 + +--- + +## ✨ 下一步 + +配置完成后,你可以: + +1. 📖 阅读[开发文档](../开发文档/小程序开发完成说明.md) +2. 🎨 自定义UI样式 +3. 🔧 添加新功能 +4. 🚀 准备上线发布 + +--- + +**祝开发顺利!** 🎉 diff --git a/Tminiprogram/小程序部署说明.md b/Tminiprogram/小程序部署说明.md new file mode 100644 index 00000000..7df2d19d --- /dev/null +++ b/Tminiprogram/小程序部署说明.md @@ -0,0 +1,463 @@ +# 🚀 Soul派对小程序 - 部署完成说明 + +**部署时间**: 2025年1月14日 +**配置状态**: ✅ 已完成配置 + +--- + +## ✅ 当前配置信息 + +### 小程序配置 + +| 项目 | 配置值 | +|------|--------| +| **AppID** | `wx0976665c3a3d5a7c` | +| **AppSecret** | `a262f1be43422f03734f205d0bca1882` | +| **API域名** | `http://kr-soul.lytiao.com` | +| **API路径** | `http://kr-soul.lytiao.com/api` | + +### 已配置文件 + +✅ `miniprogram/project.config.json` - AppID已配置 +✅ `miniprogram/app.js` - API地址已配置 +✅ `.env.production` - 生产环境配置 +✅ `app/api/wechat/login/route.ts` - 微信登录接口 +✅ `app/api/book/latest-chapters/route.ts` - 章节接口 + +--- + +## 🎯 快速测试(3步骤) + +### 第1步:启动本地服务器 + +\`\`\`bash +cd "/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验" + +# 安装依赖(如果还没安装) +pnpm install + +# 启动开发服务器 +pnpm dev +\`\`\` + +✅ 看到 `Ready in 2.3s` 表示成功 + +--- + +### 第2步:打开微信开发者工具 + +1. 打开微信开发者工具 +2. 点击 **"导入项目"** +3. 选择目录: + \`\`\` + /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram + \`\`\` +4. AppID会自动识别:`wx0976665c3a3d5a7c` +5. 点击 **"导入"** + +--- + +### 第3步:本地联调测试 + +在微信开发者工具中: + +1. 点击右上角 **"详情"** +2. 找到 **"本地设置"** +3. 勾选 **"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书"** +4. 点击 **"编译"** 按钮 + +✅ 现在可以在模拟器中测试了! + +--- + +## 📱 功能测试清单 + +### 首页测试 + +- [ ] 书籍封面显示 +- [ ] 最新章节列表 +- [ ] 点击章节跳转 +- [ ] 购买按钮响应 + +### 匹配书友测试 + +- [ ] 星空动画流畅 +- [ ] 匹配功能运行 +- [ ] 匹配成功显示 + +### 我的页面测试 + +- [ ] 点击登录功能 +- [ ] 分销中心展示 +- [ ] 海报生成功能 + +### 阅读页测试 + +- [ ] 章节内容加载 +- [ ] 目录侧滑 +- [ ] 书签功能 + +--- + +## 🌐 正式部署到服务器 + +### 域名配置检查 + +你的域名:`http://kr-soul.lytiao.com` + +#### ⚠️ 重要:需要配置HTTPS + +小程序要求所有网络请求必须使用HTTPS! + +**配置SSL证书步骤**: + +1. 登录阿里云控制台 +2. 进入 **"SSL证书"** 服务 +3. 申请免费SSL证书(DV证书) +4. 下载证书文件 +5. 在服务器上配置证书 + +**配置后域名应该是**: +\`\`\` +https://kr-soul.lytiao.com +\`\`\` + +--- + +### 服务器部署步骤 + +#### 1. 将代码上传到服务器 + +\`\`\`bash +# 方式1:使用Git +cd /var/www +git clone your-repo-url soul-party +cd soul-party + +# 方式2:使用SCP上传 +scp -r ./一场soul的创业实验 root@kr-soul.lytiao.com:/var/www/soul-party +\`\`\` + +#### 2. 安装依赖并构建 + +\`\`\`bash +# 在服务器上执行 +cd /var/www/soul-party + +# 安装依赖 +npm install + +# 构建生产版本 +npm run build +\`\`\` + +#### 3. 使用PM2启动服务 + +\`\`\`bash +# 安装PM2(如果没有) +npm install -g pm2 + +# 启动服务 +pm2 start npm --name "soul-party" -- start + +# 设置开机自启 +pm2 startup +pm2 save +\`\`\` + +#### 4. 配置Nginx反向代理 + +创建Nginx配置文件:`/etc/nginx/sites-available/soul-party` + +\`\`\`nginx +server { + listen 80; + server_name kr-soul.lytiao.com; + + # 强制跳转HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name kr-soul.lytiao.com; + + # SSL证书配置 + ssl_certificate /path/to/your/cert.pem; + ssl_certificate_key /path/to/your/key.pem; + + # API代理 + location /api { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # 静态文件 + location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} +\`\`\` + +启用配置: + +\`\`\`bash +# 创建软链接 +ln -s /etc/nginx/sites-available/soul-party /etc/nginx/sites-enabled/ + +# 测试配置 +nginx -t + +# 重启Nginx +systemctl restart nginx +\`\`\` + +--- + +### 小程序后台配置 + +#### 1. 登录小程序后台 + +访问:https://mp.weixin.qq.com/ + +使用AppID `wx0976665c3a3d5a7c` 对应的账号登录 + +#### 2. 配置服务器域名 + +**开发管理** → **开发设置** → **服务器域名** + +添加以下域名: + +\`\`\` +request合法域名: +https://kr-soul.lytiao.com + +uploadFile合法域名: +https://kr-soul.lytiao.com + +downloadFile合法域名: +https://kr-soul.lytiao.com +\`\`\` + +⚠️ **注意**:必须是HTTPS域名,HTTP会被拒绝! + +#### 3. 配置业务域名(可选) + +如果需要在小程序内打开网页: + +**开发管理** → **开发设置** → **业务域名** + +添加:`kr-soul.lytiao.com` + +--- + +## 📤 上传代码到微信后台 + +### 1. 上传代码 + +在微信开发者工具中: + +1. 点击工具栏 **"上传"** 按钮 +2. 填写版本号:`1.0.0` +3. 填写项目备注:`Soul派对小程序正式版` +4. 点击 **"上传"** + +✅ 上传成功后,代码会出现在小程序后台 + +--- + +### 2. 提交审核 + +登录小程序后台: + +1. **版本管理** → **开发版本** +2. 找到刚上传的版本 +3. 点击 **"提交审核"** +4. 填写审核信息: + - 类别:图书/阅读 + - 标签:电子书、创业、私域运营 + - 功能说明:提供电子书阅读和分销功能 + +审核时间:通常1-3个工作日 + +--- + +### 3. 发布上线 + +审核通过后: + +1. **版本管理** → **审核版本** +2. 点击 **"发布"** +3. 全量发布给所有用户 + +🎉 **上线成功!** + +--- + +## 🔧 本地开发配置 + +### 方式1:使用本地API(推荐开发时) + +**文件**: `miniprogram/app.js` + +\`\`\`javascript +apiBase: 'http://localhost:3000/api' +\`\`\` + +然后在开发者工具中勾选 **"不校验合法域名"** + +--- + +### 方式2:使用线上API + +**文件**: `miniprogram/app.js` + +\`\`\`javascript +apiBase: 'https://kr-soul.lytiao.com/api' +\`\`\` + +必须配置好HTTPS和域名白名单 + +--- + +## 📊 API接口测试 + +### 测试微信登录接口 + +\`\`\`bash +curl -X POST http://kr-soul.lytiao.com/api/wechat/login \ + -H "Content-Type: application/json" \ + -d '{"code":"test_code"}' +\`\`\` + +### 测试章节列表接口 + +\`\`\`bash +curl http://kr-soul.lytiao.com/api/book/latest-chapters +\`\`\` + +### 测试后台管理接口 + +\`\`\`bash +curl http://kr-soul.lytiao.com/api/admin +\`\`\` + +--- + +## 🎨 生成小程序码 + +### 方式1:使用微信开发者工具 + +1. 点击工具栏 **"预览"** +2. 自动生成小程序码 +3. 用微信扫码即可预览 + +--- + +### 方式2:使用官方API生成 + +需要调用微信接口: + +\`\`\`javascript +// 获取小程序码 +POST https://api.weixin.qq.com/wxa/getwxacode?access_token=TOKEN + +{ + "path": "pages/index/index", + "width": 430 +} +\`\`\` + +会生成二维码图片,保存后可分享 + +--- + +## ⚠️ 常见问题 + +### Q1: 提示"不在以下request合法域名列表中" + +**解决**: +1. 开发时:勾选"不校验合法域名" +2. 正式环境:在小程序后台配置域名白名单 + +--- + +### Q2: API请求失败 + +**检查清单**: +- [ ] 服务器是否启动? +- [ ] 域名是否配置HTTPS? +- [ ] 小程序后台是否配置域名? +- [ ] API接口是否正常? + +--- + +### Q3: 登录失败 + +**解决**: +1. 检查AppID和AppSecret是否正确 +2. 查看控制台错误信息 +3. 确认微信登录接口正常 + +--- + +## 📞 技术支持 + +### 联系方式 + +- **项目路径**: `/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验` + +### 快速命令 + +\`\`\`bash +# 启动开发服务器 +cd "/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验" +pnpm dev + +# 构建生产版本 +pnpm build + +# 启动生产服务器 +pnpm start + +# 查看日志(如果使用PM2) +pm2 logs soul-party +\`\`\` + +--- + +## ✅ 配置完成清单 + +- [x] AppID配置完成 +- [x] API地址配置完成 +- [x] 微信登录接口创建完成 +- [x] 书籍接口创建完成 +- [x] 环境变量配置完成 +- [x] 部署脚本创建完成 +- [ ] HTTPS证书配置(需要在服务器上操作) +- [ ] 小程序后台域名配置(需要在微信后台操作) +- [ ] 代码上传审核(需要在开发者工具操作) + +--- + +## 🎉 下一步 + +1. **本地测试** - 在开发者工具中测试所有功能 +2. **服务器部署** - 将代码部署到 `kr-soul.lytiao.com` +3. **配置HTTPS** - 申请并配置SSL证书 +4. **配置域名** - 在小程序后台配置服务器域名 +5. **提交审核** - 上传代码并提交审核 +6. **发布上线** - 审核通过后发布 + +--- + +**祝部署顺利!** 🚀 diff --git a/Tminiprogram/测试二维码.html b/Tminiprogram/测试二维码.html new file mode 100644 index 00000000..51ba44cb --- /dev/null +++ b/Tminiprogram/测试二维码.html @@ -0,0 +1,366 @@ + + + + + + Soul派对小程序 - 测试二维码 + + + +
+
+
Soul派对·创业实验
+
微信小程序测试版
+
+ +
+
+
配置完成,可以开始测试!
+
+ +
+
📋 当前配置
+
+
小程序AppID
+
wx0976665c3a3d5a7c
+
+
+
API域名
+
http://kr-soul.lytiao.com
+
+
+
本地开发地址
+
http://localhost:3000
+
+
+
配置状态
+
✅ 已完成
+
+
+ +
+
📱 扫码体验小程序
+
+
📱
+
请在微信开发者工具中生成预览码
+
+

+ 在开发者工具中点击"预览"按钮,自动生成小程序码 +

+
+ +
+
🚀 快速测试步骤
+ +
+
1
+
+
打开微信开发者工具
+
+ 选择"导入项目",导入以下目录: +
/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram
+ AppID会自动识别为:wx0976665c3a3d5a7c +
+
+
+ +
+
2
+
+
启用本地调试
+
+ 点击右上角"详情" → "本地设置" → 勾选"不校验合法域名"
+ (这样可以使用本地API: http://localhost:3000) +
+
+
+ +
+
3
+
+
点击编译运行
+
+ 在模拟器中查看效果,测试所有功能:
+ - 首页书籍展示
+ - 匹配书友功能
+ - 我的页面和分销中心
+ - 阅读页面 +
+
+
+ +
+
4
+
+
真机预览测试
+
+ 点击工具栏"预览"按钮,生成小程序码
+ 用微信扫码即可在手机上预览 +
+
+
+
+ +
+
⚠️
+
+
正式发布前注意事项
+
+ 1. 必须配置HTTPS证书(小程序要求必须HTTPS)
+ 2. 在小程序后台配置服务器域名白名单
+ 3. 将API地址改为:https://kr-soul.lytiao.com/api
+ 4. 上传代码到微信后台提交审核 +
+
+
+
+ + + + diff --git a/Tminiprogram/生成图标.html b/Tminiprogram/生成图标.html new file mode 100644 index 00000000..6f041cd5 --- /dev/null +++ b/Tminiprogram/生成图标.html @@ -0,0 +1,71 @@ + + + + + 生成小程序图标 + + +

小程序底部导航图标生成器

+
+ + + + diff --git a/Tminiprogram/自动部署.sh b/Tminiprogram/自动部署.sh new file mode 100644 index 00000000..b35844dd --- /dev/null +++ b/Tminiprogram/自动部署.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +# Soul派对小程序 - 自动部署脚本 +# 自动编译、测试、上传小程序 + +echo "==================================" +echo " Soul派对小程序 自动部署 " +echo "==================================" +echo "" + +# 微信开发者工具CLI路径 +CLI="/Applications/wechatwebdevtools.app/Contents/MacOS/cli" + +# 项目路径 +PROJECT_PATH="/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram" + +# 检查CLI是否存在 +if [ ! -f "$CLI" ]; then + echo "❌ 未找到微信开发者工具CLI" + echo "请确保微信开发者工具已安装" + exit 1 +fi + +echo "✅ 找到微信开发者工具" +echo "" + +# 1. 打开项目 +echo "📂 步骤1:打开项目..." +$CLI -o "$PROJECT_PATH" +sleep 2 +echo "✅ 项目已打开" +echo "" + +# 2. 编译项目(使用新的v2命令格式) +echo "🔨 步骤2:编译项目..." +$CLI build-npm --project "$PROJECT_PATH" +sleep 3 +echo "✅ 编译完成" +echo "" + +# 3. 预览(生成二维码) +echo "📱 步骤3:生成预览二维码..." +$CLI preview --project "$PROJECT_PATH" --qr-format image --qr-output "$PROJECT_PATH/preview.png" +if [ -f "$PROJECT_PATH/preview.png" ]; then + echo "✅ 二维码已生成: $PROJECT_PATH/preview.png" + open "$PROJECT_PATH/preview.png" +else + echo "⚠️ 二维码生成失败,请手动点击预览" +fi +echo "" + +# 4. 上传代码(使用新的v2命令格式) +echo "📤 步骤4:上传代码到微信后台..." +VERSION="1.0.0" +DESC="初始版本:3按钮导航+星球匹配功能,H5和小程序界面统一" + +$CLI upload --project "$PROJECT_PATH" --version "$VERSION" --desc "$DESC" + +if [ $? -eq 0 ]; then + echo "✅ 代码上传成功!" + echo "" + echo "版本:$VERSION" + echo "说明:$DESC" + echo "" + echo "==================================" + echo "🎉 部署完成!" + echo "==================================" + echo "" + echo "下一步操作:" + echo "1. 登录小程序后台:https://mp.weixin.qq.com" + echo "2. 进入「版本管理」→「开发版本」" + echo "3. 找到刚上传的版本" + echo "4. 点击「提交审核」" + echo "" +else + echo "❌ 上传失败" + echo "" + echo "可能原因:" + echo "1. 需要在微信开发者工具中登录" + echo "2. 需要手动上传(点击工具栏的上传按钮)" + echo "" +fi