fix: 修复小程序多个问题
1. 修复章节内容加载卡住问题 - 更新API返回格式和章节映射 2. 修复找伙伴页面 - 去掉今日剩余次数显示 3. 添加匹配动画 - 1-3秒转动动画后再弹出加入弹窗 4. 加入资源对接需要先绑定手机号或微信号 5. 重构加入弹窗UI - 更简洁美观 6. 修复手机号输入框问题 7. 关于作者页面 - 内容与真实章节数据保持一致(62个案例)
This commit is contained in:
@@ -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<string, { dir: string; file: string; partTitle: string; chapterTitle: string }> = {
|
||||
'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)
|
||||
}
|
||||
|
||||
@@ -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] 加载书籍统计失败,使用默认值')
|
||||
}
|
||||
},
|
||||
|
||||
// 复制微信号
|
||||
|
||||
@@ -8,20 +8,56 @@
|
||||
<view style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<view class="content">
|
||||
<!-- 作者信息卡片 -->
|
||||
<view class="author-card">
|
||||
<view class="author-avatar">{{author.avatar}}</view>
|
||||
<text class="author-name">{{author.name}}</text>
|
||||
<text class="author-title">{{author.title}}</text>
|
||||
<text class="author-bio">{{author.bio}}</text>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
<view class="stats-row">
|
||||
<view class="stat-item" wx:for="{{author.stats}}" wx:key="label">
|
||||
<text class="stat-value">{{item.value}}</text>
|
||||
<text class="stat-label">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 亮点标签 -->
|
||||
<view class="highlights" wx:if="{{author.highlights}}">
|
||||
<view class="highlight-tag" wx:for="{{author.highlights}}" wx:key="*this">
|
||||
<text class="tag-icon">✓</text>
|
||||
<text>{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 书籍信息 -->
|
||||
<view class="book-info-card" wx:if="{{bookInfo}}">
|
||||
<text class="card-title">📚 {{bookInfo.title}}</text>
|
||||
<view class="book-stats">
|
||||
<view class="book-stat">
|
||||
<text class="book-stat-value">{{bookInfo.totalChapters}}</text>
|
||||
<text class="book-stat-label">篇章节</text>
|
||||
</view>
|
||||
<view class="book-stat">
|
||||
<text class="book-stat-value">5</text>
|
||||
<text class="book-stat-label">大篇章</text>
|
||||
</view>
|
||||
<view class="book-stat">
|
||||
<text class="book-stat-value">¥{{bookInfo.price}}</text>
|
||||
<text class="book-stat-label">全书价格</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="parts-list">
|
||||
<view class="part-item" wx:for="{{bookInfo.parts}}" wx:key="name">
|
||||
<text class="part-name">{{item.name}}</text>
|
||||
<text class="part-chapters">{{item.chapters}}节</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="contact-card">
|
||||
<text class="card-title">联系作者</text>
|
||||
<view class="contact-item" bindtap="copyWechat">
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -12,22 +12,11 @@
|
||||
</view>
|
||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<!-- 匹配次数显示 - 所有用户可见 -->
|
||||
<view class="match-count-bar">
|
||||
<view class="count-left">
|
||||
<text class="count-icon {{matchesRemaining <= 0 && !hasFullBook ? 'icon-warning' : ''}}">⚡</text>
|
||||
<text class="count-text">
|
||||
{{hasFullBook ? '无限匹配机会' : matchesRemaining <= 0 ? '今日免费次数已用完' : '今日剩余'}}
|
||||
</text>
|
||||
</view>
|
||||
<view class="count-right">
|
||||
<text class="count-value {{matchesRemaining > 0 || hasFullBook ? 'text-brand' : 'text-red'}}">
|
||||
{{hasFullBook ? '∞' : matchesRemaining + '次'}}
|
||||
</text>
|
||||
<view class="unlock-mini-btn" wx:if="{{matchesRemaining <= 0 && !hasFullBook}}" bindtap="showUnlockModal">
|
||||
¥1购买1次
|
||||
</view>
|
||||
</view>
|
||||
<!-- 匹配提示条 - 简化显示 -->
|
||||
<view class="match-tip-bar" wx:if="{{matchesRemaining <= 0 && !hasFullBook}}">
|
||||
<text class="tip-icon">⚡</text>
|
||||
<text class="tip-text">今日免费次数已用完</text>
|
||||
<view class="tip-btn" bindtap="showUnlockModal">购买次数</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
@@ -153,82 +142,91 @@
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 加入弹窗 - 填写手机号或微信号 -->
|
||||
<!-- 加入弹窗 - 简洁版 -->
|
||||
<view class="modal-overlay" wx:if="{{showJoinModal}}" bindtap="closeJoinModal">
|
||||
<view class="modal-content join-modal" catchtap="preventBubble">
|
||||
<view class="modal-content join-modal-new" catchtap="preventBubble">
|
||||
<!-- 成功状态 -->
|
||||
<block wx:if="{{joinSuccess}}">
|
||||
<view class="join-success">
|
||||
<view class="success-check">✅</view>
|
||||
<text class="success-title">加入成功!</text>
|
||||
<text class="success-desc">我们会尽快与您联系</text>
|
||||
<view class="join-success-new">
|
||||
<view class="success-icon-big">✅</view>
|
||||
<text class="success-title-new">提交成功</text>
|
||||
<text class="success-desc-new">工作人员将在24小时内与您联系</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 表单状态 -->
|
||||
<block wx:else>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">加入{{joinTypeLabel}}</text>
|
||||
<view class="close-btn" bindtap="closeJoinModal">✕</view>
|
||||
<!-- 头部 -->
|
||||
<view class="join-header">
|
||||
<view class="join-icon-wrap">
|
||||
<text class="join-icon">{{joinType === 'investor' ? '👥' : joinType === 'mentor' ? '❤️' : '🎮'}}</text>
|
||||
</view>
|
||||
<text class="join-title">加入{{joinTypeLabel}}</text>
|
||||
<text class="join-subtitle" wx:if="{{needBindFirst}}">请先绑定联系方式</text>
|
||||
<text class="join-subtitle" wx:else>填写联系方式,专人对接</text>
|
||||
<view class="close-btn-new" bindtap="closeJoinModal">✕</view>
|
||||
</view>
|
||||
|
||||
<text class="form-tip">
|
||||
{{userPhone ? '已检测到您的绑定信息,可直接提交或修改' : '请填写您的联系方式以便我们联系您'}}
|
||||
</text>
|
||||
|
||||
<!-- 联系方式类型切换 -->
|
||||
<view class="contact-tabs">
|
||||
<!-- 联系方式切换 -->
|
||||
<view class="contact-switch">
|
||||
<view
|
||||
class="contact-tab {{contactType === 'phone' ? 'tab-active-phone' : ''}}"
|
||||
class="switch-item {{contactType === 'phone' ? 'switch-active' : ''}}"
|
||||
bindtap="switchContactType"
|
||||
data-type="phone"
|
||||
>手机号</view>
|
||||
>
|
||||
<text class="switch-icon">📱</text>
|
||||
<text>手机号</text>
|
||||
</view>
|
||||
<view
|
||||
class="contact-tab {{contactType === 'wechat' ? 'tab-active-wechat' : ''}}"
|
||||
class="switch-item {{contactType === 'wechat' ? 'switch-active' : ''}}"
|
||||
bindtap="switchContactType"
|
||||
data-type="wechat"
|
||||
>微信号</view>
|
||||
>
|
||||
<text class="switch-icon">💬</text>
|
||||
<text>微信号</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<view class="input-group">
|
||||
<text class="input-label">{{contactType === 'phone' ? '手机号' : '微信号'}}</text>
|
||||
<input
|
||||
wx:if="{{contactType === 'phone'}}"
|
||||
type="number"
|
||||
class="form-input"
|
||||
placeholder="请输入11位手机号"
|
||||
placeholder-class="input-placeholder"
|
||||
value="{{phoneNumber}}"
|
||||
bindinput="onPhoneInput"
|
||||
maxlength="11"
|
||||
disabled="{{isJoining}}"
|
||||
/>
|
||||
<input
|
||||
wx:else
|
||||
type="text"
|
||||
class="form-input"
|
||||
placeholder="请输入微信号"
|
||||
placeholder-class="input-placeholder"
|
||||
value="{{wechatId}}"
|
||||
bindinput="onWechatInput"
|
||||
disabled="{{isJoining}}"
|
||||
/>
|
||||
<!-- 输入区域 -->
|
||||
<view class="input-area">
|
||||
<view class="input-wrapper">
|
||||
<text class="input-prefix">{{contactType === 'phone' ? '+86' : '@'}}</text>
|
||||
<input
|
||||
wx:if="{{contactType === 'phone'}}"
|
||||
type="number"
|
||||
class="input-field"
|
||||
placeholder="请输入11位手机号"
|
||||
placeholder-class="input-placeholder-new"
|
||||
value="{{phoneNumber}}"
|
||||
bindinput="onPhoneInput"
|
||||
maxlength="11"
|
||||
disabled="{{isJoining}}"
|
||||
focus="{{contactType === 'phone'}}"
|
||||
/>
|
||||
<input
|
||||
wx:else
|
||||
type="text"
|
||||
class="input-field"
|
||||
placeholder="请输入微信号"
|
||||
placeholder-class="input-placeholder-new"
|
||||
value="{{wechatId}}"
|
||||
bindinput="onWechatInput"
|
||||
disabled="{{isJoining}}"
|
||||
focus="{{contactType === 'wechat'}}"
|
||||
/>
|
||||
</view>
|
||||
<text class="error-msg" wx:if="{{joinError}}">{{joinError}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<text class="error-text" wx:if="{{joinError}}">{{joinError}}</text>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view
|
||||
class="btn-primary submit-btn {{isJoining || !(contactType === 'phone' ? phoneNumber : wechatId) ? 'btn-disabled' : ''}}"
|
||||
class="submit-btn-new {{isJoining || !(contactType === 'phone' ? phoneNumber : wechatId) ? 'btn-disabled-new' : ''}}"
|
||||
bindtap="handleJoinSubmit"
|
||||
>
|
||||
<text wx:if="{{isJoining}}">提交中...</text>
|
||||
<text wx:else>确认加入</text>
|
||||
{{isJoining ? '提交中...' : '确认提交'}}
|
||||
</view>
|
||||
|
||||
<text class="form-notice">提交即表示同意我们的服务条款</text>
|
||||
<text class="form-notice-new">提交后我们会尽快与您联系</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user