diff --git a/app/api/book/chapter/[id]/route.ts b/app/api/book/chapter/[id]/route.ts index b3e1c79..c8e83ad 100644 --- a/app/api/book/chapter/[id]/route.ts +++ b/app/api/book/chapter/[id]/route.ts @@ -1,5 +1,5 @@ // app/api/book/chapter/[id]/route.ts -// 获取章节详情 +// 获取章节详情 - 支持小程序和Web端 import { NextRequest, NextResponse } from 'next/server' import fs from 'fs' @@ -8,19 +8,109 @@ import matter from 'gray-matter' const BOOK_DIR = path.join(process.cwd(), 'book') +// 章节ID到文件路径的映射 +const CHAPTER_MAP: Record = { + 'preface': { dir: '', file: '序言|为什么我每天早上6点在Soul开播?.md', partTitle: '序言', chapterTitle: '为什么我每天早上6点在Soul开播?' }, + 'epilogue': { dir: '', file: '尾声|这本书的真实目的.md', partTitle: '尾声', chapterTitle: '这本书的真实目的' }, + '1.1': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.1 荷包:电动车出租的被动收入模式.md', partTitle: '第一篇|真实的人', chapterTitle: '1.1 荷包:电动车出租的被动收入模式' }, + '1.2': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.2 老墨:资源整合高手的社交方法.md', partTitle: '第一篇|真实的人', chapterTitle: '1.2 老墨:资源整合高手的社交方法' }, + '1.3': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.3 笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统.md', partTitle: '第一篇|真实的人', chapterTitle: '1.3 笑声背后的MBTI' }, + '1.4': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.4 人性的三角结构:利益、情感、价值观.md', partTitle: '第一篇|真实的人', chapterTitle: '1.4 人性的三角结构' }, + '1.5': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.5 沟通差的问题:为什么你说的别人听不懂.md', partTitle: '第一篇|真实的人', chapterTitle: '1.5 沟通差的问题' }, + '2.1': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.1 相亲故事:你以为找的是人,实际是在找模式.md', partTitle: '第一篇|真实的人', chapterTitle: '2.1 相亲故事' }, + '2.2': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.2 找工作迷茫者:为什么简历解决不了人生.md', partTitle: '第一篇|真实的人', chapterTitle: '2.2 找工作迷茫者' }, + '2.3': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.3 撸运费险:小钱困住大脑的真实心理.md', partTitle: '第一篇|真实的人', chapterTitle: '2.3 撸运费险' }, + '2.4': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.4 游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力.md', partTitle: '第一篇|真实的人', chapterTitle: '2.4 游戏上瘾的年轻人' }, + '2.5': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.5 健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒.md', partTitle: '第一篇|真实的人', chapterTitle: '2.5 健康焦虑' }, + '3.1': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.1 3000万流水如何跑出来(退税模式解析).md', partTitle: '第二篇|真实的行业', chapterTitle: '3.1 3000万流水' }, + '3.2': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.2 供应链之王 vs 打工人:利润不在前端.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.2 供应链之王' }, + '3.3': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.3 社区团购的底层逻辑.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.3 社区团购' }, + '3.4': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.4 跨境电商与退税套利.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.4 跨境电商' }, + '4.1': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.1 旅游号:30天10万粉的真实逻辑.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.1 旅游号' }, + '4.2': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.2 做号工厂:如何让一个号变成一个机器.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.2 做号工厂' }, + '4.3': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.3 情绪内容为什么比专业内容更赚钱.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.3 情绪内容' }, + '4.4': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.4 猫与宠物号:为什么宠物赛道永不过时.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.4 猫与宠物号' }, + '4.5': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.5 直播间里的三种人:演员、技术工、系统流.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.5 直播间里的三种人' }, + '5.1': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.1 拍卖行抱朴:一天240万的摇号生意.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.1 拍卖行抱朴' }, + '5.2': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.2 土地拍卖:招拍挂背后的游戏规则.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.2 土地拍卖' }, + '5.3': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.3 地摊经济数字化:一个月900块的餐车生意.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.3 地摊经济' }, + '5.4': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.4 不良资产拍卖:我错过的一个亿佣金.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.4 不良资产拍卖' }, + '5.5': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.5 桶装水李总:跟物业合作的轻资产模式.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.5 桶装水李总' }, + '6.1': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.1 电商财税窗口:2016年的千万级机会.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.1 电商财税窗口' }, + '6.2': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.2 供应链金融:我不懂的杠杆游戏.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.2 供应链金融' }, + '6.3': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.3 内容红利:2019年我为什么没做抖音.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.3 内容红利' }, + '6.4': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.4 数据资产化:我还在观望的未来机会.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.4 数据资产化' }, + '7.1': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.1 投资房年轻人的迷茫:资金 vs 能力.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.1 投资房年轻人' }, + '7.2': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.2 信息差骗局:永远有人靠卖学习赚钱.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.2 信息差骗局' }, + '7.3': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.3 在Soul找恋爱但想赚钱的人.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.3 在Soul找恋爱' }, + '7.4': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.4 创业者的三种死法:冲动、轻信、没结构.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.4 创业者的三种死法' }, + '7.5': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.5 人情生意的终点:关系越多亏得越多.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.5 人情生意' }, + '8.1': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.1 流量杠杆:抖音、Soul、飞书.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.1 流量杠杆' }, + '8.2': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.2 价格杠杆:供应链与信息差.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.2 价格杠杆' }, + '8.3': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.3 时间杠杆:自动化 + AI.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.3 时间杠杆' }, + '8.4': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.4 情绪杠杆:咨询、婚恋、生意场.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.4 情绪杠杆' }, + '8.5': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.5 社交杠杆:认识谁比你会什么更重要.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.5 社交杠杆' }, + '8.6': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.6 云阿米巴:分不属于自己的钱.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.6 云阿米巴' }, + '9.1': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.1 游戏账号私域:账号即资产.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.1 游戏账号私域' }, + '9.2': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.2 健康包模式:高复购、高毛利.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.2 健康包模式' }, + '9.3': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.3 药物私域:长期关系赛道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.3 药物私域' }, + '9.4': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.4 残疾机构合作:退税 × AI × 人力成本.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.4 残疾机构合作' }, + '9.5': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.5 私域银行:粉丝即小股东.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.5 私域银行' }, + '9.6': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.6 Soul派对房:陌生人成交的最快场景.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.6 Soul派对房' }, + '9.7': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.7 飞书中台:从聊天到成交的流程化体系.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.7 飞书中台' }, + '9.8': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.8 餐饮女孩:6万营收、1万利润的死撑生意.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.8 餐饮女孩' }, + '9.9': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.9 电竞生态:从陪玩到签约到酒店的完整链条.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.9 电竞生态' }, + '9.10': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.10 淘客大佬:损耗30%的白色通道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.10 淘客大佬' }, + '9.11': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.11 蔬菜供应链:农户才是最赚钱的人.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.11 蔬菜供应链' }, + '9.12': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.12 美业整合:一个人的公司如何月入十万.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.12 美业整合' }, + '9.13': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.13 AI工具推广:一个隐藏的高利润赛道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.13 AI工具推广' }, + '9.14': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.14 大健康私域:一个月150万的70后.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.14 大健康私域' }, + '10.1': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.1 AI时代:哪些工作会消失,哪些会崛起.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.1 AI时代' }, + '10.2': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.2 一人公司:为什么越来越多人选择单干.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.2 一人公司' }, + '10.3': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.3 为什么链接能力会成为第一价值.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.3 链接能力' }, + '10.4': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.4 新型公司:Soul-飞书-线下的三位一体.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.4 新型公司' }, + '11.1': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.1 私域经济:为什么流量越来越贵.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.1 私域经济' }, + '11.2': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.2 银发经济与孤独经济:两个被忽视的万亿市场.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.2 银发经济' }, + '11.3': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.3 流量红利的终局.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.3 流量红利' }, + '11.4': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.4 大模型 + 供应链的组合拳.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.4 大模型+供应链' }, + '11.5': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.5 社会分层的最终逻辑.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.5 社会分层' }, + 'appendix-1': { dir: '附录', file: '附录1|Soul派对房精选对话.md', partTitle: '附录', chapterTitle: 'Soul派对房精选对话' }, + 'appendix-2': { dir: '附录', file: '附录2|创业者自检清单.md', partTitle: '附录', chapterTitle: '创业者自检清单' }, + 'appendix-3': { dir: '附录', file: '附录3|本书提到的工具和资源.md', partTitle: '附录', chapterTitle: '本书提到的工具和资源' }, +} + export async function GET( req: NextRequest, { params }: { params: { id: string } } ) { try { const chapterId = params.id + console.log('[Chapter API] 请求章节:', chapterId) - // 根据ID查找对应的Markdown文件 - const chapterFile = findChapterFile(chapterId) + // 先从映射表查找 + const mapping = CHAPTER_MAP[chapterId] + let chapterFile: string | null = null + let partTitle = '' + let chapterTitle = '' + + if (mapping) { + const filePath = path.join(BOOK_DIR, mapping.dir, mapping.file) + if (fs.existsSync(filePath)) { + chapterFile = filePath + partTitle = mapping.partTitle + chapterTitle = mapping.chapterTitle + } + } + + // 如果映射找不到,尝试动态查找 + if (!chapterFile) { + chapterFile = findChapterFile(chapterId) + } if (!chapterFile) { + console.log('[Chapter API] 章节不存在:', chapterId) return NextResponse.json( - { error: '章节不存在' }, + { error: '章节不存在', success: false }, { status: 404 } ) } @@ -28,62 +118,74 @@ export async function GET( const fileContent = fs.readFileSync(chapterFile, 'utf-8') const { data, content } = matter(fileContent) - // 判断是否需要购买(前3章免费) - const needPurchase = !isFreeChapter(chapterId) + // 判断是否免费章节 + const isFree = isFreeChapter(chapterId) - // 如果需要购买,检查用户是否已购买 - // TODO: 从token中获取用户信息,检查购买状态 - - const chapter = { + console.log('[Chapter API] 返回章节内容:', chapterId, '长度:', content.length) + + // 返回小程序兼容的格式 + return NextResponse.json({ + success: true, id: chapterId, - title: data.title || path.basename(chapterFile, '.md'), + title: data.title || chapterTitle || path.basename(chapterFile, '.md'), content: content, + partTitle: partTitle || data.category || '', + chapterTitle: chapterTitle || data.title || '', words: content.length, updateTime: data.date || fs.statSync(chapterFile).mtime.toISOString(), - needPurchase, - prevChapterId: null, // TODO: 实现上下章导航 - nextChapterId: null - } - - return NextResponse.json(chapter) + isFree, + needPurchase: !isFree + }) } catch (error) { - console.error('获取章节失败:', error) + console.error('[Chapter API] 获取章节失败:', error) return NextResponse.json( - { error: '获取章节失败' }, + { error: '获取章节失败', success: false }, { status: 500 } ) } } -// 查找章节文件 +// 递归查找章节文件 function findChapterFile(chapterId: string): string | null { - const categories = fs.readdirSync(BOOK_DIR).filter(item => { - const itemPath = path.join(BOOK_DIR, item) - return fs.statSync(itemPath).isDirectory() - }) - - for (const category of categories) { - const categoryPath = path.join(BOOK_DIR, category) - const files = fs.readdirSync(categoryPath).filter(file => file.endsWith('.md')) - - for (const file of files) { - const slug = `${category}/${file.replace('.md', '')}` - if (slug === chapterId || file.replace('.md', '') === chapterId) { - return path.join(categoryPath, file) + function searchDir(dir: string): string | null { + try { + const items = fs.readdirSync(dir) + + for (const item of items) { + const itemPath = path.join(dir, item) + const stat = fs.statSync(itemPath) + + if (stat.isDirectory()) { + const result = searchDir(itemPath) + if (result) return result + } else if (item.endsWith('.md')) { + // 检查文件名是否匹配章节ID + if (item.startsWith(chapterId + ' ') || + item.includes(chapterId) || + item.replace('.md', '') === chapterId) { + return itemPath + } + } } + } catch (e) { + // 忽略权限错误 } + return null } - - return null + + return searchDir(BOOK_DIR) } -// 判断是否免费章节(前3章免费) +// 判断是否免费章节 function isFreeChapter(chapterId: string): boolean { const freeChapters = [ - '序言', - '第一章', - '第二章' + 'preface', + 'epilogue', + '1.1', + 'appendix-1', + 'appendix-2', + 'appendix-3' ] - return freeChapters.some(free => chapterId.includes(free)) + return freeChapters.includes(chapterId) } diff --git a/miniprogram/pages/about/about.js b/miniprogram/pages/about/about.js index 69a0acf..b1a10bd 100644 --- a/miniprogram/pages/about/about.js +++ b/miniprogram/pages/about/about.js @@ -1,5 +1,6 @@ /** * Soul创业实验 - 关于作者页 + * 开发: 卡若 */ const app = getApp() @@ -9,17 +10,34 @@ Page({ author: { name: '卡若', avatar: 'K', - title: 'Soul派对房主理人', - bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。', + title: 'Soul派对房主理人 · 私域运营专家', + bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例,涵盖电商、内容、传统行业等多个领域。', stats: [ - { label: '派对房分享', value: '500+' }, - { label: '直播天数', value: '365+' }, - { label: '商业案例', value: '62+' } + { label: '商业案例', value: '62' }, + { label: '连续直播', value: '365天' }, + { label: '派对分享', value: '1000+' } ], contact: { wechat: '28533368', phone: '15880802661' - } + }, + 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 } }, @@ -27,6 +45,26 @@ Page({ 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] 加载书籍统计失败,使用默认值') + } }, // 复制微信号 diff --git a/miniprogram/pages/about/about.wxml b/miniprogram/pages/about/about.wxml index 7e2b952..fcf29ed 100644 --- a/miniprogram/pages/about/about.wxml +++ b/miniprogram/pages/about/about.wxml @@ -8,20 +8,56 @@ + {{author.avatar}} {{author.name}} {{author.title}} {{author.bio}} + {{item.value}} {{item.label}} + + + + + + {{item}} + + + + + 📚 {{bookInfo.title}} + + + {{bookInfo.totalChapters}} + 篇章节 + + + 5 + 大篇章 + + + ¥{{bookInfo.price}} + 全书价格 + + + + + {{item.name}} + {{item.chapters}}节 + + + + + 联系作者 diff --git a/miniprogram/pages/about/about.wxss b/miniprogram/pages/about/about.wxss index 5ae7304..337aa04 100644 --- a/miniprogram/pages/about/about.wxss +++ b/miniprogram/pages/about/about.wxss @@ -22,3 +22,19 @@ .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/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js index 8b8207b..723d0d0 100644 --- a/miniprogram/pages/match/match.js +++ b/miniprogram/pages/match/match.js @@ -52,6 +52,7 @@ Page({ isJoining: false, joinSuccess: false, joinError: '', + needBindFirst: false, // 解锁弹窗 showUnlockModal: false @@ -172,15 +173,27 @@ Page({ handleMatchClick() { const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType) - // 如果是需要填写联系方式的类型,直接弹出加入弹窗 + // 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募) if (currentType && currentType.showJoinAfterMatch) { - this.setData({ - showJoinModal: true, - joinType: currentType.id, - joinTypeLabel: currentType.matchLabel || currentType.label, - joinSuccess: false, - joinError: '' - }) + // 先检查是否已绑定联系方式 + 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 } @@ -192,6 +205,36 @@ Page({ 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() { diff --git a/miniprogram/pages/match/match.wxml b/miniprogram/pages/match/match.wxml index c761767..745a01c 100644 --- a/miniprogram/pages/match/match.wxml +++ b/miniprogram/pages/match/match.wxml @@ -12,22 +12,11 @@ - - - - - - {{hasFullBook ? '无限匹配机会' : matchesRemaining <= 0 ? '今日免费次数已用完' : '今日剩余'}} - - - - - {{hasFullBook ? '∞' : matchesRemaining + '次'}} - - - ¥1购买1次 - - + + + + 今日免费次数已用完 + 购买次数 @@ -153,82 +142,91 @@ - + - + - - - 加入成功! - 我们会尽快与您联系 + + + 提交成功 + 工作人员将在24小时内与您联系 - - 加入{{joinTypeLabel}} - + + + + {{joinType === 'investor' ? '👥' : joinType === 'mentor' ? '❤️' : '🎮'}} + + 加入{{joinTypeLabel}} + 请先绑定联系方式 + 填写联系方式,专人对接 + - - {{userPhone ? '已检测到您的绑定信息,可直接提交或修改' : '请填写您的联系方式以便我们联系您'}} - - - - + + 手机号 + > + 📱 + 手机号 + 微信号 + > + 💬 + 微信号 + - - - {{contactType === 'phone' ? '手机号' : '微信号'}} - - + + + + {{contactType === 'phone' ? '+86' : '@'}} + + + + {{joinError}} - - {{joinError}} - - 提交中... - 确认加入 + {{isJoining ? '提交中...' : '确认提交'}} - 提交即表示同意我们的服务条款 + 提交后我们会尽快与您联系 diff --git a/miniprogram/pages/match/match.wxss b/miniprogram/pages/match/match.wxss index 41d47c5..ceb98ce 100644 --- a/miniprogram/pages/match/match.wxss +++ b/miniprogram/pages/match/match.wxss @@ -52,67 +52,35 @@ width: 100%; } -/* ===== 匹配次数条 ===== */ -.match-count-bar { +/* ===== 匹配提示条 - 简化版 ===== */ +.match-tip-bar { display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - margin: 24rpx 32rpx; - padding: 24rpx 32rpx; - background: #1c1c1e; - border-radius: 24rpx; - border: 2rpx solid rgba(255, 255, 255, 0.05); -} - -.count-left { - display: flex; - flex-direction: row; - align-items: center; - gap: 12rpx; -} - -.count-icon { - font-size: 28rpx; - width: 32rpx; - text-align: center; -} - -.count-icon.icon-warning { - color: #FFD700; -} - -.count-text { - font-size: 28rpx; - color: rgba(255, 255, 255, 0.7); -} - -.count-right { - display: flex; - flex-direction: row; 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); } -.count-value { - font-size: 32rpx; - font-weight: 600; +.tip-icon { + font-size: 28rpx; } -.count-text { +.tip-text { font-size: 26rpx; color: rgba(255, 255, 255, 0.7); } -.count-right { - display: flex; - align-items: center; - gap: 16rpx; -} - -.count-value { - font-size: 32rpx; - font-weight: 700; +.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 { @@ -135,15 +103,6 @@ color: #FFD700; } -.unlock-mini-btn { - padding: 12rpx 24rpx; - background: rgba(255, 215, 0, 0.2); - color: #FFD700; - font-size: 22rpx; - font-weight: 500; - border-radius: 24rpx; -} - /* ===== 主内容区 ===== */ .main-content { padding: 0 32rpx; @@ -737,7 +696,182 @@ 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;