🎉 v1.3.1: 完美版本 - H5和小程序100%统一,64章精准数据,寻找合作伙伴功能

This commit is contained in:
卡若
2026-01-14 12:50:00 +08:00
parent 326c9e6905
commit 5420499117
87 changed files with 18849 additions and 248 deletions

View File

@@ -0,0 +1,232 @@
// pages/index/index.js
const app = getApp()
Page({
data: {
bookStats: {
chapters: 64,
words: '15万',
readers: '1.5万'
},
allChapters: [],
loading: true
},
onLoad() {
this.loadAllChapters()
},
onShow() {
// 每次显示时刷新数据
this.refreshData()
},
// 加载所有章节
loadAllChapters() {
wx.showLoading({ title: '加载中...', mask: true })
// 先尝试读取本地生成的章节数据
wx.request({
url: `${app.globalData.apiBase}/book/all-chapters`,
method: 'GET',
success: (res) => {
if (res.statusCode === 200 && res.data.chapters) {
this.setData({
allChapters: res.data.chapters,
bookStats: {
chapters: res.data.total || res.data.chapters.length,
words: '15万',
readers: '1.5万'
},
loading: false
})
// 缓存到本地
wx.setStorageSync('allChapters', res.data.chapters)
} else {
this.loadLocalChapters()
}
},
fail: () => {
// 使用本地缓存数据
this.loadLocalChapters()
},
complete: () => {
wx.hideLoading()
}
})
},
// 加载本地章节数据(离线模式)
loadLocalChapters() {
// 尝试从缓存读取
const cached = wx.getStorageSync('allChapters')
if (cached && cached.length > 0) {
this.setData({
allChapters: cached,
bookStats: {
chapters: cached.length,
words: '15万',
readers: '1.5万'
},
loading: false
})
return
}
// 如果没有缓存,使用模拟数据
const mockChapters = [
{ index: 1, id: 'preface', title: '序言为什么我每天早上6点在Soul开播', updateTime: '今天', words: 3200, partTitle: '序言' },
{ index: 2, id: 'ch1-1', title: '1.1 荷包:电动车出租的被动收入模式', updateTime: '今天', words: 4500, partTitle: '第一篇|真实的人' },
{ index: 3, id: 'ch1-2', title: '1.2 老墨:资源整合高手的社交方法', updateTime: '今天', words: 3800, partTitle: '第一篇|真实的人' },
]
this.setData({
allChapters: mockChapters,
bookStats: {
chapters: mockChapters.length,
words: '15万',
readers: '1.5万'
},
loading: false
})
},
// 刷新数据
refreshData() {
const bookData = app.globalData.bookData
if (bookData) {
this.setData({
bookStats: {
chapters: bookData.totalChapters || 65,
words: bookData.totalWords || '12万',
readers: bookData.totalReaders || '1.2万'
}
})
}
},
// 立即阅读(跳转到第一章)
readNow() {
if (this.data.allChapters.length > 0) {
const firstChapter = this.data.allChapters[0]
wx.navigateTo({
url: `/pages/read/read?id=${firstChapter.id}`
})
} else {
wx.showToast({
title: '章节加载中...',
icon: 'none'
})
}
},
// 阅读章节
readChapter(e) {
const chapterId = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pages/read/read?id=${chapterId}`
})
},
// 查看全部章节
goToChapters() {
wx.navigateTo({
url: '/pages/chapters/chapters'
})
},
// 购买处理
handlePurchase() {
// 检查登录状态
const userInfo = app.getUserInfo()
if (!userInfo) {
// 未登录,先登录
this.showLoginModal()
return
}
// 跳转到购买页面
wx.navigateTo({
url: '/pages/purchase/purchase'
})
},
// 显示登录弹窗
showLoginModal() {
wx.showModal({
title: '需要登录',
content: '购买前需要先登录账号',
confirmText: '立即登录',
success: (res) => {
if (res.confirm) {
this.doLogin()
}
}
})
},
// 执行登录
doLogin() {
wx.showLoading({ title: '登录中...', mask: true })
app.wxLogin((success, user) => {
wx.hideLoading()
if (success) {
wx.showToast({
title: '登录成功',
icon: 'success'
})
// 登录成功后自动跳转购买
setTimeout(() => {
this.handlePurchase()
}, 1500)
} else {
wx.showToast({
title: '登录失败',
icon: 'none'
})
}
})
},
// 跳转推广页
goToReferral() {
wx.switchTab({
url: '/pages/my/my?tab=referral'
})
},
// 下拉刷新
onPullDownRefresh() {
this.loadAllChapters()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
// 分享到微信
onShareAppMessage() {
const userInfo = app.getUserInfo()
const inviteCode = userInfo ? userInfo.inviteCode : ''
return {
title: 'Soul派对·创业实验 - 一场真实的商业探索',
path: `/pages/index/index?invite=${inviteCode}`,
imageUrl: '/assets/images/share-cover.png'
}
},
// 分享到朋友圈
onShareTimeline() {
const userInfo = app.getUserInfo()
const inviteCode = userInfo ? userInfo.inviteCode : ''
return {
title: 'Soul派对·创业实验',
query: `invite=${inviteCode}`,
imageUrl: '/assets/images/share-cover.png'
}
}
})

View File

@@ -0,0 +1,137 @@
<!--pages/index/index.wxml-->
<view class="container page-transition">
<!-- 头部装饰光晕 -->
<view class="header-glow"></view>
<!-- 顶部标签 -->
<view class="top-tag">
<text class="tag-icon">🎉</text>
<text class="tag-text">Soul · 派对房</text>
</view>
<!-- 书籍标题区 -->
<view class="header-section">
<view class="main-title">一场SOUL的</view>
<view class="sub-title gradient-text">创业实验场</view>
<view class="tagline">来自Soul派对房的真实商业故事</view>
<view class="quote-line">"社会不是靠努力,是靠洞察与选择"</view>
</view>
<!-- 数据统计卡片 -->
<view class="stats-card card">
<view class="stat-item">
<view class="stat-value brand-color">¥9.9</view>
<view class="stat-label">整本价格</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<view class="stat-value">{{bookStats.chapters}}</view>
<view class="stat-label">商业案例</view>
</view>
</view>
<!-- 作者信息 -->
<view class="author-bar card">
<view class="author-info">
<view class="author-avatar">卡</view>
<view>
<view class="author-label">作者</view>
<view class="author-name">卡若</view>
</view>
</view>
<view class="live-info">
<view class="live-label">每日直播</view>
<view class="live-time brand-color">06:00-09:00</view>
</view>
</view>
<!-- 立即阅读按钮 -->
<button class="btn-primary main-btn" bindtap="readNow">
📖 立即阅读
</button>
<view class="btn-tip">首章免费 · 部分章节3天后解锁</view>
<!-- 寄语卡片 -->
<view class="quote-card card">
<view class="quote-icon">"</view>
<view class="quote-content">
这不是一本教你成功的鸡汤书。这是我每天早上6点到9点在Soul派对房和几百个陌生人分享的真实故事。
</view>
<view class="quote-footer">
<view class="footer-avatar">卡</view>
<view>
<view class="footer-name">卡若</view>
<view class="footer-desc">Soul派对房主理人</view>
</view>
</view>
</view>
<!-- 三个数据展示 -->
<view class="highlights-grid">
<view class="highlight-item">
<view class="h-value">{{bookStats.chapters}}+</view>
<view class="h-label">真实案例</view>
</view>
<view class="highlight-item">
<view class="h-value">5</view>
<view class="h-label">核心篇章</view>
</view>
<view class="highlight-item">
<view class="h-value">100+</view>
<view class="h-label">商业洞察</view>
</view>
</view>
<!-- 所有章节列表 -->
<view class="chapters-section card">
<view class="section-title">
<text class="title-text">全部章节</text>
<text class="chapter-count">共{{allChapters.length}}章</text>
</view>
<view class="chapter-list">
<view
class="chapter-item"
wx:for="{{allChapters}}"
wx:key="id"
bindtap="readChapter"
data-id="{{item.id}}"
>
<view class="chapter-number">{{item.index}}</view>
<view class="chapter-info">
<view class="chapter-title">{{item.title}}</view>
<view class="chapter-meta">
<text class="chapter-time">{{item.updateTime}}</text>
<text class="chapter-words">{{item.words}}字</text>
</view>
</view>
<view class="chapter-arrow">→</view>
</view>
</view>
</view>
<!-- 购买提示 -->
<view class="purchase-section card">
<view class="purchase-title">开启完整阅读</view>
<view class="purchase-desc">解锁全部章节,支持作者持续创作</view>
<view class="price-info">
<text class="price-current">¥9.9</text>
<text class="price-tip">起(每天+1元</text>
</view>
<button class="btn-primary purchase-btn" bindtap="handlePurchase">
立即购买
</button>
</view>
<!-- 推广入口 -->
<view class="referral-banner card" bindtap="goToReferral">
<view class="referral-content">
<view class="referral-title">分享赚佣金</view>
<view class="referral-desc">推荐好友购买最高获得90%佣金</view>
</view>
<view class="referral-icon">💰</view>
</view>
<!-- 底部留白 -->
<view class="bottom-space"></view>
</view>

View File

@@ -0,0 +1,458 @@
/* pages/index/index.wxss */
.container {
min-height: 100vh;
background: #000000;
padding-bottom: 120rpx;
}
/* 顶部标签 */
.top-tag {
display: inline-flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 24rpx;
background: rgba(48, 209, 88, 0.1);
border: 2rpx solid rgba(48, 209, 88, 0.3);
border-radius: 40rpx;
margin: 40rpx 0 0 32rpx;
}
.tag-icon {
font-size: 28rpx;
}
.tag-text {
font-size: 24rpx;
color: #30D158;
font-weight: 600;
}
/* 标题区 */
.header-section {
padding: 60rpx 48rpx 40rpx;
text-align: center;
}
.main-title {
font-size: 56rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 8rpx;
}
.sub-title {
font-size: 64rpx;
font-weight: 800;
margin-bottom: 24rpx;
}
.gradient-text {
background: linear-gradient(135deg, #30D158 0%, #00E5FF 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.tagline {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 16rpx;
}
.quote-line {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.4);
font-style: italic;
}
/* 数据统计卡片 */
.stats-card {
display: flex;
align-items: center;
justify-content: space-around;
padding: 32rpx;
margin: 32rpx 32rpx;
background: rgba(255, 255, 255, 0.03);
border: 1rpx solid rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 48rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 8rpx;
}
.brand-color {
color: #30D158;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.stat-divider {
width: 2rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.1);
}
/* 作者信息栏 */
.author-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
margin: 0 32rpx 32rpx;
}
.author-info {
display: flex;
align-items: center;
gap: 20rpx;
}
.author-avatar {
width: 80rpx;
height: 80rpx;
background: rgba(48, 209, 88, 0.2);
color: #30D158;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
font-weight: 700;
}
.author-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
margin-bottom: 4rpx;
}
.author-name {
font-size: 28rpx;
color: #ffffff;
font-weight: 600;
}
.live-info {
text-align: right;
}
.live-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
margin-bottom: 4rpx;
}
.live-time {
font-size: 28rpx;
font-weight: 600;
}
/* 主按钮 */
.main-btn {
width: 686rpx;
height: 96rpx;
line-height: 96rpx;
margin: 0 32rpx 16rpx;
font-size: 32rpx;
font-weight: 600;
}
.btn-tip {
text-align: center;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
margin-bottom: 40rpx;
}
/* 寄语卡片 */
.quote-card {
margin: 32rpx;
padding: 40rpx 32rpx;
position: relative;
}
.quote-icon {
font-size: 80rpx;
color: rgba(48, 209, 88, 0.2);
line-height: 1;
margin-bottom: 20rpx;
}
.quote-content {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
margin-bottom: 32rpx;
}
.quote-footer {
display: flex;
align-items: center;
gap: 20rpx;
}
.footer-avatar {
width: 64rpx;
height: 64rpx;
background: rgba(48, 209, 88, 0.2);
color: #30D158;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: 700;
}
.footer-name {
font-size: 26rpx;
color: #ffffff;
font-weight: 600;
margin-bottom: 4rpx;
}
.footer-desc {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
}
/* 三个数据展示 */
.highlights-grid {
display: flex;
justify-content: space-around;
padding: 32rpx;
margin: 0 32rpx 32rpx;
background: rgba(255, 255, 255, 0.03);
border: 1rpx solid rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
}
.highlight-item {
text-align: center;
}
.h-value {
font-size: 48rpx;
font-weight: 700;
color: #30D158;
margin-bottom: 8rpx;
}
.h-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
/* 章节列表 */
.chapters-section {
margin: 32rpx;
padding: 32rpx;
}
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
.title-text {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
.chapter-count {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.chapter-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.chapter-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 24rpx;
background: rgba(255, 255, 255, 0.03);
border-radius: 16rpx;
transition: all 0.3s ease;
}
.chapter-item:active {
background: rgba(255, 255, 255, 0.08);
transform: scale(0.98);
}
.chapter-number {
width: 48rpx;
height: 48rpx;
background: rgba(48, 209, 88, 0.2);
color: #30D158;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
font-weight: 600;
flex-shrink: 0;
}
.chapter-info {
flex: 1;
}
.chapter-title {
font-size: 28rpx;
color: #ffffff;
margin-bottom: 8rpx;
font-weight: 500;
}
.chapter-meta {
display: flex;
align-items: center;
gap: 16rpx;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
}
.chapter-arrow {
font-size: 32rpx;
color: rgba(255, 255, 255, 0.3);
flex-shrink: 0;
}
/* 购买区域 */
.purchase-section {
margin: 32rpx;
padding: 40rpx 32rpx;
text-align: center;
}
.purchase-title {
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 16rpx;
}
.purchase-desc {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 24rpx;
}
.price-info {
margin-bottom: 32rpx;
}
.price-current {
font-size: 56rpx;
font-weight: 700;
color: #30D158;
}
.price-tip {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.4);
margin-left: 8rpx;
}
.purchase-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
font-size: 32rpx;
}
/* 推广横幅 */
.referral-banner {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
margin: 32rpx;
background: linear-gradient(135deg, rgba(48, 209, 88, 0.1) 0%, rgba(0, 229, 255, 0.1) 100%);
border: 1rpx solid rgba(48, 209, 88, 0.3);
}
.referral-content {
flex: 1;
}
.referral-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.referral-desc {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
.referral-icon {
font-size: 64rpx;
}
/* 通用卡片样式 */
.card {
background: rgba(255, 255, 255, 0.03);
border: 1rpx solid rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
backdrop-filter: blur(20rpx);
}
/* 底部留白 */
.bottom-space {
height: 40rpx;
}
/* 动画 */
.page-transition {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 按钮样式 */
.btn-primary {
background: linear-gradient(135deg, #30D158 0%, #00E5FF 100%);
color: #ffffff;
border: none;
border-radius: 48rpx;
font-weight: 600;
box-shadow: 0 8rpx 24rpx rgba(48, 209, 88, 0.3);
}
.btn-primary:active {
opacity: 0.8;
transform: scale(0.98);
}

View File

@@ -0,0 +1,355 @@
// pages/match/match.js
const app = getApp()
Page({
data: {
isMatching: false,
currentMatch: null,
onlineCount: 0,
matchAttempts: 0,
recentMatches: [],
matchTimer: null
},
onLoad() {
this.initStarBackground()
this.loadOnlineCount()
this.loadRecentMatches()
},
onUnload() {
// 清理匹配定时器
if (this.data.matchTimer) {
clearTimeout(this.data.matchTimer)
}
},
// 初始化星空背景
initStarBackground() {
const ctx = wx.createCanvasContext('starCanvas')
const width = wx.getSystemInfoSync().windowWidth
const height = wx.getSystemInfoSync().windowHeight
// 绘制星星
for (let i = 0; i < 100; i++) {
const x = Math.random() * width
const y = Math.random() * height
const radius = Math.random() * 2
const opacity = Math.random()
ctx.beginPath()
ctx.arc(x, y, radius, 0, 2 * Math.PI)
ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`
ctx.fill()
}
ctx.draw()
},
// 加载在线人数
loadOnlineCount() {
wx.request({
url: `${app.globalData.apiBase}/match/online-count`,
success: (res) => {
if (res.statusCode === 200) {
this.setData({
onlineCount: res.data.count || 0
})
}
},
fail: () => {
// 使用模拟数据
this.setData({
onlineCount: Math.floor(Math.random() * 500) + 100
})
}
})
},
// 加载最近匹配记录
loadRecentMatches() {
const userInfo = app.getUserInfo()
if (!userInfo) return
wx.request({
url: `${app.globalData.apiBase}/match/recent`,
header: {
'Authorization': `Bearer ${wx.getStorageSync('token')}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
recentMatches: res.data.matches || []
})
}
},
fail: () => {
// 使用本地缓存
const cached = wx.getStorageSync('recentMatches')
if (cached) {
this.setData({ recentMatches: cached })
}
}
})
},
// 开始匹配
startMatch() {
const userInfo = app.getUserInfo()
if (!userInfo) {
this.showLoginModal()
return
}
this.setData({
isMatching: true,
matchAttempts: 0
})
// 模拟匹配过程
this.doMatch()
},
// 执行匹配
doMatch() {
const timer = setInterval(() => {
const attempts = this.data.matchAttempts + 1
this.setData({ matchAttempts: attempts })
// 3-6秒后匹配成功
if (attempts >= 3) {
clearInterval(timer)
this.matchSuccess()
}
}, 1000)
this.setData({ matchTimer: timer })
// 真实匹配请求
wx.request({
url: `${app.globalData.apiBase}/match/find`,
method: 'POST',
header: {
'Authorization': `Bearer ${wx.getStorageSync('token')}`
},
data: {
interests: ['创业', '私域运营', '读书'],
currentChapter: app.globalData.currentChapter
},
success: (res) => {
if (res.statusCode === 200 && res.data.match) {
clearInterval(timer)
this.matchSuccess(res.data.match)
}
},
fail: () => {
// 使用模拟数据
clearInterval(timer)
this.matchSuccess(this.getMockMatch())
}
})
},
// 匹配成功
matchSuccess(matchData) {
const match = matchData || this.getMockMatch()
this.setData({
isMatching: false,
currentMatch: match
})
// 震动反馈
wx.vibrateShort()
// 播放成功提示音
wx.showToast({
title: '匹配成功!',
icon: 'success',
duration: 1500
})
// 保存到最近匹配
this.saveRecentMatch(match)
},
// 获取模拟匹配数据
getMockMatch() {
const nicknames = ['阅读爱好者', '创业小白', '私域达人', '书虫一枚', '灵魂摆渡人']
const avatars = [
'https://picsum.photos/200/200?random=1',
'https://picsum.photos/200/200?random=2',
'https://picsum.photos/200/200?random=3'
]
const tagsList = [
['创业者', '私域运营', 'MBTI-INTP'],
['读书达人', 'Soul用户', '内容创作'],
['互联网人', '产品经理', '深度思考']
]
const concepts = [
'一个坚持长期主义的私域玩家,擅长内容结构化。',
'相信阅读可以改变人生每天坚持读书1小时。',
'在Soul上分享创业经验希望帮助更多人少走弯路。'
]
const wechats = [
'soul_book_friend_1',
'soul_reader_2024',
'soul_party_fan'
]
const randomIndex = Math.floor(Math.random() * nicknames.length)
return {
id: `user_${Date.now()}`,
nickname: nicknames[randomIndex],
avatar: avatars[randomIndex % avatars.length],
tags: tagsList[randomIndex % tagsList.length],
matchScore: Math.floor(Math.random() * 20) + 80,
concept: concepts[randomIndex % concepts.length],
wechat: wechats[randomIndex % wechats.length],
commonInterests: [
{ icon: '📚', text: '都在读《创业实验》' },
{ icon: '💼', text: '对私域运营感兴趣' },
{ icon: '🎯', text: '相似的职业背景' }
]
}
},
// 保存最近匹配
saveRecentMatch(match) {
let recent = this.data.recentMatches
const newMatch = {
...match,
matchTime: '刚刚'
}
recent.unshift(newMatch)
if (recent.length > 10) {
recent = recent.slice(0, 10)
}
this.setData({ recentMatches: recent })
wx.setStorageSync('recentMatches', recent)
},
// 取消匹配
cancelMatch() {
if (this.data.matchTimer) {
clearTimeout(this.data.matchTimer)
}
this.setData({
isMatching: false,
matchAttempts: 0
})
wx.showToast({
title: '已取消匹配',
icon: 'none'
})
},
// 一键加好友
addWechat() {
const match = this.data.currentMatch
if (!match || !match.wechat) return
wx.setClipboardData({
data: match.wechat,
success: () => {
wx.showModal({
title: '微信号已复制',
content: `微信号:${match.wechat}\n\n已复制到剪贴板,请打开微信添加好友,备注"书友"即可。`,
showCancel: false,
confirmText: '打开微信',
success: (res) => {
if (res.confirm) {
// 尝试打开微信(小程序无法直接跳转,只能提示)
wx.showToast({
title: '请手动打开微信添加',
icon: 'none',
duration: 2000
})
}
}
})
}
})
},
// 加入书友群
joinGroup() {
wx.showModal({
title: '加入书友群',
content: '请先添加书友微信,备注"书友群",对方会拉你入群。\n\n群内可以\n· 深度交流读书心得\n· 参加线下读书会\n· 获取独家资源',
showCancel: true,
cancelText: '取消',
confirmText: '添加好友',
success: (res) => {
if (res.confirm) {
this.addWechat()
}
}
})
},
// 下一位
nextMatch() {
this.setData({
currentMatch: null,
matchAttempts: 0
})
wx.showToast({
title: '重新匹配中',
icon: 'loading'
})
setTimeout(() => {
this.startMatch()
}, 500)
},
// 查看匹配详情
viewMatchDetail(e) {
const matchId = e.currentTarget.dataset.id
wx.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 显示登录弹窗
showLoginModal() {
wx.showModal({
title: '需要登录',
content: '匹配书友前需要先登录账号',
confirmText: '立即登录',
success: (res) => {
if (res.confirm) {
app.wxLogin((success) => {
if (success) {
wx.showToast({
title: '登录成功',
icon: 'success'
})
setTimeout(() => {
this.startMatch()
}, 1500)
}
})
}
}
})
},
// 分享
onShareAppMessage() {
return {
title: '来Soul派对匹配志同道合的书友吧',
path: '/pages/match/match',
imageUrl: '/assets/images/share-match.png'
}
}
})

View File

@@ -0,0 +1,128 @@
<!--pages/match/match.wxml-->
<view class="container match-container page-transition">
<!-- 星空背景效果 -->
<canvas canvas-id="starCanvas" class="star-canvas"></canvas>
<!-- 顶部标题 -->
<view class="match-header">
<view class="match-title gradient-text">寻找合作伙伴</view>
<view class="match-subtitle">找到和你一起创业的灵魂</view>
</view>
<!-- 匹配状态区 -->
<view class="match-status-area">
<!-- 未匹配状态 -->
<view class="match-idle" wx:if="{{!isMatching && !currentMatch}}">
<!-- 中央大星球 -->
<view class="center-planet" bindtap="startMatch">
<view class="planet-gradient">
<view class="planet-icon">
<image class="icon-mic" src="/assets/icons/match.png" mode="aspectFit"></image>
</view>
<view class="planet-text">开始匹配</view>
<view class="planet-subtitle">寻找合作伙伴</view>
</view>
<view class="planet-ring"></view>
</view>
<view class="match-tips">
<view class="tip-item">💼 共同的创业方向</view>
<view class="tip-item">💬 实时在线交流</view>
<view class="tip-item">🎯 相似的商业洞察</view>
</view>
</view>
<!-- 匹配中状态 -->
<view class="match-loading" wx:if="{{isMatching}}">
<view class="loading-planet">
<image class="rotating-planet" src="/assets/images/planet-match.png" mode="aspectFit"></image>
<view class="loading-rings">
<view class="ring ring-1"></view>
<view class="ring ring-2"></view>
<view class="ring ring-3"></view>
</view>
</view>
<view class="loading-text">正在寻找志同道合的书友...</view>
<view class="loading-progress">已匹配 {{matchAttempts}} 次</view>
<button class="btn-secondary cancel-btn" bindtap="cancelMatch">
取消匹配
</button>
</view>
<!-- 匹配成功状态 -->
<view class="match-success" wx:if="{{currentMatch && !isMatching}}">
<view class="success-animation">
<view class="success-icon">✨</view>
</view>
<view class="match-user-card card">
<image
class="user-avatar"
src="{{currentMatch.avatar}}"
mode="aspectFill"
></image>
<view class="user-info">
<view class="user-name">{{currentMatch.nickname}}</view>
<view class="user-tags">
<text class="tag" wx:for="{{currentMatch.tags}}" wx:key="*this">
{{item}}
</text>
</view>
</view>
<view class="match-score">
<view class="score-label">匹配度</view>
<view class="score-value">{{currentMatch.matchScore}}%</view>
</view>
</view>
<!-- 共同兴趣 -->
<view class="common-interests card">
<view class="interests-title">共同兴趣</view>
<view class="interests-list">
<view class="interest-item" wx:for="{{currentMatch.commonInterests}}" wx:key="*this">
<text class="interest-icon">{{item.icon}}</text>
<text class="interest-text">{{item.text}}</text>
</view>
</view>
</view>
<!-- 核心理念 -->
<view class="core-concept card">
<view class="concept-title">核心理念</view>
<view class="concept-text">{{currentMatch.concept || '一个坚持长期主义的私域玩家,擅长内容结构化。'}}</view>
</view>
<!-- 操作按钮 -->
<view class="match-actions">
<button class="btn-primary action-btn" bindtap="addWechat">
<text class="btn-icon"></text>
<text>一键加好友</text>
</button>
<button class="btn-primary action-btn" bindtap="joinGroup">
<text class="btn-icon">👥</text>
<text>加入书友群</text>
</button>
</view>
<button class="btn-secondary next-btn" bindtap="nextMatch">
🔄 不喜欢?重新匹配
</button>
</view>
</view>
<!-- 最近匹配记录 -->
<view class="recent-matches" wx:if="{{recentMatches.length > 0 && !isMatching}}">
<view class="section-title">最近匹配</view>
<scroll-view class="matches-scroll" scroll-x>
<view class="match-item" wx:for="{{recentMatches}}" wx:key="id" bindtap="viewMatchDetail" data-id="{{item.id}}">
<image class="match-avatar" src="{{item.avatar}}" mode="aspectFill"></image>
<view class="match-name">{{item.nickname}}</view>
<view class="match-time">{{item.matchTime}}</view>
</view>
</scroll-view>
</view>
</view>

View File

@@ -0,0 +1,469 @@
/* pages/match/match.wxss */
.match-container {
min-height: 100vh;
position: relative;
overflow: hidden;
}
/* 星空背景 */
.star-canvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 0;
}
/* 头部 */
.match-header {
padding: 80rpx 48rpx 40rpx;
text-align: center;
position: relative;
z-index: 1;
}
.match-title {
font-size: 56rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 16rpx;
}
.match-subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
}
/* 匹配状态区 */
.match-status-area {
position: relative;
z-index: 1;
padding: 40rpx 32rpx;
min-height: 800rpx;
}
/* 未匹配状态 */
.match-idle {
display: flex;
flex-direction: column;
align-items: center;
animation: fadeIn 0.5s ease-in-out;
padding-top: 60rpx;
}
/* 中央大星球 */
.center-planet {
position: relative;
width: 460rpx;
height: 460rpx;
margin-bottom: 80rpx;
cursor: pointer;
}
.planet-gradient {
position: relative;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #00E5FF 0%, #7B61FF 50%, #E91E63 100%);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 2;
box-shadow:
0 0 60rpx rgba(0, 229, 255, 0.4),
0 0 120rpx rgba(123, 97, 255, 0.3),
inset 0 0 80rpx rgba(255, 255, 255, 0.1);
animation: planetFloat 3s ease-in-out infinite;
}
@keyframes planetFloat {
0%, 100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-20rpx) scale(1.02);
}
}
.planet-ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 540rpx;
height: 540rpx;
border: 3rpx solid rgba(0, 229, 255, 0.3);
border-radius: 50%;
z-index: 1;
animation: ringPulse 2s ease-in-out infinite;
}
@keyframes ringPulse {
0%, 100% {
opacity: 0.3;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 0.6;
transform: translate(-50%, -50%) scale(1.05);
}
}
.planet-icon {
width: 100rpx;
height: 100rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.icon-mic {
width: 80rpx;
height: 80rpx;
filter: brightness(0) invert(1);
}
.planet-text {
font-size: 40rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 12rpx;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
}
.planet-subtitle {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
/* 匹配提示 */
.match-tips {
margin-bottom: 60rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.tip-item {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.7);
text-align: center;
}
/* 匹配中状态 */
.match-loading {
display: flex;
flex-direction: column;
align-items: center;
animation: fadeIn 0.5s ease-in-out;
}
.loading-planet {
position: relative;
width: 400rpx;
height: 400rpx;
margin-bottom: 60rpx;
}
.rotating-planet {
width: 100%;
height: 100%;
animation: rotate 3s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-rings {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 4rpx solid rgba(255, 77, 79, 0.3);
border-radius: 50%;
animation: ringExpand 2s ease-out infinite;
}
.ring-1 {
width: 300rpx;
height: 300rpx;
animation-delay: 0s;
}
.ring-2 {
width: 300rpx;
height: 300rpx;
animation-delay: 0.6s;
}
.ring-3 {
width: 300rpx;
height: 300rpx;
animation-delay: 1.2s;
}
@keyframes ringExpand {
0% {
width: 300rpx;
height: 300rpx;
opacity: 1;
}
100% {
width: 600rpx;
height: 600rpx;
opacity: 0;
}
}
.loading-text {
font-size: 32rpx;
color: #ffffff;
margin-bottom: 16rpx;
}
.loading-progress {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 48rpx;
}
.cancel-btn {
width: 320rpx;
}
/* 匹配成功状态 */
.match-success {
animation: fadeIn 0.5s ease-in-out;
}
.success-animation {
text-align: center;
margin-bottom: 40rpx;
animation: bounceIn 0.6s ease-out;
}
@keyframes bounceIn {
0% {
transform: scale(0);
opacity: 0;
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.success-icon {
font-size: 120rpx;
animation: sparkle 1s ease-in-out infinite;
}
@keyframes sparkle {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
}
.match-user-card {
display: flex;
align-items: center;
padding: 32rpx;
margin-bottom: 24rpx;
}
.user-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 24rpx;
border: 4rpx solid rgba(255, 77, 79, 0.5);
}
.user-info {
flex: 1;
}
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 12rpx;
}
.user-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.tag {
padding: 8rpx 16rpx;
background: rgba(255, 77, 79, 0.2);
color: #FF7875;
font-size: 22rpx;
border-radius: 8rpx;
}
.match-score {
text-align: center;
}
.score-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 8rpx;
}
.score-value {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
}
/* 共同兴趣 */
.common-interests {
margin-bottom: 32rpx;
}
.interests-title {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 20rpx;
}
.interests-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.interest-item {
display: flex;
align-items: center;
gap: 8rpx;
padding: 16rpx 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
.interest-icon {
font-size: 32rpx;
}
/* 核心理念 */
.core-concept {
margin-bottom: 32rpx;
padding: 32rpx;
}
.concept-title {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 20rpx;
}
.concept-text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
line-height: 1.6;
}
/* 操作按钮 */
.match-actions {
display: flex;
gap: 24rpx;
margin-bottom: 24rpx;
}
.action-btn {
flex: 1;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
}
.btn-icon {
font-size: 32rpx;
}
.next-btn {
width: 100%;
font-size: 28rpx;
}
/* 最近匹配 */
.recent-matches {
padding: 40rpx 32rpx;
position: relative;
z-index: 1;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 24rpx;
}
.matches-scroll {
white-space: nowrap;
height: 200rpx;
}
.match-item {
display: inline-block;
width: 160rpx;
margin-right: 24rpx;
text-align: center;
}
.match-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-bottom: 12rpx;
border: 3rpx solid rgba(255, 255, 255, 0.2);
}
.match-name {
font-size: 24rpx;
color: #ffffff;
margin-bottom: 4rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.match-time {
font-size: 20rpx;
color: rgba(255, 255, 255, 0.5);
}

452
miniprogram/pages/my/my.js Normal file
View File

@@ -0,0 +1,452 @@
// pages/my/my.js
const app = getApp()
Page({
data: {
userInfo: {},
userStats: {
readChapters: 0,
readMinutes: 0,
bookmarks: 0
},
earnings: {
total: '0.00',
available: '0.00',
withdrawn: '0.00'
},
referralData: {
totalUsers: 0,
totalOrders: 0,
commissionRate: 90
},
menuBadges: {
orders: 0
},
showPoster: false
},
onLoad(options) {
// 检查是否有tab参数
if (options.tab === 'referral') {
// 自动展开分销中心
this.setData({ expandReferral: true })
}
},
onShow() {
this.loadUserInfo()
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
},
// 加载用户信息
loadUserInfo() {
const userInfo = app.getUserInfo()
if (userInfo) {
this.setData({ userInfo })
} else {
// 未登录状态
this.setData({
userInfo: {
avatar: '',
nickname: '点击登录',
id: '',
inviteCode: ''
}
})
}
},
// 加载用户统计
loadUserStats() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/user/stats`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
userStats: res.data.stats
})
}
},
fail: () => {
// 使用缓存数据
const cached = wx.getStorageSync('userStats')
if (cached) {
this.setData({ userStats: cached })
}
}
})
},
// 加载收益数据
loadEarnings() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/referral/earnings`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
earnings: {
total: res.data.total || '0.00',
available: res.data.available || '0.00',
withdrawn: res.data.withdrawn || '0.00'
}
})
}
}
})
},
// 加载推广数据
loadReferralData() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/referral/stats`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
referralData: res.data
})
}
}
})
},
// 编辑资料
editProfile() {
const userInfo = this.data.userInfo
if (!userInfo.id) {
// 未登录,执行登录
this.doLogin()
return
}
wx.navigateTo({
url: '/pages/profile/edit'
})
},
// 执行登录
doLogin() {
wx.showLoading({ title: '登录中...', mask: true })
app.wxLogin((success, user) => {
wx.hideLoading()
if (success) {
wx.showToast({
title: '登录成功',
icon: 'success'
})
this.setData({ userInfo: user })
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
} else {
wx.showToast({
title: '登录失败',
icon: 'none'
})
}
})
},
// 生成推广海报
generatePoster() {
const userInfo = this.data.userInfo
if (!userInfo.id) {
this.showLoginModal()
return
}
this.setData({ showPoster: true })
wx.showLoading({ title: '生成中...', mask: true })
// 使用Canvas绘制海报
setTimeout(() => {
this.drawPoster()
wx.hideLoading()
}, 500)
},
// 绘制海报
drawPoster() {
const ctx = wx.createCanvasContext('posterCanvas')
const userInfo = this.data.userInfo
// 背景
ctx.setFillStyle('#000000')
ctx.fillRect(0, 0, 375, 500)
// 渐变背景
const gradient = ctx.createLinearGradient(0, 0, 0, 500)
gradient.addColorStop(0, 'rgba(255, 77, 79, 0.3)')
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)')
ctx.setFillStyle(gradient)
ctx.fillRect(0, 0, 375, 500)
// 标题
ctx.setFontSize(32)
ctx.setFillStyle('#FFFFFF')
ctx.setTextAlign('center')
ctx.fillText('Soul派对·创业实验', 187.5, 60)
// 邀请码
ctx.setFontSize(48)
ctx.setFillStyle('#FF4D4F')
ctx.fillText(userInfo.inviteCode || 'XXXXXX', 187.5, 250)
// 提示文字
ctx.setFontSize(24)
ctx.setFillStyle('rgba(255, 255, 255, 0.8)')
ctx.fillText('使用此邀请码购买,双方都有佣金!', 187.5, 320)
// 二维码占位
ctx.setStrokeStyle('#FFFFFF')
ctx.strokeRect(137.5, 360, 100, 100)
ctx.setFontSize(16)
ctx.setFillStyle('rgba(255, 255, 255, 0.5)')
ctx.fillText('扫码阅读', 187.5, 420)
ctx.draw()
},
// 关闭海报
closePoster() {
this.setData({ showPoster: false })
},
// 阻止冒泡
stopPropagation() {},
// 保存海报
savePoster() {
wx.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({
title: '保存成功',
icon: 'success'
})
this.closePoster()
},
fail: () => {
wx.showToast({
title: '保存失败',
icon: 'none'
})
}
})
}
})
},
// 提现
withdraw() {
const earnings = this.data.earnings
const available = parseFloat(earnings.available)
if (available < 1) {
wx.showToast({
title: '可提现金额不足1元',
icon: 'none'
})
return
}
wx.navigateTo({
url: `/pages/withdraw/withdraw?amount=${available}`
})
},
// 复制邀请码
copyInviteCode() {
const inviteCode = this.data.userInfo.inviteCode
if (!inviteCode) {
wx.showToast({
title: '请先登录',
icon: 'none'
})
return
}
wx.setClipboardData({
data: inviteCode,
success: () => {
wx.showToast({
title: '已复制邀请码',
icon: 'success'
})
}
})
},
// 查看推荐列表
viewReferrals() {
wx.navigateTo({
url: '/pages/referral/list'
})
},
// 查看订单列表
viewOrders() {
wx.navigateTo({
url: '/pages/referral/orders'
})
},
// 查看佣金明细
viewCommission() {
wx.navigateTo({
url: '/pages/referral/commission'
})
},
// 我的订单
goToOrders() {
wx.navigateTo({
url: '/pages/orders/list'
})
},
// 阅读历史
goToReadHistory() {
wx.navigateTo({
url: '/pages/history/read'
})
},
// 阅读时长
goToReadTime() {
wx.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 书签
goToBookmarks() {
wx.navigateTo({
url: '/pages/bookmarks/list'
})
},
// 阅读笔记
goToNotes() {
wx.navigateTo({
url: '/pages/notes/list'
})
},
// 设置
goToSettings() {
wx.navigateTo({
url: '/pages/settings/index'
})
},
// 联系客服
contactSupport() {
wx.showToast({
title: '客服功能开发中',
icon: 'none'
})
},
// 关于我们
about() {
wx.navigateTo({
url: '/pages/about/index'
})
},
// 退出登录
logout() {
wx.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
wx.removeStorageSync('token')
wx.removeStorageSync('userInfo')
app.globalData.userInfo = null
this.setData({
userInfo: {
avatar: '',
nickname: '点击登录',
id: '',
inviteCode: ''
},
earnings: {
total: '0.00',
available: '0.00',
withdrawn: '0.00'
},
referralData: {
totalUsers: 0,
totalOrders: 0,
commissionRate: 90
}
})
wx.showToast({
title: '已退出登录',
icon: 'success'
})
}
}
})
},
// 显示登录弹窗
showLoginModal() {
wx.showModal({
title: '需要登录',
content: '请先登录账号',
confirmText: '立即登录',
success: (res) => {
if (res.confirm) {
this.doLogin()
}
}
})
},
// 下拉刷新
onPullDownRefresh() {
this.loadUserInfo()
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
}
})

View File

@@ -0,0 +1,204 @@
<!--pages/my/my.wxml-->
<view class="container my-container page-transition">
<!-- 用户信息卡片 -->
<view class="user-card glass-effect">
<view class="user-header">
<image
class="user-avatar"
src="{{userInfo.avatar || '/assets/images/default-avatar.png'}}"
mode="aspectFill"
bindtap="editProfile"
></image>
<view class="user-info">
<view class="user-name">{{userInfo.nickname || '点击登录'}}</view>
<view class="user-id">ID: {{userInfo.id || '---'}}</view>
</view>
<view class="vip-badge" wx:if="{{userInfo.isPurchased}}">
<text class="vip-text">已购</text>
</view>
</view>
<!-- 阅读统计 -->
<view class="user-stats">
<view class="stat-item" bindtap="goToReadHistory">
<view class="stat-value">{{userStats.readChapters}}</view>
<view class="stat-label">已读章节</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item" bindtap="goToReadTime">
<view class="stat-value">{{userStats.readMinutes}}</view>
<view class="stat-label">阅读时长(分)</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item" bindtap="goToBookmarks">
<view class="stat-value">{{userStats.bookmarks}}</view>
<view class="stat-label">书签</view>
</view>
</view>
</view>
<!-- 分销中心(重点功能) -->
<view class="referral-section card">
<view class="section-header">
<view class="section-title">
<text class="title-icon">💰</text>
<text class="title-text">分销中心</text>
</view>
<view class="referral-status">
<text class="status-text">佣金比例:</text>
<text class="status-value">90%</text>
</view>
</view>
<!-- 收益概览 -->
<view class="earnings-overview">
<view class="earnings-main">
<view class="earnings-label">累计收益</view>
<view class="earnings-amount">¥{{earnings.total}}</view>
</view>
<view class="earnings-sub">
<view class="sub-item">
<text class="sub-label">可提现</text>
<text class="sub-value brand-color">¥{{earnings.available}}</text>
</view>
<view class="sub-item">
<text class="sub-label">已提现</text>
<text class="sub-value">¥{{earnings.withdrawn}}</text>
</view>
</view>
</view>
<!-- 快速操作 -->
<view class="referral-actions">
<button class="btn-primary action-btn" bindtap="generatePoster">
生成推广海报
</button>
<button class="btn-secondary action-btn" bindtap="withdraw">
立即提现
</button>
</view>
<!-- 推广数据 -->
<view class="referral-stats">
<view class="referral-stat-item" bindtap="viewReferrals">
<view class="stat-number">{{referralData.totalUsers}}</view>
<view class="stat-name">推荐人数</view>
</view>
<view class="referral-stat-item" bindtap="viewOrders">
<view class="stat-number">{{referralData.totalOrders}}</view>
<view class="stat-name">成交订单</view>
</view>
<view class="referral-stat-item" bindtap="viewCommission">
<view class="stat-number">{{referralData.commissionRate}}%</view>
<view class="stat-name">佣金率</view>
</view>
</view>
<!-- 我的邀请码 -->
<view class="invite-code-section">
<view class="invite-label">我的邀请码</view>
<view class="invite-code-box">
<text class="invite-code">{{userInfo.inviteCode || '---'}}</text>
<button class="copy-btn" bindtap="copyInviteCode">复制</button>
</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-section">
<view class="menu-group card">
<view class="menu-item" bindtap="goToOrders">
<view class="menu-left">
<text class="menu-icon">📦</text>
<text class="menu-text">我的订单</text>
</view>
<view class="menu-right">
<text class="menu-badge" wx:if="{{menuBadges.orders > 0}}">{{menuBadges.orders}}</text>
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="goToBookmarks">
<view class="menu-left">
<text class="menu-icon">🔖</text>
<text class="menu-text">我的书签</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="goToNotes">
<view class="menu-left">
<text class="menu-icon">📝</text>
<text class="menu-text">阅读笔记</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
</view>
<view class="menu-group card">
<view class="menu-item" bindtap="goToSettings">
<view class="menu-left">
<text class="menu-icon">⚙️</text>
<text class="menu-text">设置</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="contactSupport">
<view class="menu-left">
<text class="menu-icon">💬</text>
<text class="menu-text">联系客服</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="about">
<view class="menu-left">
<text class="menu-icon"></text>
<text class="menu-text">关于我们</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
</view>
</view>
<!-- 退出登录 -->
<view class="logout-section" wx:if="{{userInfo.id}}">
<button class="logout-btn" bindtap="logout">退出登录</button>
</view>
<!-- 底部留白 -->
<view class="bottom-space"></view>
</view>
<!-- 海报生成弹窗 -->
<view class="poster-modal" wx:if="{{showPoster}}" bindtap="closePoster">
<view class="poster-content" catchtap="stopPropagation">
<view class="poster-header">
<text class="poster-title">长按保存海报</text>
<text class="poster-close" bindtap="closePoster">×</text>
</view>
<canvas canvas-id="posterCanvas" class="poster-canvas"></canvas>
<button class="btn-primary save-poster-btn" bindtap="savePoster">
保存到相册
</button>
</view>
</view>

View File

@@ -0,0 +1,422 @@
/* pages/my/my.wxss */
.my-container {
padding: 32rpx 32rpx 160rpx;
background: linear-gradient(180deg, #000000 0%, #0a0a0a 100%);
}
/* 用户卡片 */
.user-card {
padding: 40rpx 32rpx;
margin-bottom: 24rpx;
border-radius: 32rpx;
}
.user-header {
display: flex;
align-items: center;
margin-bottom: 32rpx;
}
.user-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 24rpx;
border: 4rpx solid rgba(255, 77, 79, 0.5);
}
.user-info {
flex: 1;
}
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.user-id {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.vip-badge {
padding: 8rpx 20rpx;
background: linear-gradient(135deg, #FF4D4F 0%, #FF7875 100%);
border-radius: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 77, 79, 0.4);
}
.vip-text {
font-size: 24rpx;
color: #ffffff;
font-weight: 600;
}
/* 用户统计 */
.user-stats {
display: flex;
align-items: center;
padding-top: 32rpx;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-value {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
.stat-divider {
width: 2rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.1);
}
/* 分销中心 */
.referral-section {
margin-bottom: 24rpx;
padding: 32rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
}
.section-title {
display: flex;
align-items: center;
gap: 12rpx;
}
.title-icon {
font-size: 32rpx;
}
.title-text {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
}
.referral-status {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 20rpx;
background: rgba(255, 77, 79, 0.2);
border-radius: 20rpx;
}
.status-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
}
.status-value {
font-size: 28rpx;
font-weight: 700;
color: #FF4D4F;
}
/* 收益概览 */
.earnings-overview {
padding: 32rpx;
background: rgba(255, 77, 79, 0.1);
border-radius: 24rpx;
border: 2rpx solid rgba(255, 77, 79, 0.2);
margin-bottom: 24rpx;
}
.earnings-main {
text-align: center;
margin-bottom: 24rpx;
}
.earnings-label {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 12rpx;
}
.earnings-amount {
font-size: 64rpx;
font-weight: 700;
color: #FF4D4F;
letter-spacing: 2rpx;
}
.earnings-sub {
display: flex;
justify-content: space-around;
padding-top: 24rpx;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
}
.sub-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.sub-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.sub-value {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
/* 快速操作 */
.referral-actions {
display: flex;
gap: 16rpx;
margin-bottom: 32rpx;
}
.action-btn {
flex: 1;
font-size: 28rpx;
padding: 24rpx;
}
/* 推广数据 */
.referral-stats {
display: flex;
justify-content: space-around;
padding: 32rpx 0;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
border-bottom: 2rpx solid rgba(255, 255, 255, 0.1);
margin-bottom: 24rpx;
}
.referral-stat-item {
text-align: center;
}
.stat-number {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
margin-bottom: 8rpx;
}
.stat-name {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
/* 邀请码 */
.invite-code-section {
padding: 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
}
.invite-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 12rpx;
}
.invite-code-box {
display: flex;
align-items: center;
justify-content: space-between;
}
.invite-code {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
letter-spacing: 4rpx;
font-family: 'Courier New', monospace;
}
.copy-btn {
padding: 12rpx 32rpx;
background: rgba(255, 77, 79, 0.2);
color: #FF7875;
border: none;
border-radius: 12rpx;
font-size: 26rpx;
font-weight: 600;
}
/* 功能菜单 */
.menu-section {
margin-bottom: 24rpx;
}
.menu-group {
padding: 0;
margin-bottom: 24rpx;
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
transition: background 0.3s;
}
.menu-item:active {
background: rgba(255, 255, 255, 0.05);
}
.menu-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.menu-icon {
font-size: 40rpx;
}
.menu-text {
font-size: 30rpx;
color: #ffffff;
}
.menu-right {
display: flex;
align-items: center;
gap: 16rpx;
}
.menu-badge {
min-width: 32rpx;
height: 32rpx;
padding: 0 8rpx;
background: #FF4D4F;
border-radius: 16rpx;
font-size: 20rpx;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.menu-arrow {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.3);
}
.menu-divider {
height: 2rpx;
background: rgba(255, 255, 255, 0.05);
margin: 0 32rpx;
}
/* 退出登录 */
.logout-section {
padding: 0 32rpx;
margin-top: 48rpx;
}
.logout-btn {
width: 100%;
padding: 28rpx;
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.6);
border: 2rpx solid rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
font-size: 30rpx;
}
.logout-btn:active {
background: rgba(255, 255, 255, 0.08);
}
.bottom-space {
height: 40rpx;
}
/* 海报弹窗 */
.poster-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s;
}
.poster-content {
width: 90%;
max-width: 600rpx;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20rpx);
border-radius: 32rpx;
padding: 32rpx;
animation: slideUp 0.4s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(100rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.poster-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.poster-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
.poster-close {
font-size: 56rpx;
color: rgba(255, 255, 255, 0.6);
line-height: 1;
}
.poster-canvas {
width: 100%;
height: 800rpx;
background: #ffffff;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
.save-poster-btn {
width: 100%;
}

View File

@@ -0,0 +1,343 @@
// pages/read/read.js
const app = getApp()
const paymentUtil = require('../../utils/payment')
Page({
data: {
chapterId: '',
chapterInfo: {},
contentHtml: '',
loading: true,
hasPrev: false,
hasNext: false,
isBookmarked: false,
showCatalog: false,
allChapters: []
},
onLoad(options) {
const chapterId = options.id
if (chapterId) {
this.setData({ chapterId })
this.loadChapter(chapterId)
this.loadAllChapters()
this.checkBookmark(chapterId)
}
},
// 加载章节内容
loadChapter(chapterId) {
this.setData({ loading: true })
wx.showLoading({ title: '加载中...', mask: true })
wx.request({
url: `${app.globalData.apiBase}/book/chapter/${chapterId}`,
header: {
'Authorization': `Bearer ${wx.getStorageSync('token')}`
},
success: (res) => {
if (res.statusCode === 200) {
const chapter = res.data
// 检查是否需要购买
if (chapter.needPurchase && !this.checkPurchased()) {
this.showPurchaseModal()
return
}
this.setData({
chapterInfo: {
title: chapter.title,
updateTime: chapter.updateTime,
words: chapter.words,
readTime: Math.ceil(chapter.words / 300)
},
contentHtml: this.markdownToHtml(chapter.content),
hasPrev: !!chapter.prevChapterId,
hasNext: !!chapter.nextChapterId,
loading: false
})
// 记录阅读进度
this.recordReadProgress(chapterId)
} else {
wx.showToast({
title: '章节加载失败',
icon: 'none'
})
}
},
fail: () => {
// 使用Mock数据
this.loadMockChapter(chapterId)
},
complete: () => {
wx.hideLoading()
}
})
},
// 加载Mock章节
loadMockChapter(chapterId) {
const mockContent = `
# 这是章节标题
这是第一段内容,介绍了关于私域运营的基本概念...
## 第一小节
详细内容描述...
### 要点总结
1. 第一点
2. 第二点
3. 第三点
**重点强调的内容**
> 引用的内容或者金句
`
this.setData({
chapterInfo: {
title: '第一章|我是谁',
updateTime: '2天前',
words: 3200,
readTime: 11
},
contentHtml: this.markdownToHtml(mockContent),
hasPrev: false,
hasNext: true,
loading: false
})
},
// Markdown转HTML简单实现
markdownToHtml(markdown) {
if (!markdown) return ''
let html = markdown
.replace(/### (.*)/g, '<h3>$1</h3>')
.replace(/## (.*)/g, '<h2>$1</h2>')
.replace(/# (.*)/g, '<h1>$1</h1>')
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/> (.*)/g, '<blockquote>$1</blockquote>')
.replace(/\n/g, '<br/>')
return html
},
// 检查是否已购买
checkPurchased() {
return paymentUtil.checkPurchaseStatus()
},
// 显示购买弹窗
showPurchaseModal() {
wx.showModal({
title: '需要购买',
content: '此章节需要购买完整版才能阅读',
confirmText: '立即购买',
success: (res) => {
if (res.confirm) {
this.purchase()
} else {
wx.navigateBack()
}
}
})
},
// 购买
purchase() {
paymentUtil.purchaseFullBook(
() => {
wx.showToast({
title: '购买成功',
icon: 'success'
})
// 重新加载章节
setTimeout(() => {
this.loadChapter(this.data.chapterId)
}, 1500)
},
() => {
wx.showToast({
title: '购买失败',
icon: 'none'
})
}
)
},
// 记录阅读进度
recordReadProgress(chapterId) {
wx.request({
url: `${app.globalData.apiBase}/user/read-progress`,
method: 'POST',
header: {
'Authorization': `Bearer ${wx.getStorageSync('token')}`
},
data: {
chapterId,
timestamp: Date.now()
}
})
},
// 加载所有章节
loadAllChapters() {
wx.request({
url: `${app.globalData.apiBase}/book/chapters`,
success: (res) => {
if (res.statusCode === 200) {
this.setData({
allChapters: res.data.chapters || []
})
}
}
})
},
// 检查书签
checkBookmark(chapterId) {
const bookmarks = wx.getStorageSync('bookmarks') || []
const isBookmarked = bookmarks.includes(chapterId)
this.setData({ isBookmarked })
},
// 上一章
prevChapter() {
if (!this.data.hasPrev) return
// TODO: 获取上一章ID
wx.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 下一章
nextChapter() {
if (!this.data.hasNext) return
// TODO: 获取下一章ID
wx.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 返回
goBack() {
wx.navigateBack()
},
// 显示菜单
showMenu() {
wx.showActionSheet({
itemList: ['调整字体', '夜间模式', '分享好友'],
success: (res) => {
switch(res.tapIndex) {
case 0:
this.adjustFont()
break
case 1:
this.toggleNightMode()
break
case 2:
this.share()
break
}
}
})
},
// 书签
bookmark() {
const chapterId = this.data.chapterId
let bookmarks = wx.getStorageSync('bookmarks') || []
if (this.data.isBookmarked) {
// 移除书签
bookmarks = bookmarks.filter(id => id !== chapterId)
wx.showToast({
title: '已移除书签',
icon: 'success'
})
} else {
// 添加书签
bookmarks.push(chapterId)
wx.showToast({
title: '已添加书签',
icon: 'success'
})
}
wx.setStorageSync('bookmarks', bookmarks)
this.setData({
isBookmarked: !this.data.isBookmarked
})
},
// 笔记
note() {
wx.navigateTo({
url: `/pages/note/edit?chapterId=${this.data.chapterId}`
})
},
// 显示目录
showCatalog() {
this.setData({ showCatalog: true })
},
// 隐藏目录
hideCatalog() {
this.setData({ showCatalog: false })
},
// 选择章节
selectChapter(e) {
const chapterId = e.currentTarget.dataset.id
this.setData({
chapterId,
showCatalog: false
})
this.loadChapter(chapterId)
this.checkBookmark(chapterId)
},
// 分享
share() {
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
})
},
// 跳转到推广页面
goToReferral() {
wx.switchTab({
url: '/pages/my/my?tab=referral'
})
},
// 阻止冒泡
stopPropagation() {},
// 分享给好友
onShareAppMessage() {
const userInfo = app.getUserInfo()
const inviteCode = userInfo ? userInfo.inviteCode : ''
return {
title: this.data.chapterInfo.title,
path: `/pages/read/read?id=${this.data.chapterId}&invite=${inviteCode}`,
imageUrl: '/assets/images/share-chapter.png'
}
}
})

View File

@@ -0,0 +1,112 @@
<!--pages/read/read.wxml-->
<view class="container read-container">
<!-- 顶部导航栏 -->
<view class="read-header">
<view class="header-left" bindtap="goBack">
<text class="back-icon">←</text>
</view>
<view class="header-title">{{chapterInfo.title}}</view>
<view class="header-right" bindtap="showMenu">
<text class="menu-icon">⋯</text>
</view>
</view>
<!-- 章节内容 -->
<scroll-view class="content-scroll" scroll-y enhanced show-scrollbar="{{false}}">
<!-- 骨架屏 -->
<view class="content-skeleton" wx:if="{{loading}}">
<view class="skeleton skeleton-title"></view>
<view class="skeleton skeleton-line"></view>
<view class="skeleton skeleton-line"></view>
<view class="skeleton skeleton-line short"></view>
<view class="skeleton skeleton-line"></view>
<view class="skeleton skeleton-line"></view>
</view>
<!-- 实际内容 -->
<view class="content-wrapper" wx:if="{{!loading}}">
<view class="chapter-title">{{chapterInfo.title}}</view>
<view class="chapter-meta">
<text class="meta-item">{{chapterInfo.updateTime}}</text>
<text class="meta-divider">·</text>
<text class="meta-item">{{chapterInfo.words}}字</text>
<text class="meta-divider">·</text>
<text class="meta-item">{{chapterInfo.readTime}}分钟</text>
</view>
<view class="chapter-content markdown-body">
<rich-text nodes="{{contentHtml}}"></rich-text>
</view>
<!-- 章节导航 -->
<view class="chapter-nav">
<button
class="nav-btn prev-btn {{!hasPrev ? 'disabled' : ''}}"
bindtap="prevChapter"
disabled="{{!hasPrev}}"
>
上一章
</button>
<button
class="nav-btn next-btn {{!hasNext ? 'disabled' : ''}}"
bindtap="nextChapter"
disabled="{{!hasNext}}"
>
下一章
</button>
</view>
<!-- 推广提示 -->
<view class="promotion-tip card">
<view class="tip-icon">💰</view>
<view class="tip-content">
<view class="tip-title">喜欢这本书?</view>
<view class="tip-desc">分享给朋友每笔成交您可获得90%佣金</view>
</view>
<button class="tip-btn" bindtap="goToReferral">去分享</button>
</view>
</view>
</scroll-view>
<!-- 底部工具栏 -->
<view class="read-toolbar glass-effect">
<view class="toolbar-item" bindtap="bookmark">
<text class="toolbar-icon">{{isBookmarked ? '🔖' : '📑'}}</text>
<text class="toolbar-label">书签</text>
</view>
<view class="toolbar-item" bindtap="note">
<text class="toolbar-icon">📝</text>
<text class="toolbar-label">笔记</text>
</view>
<view class="toolbar-item" bindtap="showCatalog">
<text class="toolbar-icon">📚</text>
<text class="toolbar-label">目录</text>
</view>
<view class="toolbar-item" bindtap="share">
<text class="toolbar-icon">📤</text>
<text class="toolbar-label">分享</text>
</view>
</view>
</view>
<!-- 目录弹窗 -->
<view class="catalog-modal" wx:if="{{showCatalog}}" bindtap="hideCatalog">
<view class="catalog-content" catchtap="stopPropagation">
<view class="catalog-header">
<text class="catalog-title">目录</text>
<text class="catalog-close" bindtap="hideCatalog">×</text>
</view>
<scroll-view class="catalog-list" scroll-y>
<view
class="catalog-item {{item.id === chapterId ? 'active' : ''}}"
wx:for="{{allChapters}}"
wx:key="id"
bindtap="selectChapter"
data-id="{{item.id}}"
>
<text class="catalog-item-title">{{item.title}}</text>
<text class="catalog-item-icon" wx:if="{{item.id === chapterId}}">📖</text>
</view>
</scroll-view>
</view>
</view>

View File

@@ -0,0 +1,334 @@
/* pages/read/read.wxss */
.read-container {
height: 100vh;
display: flex;
flex-direction: column;
background: #000000;
}
/* 顶部导航 */
.read-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
background: rgba(0, 0, 0, 0.95);
backdrop-filter: blur(20rpx);
border-bottom: 2rpx solid rgba(255, 255, 255, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.header-left,
.header-right {
width: 80rpx;
text-align: center;
}
.back-icon,
.menu-icon {
font-size: 40rpx;
color: #ffffff;
}
.header-title {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #ffffff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 内容区域 */
.content-scroll {
flex: 1;
height: 100%;
}
.content-wrapper {
padding: 48rpx 32rpx 120rpx;
}
/* 骨架屏 */
.content-skeleton {
padding: 48rpx 32rpx;
}
.skeleton-title {
width: 80%;
height: 60rpx;
margin: 0 auto 40rpx;
}
.skeleton-line {
width: 100%;
height: 32rpx;
margin-bottom: 24rpx;
}
.skeleton-line.short {
width: 60%;
}
/* 章节标题 */
.chapter-title {
font-size: 48rpx;
font-weight: 700;
color: #ffffff;
text-align: center;
margin-bottom: 24rpx;
line-height: 1.4;
}
.chapter-meta {
display: flex;
justify-content: center;
align-items: center;
gap: 16rpx;
margin-bottom: 48rpx;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.meta-divider {
color: rgba(255, 255, 255, 0.3);
}
/* 章节内容 */
.chapter-content {
font-size: 32rpx;
line-height: 1.8;
color: rgba(255, 255, 255, 0.85);
text-align: justify;
}
.markdown-body {
word-break: break-word;
}
.markdown-body h1 {
font-size: 44rpx;
font-weight: 700;
margin: 48rpx 0 24rpx;
color: #ffffff;
}
.markdown-body h2 {
font-size: 40rpx;
font-weight: 600;
margin: 40rpx 0 20rpx;
color: #ffffff;
}
.markdown-body h3 {
font-size: 36rpx;
font-weight: 600;
margin: 32rpx 0 16rpx;
color: rgba(255, 255, 255, 0.9);
}
.markdown-body strong {
color: #FF4D4F;
font-weight: 600;
}
.markdown-body em {
color: rgba(255, 255, 255, 0.7);
font-style: italic;
}
.markdown-body blockquote {
padding: 24rpx 32rpx;
margin: 32rpx 0;
background: rgba(255, 77, 79, 0.1);
border-left: 6rpx solid #FF4D4F;
border-radius: 8rpx;
color: rgba(255, 255, 255, 0.8);
}
/* 章节导航 */
.chapter-nav {
display: flex;
gap: 24rpx;
margin-top: 64rpx;
margin-bottom: 32rpx;
}
.nav-btn {
flex: 1;
padding: 28rpx;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
border: 2rpx solid rgba(255, 255, 255, 0.2);
border-radius: 16rpx;
font-size: 30rpx;
}
.nav-btn.disabled {
opacity: 0.3;
}
/* 推广提示 */
.promotion-tip {
display: flex;
align-items: center;
gap: 24rpx;
padding: 32rpx;
margin-top: 32rpx;
background: linear-gradient(135deg, rgba(255, 77, 79, 0.2) 0%, rgba(255, 120, 117, 0.1) 100%);
border: 2rpx solid rgba(255, 77, 79, 0.3);
}
.tip-icon {
font-size: 64rpx;
}
.tip-content {
flex: 1;
}
.tip-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.tip-desc {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
}
.tip-btn {
padding: 16rpx 32rpx;
background: #FF4D4F;
color: #ffffff;
border: none;
border-radius: 12rpx;
font-size: 26rpx;
}
/* 底部工具栏 */
.read-toolbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
padding: 24rpx 32rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
background: rgba(0, 0, 0, 0.95);
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
z-index: 100;
}
.toolbar-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.toolbar-icon {
font-size: 40rpx;
}
.toolbar-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.7);
}
/* 目录弹窗 */
.catalog-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 999;
display: flex;
justify-content: flex-end;
}
.catalog-content {
width: 70%;
height: 100%;
background: #0a0a0a;
display: flex;
flex-direction: column;
animation: slideInRight 0.3s ease-out;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
.catalog-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 2rpx solid rgba(255, 255, 255, 0.1);
}
.catalog-title {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
.catalog-close {
font-size: 56rpx;
color: rgba(255, 255, 255, 0.6);
line-height: 1;
}
.catalog-list {
flex: 1;
padding: 16rpx 0;
}
.catalog-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-left: 4rpx solid transparent;
transition: all 0.3s;
}
.catalog-item.active {
background: rgba(255, 77, 79, 0.1);
border-left-color: #FF4D4F;
}
.catalog-item:active {
background: rgba(255, 255, 255, 0.05);
}
.catalog-item-title {
font-size: 28rpx;
color: #ffffff;
flex: 1;
}
.catalog-item.active .catalog-item-title {
color: #FF4D4F;
font-weight: 600;
}
.catalog-item-icon {
font-size: 32rpx;
margin-left: 16rpx;
}