更新小程序配置,重构页面结构,删除不再使用的地址管理和章节页面,优化项目结构以提升可维护性;调整全局样式,增强组件的可复用性和一致性。
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
authorInfo: {
|
||||
name: '卡若',
|
||||
description: '连续创业者,私域运营专家',
|
||||
liveTime: '06:00-09:00',
|
||||
platform: 'Soul派对房'
|
||||
},
|
||||
authorInitial: '卡',
|
||||
stats: [
|
||||
{ value: '55+', label: '真实案例', icon: '📖' },
|
||||
{ value: '10000+', label: '派对房听众', icon: '👥' },
|
||||
{ value: '15年', label: '创业经验', icon: '🏆' },
|
||||
{ value: '3000万', label: '最高年流水', icon: '📈' }
|
||||
],
|
||||
milestones: [
|
||||
{ year: '2007-2014', event: '游戏电竞创业历程,从魔兽世界代练起步' },
|
||||
{ year: '2015', event: '转型电商,做天猫虚拟充值' },
|
||||
{ year: '2016-2019', event: '深耕电商领域,团队扩张到200人,年流水3000万' },
|
||||
{ year: '2019-2020', event: '公司变故,重整旗鼓' },
|
||||
{ year: '2020-2025', event: '电竞、地摊、大健康、私域多领域探索' },
|
||||
{ year: '2025.10.15', event: '在Soul派对房开启每日分享,记录真实商业案例' }
|
||||
]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
const authorInfo = this.data.authorInfo
|
||||
const authorInitial = authorInfo.name ? authorInfo.name.charAt(0) : '卡'
|
||||
this.setData({ statusBarHeight, navBarHeight, authorInitial })
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
|
||||
},
|
||||
|
||||
onJoinParty() {
|
||||
wx.showToast({
|
||||
title: '请关注小程序或联系客服加入派对群',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "关于作者",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="height: {{navBarHeight || (statusBarHeight + 44)}}px; padding-top: {{statusBarHeight || 44}}px; box-sizing: border-box;">
|
||||
<view class="nav-inner safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="nav-title">关于作者</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="main">
|
||||
<view class="card author-card">
|
||||
<view class="author-avatar">{{authorInitial}}</view>
|
||||
<text class="author-name">{{authorInfo.name}}</text>
|
||||
<text class="author-desc">{{authorInfo.description}}</text>
|
||||
<view class="author-tags">
|
||||
<text class="tag brand">🕐 每日 {{authorInfo.liveTime}}</text>
|
||||
<text class="tag">💬 {{authorInfo.platform}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats">
|
||||
<view class="stat-item" wx:for="{{stats}}" wx:key="label">
|
||||
<text class="stat-icon">{{item.icon}}</text>
|
||||
<text class="stat-value">{{item.value}}</text>
|
||||
<text class="stat-label">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card section">
|
||||
<text class="section-title">关于这本书</text>
|
||||
<view class="section-paras">
|
||||
<text class="section-para">"这不是一本教你成功的鸡汤书。"</text>
|
||||
<text class="section-para">这是我每天早上6点到9点,在Soul派对房和几百个陌生人分享的真实故事。</text>
|
||||
<text class="section-para brand">"社会不是靠努力,是靠洞察与选择。"</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card section">
|
||||
<text class="section-title">创业历程</text>
|
||||
<view class="timeline">
|
||||
<view class="timeline-item" wx:for="{{milestones}}" wx:key="year">
|
||||
<view class="timeline-dot-wrap">
|
||||
<view class="timeline-dot"></view>
|
||||
<view class="timeline-line" wx:if="{{index < milestones.length - 1}}"></view>
|
||||
</view>
|
||||
<view class="timeline-content">
|
||||
<text class="milestone-year">{{item.year}}</text>
|
||||
<text class="milestone-event">{{item.event}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card join-card">
|
||||
<text class="join-title">想听更多真实故事?</text>
|
||||
<text class="join-desc">每天早上6-9点,卡若在Soul派对房免费分享</text>
|
||||
<view class="btn-join" bindtap="onJoinParty">💬 加入派对群</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,35 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; padding-bottom: 80rpx; }
|
||||
.nav-bar { background: rgba(0,0,0,0.9); border-bottom: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; display: flex; flex-direction: column; justify-content: flex-end; }
|
||||
.nav-inner { display: flex; align-items: center; padding: 0 24rpx; height: 88rpx; min-height: 44px; flex-shrink: 0; }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; padding: 16rpx 0; }
|
||||
.nav-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
.main { padding: 32rpx; }
|
||||
.card { border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border: 2rpx solid rgba(0,206,209,0.2); }
|
||||
.author-card { display: flex; flex-direction: column; align-items: center; text-align: center; }
|
||||
.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); display: flex; align-items: center; justify-content: center; font-size: 60rpx; font-weight: 700; color: #fff; margin-bottom: 24rpx; }
|
||||
.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.author-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
|
||||
.author-tags { display: flex; gap: 16rpx; margin-top: 24rpx; justify-content: center; flex-wrap: wrap; }
|
||||
.tag { font-size: 22rpx; padding: 12rpx 24rpx; border-radius: 32rpx; background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.5); }
|
||||
.tag.brand { background: rgba(0,206,209,0.1); color: #00CED1; }
|
||||
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16rpx; margin-bottom: 24rpx; }
|
||||
.stat-item { padding: 24rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); text-align: center; }
|
||||
.stat-icon { font-size: 40rpx; display: block; margin-bottom: 12rpx; opacity: 0.9; }
|
||||
.stat-value { font-size: 32rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.stat-label { font-size: 20rpx; color: rgba(255,255,255,0.4); }
|
||||
.section-title { font-size: 32rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 16rpx; }
|
||||
.section-paras { }
|
||||
.section-para { font-size: 28rpx; color: rgba(255,255,255,0.8); line-height: 1.7; display: block; margin-bottom: 16rpx; }
|
||||
.section-para.brand { color: #00CED1; font-weight: 500; }
|
||||
.timeline { }
|
||||
.timeline-item { display: flex; gap: 24rpx; }
|
||||
.timeline-dot-wrap { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
|
||||
.timeline-dot { width: 16rpx; height: 16rpx; border-radius: 50%; background: #00CED1; }
|
||||
.timeline-line { width: 4rpx; flex: 1; min-height: 24rpx; background: rgba(255,255,255,0.2); margin-top: 8rpx; }
|
||||
.timeline-content { flex: 1; padding-bottom: 24rpx; }
|
||||
.milestone-year { font-size: 28rpx; color: #00CED1; font-weight: 600; display: block; margin-bottom: 8rpx; }
|
||||
.milestone-event { font-size: 26rpx; color: rgba(255,255,255,0.7); display: block; line-height: 1.5; }
|
||||
.join-card { background: linear-gradient(90deg, rgba(0,206,209,0.1) 0%, rgba(32,178,170,0.05) 100%); border-color: rgba(0,206,209,0.2); text-align: center; }
|
||||
.join-title { font-size: 32rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 8rpx; }
|
||||
.join-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 24rpx; }
|
||||
.btn-join { width: 100%; padding: 24rpx; border-radius: 24rpx; background: #00CED1; color: #fff; font-size: 30rpx; font-weight: 500; text-align: center; }
|
||||
@@ -1,117 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
id: '',
|
||||
isEdit: false,
|
||||
name: '',
|
||||
phone: '',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
detail: '',
|
||||
isDefault: false,
|
||||
loading: false,
|
||||
saving: false
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
const id = (options && options.id) ? decodeURIComponent(options.id) : ''
|
||||
this.setData({ statusBarHeight, navBarHeight, id, isEdit: !!id })
|
||||
if (id) this.loadAddress(id)
|
||||
},
|
||||
|
||||
loadAddress(id) {
|
||||
this.setData({ loading: true })
|
||||
app.request('/api/user/addresses/' + encodeURIComponent(id))
|
||||
.then(res => {
|
||||
const item = res && res.item ? res.item : null
|
||||
if (!item) {
|
||||
this.setData({ loading: false })
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
loading: false,
|
||||
name: item.name || '',
|
||||
phone: item.phone || '',
|
||||
province: item.province || '',
|
||||
city: item.city || '',
|
||||
district: item.district || '',
|
||||
detail: item.detail || '',
|
||||
isDefault: !!item.isDefault
|
||||
})
|
||||
})
|
||||
.catch(() => this.setData({ loading: false }))
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
||||
},
|
||||
|
||||
onNameInput(e) { this.setData({ name: (e.detail && e.detail.value) || '' }) },
|
||||
onPhoneInput(e) { this.setData({ phone: (e.detail && e.detail.value) || '' }) },
|
||||
onProvinceInput(e) { this.setData({ province: (e.detail && e.detail.value) || '' }) },
|
||||
onCityInput(e) { this.setData({ city: (e.detail && e.detail.value) || '' }) },
|
||||
onDistrictInput(e) { this.setData({ district: (e.detail && e.detail.value) || '' }) },
|
||||
onDetailInput(e) { this.setData({ detail: (e.detail && e.detail.value) || '' }) },
|
||||
onDefaultChange(e) { this.setData({ isDefault: !!e.detail.value }) },
|
||||
|
||||
submit() {
|
||||
const { id, isEdit, name, phone, province, city, district, detail, isDefault } = this.data
|
||||
const user = app.globalData.userInfo
|
||||
if (!user || !user.id) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!name || !name.trim()) {
|
||||
wx.showToast({ title: '请输入姓名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!phone || !phone.trim()) {
|
||||
wx.showToast({ title: '请输入手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!/^1[3-9]\d{9}$/.test(phone.trim())) {
|
||||
wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!detail || !detail.trim()) {
|
||||
wx.showToast({ title: '请输入详细地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ saving: true })
|
||||
const body = {
|
||||
userId: user.id,
|
||||
name: name.trim(),
|
||||
phone: phone.trim(),
|
||||
province: (province || '').trim(),
|
||||
city: (city || '').trim(),
|
||||
district: (district || '').trim(),
|
||||
detail: detail.trim(),
|
||||
isDefault: !!isDefault
|
||||
}
|
||||
if (isEdit && id) {
|
||||
app.request('/api/user/addresses/' + encodeURIComponent(id), {
|
||||
method: 'PUT',
|
||||
data: body
|
||||
}).then(() => {
|
||||
this.setData({ saving: false })
|
||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
}).catch(() => this.setData({ saving: false }))
|
||||
} else {
|
||||
app.request('/api/user/addresses', {
|
||||
method: 'POST',
|
||||
data: body
|
||||
}).then(() => {
|
||||
this.setData({ saving: false })
|
||||
wx.showToast({ title: '添加成功', icon: 'success' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
}).catch(() => this.setData({ saving: false }))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "编辑地址",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="header-title">{{isEdit ? '编辑地址' : '新增地址'}}</text>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{loading}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">加载中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="form">
|
||||
<view class="form-item">
|
||||
<text class="form-label">姓名</text>
|
||||
<input class="form-input" placeholder="请输入姓名" value="{{name}}" bindinput="onNameInput" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" type="number" maxlength="11" placeholder="请输入11位手机号" value="{{phone}}" bindinput="onPhoneInput" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">省份</text>
|
||||
<input class="form-input" placeholder="选填" value="{{province}}" bindinput="onProvinceInput" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">城市</text>
|
||||
<input class="form-input" placeholder="选填" value="{{city}}" bindinput="onCityInput" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">区/县</text>
|
||||
<input class="form-input" placeholder="选填" value="{{district}}" bindinput="onDistrictInput" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">详细地址</text>
|
||||
<textarea class="form-textarea" placeholder="街道、门牌号等" value="{{detail}}" bindinput="onDetailInput" />
|
||||
</view>
|
||||
<view class="form-item row">
|
||||
<text class="form-label">设为默认地址</text>
|
||||
<switch checked="{{isDefault}}" bindchange="onDefaultChange" color="#00CED1" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-save {{saving ? 'disabled' : ''}}" bindtap="submit">
|
||||
{{saving ? '保存中...' : '保存'}}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -1,20 +0,0 @@
|
||||
page { background: #000; color: #fff; }
|
||||
.page { min-height: 100vh; padding-bottom: 80rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
|
||||
.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
|
||||
.empty-wrap { padding: 80rpx 48rpx; text-align: center; }
|
||||
.empty-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
|
||||
.form { padding: 32rpx; }
|
||||
.form-item { margin-bottom: 32rpx; }
|
||||
.form-item.row { display: flex; align-items: center; justify-content: space-between; }
|
||||
.form-label { font-size: 28rpx; color: rgba(255,255,255,0.6); display: block; margin-bottom: 16rpx; }
|
||||
.form-item.row .form-label { margin-bottom: 0; }
|
||||
.form-input { width: 100%; padding: 24rpx 32rpx; border-radius: 16rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 28rpx; box-sizing: border-box; }
|
||||
.form-textarea { width: 100%; min-height: 160rpx; padding: 24rpx 32rpx; border-radius: 16rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 28rpx; box-sizing: border-box; }
|
||||
|
||||
.btn-save { margin: 32rpx; padding: 28rpx; border-radius: 24rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; text-align: center; box-sizing: border-box; }
|
||||
.btn-save.disabled { opacity: 0.5; }
|
||||
@@ -1,80 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
user: null,
|
||||
list: [],
|
||||
loading: true
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
this.syncUser()
|
||||
this.loadList()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadList()
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const user = app.globalData.userInfo || null
|
||||
this.setData({ user })
|
||||
},
|
||||
|
||||
loadList() {
|
||||
const user = app.globalData.userInfo
|
||||
if (!user || !user.id) {
|
||||
this.setData({ list: [], loading: false })
|
||||
return
|
||||
}
|
||||
this.setData({ loading: true })
|
||||
app.request('/api/user/addresses?userId=' + encodeURIComponent(user.id))
|
||||
.then(res => {
|
||||
const list = (res && res.list) ? res.list : []
|
||||
this.setData({ list, loading: false })
|
||||
})
|
||||
.catch(() => this.setData({ loading: false }))
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
||||
},
|
||||
|
||||
goAdd() {
|
||||
wx.navigateTo({ url: '/pages/address-edit/address-edit' })
|
||||
},
|
||||
|
||||
goEdit(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (id) wx.navigateTo({ url: '/pages/address-edit/address-edit?id=' + encodeURIComponent(id) })
|
||||
},
|
||||
|
||||
deleteAddr(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (!id) return
|
||||
const that = this
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除该收货地址吗?',
|
||||
success(res) {
|
||||
if (!res.confirm) return
|
||||
app.request('/api/user/addresses/' + encodeURIComponent(id), { method: 'DELETE' })
|
||||
.then(data => {
|
||||
if (data && data.success) {
|
||||
const list = that.data.list.filter(item => item.id !== id)
|
||||
that.setData({ list })
|
||||
wx.showToast({ title: '已删除', icon: 'success' })
|
||||
} else {
|
||||
wx.showToast({ title: (data && data.message) ? data.message : '删除失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
.catch(() => wx.showToast({ title: '删除失败', icon: 'none' }))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "地址管理",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="header-title">收货地址</text>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!user}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">请先登录</text>
|
||||
<view class="btn-primary" bindtap="goBack">去登录</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{loading}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">加载中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{list.length === 0}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-icon">📍</text>
|
||||
<text class="empty-desc">暂无收货地址</text>
|
||||
<text class="empty-hint">点击下方按钮添加</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="addr-list">
|
||||
<view
|
||||
class="addr-card"
|
||||
wx:for="{{list}}"
|
||||
wx:key="id"
|
||||
>
|
||||
<view class="addr-row">
|
||||
<text class="addr-name">{{item.name}}</text>
|
||||
<text class="addr-phone">{{item.phone}}</text>
|
||||
<text class="addr-default" wx:if="{{item.isDefault}}">默认</text>
|
||||
</view>
|
||||
<text class="addr-full">{{item.fullAddress}}</text>
|
||||
<view class="addr-actions">
|
||||
<view class="addr-btn" data-id="{{item.id}}" bindtap="goEdit">编辑</view>
|
||||
<view class="addr-btn danger" data-id="{{item.id}}" bindtap="deleteAddr">删除</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="btn-add" wx:if="{{user}}" bindtap="goAdd">➕ 新增收货地址</view>
|
||||
</view>
|
||||
@@ -1,25 +0,0 @@
|
||||
page { background: #000; color: #fff; }
|
||||
.page { min-height: 100vh; padding-bottom: 160rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
|
||||
.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
|
||||
.empty-wrap { padding: 80rpx 48rpx; text-align: center; }
|
||||
.empty-icon { font-size: 96rpx; display: block; margin-bottom: 24rpx; opacity: 0.5; }
|
||||
.empty-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 16rpx; }
|
||||
.empty-hint { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; }
|
||||
.btn-primary { display: inline-block; padding: 24rpx 64rpx; border-radius: 48rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; }
|
||||
|
||||
.addr-list { padding: 32rpx; }
|
||||
.addr-card { padding: 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 24rpx; box-sizing: border-box; }
|
||||
.addr-row { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; flex-wrap: wrap; }
|
||||
.addr-name { font-size: 30rpx; color: #fff; font-weight: 500; }
|
||||
.addr-phone { font-size: 26rpx; color: rgba(255,255,255,0.5); }
|
||||
.addr-default { font-size: 22rpx; padding: 4rpx 16rpx; border-radius: 8rpx; background: rgba(0,206,209,0.2); color: #00CED1; }
|
||||
.addr-full { font-size: 26rpx; color: rgba(255,255,255,0.6); line-height: 1.5; display: block; margin-bottom: 24rpx; }
|
||||
.addr-actions { display: flex; justify-content: flex-end; gap: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.addr-btn { font-size: 28rpx; color: #00CED1; }
|
||||
.addr-btn.danger { color: #f87171; }
|
||||
|
||||
.btn-add { position: fixed; bottom: 0; left: 0; right: 0; margin: 32rpx; padding: 28rpx; border-radius: 24rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; text-align: center; box-sizing: border-box; }
|
||||
@@ -1,130 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
totalSections: 62,
|
||||
bookData: [],
|
||||
expandedPart: 'part-1',
|
||||
hasFullBook: false,
|
||||
purchasedSections: [],
|
||||
appendixList: [
|
||||
{ id: 'appendix-1', title: '附录1|Soul派对房精选对话' },
|
||||
{ id: 'appendix-2', title: '附录2|创业者自检清单' },
|
||||
{ id: 'appendix-3', title: '附录3|本书提到的工具和资源' }
|
||||
]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setNavBarHeight()
|
||||
this.loadChapters()
|
||||
this.syncUserStatus()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 1 })
|
||||
this.setNavBarHeight()
|
||||
this.syncUserStatus()
|
||||
},
|
||||
|
||||
setNavBarHeight() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
loadChapters() {
|
||||
app.request('/api/book/all-chapters').then((res) => {
|
||||
if (res && res.data && Array.isArray(res.data)) {
|
||||
const bookData = this.normalizeBookData(res.data)
|
||||
const totalSections = res.totalSections || res.total || res.data.length || 62
|
||||
this.setData({ bookData, totalSections })
|
||||
} else if (res && res.chapters) {
|
||||
const bookData = this.normalizeBookData(res.chapters)
|
||||
this.setData({ bookData, totalSections: res.chapters.length || 62 })
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
normalizeBookData(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return []
|
||||
const partOrder = ['part-1', 'part-2', 'part-3', 'part-4', 'part-5']
|
||||
const partTitles = ['真实的人', '真实的行业', '真实的错误', '真实的赚钱', '真实的未来']
|
||||
const partMap = {}
|
||||
const subtitles = ['人性观察与社交逻辑', '社会运作的底层规则', '错过机会比失败更贵', '所有行业的杠杆结构', '人与系统的关系']
|
||||
partOrder.forEach((id, i) => {
|
||||
partMap[id] = {
|
||||
id,
|
||||
number: String(i + 1).padStart(2, '0'),
|
||||
title: partTitles[i] || ('篇' + (i + 1)),
|
||||
subtitle: subtitles[i] || '',
|
||||
chapters: []
|
||||
}
|
||||
})
|
||||
list.forEach((s) => {
|
||||
let partId = s.partId || s.part_id
|
||||
if (!partId && s.id) {
|
||||
const num = String(s.id).split('.')[0]
|
||||
partId = partOrder[parseInt(num, 10) - 1] || 'part-1'
|
||||
}
|
||||
partId = partId || 'part-1'
|
||||
if (!partMap[partId]) partMap[partId] = { id: partId, number: '99', title: '其他', subtitle: '', chapters: [] }
|
||||
const chId = s.chapterId || s.chapter_id || 'ch1'
|
||||
let ch = partMap[partId].chapters.find(c => c.id === chId)
|
||||
if (!ch) {
|
||||
ch = { id: chId, title: s.chapterTitle || s.chapter_title || '章节', sections: [] }
|
||||
partMap[partId].chapters.push(ch)
|
||||
}
|
||||
ch.sections.push({
|
||||
id: s.id,
|
||||
title: s.sectionTitle || s.title || s.section_title || '',
|
||||
isFree: !!s.isFree || !!s.is_free,
|
||||
price: s.price != null ? s.price : 1
|
||||
})
|
||||
})
|
||||
const out = partOrder.map(id => partMap[id]).filter(p => p.chapters.length > 0)
|
||||
out.forEach(p => {
|
||||
p.sectionCount = p.chapters.reduce((acc, ch) => acc + (ch.sections ? ch.sections.length : 0), 0)
|
||||
})
|
||||
if (out.length === 0) {
|
||||
const sections = list.map(s => ({ id: s.id, title: s.sectionTitle || s.title || s.section_title || '', isFree: !!s.isFree || !!s.is_free, price: s.price != null ? s.price : 1 }))
|
||||
const single = { id: 'part-1', number: '01', title: '全部', subtitle: '', sectionCount: sections.length, chapters: [{ id: 'ch1', title: '章节', sections }] }
|
||||
return [single]
|
||||
}
|
||||
return out
|
||||
},
|
||||
|
||||
syncUserStatus() {
|
||||
const { hasFullBook, purchasedSections } = app.globalData
|
||||
this.setData({ hasFullBook: !!hasFullBook, purchasedSections: purchasedSections || [] })
|
||||
},
|
||||
|
||||
hasPurchased(sectionId) {
|
||||
const { hasFullBook, purchasedSections } = app.globalData
|
||||
if (hasFullBook) return true
|
||||
return (purchasedSections || []).indexOf(sectionId) >= 0
|
||||
},
|
||||
|
||||
togglePart(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
const expandedPart = this.data.expandedPart === id ? '' : id
|
||||
this.setData({ expandedPart })
|
||||
},
|
||||
|
||||
goToSearch() {
|
||||
wx.navigateTo({ url: '/pages/search/search' })
|
||||
},
|
||||
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (!id) return
|
||||
wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadChapters()
|
||||
this.syncUserStatus()
|
||||
wx.stopPullDownRefresh()
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "目录",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header">
|
||||
<view class="header-inner safe-header-right">
|
||||
<view class="header-placeholder"></view>
|
||||
<text class="header-title">目录</text>
|
||||
<view class="header-btn" bindtap="goToSearch">🔍</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="book-card">
|
||||
<view class="book-icon">📖</view>
|
||||
<view class="book-info">
|
||||
<text class="book-title">一场SOUL的创业实验场</text>
|
||||
<text class="book-desc">来自Soul派对房的真实商业故事</text>
|
||||
</view>
|
||||
<view class="book-stat">
|
||||
<text class="book-num">{{totalSections}}</text>
|
||||
<text class="book-label">章节</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="preface-row" bindtap="goToRead" data-id="preface">
|
||||
<view class="preface-left">
|
||||
<view class="preface-icon">📄</view>
|
||||
<text class="preface-text">序言|为什么我每天早上6点在Soul开播?</text>
|
||||
</view>
|
||||
<text class="tag-free">免费</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="parts" wx:for="{{bookData}}" wx:key="id">
|
||||
<view class="part-head" data-id="{{item.id}}" bindtap="togglePart">
|
||||
<view class="part-left">
|
||||
<view class="part-num">{{item.number}}</view>
|
||||
<view class="part-info">
|
||||
<text class="part-title">{{item.title}}</text>
|
||||
<text class="part-subtitle">{{item.subtitle}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="part-count">{{item.sectionCount != null ? item.sectionCount : (item.chapters && item.chapters.length)}}章</text>
|
||||
<text class="arrow {{expandedPart === item.id ? 'expanded' : ''}}">›</text>
|
||||
</view>
|
||||
<view class="part-body" wx:if="{{expandedPart === item.id}}">
|
||||
<view class="chapter-block" wx:for="{{item.chapters}}" wx:for-item="ch" wx:key="id">
|
||||
<view class="chapter-title">{{ch.title}}</view>
|
||||
<view
|
||||
class="section-row"
|
||||
wx:for="{{ch.sections}}"
|
||||
wx:for-item="sec"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{sec.id}}"
|
||||
>
|
||||
<text class="section-lock">{{sec.isFree || (hasFullBook || (purchasedSections && purchasedSections.indexOf(sec.id) >= 0)) ? '✓' : '🔒'}}</text>
|
||||
<text class="section-text {{sec.isFree || (hasFullBook || (purchasedSections && purchasedSections.indexOf(sec.id) >= 0)) ? '' : 'locked'}}">{{sec.id}} {{sec.title}}</text>
|
||||
<text class="section-tag" wx:if="{{sec.isFree}}">免费</text>
|
||||
<text class="section-tag purchased" wx:elif="{{hasFullBook || (purchasedSections && purchasedSections.indexOf(sec.id) >= 0)}}">已购</text>
|
||||
<text class="section-price" wx:else>¥{{sec.price}}</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="preface-row" bindtap="goToRead" data-id="epilogue">
|
||||
<view class="preface-left">
|
||||
<view class="preface-icon">📄</view>
|
||||
<text class="preface-text">尾声|这本书的真实目的</text>
|
||||
</view>
|
||||
<text class="tag-free">免费</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="appendix-block">
|
||||
<text class="appendix-label">附录</text>
|
||||
<view class="appendix-item" wx:for="{{appendixList}}" wx:key="id" bindtap="goToRead" data-id="{{item.id}}">
|
||||
<text class="appendix-title">{{item.title}}</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
@@ -1,48 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; padding-bottom: 200rpx; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { background: rgba(0,0,0,0.9); border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.header-inner { display: flex; align-items: center; justify-content: space-between; padding: 24rpx 32rpx; }
|
||||
.header-placeholder { width: 64rpx; }
|
||||
.header-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
|
||||
.header-btn { width: 64rpx; height: 64rpx; border-radius: 50%; background: #2c2c2e; display: flex; align-items: center; justify-content: center; font-size: 32rpx; }
|
||||
.book-card { margin: 32rpx; padding: 32rpx; border-radius: 32rpx; background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border: 2rpx solid rgba(0,206,209,0.2); display: flex; align-items: center; gap: 24rpx; }
|
||||
.book-icon { width: 96rpx; height: 96rpx; border-radius: 24rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); display: flex; align-items: center; justify-content: center; font-size: 48rpx; }
|
||||
.book-info { flex: 1; }
|
||||
.book-title { font-size: 32rpx; font-weight: 600; color: #fff; display: block; }
|
||||
.book-desc { font-size: 22rpx; color: rgba(255,255,255,0.4); margin-top: 8rpx; display: block; }
|
||||
.book-stat { text-align: right; }
|
||||
.book-num { font-size: 40rpx; font-weight: 700; color: #00CED1; display: block; }
|
||||
.book-label { font-size: 20rpx; color: rgba(255,255,255,0.4); }
|
||||
.preface-row { margin: 0 32rpx 24rpx; padding: 24rpx 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); display: flex; align-items: center; }
|
||||
.preface-left { display: flex; align-items: center; gap: 24rpx; flex: 1; }
|
||||
.preface-icon { width: 64rpx; height: 64rpx; border-radius: 16rpx; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 32rpx; }
|
||||
.preface-text { font-size: 28rpx; color: #fff; }
|
||||
.tag-free { font-size: 22rpx; color: #00CED1; background: rgba(0,206,209,0.1); padding: 6rpx 16rpx; border-radius: 8rpx; margin-right: 16rpx; }
|
||||
.arrow { font-size: 32rpx; color: rgba(255,255,255,0.4); }
|
||||
.arrow.expanded { transform: rotate(90deg); }
|
||||
.parts { margin: 0 32rpx 24rpx; }
|
||||
.part-head { padding: 24rpx 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); display: flex; align-items: center; }
|
||||
.part-left { display: flex; align-items: center; gap: 24rpx; flex: 1; }
|
||||
.part-num { width: 64rpx; height: 64rpx; border-radius: 16rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 700; color: #fff; }
|
||||
.part-info { }
|
||||
.part-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; }
|
||||
.part-subtitle { font-size: 20rpx; color: rgba(255,255,255,0.4); }
|
||||
.part-count { font-size: 22rpx; color: rgba(255,255,255,0.4); margin-right: 16rpx; }
|
||||
.part-body { margin-top: 16rpx; margin-left: 24rpx; padding: 16rpx; border-radius: 16rpx; background: rgba(28,28,30,0.5); border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.chapter-block { margin-bottom: 24rpx; }
|
||||
.chapter-block:last-child { margin-bottom: 0; }
|
||||
.chapter-title { font-size: 24rpx; color: rgba(255,255,255,0.6); padding: 16rpx 0; border-bottom: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 8rpx; }
|
||||
.section-row { display: flex; align-items: center; padding: 20rpx 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.section-row:last-child { border-bottom: none; }
|
||||
.section-lock { font-size: 28rpx; margin-right: 16rpx; }
|
||||
.section-text { flex: 1; font-size: 24rpx; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.section-text.locked { color: rgba(255,255,255,0.5); }
|
||||
.section-tag { font-size: 20rpx; color: #00CED1; background: rgba(0,206,209,0.1); padding: 4rpx 12rpx; border-radius: 6rpx; margin-right: 8rpx; }
|
||||
.section-tag.purchased { background: transparent; }
|
||||
.section-price { font-size: 20rpx; color: rgba(255,255,255,0.4); margin-right: 8rpx; }
|
||||
.appendix-block { margin: 0 32rpx 24rpx; padding: 24rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.appendix-label { font-size: 24rpx; font-weight: 500; color: rgba(255,255,255,0.5); display: block; margin-bottom: 16rpx; }
|
||||
.appendix-item { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 0; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.appendix-item:last-child { border-bottom: none; }
|
||||
.appendix-title { font-size: 24rpx; color: rgba(255,255,255,0.8); }
|
||||
.bottom-space { height: 40rpx; }
|
||||
@@ -1,69 +1,17 @@
|
||||
const app = getApp()
|
||||
const mp = require('miniprogram-render')
|
||||
const getBaseConfig = require('../base.js')
|
||||
const config = require('../../config')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
totalSections: 62,
|
||||
purchasedCount: 0,
|
||||
hasFullBook: false,
|
||||
featuredSections: [
|
||||
{ id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', tagClass: 'tag-free', part: '真实的人' },
|
||||
{ id: '3.1', title: '3000万流水如何跑出来', tag: '热门', tagClass: 'tag-pink', part: '真实的行业' },
|
||||
{ id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', tagClass: 'tag-purple', part: '真实的赚钱' }
|
||||
],
|
||||
latestSection: { id: '9.14', title: '大健康私域:一个月150万的70后', part: '真实的赚钱' },
|
||||
partsList: [
|
||||
{ id: 'part-1', number: '01', title: '真实的人', subtitle: '人性观察与社交逻辑' },
|
||||
{ id: 'part-2', number: '02', title: '真实的行业', subtitle: '社会运作的底层规则' },
|
||||
{ id: 'part-3', number: '03', title: '真实的错误', subtitle: '错过机会比失败更贵' },
|
||||
{ id: 'part-4', number: '04', title: '真实的赚钱', subtitle: '所有行业的杠杆结构' },
|
||||
{ id: 'part-5', number: '05', title: '真实的未来', subtitle: '人与系统的关系' }
|
||||
]
|
||||
},
|
||||
function init(window, document) {require('../../common/index.js')(window, document)}
|
||||
|
||||
onLoad(options) {
|
||||
this.setNavBarHeight()
|
||||
if (options && options.ref) app.handleReferralCode({ query: options })
|
||||
this.loadBookData()
|
||||
this.updateUserStatus()
|
||||
},
|
||||
const baseConfig = getBaseConfig(mp, config, init)
|
||||
|
||||
setNavBarHeight() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 0 })
|
||||
this.setNavBarHeight()
|
||||
this.updateUserStatus()
|
||||
},
|
||||
|
||||
loadBookData() {
|
||||
app.request('/api/book/all-chapters').then((res) => {
|
||||
if (res && res.data) this.setData({ totalSections: res.totalSections || 62 })
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
updateUserStatus() {
|
||||
const { hasFullBook, purchasedSections } = app.globalData
|
||||
const total = this.data.totalSections || 62
|
||||
const count = hasFullBook ? total : (purchasedSections?.length || 0)
|
||||
this.setData({ hasFullBook, purchasedCount: count })
|
||||
},
|
||||
|
||||
goToChapters() { wx.switchTab({ url: '/pages/chapters/chapters' }) },
|
||||
goToSearch() { wx.navigateTo({ url: '/pages/search/search' }) },
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({ url: '/pages/read/read?id=' + (id || '') })
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadBookData()
|
||||
this.updateUserStatus()
|
||||
wx.stopPullDownRefresh()
|
||||
}
|
||||
Component({
|
||||
...baseConfig.base,
|
||||
methods: {
|
||||
...baseConfig.methods,
|
||||
|
||||
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
"navigationBarTitleText": "Soul创业实验",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"element": "miniprogram-element"
|
||||
}
|
||||
}
|
||||
@@ -1,88 +1 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="header-content">
|
||||
<view class="logo-section">
|
||||
<view class="logo-icon"><text class="logo-text">S</text></view>
|
||||
<view class="logo-info">
|
||||
<view class="logo-title"><text class="text-white">Soul</text><text class="brand-color">创业实验</text></view>
|
||||
<text class="logo-subtitle">来自派对房的真实故事</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chapter-badge">{{totalSections}}章</view>
|
||||
</view>
|
||||
<view class="search-bar" bindtap="goToSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">搜索章节...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="main-content">
|
||||
<view class="banner-card" bindtap="goToRead" data-id="{{latestSection.id}}">
|
||||
<view class="banner-glow"></view>
|
||||
<text class="banner-tag">最新更新</text>
|
||||
<view class="banner-title">{{latestSection.title}}</view>
|
||||
<view class="banner-part">{{latestSection.part}}</view>
|
||||
<view class="banner-action"><text class="banner-action-text">开始阅读</text> →</view>
|
||||
</view>
|
||||
|
||||
<view class="progress-card card">
|
||||
<view class="progress-header">
|
||||
<text class="progress-title">我的阅读</text>
|
||||
<text class="progress-count">{{purchasedCount}}/{{totalSections}}章</text>
|
||||
</view>
|
||||
<view class="progress-bar-bg">
|
||||
<view class="progress-bar-fill" style="width: {{totalSections ? (purchasedCount / totalSections * 100) : 0}}%;"></view>
|
||||
</view>
|
||||
<view class="progress-stats">
|
||||
<view class="stat-item"><text class="stat-value brand-color">{{purchasedCount}}</text><text class="stat-label">已读</text></view>
|
||||
<view class="stat-item"><text class="stat-value">{{totalSections - purchasedCount}}</text><text class="stat-label">待读</text></view>
|
||||
<view class="stat-item"><text class="stat-value">5</text><text class="stat-label">篇章</text></view>
|
||||
<view class="stat-item"><text class="stat-value">{{totalSections}}</text><text class="stat-label">章节</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">精选推荐</text>
|
||||
<view class="section-more" bindtap="goToChapters"><text class="more-text">查看全部</text> →</view>
|
||||
</view>
|
||||
<view class="featured-list">
|
||||
<view class="featured-item" wx:for="{{featuredSections}}" wx:key="id" bindtap="goToRead" data-id="{{item.id}}">
|
||||
<view class="featured-content">
|
||||
<view class="featured-meta">
|
||||
<text class="featured-id brand-color">{{item.id}}</text>
|
||||
<text class="tag {{item.tagClass}}">{{item.tag}}</text>
|
||||
</view>
|
||||
<text class="featured-title">{{item.title}}</text>
|
||||
<text class="featured-part">{{item.part}}</text>
|
||||
</view>
|
||||
<text class="featured-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">内容概览</text>
|
||||
<view class="parts-list">
|
||||
<view class="part-item" wx:for="{{partsList}}" wx:key="id" bindtap="goToChapters">
|
||||
<view class="part-icon"><text class="part-number">{{item.number}}</text></view>
|
||||
<view class="part-info">
|
||||
<text class="part-title">{{item.title}}</text>
|
||||
<text class="part-subtitle">{{item.subtitle}}</text>
|
||||
</view>
|
||||
<text class="part-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="preface-card" bindtap="goToRead" data-id="preface">
|
||||
<view class="preface-content">
|
||||
<text class="preface-title">序言</text>
|
||||
<text class="preface-desc">为什么我每天早上6点在Soul开播?</text>
|
||||
</view>
|
||||
<view class="tag tag-free">免费</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
<page-meta root-font-size="{{rootFontSize}}" page-style="{{pageStyle}}"></page-meta><element wx:if="{{pageId}}" class="{{bodyClass}}" style="{{bodyStyle}}" data-private-node-id="e-body" data-private-page-id="{{pageId}}" ></element>
|
||||
@@ -1,65 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; padding-bottom: 200rpx; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { padding: 0 32rpx 32rpx; }
|
||||
.header-content { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32rpx; padding-top: 24rpx; }
|
||||
.logo-section { display: flex; align-items: center; gap: 16rpx; }
|
||||
.logo-icon { width: 80rpx; height: 80rpx; border-radius: 20rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); display: flex; align-items: center; justify-content: center; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
|
||||
.logo-text { color: #fff; font-size: 36rpx; font-weight: 700; }
|
||||
.logo-info { display: flex; flex-direction: column; }
|
||||
.logo-title { font-size: 36rpx; font-weight: 700; }
|
||||
.text-white { color: #fff; }
|
||||
.brand-color { color: #00CED1; }
|
||||
.logo-subtitle { font-size: 22rpx; color: rgba(255,255,255,0.4); margin-top: 4rpx; }
|
||||
.chapter-badge { font-size: 22rpx; color: #00CED1; background: rgba(0,206,209,0.1); padding: 8rpx 16rpx; border-radius: 32rpx; }
|
||||
.search-bar { display: flex; align-items: center; gap: 24rpx; padding: 24rpx 32rpx; background: #1c1c1e; border-radius: 24rpx; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.search-icon { font-size: 32rpx; opacity: 0.6; }
|
||||
.search-placeholder { font-size: 28rpx; color: rgba(255,255,255,0.4); }
|
||||
.main-content { padding: 0 32rpx; box-sizing: border-box; }
|
||||
.banner-card { position: relative; padding: 40rpx; border-radius: 32rpx; overflow: hidden; background: linear-gradient(135deg, #0d3331 0%, #1a1a2e 50%, #16213e 100%); margin-bottom: 24rpx; }
|
||||
.banner-glow { position: absolute; top: 0; right: 0; width: 256rpx; height: 256rpx; background: #00CED1; border-radius: 50%; filter: blur(120rpx); opacity: 0.2; }
|
||||
.banner-tag { display: inline-block; padding: 8rpx 16rpx; background: #00CED1; color: #000; font-size: 22rpx; font-weight: 500; border-radius: 8rpx; margin-bottom: 24rpx; }
|
||||
.banner-title { font-size: 36rpx; font-weight: 700; color: #fff; margin-bottom: 16rpx; padding-right: 64rpx; }
|
||||
.banner-part { font-size: 28rpx; color: rgba(255,255,255,0.6); margin-bottom: 24rpx; }
|
||||
.banner-action { display: flex; align-items: center; gap: 8rpx; }
|
||||
.banner-action-text { font-size: 28rpx; color: #00CED1; font-weight: 500; }
|
||||
.card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; border: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 24rpx; }
|
||||
.progress-card { margin-bottom: 24rpx; }
|
||||
.progress-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
|
||||
.progress-title { font-size: 28rpx; color: #fff; font-weight: 500; }
|
||||
.progress-count { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.progress-bar-bg { width: 100%; height: 16rpx; background: #2c2c2e; border-radius: 8rpx; overflow: hidden; margin-bottom: 24rpx; }
|
||||
.progress-bar-fill { height: 100%; background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); border-radius: 8rpx; }
|
||||
.progress-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24rpx; }
|
||||
.stat-item { text-align: center; }
|
||||
.stat-value { font-size: 36rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.section { margin-bottom: 24rpx; }
|
||||
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
|
||||
.section-title { font-size: 32rpx; font-weight: 600; color: #fff; }
|
||||
.section-more { display: flex; align-items: center; gap: 8rpx; }
|
||||
.more-text { font-size: 24rpx; color: #00CED1; }
|
||||
.featured-list { display: flex; flex-direction: column; gap: 24rpx; }
|
||||
.featured-item { display: flex; align-items: flex-start; justify-content: space-between; padding: 32rpx; background: #1c1c1e; border-radius: 24rpx; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.featured-content { flex: 1; }
|
||||
.featured-meta { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
|
||||
.featured-id { font-size: 24rpx; font-weight: 500; }
|
||||
.tag { font-size: 22rpx; padding: 6rpx 16rpx; border-radius: 8rpx; }
|
||||
.tag-free { background: rgba(0,206,209,0.1); color: #00CED1; }
|
||||
.tag-pink { background: rgba(233,30,99,0.1); color: #E91E63; }
|
||||
.tag-purple { background: rgba(123,97,255,0.1); color: #7B61FF; }
|
||||
.featured-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; margin-bottom: 8rpx; }
|
||||
.featured-part { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.featured-arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); }
|
||||
.parts-list { display: flex; flex-direction: column; gap: 24rpx; }
|
||||
.part-item { display: flex; align-items: center; gap: 24rpx; padding: 32rpx; background: #1c1c1e; border-radius: 24rpx; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.part-icon { width: 80rpx; height: 80rpx; border-radius: 16rpx; background: linear-gradient(135deg, rgba(0,206,209,0.2) 0%, rgba(32,178,170,0.1) 100%); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.part-number { font-size: 28rpx; font-weight: 700; color: #00CED1; }
|
||||
.part-info { flex: 1; min-width: 0; }
|
||||
.part-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; margin-bottom: 4rpx; }
|
||||
.part-subtitle { font-size: 22rpx; color: rgba(255,255,255,0.4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.part-arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); }
|
||||
.preface-card { display: flex; align-items: center; justify-content: space-between; padding: 32rpx; border-radius: 24rpx; background: linear-gradient(90deg, rgba(0,206,209,0.1) 0%, transparent 100%); border: 2rpx solid rgba(0,206,209,0.2); margin-bottom: 24rpx; }
|
||||
.preface-content { flex: 1; }
|
||||
.preface-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; margin-bottom: 8rpx; }
|
||||
.preface-desc { font-size: 24rpx; color: rgba(255,255,255,0.6); }
|
||||
.bottom-space { height: 40rpx; }
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
const MATCH_TYPES = [
|
||||
{ id: 'partner', label: '创业合伙', matchLabel: '创业伙伴', icon: '⭐' },
|
||||
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥' },
|
||||
{ id: 'mentor', label: '导师顾问', matchLabel: '商业顾问', icon: '❤️' },
|
||||
{ id: 'team', label: '团队招募', matchLabel: '加入项目', icon: '🎮' }
|
||||
]
|
||||
const FREE_MATCH_LIMIT = 1
|
||||
|
||||
function getStoredContact() {
|
||||
try {
|
||||
return {
|
||||
phone: wx.getStorageSync('user_phone') || '',
|
||||
wechat: wx.getStorageSync('user_wechat') || ''
|
||||
}
|
||||
} catch (e) { return { phone: '', wechat: '' } }
|
||||
}
|
||||
|
||||
function getTodayMatchCount() {
|
||||
try {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const stored = wx.getStorageSync('match_count_data')
|
||||
if (stored) {
|
||||
const data = typeof stored === 'string' ? JSON.parse(stored) : stored
|
||||
if (data.date === today) return data.count || 0
|
||||
}
|
||||
} catch (e) {}
|
||||
return 0
|
||||
}
|
||||
|
||||
function saveTodayMatchCount(count) {
|
||||
try {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
wx.setStorageSync('match_count_data', JSON.stringify({ date: today, count }))
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function saveContact(phone, wechat) {
|
||||
try {
|
||||
if (phone) wx.setStorageSync('user_phone', phone)
|
||||
if (wechat) wx.setStorageSync('user_wechat', wechat)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
matchEnabled: false,
|
||||
matchTypes: MATCH_TYPES,
|
||||
selectedType: 'partner',
|
||||
currentMatchLabel: '创业伙伴',
|
||||
hasPurchased: false,
|
||||
todayMatchCount: 0,
|
||||
totalMatchesAllowed: 1,
|
||||
matchesRemaining: 0,
|
||||
needPayToMatch: false,
|
||||
isMatching: false,
|
||||
currentMatch: null,
|
||||
matchAttempts: 0,
|
||||
showUnlockModal: false,
|
||||
showJoinModal: false,
|
||||
joinType: null,
|
||||
phoneNumber: '',
|
||||
wechatId: '',
|
||||
contactType: 'phone',
|
||||
isJoining: false,
|
||||
joinSuccess: false,
|
||||
joinError: '',
|
||||
isUnlocking: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setNavBarHeight()
|
||||
const contact = getStoredContact()
|
||||
this.setData({ phoneNumber: contact.phone, wechatId: contact.wechat })
|
||||
app.loadFeatureConfig().then(() => this.syncState())
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 2 })
|
||||
this.setNavBarHeight()
|
||||
app.loadFeatureConfig().then(() => this.syncState())
|
||||
},
|
||||
|
||||
setNavBarHeight() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
syncState() {
|
||||
const matchEnabled = app.globalData.matchEnabled === true
|
||||
const user = app.globalData.userInfo
|
||||
const hasFullBook = !!app.globalData.hasFullBook
|
||||
const purchasedSections = app.globalData.purchasedSections || []
|
||||
const hasPurchased = hasFullBook || purchasedSections.length > 0
|
||||
const todayMatchCount = getTodayMatchCount()
|
||||
const totalMatchesAllowed = hasFullBook ? 999999 : FREE_MATCH_LIMIT + purchasedSections.length
|
||||
const matchesRemaining = hasFullBook ? 999999 : Math.max(0, totalMatchesAllowed - todayMatchCount)
|
||||
const needPayToMatch = !hasFullBook && matchesRemaining <= 0
|
||||
if (user && user.phone) this.setData({ phoneNumber: user.phone })
|
||||
this.setData({
|
||||
matchEnabled,
|
||||
hasPurchased,
|
||||
todayMatchCount,
|
||||
totalMatchesAllowed,
|
||||
matchesRemaining,
|
||||
needPayToMatch
|
||||
})
|
||||
},
|
||||
|
||||
selectType(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
const t = MATCH_TYPES.find(x => x.id === id)
|
||||
this.setData({ selectedType: id, currentMatchLabel: t ? t.matchLabel : '创业伙伴' })
|
||||
},
|
||||
|
||||
startMatch() {
|
||||
if (!this.data.hasPurchased) {
|
||||
wx.showToast({ title: '购买书籍后可使用', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (this.data.needPayToMatch) {
|
||||
this.setData({ showUnlockModal: true })
|
||||
return
|
||||
}
|
||||
this.setData({ isMatching: true, currentMatch: null, matchAttempts: 0 })
|
||||
let attempts = 0
|
||||
const timer = setInterval(() => {
|
||||
attempts++
|
||||
this.setData({ matchAttempts: attempts })
|
||||
}, 1000)
|
||||
const delay = 3000 + Math.random() * 3000
|
||||
setTimeout(() => {
|
||||
clearInterval(timer)
|
||||
const matched = this.getMockMatch()
|
||||
const newCount = this.data.todayMatchCount + 1
|
||||
saveTodayMatchCount(newCount)
|
||||
this.reportMatchToCKB(matched)
|
||||
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
|
||||
const showJoinAfter = currentType && (currentType.id === 'investor' || currentType.id === 'mentor' || currentType.id === 'team')
|
||||
this.setData({
|
||||
isMatching: false,
|
||||
currentMatch: matched,
|
||||
todayMatchCount: newCount,
|
||||
matchesRemaining: this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount),
|
||||
needPayToMatch: !this.data.hasFullBook && (this.data.totalMatchesAllowed - newCount) <= 0
|
||||
})
|
||||
if (showJoinAfter) {
|
||||
this.setData({ showJoinModal: true, joinType: this.data.selectedType, joinSuccess: false, joinError: '' })
|
||||
}
|
||||
}, delay)
|
||||
},
|
||||
|
||||
getMockMatch() {
|
||||
const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者']
|
||||
const concepts = [
|
||||
'专注私域流量运营5年,帮助100+品牌实现从0到1的增长。',
|
||||
'连续创业者,擅长商业模式设计和资源整合。',
|
||||
'在Soul分享真实创业故事,希望找到志同道合的合作伙伴。'
|
||||
]
|
||||
const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan']
|
||||
const i = Math.floor(Math.random() * nicknames.length)
|
||||
const typeLabel = MATCH_TYPES.find(t => t.id === this.data.selectedType)
|
||||
return {
|
||||
id: 'user_' + Date.now(),
|
||||
nickname: nicknames[i],
|
||||
avatar: '',
|
||||
tags: ['创业者', '私域运营', typeLabel ? typeLabel.label : ''],
|
||||
matchScore: 80 + Math.floor(Math.random() * 20),
|
||||
concept: concepts[i % concepts.length],
|
||||
wechat: wechats[i % wechats.length],
|
||||
commonInterests: [
|
||||
{ icon: '📚', text: '都在读《创业实验》' },
|
||||
{ icon: '💼', text: '对私域运营感兴趣' },
|
||||
{ icon: '🎯', text: '相似的创业方向' }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
reportMatchToCKB(matched) {
|
||||
app.request('/api/ckb/match', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
matchType: this.data.selectedType,
|
||||
phone: this.data.phoneNumber || (app.globalData.userInfo && app.globalData.userInfo.phone) || '',
|
||||
wechat: this.data.wechatId || (app.globalData.userInfo && app.globalData.userInfo.wechat) || '',
|
||||
userId: (app.globalData.userInfo && app.globalData.userInfo.id) || '',
|
||||
nickname: (app.globalData.userInfo && app.globalData.userInfo.nickname) || '',
|
||||
matchedUser: { id: matched.id, nickname: matched.nickname, matchScore: matched.matchScore }
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
cancelMatch() {
|
||||
this.setData({ isMatching: false })
|
||||
},
|
||||
|
||||
nextMatch() {
|
||||
if (this.data.needPayToMatch) {
|
||||
this.setData({ showUnlockModal: true })
|
||||
return
|
||||
}
|
||||
this.setData({ currentMatch: null })
|
||||
this.startMatch()
|
||||
},
|
||||
|
||||
addWechat() {
|
||||
const m = this.data.currentMatch
|
||||
if (!m || !m.wechat) return
|
||||
wx.setClipboardData({
|
||||
data: m.wechat,
|
||||
success: () => {
|
||||
wx.showModal({
|
||||
title: '已复制微信号',
|
||||
content: '请打开微信添加好友,备注"创业合作"即可。',
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
closeUnlockModal() {
|
||||
if (!this.data.isUnlocking) this.setData({ showUnlockModal: false })
|
||||
},
|
||||
|
||||
goBuySection() {
|
||||
this.setData({ showUnlockModal: false })
|
||||
wx.switchTab({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
closeJoinModal() {
|
||||
if (!this.data.isJoining) this.setData({ showJoinModal: false })
|
||||
},
|
||||
|
||||
setContactType(e) {
|
||||
const t = e.currentTarget.dataset.type
|
||||
this.setData({ contactType: t })
|
||||
},
|
||||
|
||||
onPhoneInput(e) {
|
||||
this.setData({ phoneNumber: (e.detail && e.detail.value) || '' })
|
||||
},
|
||||
|
||||
onWechatInput(e) {
|
||||
this.setData({ wechatId: (e.detail && e.detail.value) || '' })
|
||||
},
|
||||
|
||||
submitJoin() {
|
||||
const { contactType, phoneNumber, wechatId } = this.data
|
||||
if (contactType === 'phone' && (!phoneNumber || phoneNumber.length !== 11)) {
|
||||
this.setData({ joinError: '请输入正确的11位手机号' })
|
||||
return
|
||||
}
|
||||
if (contactType === 'wechat' && (!wechatId || wechatId.length < 6)) {
|
||||
this.setData({ joinError: '请输入正确的微信号(至少6位)' })
|
||||
return
|
||||
}
|
||||
this.setData({ isJoining: true, joinError: '' })
|
||||
app.request('/api/ckb/join', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
type: this.data.joinType,
|
||||
phone: contactType === 'phone' ? phoneNumber : '',
|
||||
wechat: contactType === 'wechat' ? wechatId : '',
|
||||
userId: app.globalData.userInfo && app.globalData.userInfo.id
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res && res.success) {
|
||||
saveContact(phoneNumber, wechatId)
|
||||
this.setData({ joinSuccess: true })
|
||||
setTimeout(() => this.setData({ showJoinModal: false, joinSuccess: false }), 2000)
|
||||
} else {
|
||||
this.setData({ joinError: (res && res.message) || '加入失败,请稍后重试' })
|
||||
}
|
||||
}).catch(() => {
|
||||
this.setData({ joinError: '网络错误,请检查网络后重试' })
|
||||
}).finally(() => {
|
||||
this.setData({ isJoining: false })
|
||||
})
|
||||
},
|
||||
|
||||
goToChapters() {
|
||||
wx.switchTab({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
goToIndex() {
|
||||
wx.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "找伙伴",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
|
||||
<block wx:if="{{!matchEnabled}}">
|
||||
<view class="closed-wrap">
|
||||
<view class="closed-icon">🔒</view>
|
||||
<text class="closed-title">功能暂未开放</text>
|
||||
<text class="closed-desc">找伙伴功能即将上线,请先逛逛首页与目录。</text>
|
||||
<view class="btn-primary" bindtap="goToIndex">返回首页</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="header safe-header-right">
|
||||
<text class="header-title">找伙伴</text>
|
||||
<view class="header-btn"></view>
|
||||
</view>
|
||||
|
||||
<view class="match-count-card" wx:if="{{hasPurchased}}">
|
||||
<view class="match-count-left">
|
||||
<text class="match-count-label {{matchesRemaining <= 0 && !needPayToMatch ? '' : ''}}">{{needPayToMatch ? '今日匹配机会已用完' : (totalMatchesAllowed > 999 ? '无限匹配机会' : '剩余匹配机会')}}</text>
|
||||
</view>
|
||||
<view class="match-count-right">
|
||||
<text class="match-count-num {{matchesRemaining > 0 ? 'active' : 'red'}}">{{totalMatchesAllowed > 999 ? '无限' : matchesRemaining + '/' + totalMatchesAllowed}}</text>
|
||||
<view class="btn-buy-section" wx:if="{{needPayToMatch}}" bindtap="goToChapters">购买小节+1次</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!isMatching && !currentMatch}}">
|
||||
<view class="idle-wrap">
|
||||
<view class="circle-wrap {{hasPurchased ? 'active' : ''}} {{needPayToMatch ? 'need-pay' : ''}}" bindtap="startMatch">
|
||||
<view class="circle-inner">
|
||||
<block wx:if="{{!hasPurchased}}">
|
||||
<text class="circle-icon">🔒</text>
|
||||
<text class="circle-title">购买后解锁</text>
|
||||
<text class="circle-desc">购买9.9元即可使用</text>
|
||||
</block>
|
||||
<block wx:elif="{{needPayToMatch}}">
|
||||
<text class="circle-icon gold">⚡</text>
|
||||
<text class="circle-title">需要解锁</text>
|
||||
<text class="circle-desc">今日免费次数已用完</text>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<text class="circle-icon">👥</text>
|
||||
<text class="circle-title">开始匹配</text>
|
||||
<text class="circle-desc">匹配{{currentMatchLabel}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<text class="idle-mode">当前模式: {{selectedType === 'partner' ? '创业合伙' : (selectedType === 'investor' ? '资源对接' : (selectedType === 'mentor' ? '导师顾问' : '团队招募'))}}</text>
|
||||
<view class="buy-tip" wx:if="{{!hasPurchased}}" bindtap="goToChapters">
|
||||
<view class="buy-tip-left">
|
||||
<text class="buy-tip-title">购买书籍解锁匹配功能</text>
|
||||
<text class="buy-tip-desc">仅需9.9元,每天3次免费匹配</text>
|
||||
</view>
|
||||
<view class="btn-go-buy">去购买</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<text class="type-label">选择匹配类型</text>
|
||||
<view class="type-grid">
|
||||
<view class="type-item {{selectedType === item.id ? 'active' : ''}}" wx:for="{{matchTypes}}" wx:key="id" data-id="{{item.id}}" bindtap="selectType">
|
||||
<text class="type-icon">{{item.icon}}</text>
|
||||
<text class="type-text">{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:if="{{isMatching}}">
|
||||
<view class="matching-wrap">
|
||||
<view class="matching-spinner"></view>
|
||||
<text class="matching-title">正在匹配{{currentMatchLabel}}...</text>
|
||||
<text class="matching-count">已匹配 {{matchAttempts}} 次</text>
|
||||
<view class="btn-cancel" bindtap="cancelMatch">取消匹配</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:if="{{currentMatch && !isMatching}}">
|
||||
<view class="matched-wrap">
|
||||
<text class="matched-emoji">✨</text>
|
||||
<view class="matched-card">
|
||||
<view class="matched-head">
|
||||
<image class="matched-avatar" src="{{currentMatch.avatar || '/images/placeholder-user.jpg'}}" mode="aspectFill" />
|
||||
<view class="matched-info">
|
||||
<text class="matched-name">{{currentMatch.nickname}}</text>
|
||||
<view class="matched-tags">
|
||||
<text class="matched-tag" wx:for="{{currentMatch.tags}}" wx:key="*this" wx:for-item="tag">{{tag}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="matched-score-wrap">
|
||||
<text class="matched-score">{{currentMatch.matchScore}}%</text>
|
||||
<text class="matched-score-label">匹配度</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="matched-interests">
|
||||
<text class="matched-label">共同兴趣</text>
|
||||
<view class="interest-row" wx:for="{{currentMatch.commonInterests}}" wx:key="text">
|
||||
<text class="interest-icon">{{item.icon}}</text>
|
||||
<text class="interest-text">{{item.text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="matched-concept">
|
||||
<text class="matched-label">核心理念</text>
|
||||
<text class="matched-concept-text">{{currentMatch.concept}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-add-wechat" bindtap="addWechat">一键加好友</view>
|
||||
<view class="btn-next" bindtap="nextMatch">重新匹配</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<view class="mask" wx:if="{{showUnlockModal}}" catchtap="closeUnlockModal">
|
||||
<view class="modal unlock-modal" catchtap="">
|
||||
<view class="modal-icon-wrap"><text class="modal-icon gold">⚡</text></view>
|
||||
<text class="modal-title">匹配机会已用完</text>
|
||||
<text class="modal-desc">每购买一个小节内容即可额外获得1次匹配机会</text>
|
||||
<view class="modal-row"><text class="modal-row-label">解锁方式</text><text class="modal-row-value">购买任意小节</text></view>
|
||||
<view class="modal-row"><text class="modal-row-label">获得次数</text><text class="modal-row-value brand">+1次匹配</text></view>
|
||||
<view class="btn-primary" bindtap="goBuySection">去购买小节 (¥1/节)</view>
|
||||
<view class="btn-ghost" bindtap="closeUnlockModal">明天再来</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mask" wx:if="{{showJoinModal}}" catchtap="closeJoinModal">
|
||||
<view class="modal join-modal" catchtap="">
|
||||
<view class="modal-head">
|
||||
<text class="modal-head-title">加入{{joinType === 'partner' ? '创业伙伴' : (joinType === 'investor' ? '资源对接' : (joinType === 'mentor' ? '商业顾问' : '加入项目'))}}</text>
|
||||
<view class="modal-close" bindtap="closeJoinModal">×</view>
|
||||
</view>
|
||||
<view class="modal-body" wx:if="{{!joinSuccess}}">
|
||||
<text class="modal-hint">请填写您的联系方式以便我们联系您</text>
|
||||
<view class="contact-tabs">
|
||||
<view class="contact-tab {{contactType === 'phone' ? 'active' : ''}}" data-type="phone" bindtap="setContactType">手机号</view>
|
||||
<view class="contact-tab {{contactType === 'wechat' ? 'active' : ''}}" data-type="wechat" bindtap="setContactType">微信号</view>
|
||||
</view>
|
||||
<view class="input-wrap" wx:if="{{contactType === 'phone'}}">
|
||||
<input class="input" type="number" maxlength="11" placeholder="请输入11位手机号" value="{{phoneNumber}}" bindinput="onPhoneInput" />
|
||||
</view>
|
||||
<view class="input-wrap" wx:else>
|
||||
<input class="input" placeholder="请输入微信号" value="{{wechatId}}" bindinput="onWechatInput" />
|
||||
</view>
|
||||
<text class="error-text" wx:if="{{joinError}}">{{joinError}}</text>
|
||||
<view class="btn-primary {{(contactType === 'phone' ? !phoneNumber : !wechatId) || isJoining ? 'disabled' : ''}}" bindtap="submitJoin">{{isJoining ? '提交中...' : '确认加入'}}</view>
|
||||
</view>
|
||||
<view class="modal-body success-wrap" wx:else>
|
||||
<text class="success-emoji">✓</text>
|
||||
<text class="success-title">加入成功!</text>
|
||||
<text class="success-desc">我们会尽快与您联系</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
@@ -1,103 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; padding-bottom: 200rpx; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.bottom-space { height: 40rpx; }
|
||||
|
||||
.closed-wrap { display: flex; flex-direction: column; align-items: center; padding: 120rpx 48rpx; }
|
||||
.closed-icon { font-size: 120rpx; margin-bottom: 32rpx; opacity: 0.6; }
|
||||
.closed-title { font-size: 40rpx; font-weight: 600; color: #fff; margin-bottom: 16rpx; }
|
||||
.closed-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); text-align: center; margin-bottom: 64rpx; }
|
||||
.btn-primary { padding: 24rpx 64rpx; border-radius: 48rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 32rpx; font-weight: 600; }
|
||||
.btn-primary.disabled { opacity: 0.5; }
|
||||
|
||||
.header { display: flex; align-items: center; justify-content: space-between; padding: 24rpx 32rpx 16rpx; }
|
||||
.header-title { font-size: 48rpx; font-weight: 700; color: #fff; }
|
||||
.header-btn { width: 80rpx; height: 80rpx; border-radius: 50%; background: #1c1c1e; }
|
||||
|
||||
.match-count-card { margin: 0 32rpx 24rpx; padding: 24rpx 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); display: flex; align-items: center; justify-content: space-between; }
|
||||
.match-count-card .match-count-num.red { color: #f87171; }
|
||||
.match-count-card .match-count-num.active { color: #00E5FF; }
|
||||
.match-count-label { font-size: 28rpx; color: rgba(255,255,255,0.7); }
|
||||
.match-count-num { font-size: 36rpx; font-weight: 700; }
|
||||
.btn-buy-section { padding: 12rpx 24rpx; border-radius: 32rpx; background: rgba(255,215,0,0.2); font-size: 24rpx; color: #FFD700; margin-left: 16rpx; }
|
||||
|
||||
.idle-wrap { padding: 0 32rpx; display: flex; flex-direction: column; align-items: center; }
|
||||
.circle-wrap { width: 560rpx; height: 560rpx; border-radius: 50%; margin-bottom: 48rpx; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 50%); border: 4rpx solid rgba(255,255,255,0.1); }
|
||||
.circle-wrap.active { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%); border-color: rgba(0,229,255,0.3); }
|
||||
.circle-wrap.need-pay { border-color: rgba(255,215,0,0.3); }
|
||||
.circle-inner { text-align: center; }
|
||||
.circle-icon { font-size: 96rpx; display: block; margin-bottom: 24rpx; }
|
||||
.circle-icon.gold { color: #FFD700; }
|
||||
.circle-title { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; }
|
||||
.circle-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
.idle-mode { font-size: 28rpx; color: rgba(255,255,255,0.5); margin-bottom: 32rpx; }
|
||||
.buy-tip { width: 100%; padding: 32rpx; border-radius: 24rpx; background: linear-gradient(90deg, rgba(0,229,255,0.1) 0%, transparent 100%); border: 2rpx solid rgba(0,229,255,0.2); display: flex; align-items: center; justify-content: space-between; margin-bottom: 32rpx; }
|
||||
.buy-tip-left { }
|
||||
.buy-tip-title { font-size: 30rpx; color: #fff; font-weight: 500; display: block; }
|
||||
.buy-tip-desc { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
|
||||
.btn-go-buy { padding: 16rpx 32rpx; border-radius: 16rpx; background: #00E5FF; color: #000; font-size: 28rpx; font-weight: 500; }
|
||||
.divider { width: 100%; height: 2rpx; background: rgba(255,255,255,0.1); margin-bottom: 24rpx; }
|
||||
.type-label { font-size: 28rpx; color: rgba(255,255,255,0.4); margin-bottom: 24rpx; align-self: flex-start; }
|
||||
.type-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24rpx; width: 100%; }
|
||||
.type-item { padding: 32rpx 16rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid transparent; display: flex; flex-direction: column; align-items: center; gap: 16rpx; }
|
||||
.type-item.active { background: rgba(0,229,255,0.1); border-color: rgba(0,229,255,0.5); }
|
||||
.type-icon { font-size: 48rpx; }
|
||||
.type-text { font-size: 24rpx; color: rgba(255,255,255,0.6); }
|
||||
.type-item.active .type-text { color: #00E5FF; }
|
||||
|
||||
.matching-wrap { padding: 80rpx 32rpx; text-align: center; }
|
||||
.matching-spinner { width: 400rpx; height: 400rpx; margin: 0 auto 48rpx; border-radius: 50%; border: 8rpx solid transparent; border-top-color: #00E5FF; border-right-color: #7B61FF; border-bottom-color: #E91E63; animation: spin 1s linear infinite; }
|
||||
.matching-title { font-size: 40rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 16rpx; }
|
||||
.matching-count { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 48rpx; }
|
||||
.btn-cancel { display: inline-block; padding: 24rpx 64rpx; border-radius: 48rpx; background: #1c1c1e; color: #fff; border: 2rpx solid rgba(255,255,255,0.1); font-size: 28rpx; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.matched-wrap { padding: 0 32rpx; }
|
||||
.matched-emoji { font-size: 120rpx; display: block; text-align: center; margin-bottom: 32rpx; }
|
||||
.matched-card { padding: 40rpx; border-radius: 32rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 24rpx; }
|
||||
.matched-head { display: flex; align-items: center; gap: 32rpx; margin-bottom: 32rpx; padding-bottom: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.matched-avatar { width: 128rpx; height: 128rpx; border-radius: 50%; border: 4rpx solid #00E5FF; flex-shrink: 0; }
|
||||
.matched-info { flex: 1; min-width: 0; }
|
||||
.matched-name { font-size: 36rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 16rpx; }
|
||||
.matched-tags { display: flex; flex-wrap: wrap; gap: 8rpx; }
|
||||
.matched-tag { font-size: 22rpx; padding: 8rpx 16rpx; border-radius: 8rpx; background: rgba(0,229,255,0.2); color: #00E5FF; }
|
||||
.matched-score-wrap { text-align: center; }
|
||||
.matched-score { font-size: 48rpx; font-weight: 700; color: #00E5FF; display: block; }
|
||||
.matched-score-label { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.matched-interests { margin-bottom: 24rpx; padding-bottom: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.matched-concept { }
|
||||
.matched-label { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 16rpx; }
|
||||
.interest-row { display: flex; align-items: center; gap: 16rpx; margin-bottom: 8rpx; font-size: 28rpx; color: rgba(255,255,255,0.8); }
|
||||
.interest-icon { }
|
||||
.interest-text { }
|
||||
.matched-concept-text { font-size: 28rpx; color: rgba(255,255,255,0.7); line-height: 1.6; }
|
||||
.btn-add-wechat { width: 100%; padding: 32rpx; border-radius: 24rpx; background: #00E5FF; color: #000; font-size: 32rpx; font-weight: 500; text-align: center; margin-bottom: 24rpx; }
|
||||
.btn-next { width: 100%; padding: 32rpx; border-radius: 24rpx; background: #1c1c1e; color: #fff; border: 2rpx solid rgba(255,255,255,0.1); font-size: 32rpx; text-align: center; }
|
||||
|
||||
.mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 48rpx; box-sizing: border-box; }
|
||||
.modal { width: 100%; max-width: 600rpx; background: #1c1c1e; border-radius: 32rpx; padding: 48rpx; }
|
||||
.modal-icon-wrap { width: 128rpx; height: 128rpx; margin: 0 auto 32rpx; border-radius: 50%; background: rgba(255,215,0,0.2); display: flex; align-items: center; justify-content: center; }
|
||||
.modal-icon { font-size: 64rpx; }
|
||||
.modal-icon.gold { color: #FFD700; }
|
||||
.modal-title { font-size: 40rpx; font-weight: 700; color: #fff; display: block; text-align: center; margin-bottom: 16rpx; }
|
||||
.modal-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); text-align: center; display: block; margin-bottom: 32rpx; }
|
||||
.modal-row { display: flex; justify-content: space-between; padding: 16rpx 0; font-size: 28rpx; }
|
||||
.modal-row-label { color: rgba(255,255,255,0.5); }
|
||||
.modal-row-value { color: #fff; }
|
||||
.modal-row-value.brand { color: #00E5FF; }
|
||||
.btn-ghost { width: 100%; padding: 24rpx; border-radius: 24rpx; background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.6); font-size: 28rpx; text-align: center; margin-top: 24rpx; }
|
||||
|
||||
.join-modal .modal-head { display: flex; align-items: center; justify-content: space-between; padding-bottom: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); margin-bottom: 24rpx; }
|
||||
.modal-head-title { font-size: 36rpx; font-weight: 600; color: #fff; }
|
||||
.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 40rpx; color: rgba(255,255,255,0.6); }
|
||||
.modal-hint { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 24rpx; }
|
||||
.contact-tabs { display: flex; gap: 16rpx; margin-bottom: 24rpx; }
|
||||
.contact-tab { flex: 1; padding: 20rpx; border-radius: 16rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.1); text-align: center; font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
.contact-tab.active { background: rgba(0,229,255,0.2); border-color: rgba(0,229,255,0.3); color: #00E5FF; }
|
||||
.input-wrap { margin-bottom: 24rpx; }
|
||||
.input { width: 100%; padding: 24rpx 32rpx; border-radius: 24rpx; background: rgba(0,0,0,0.3); border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 28rpx; box-sizing: border-box; }
|
||||
.error-text { font-size: 24rpx; color: #f87171; display: block; margin-bottom: 16rpx; }
|
||||
.join-modal .btn-primary { width: 100%; text-align: center; margin-top: 16rpx; }
|
||||
.success-wrap { text-align: center; padding: 48rpx 0; }
|
||||
.success-emoji { font-size: 128rpx; color: #00E5FF; display: block; margin-bottom: 24rpx; }
|
||||
.success-title { font-size: 36rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 8rpx; }
|
||||
.success-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
@@ -1,163 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
userInitial: 'U',
|
||||
userIdSuffix: '---',
|
||||
earningsText: '0.00',
|
||||
pendingEarningsText: '0.00',
|
||||
earningsDisplay: '--',
|
||||
purchasedCount: 0,
|
||||
totalSections: 62,
|
||||
matchEnabled: false,
|
||||
activeTab: 'overview',
|
||||
completedOrders: 0,
|
||||
recentChapters: [],
|
||||
totalReadTime: 50,
|
||||
matchHistoryCount: 0,
|
||||
showBindModal: false,
|
||||
bindType: 'phone',
|
||||
bindValue: '',
|
||||
isBinding: false,
|
||||
bindError: ''
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setNavBarHeight()
|
||||
this.syncUser()
|
||||
app.loadFeatureConfig().then(() => {
|
||||
this.setData({ matchEnabled: app.globalData.matchEnabled === true })
|
||||
})
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 3 })
|
||||
this.setNavBarHeight()
|
||||
this.syncUser()
|
||||
app.loadFeatureConfig().then(() => {
|
||||
this.setData({ matchEnabled: app.globalData.matchEnabled === true })
|
||||
})
|
||||
},
|
||||
|
||||
setNavBarHeight() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const isLoggedIn = !!app.globalData.isLoggedIn
|
||||
const user = app.globalData.userInfo || null
|
||||
const total = app.globalData.bookData ? (Array.isArray(app.globalData.bookData) ? app.globalData.bookData.length : 62) : 62
|
||||
const purchasedSections = app.globalData.purchasedSections || []
|
||||
const hasFullBook = !!app.globalData.hasFullBook
|
||||
const purchasedCount = hasFullBook ? total : purchasedSections.length
|
||||
const recentChapters = (purchasedSections && purchasedSections.slice(-5)) || []
|
||||
const userInitial = user && user.nickname ? String(user.nickname).charAt(0) : 'U'
|
||||
const userIdSuffix = user && user.id ? String(user.id).slice(-8) : '---'
|
||||
const earningsText = Number(user && user.earnings != null ? user.earnings : 0).toFixed(2)
|
||||
const pendingEarningsText = Number(user && user.pendingEarnings != null ? user.pendingEarnings : 0).toFixed(2)
|
||||
const earningsDisplay = user && (user.earnings != null) && Number(user.earnings) > 0 ? '¥' + Number(user.earnings).toFixed(0) : '--'
|
||||
this.setData({
|
||||
isLoggedIn,
|
||||
user,
|
||||
userInitial,
|
||||
userIdSuffix,
|
||||
earningsText,
|
||||
pendingEarningsText,
|
||||
earningsDisplay,
|
||||
totalSections: total,
|
||||
purchasedCount,
|
||||
completedOrders: 0,
|
||||
recentChapters,
|
||||
totalReadTime: 50 + Math.floor(Math.random() * 150),
|
||||
matchHistoryCount: 0
|
||||
})
|
||||
},
|
||||
|
||||
doLogin() {
|
||||
app.login().then(() => {
|
||||
this.syncUser()
|
||||
wx.showToast({ title: '登录成功', icon: 'success' })
|
||||
})
|
||||
},
|
||||
|
||||
setActiveTab(e) {
|
||||
const tab = e.currentTarget.dataset.tab
|
||||
this.setData({ activeTab: tab })
|
||||
},
|
||||
|
||||
goAbout() { wx.navigateTo({ url: '/pages/about/about' }) },
|
||||
goPurchases() { wx.navigateTo({ url: '/pages/purchases/purchases' }) },
|
||||
goReferral() { wx.navigateTo({ url: '/pages/referral/referral' }) },
|
||||
goSettings() { wx.navigateTo({ url: '/pages/settings/settings' }) },
|
||||
goMatch() { wx.switchTab({ url: '/pages/match/match' }) },
|
||||
goChapters() { wx.switchTab({ url: '/pages/chapters/chapters' }) },
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (id) wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
|
||||
},
|
||||
|
||||
openBindModal(e) {
|
||||
const type = e.currentTarget.dataset.type
|
||||
const user = this.data.user
|
||||
let bindValue = ''
|
||||
if (type === 'phone' && user && user.phone) bindValue = user.phone
|
||||
if (type === 'wechat' && user && user.wechat) bindValue = user.wechat
|
||||
if (type === 'alipay' && user && user.alipay) bindValue = user.alipay
|
||||
this.setData({
|
||||
showBindModal: true,
|
||||
bindType: type,
|
||||
bindValue,
|
||||
bindError: ''
|
||||
})
|
||||
},
|
||||
|
||||
closeBindModal() {
|
||||
if (!this.data.isBinding) this.setData({ showBindModal: false, bindValue: '', bindError: '' })
|
||||
},
|
||||
|
||||
onBindInput(e) {
|
||||
this.setData({ bindValue: (e.detail && e.detail.value) || '', bindError: '' })
|
||||
},
|
||||
|
||||
submitBind() {
|
||||
const { bindType, bindValue } = this.data
|
||||
if (!bindValue || !bindValue.trim()) {
|
||||
this.setData({ bindError: '请输入内容' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'phone' && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
this.setData({ bindError: '请输入正确的手机号' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'wechat' && bindValue.length < 6) {
|
||||
this.setData({ bindError: '微信号至少6位' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'alipay' && !bindValue.includes('@') && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
this.setData({ bindError: '请输入正确的支付宝账号' })
|
||||
return
|
||||
}
|
||||
this.setData({ isBinding: true, bindError: '' })
|
||||
app.request('/api/user/update', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: this.data.user && this.data.user.id,
|
||||
[bindType]: bindValue
|
||||
}
|
||||
}).then(() => {
|
||||
const user = { ...this.data.user, [bindType]: bindValue }
|
||||
app.globalData.userInfo = user
|
||||
wx.setStorageSync('userInfo', user)
|
||||
this.setData({ user, showBindModal: false, bindValue: '', isBinding: false })
|
||||
wx.showToast({ title: '绑定成功', icon: 'success' })
|
||||
}).catch(() => {
|
||||
this.setData({ bindError: '绑定失败,请重试', isBinding: false })
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "我的",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<text class="header-title">我的</text>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!isLoggedIn}}">
|
||||
<view class="card user-card">
|
||||
<view class="user-row">
|
||||
<view class="avatar-placeholder">👤</view>
|
||||
<view class="user-info">
|
||||
<view class="btn-login" bindtap="doLogin">点击登录</view>
|
||||
<text class="user-desc">解锁专属权益</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats">
|
||||
<view class="stat"><text class="stat-num brand-color">0</text><text class="stat-label">已购章节</text></view>
|
||||
<view class="stat"><text class="stat-num brand-color">0</text><text class="stat-label">推荐好友</text></view>
|
||||
<view class="stat"><text class="stat-num gold-color">--</text><text class="stat-label">待领收益</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card referral-card">
|
||||
<view class="referral-left">
|
||||
<view class="referral-icon">🎁</view>
|
||||
<view>
|
||||
<text class="referral-title">推广赚收益</text>
|
||||
<text class="referral-desc">登录后查看详情</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-ref" bindtap="doLogin">立即登录</view>
|
||||
</view>
|
||||
<view class="menu">
|
||||
<view class="menu-item" bindtap="doLogin">
|
||||
<text class="menu-icon">📦</text>
|
||||
<text class="menu-text">我的订单</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="goAbout">
|
||||
<view class="menu-icon-circle brand">ℹ</view>
|
||||
<text class="menu-text">关于作者</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="card user-card">
|
||||
<view class="user-row">
|
||||
<view class="avatar">{{userInitial}}</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{user.nickname || '用户'}}</text>
|
||||
<text class="user-id">ID: {{userIdSuffix}}</text>
|
||||
</view>
|
||||
<view class="tag">⭐ 创业伙伴</view>
|
||||
</view>
|
||||
<view class="stats">
|
||||
<view class="stat"><text class="stat-num brand-color">{{purchasedCount}}</text><text class="stat-label">已购章节</text></view>
|
||||
<view class="stat"><text class="stat-num brand-color">{{user.referralCount || 0}}</text><text class="stat-label">推荐好友</text></view>
|
||||
<view class="stat"><text class="stat-num gold-color">{{earningsDisplay}}</text><text class="stat-label">待领收益</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card earnings-card" bindtap="goReferral">
|
||||
<view class="earnings-head">
|
||||
<view class="earnings-title-row">
|
||||
<text class="earnings-icon">💰</text>
|
||||
<text class="earnings-title">我的收益</text>
|
||||
</view>
|
||||
<text class="earnings-link">推广中心 ›</text>
|
||||
</view>
|
||||
<view class="earnings-row">
|
||||
<view class="earnings-item">
|
||||
<text class="earnings-label">累计收益</text>
|
||||
<text class="earnings-value gold">¥{{earningsText}}</text>
|
||||
</view>
|
||||
<view class="earnings-item">
|
||||
<text class="earnings-label">可提现</text>
|
||||
<text class="earnings-value">{{pendingEarningsText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-earnings">推广中心 / 提现</view>
|
||||
</view>
|
||||
|
||||
<view class="tabs">
|
||||
<view class="tab {{activeTab === 'overview' ? 'active' : ''}}" data-tab="overview" bindtap="setActiveTab">📋 概览</view>
|
||||
<view class="tab {{activeTab === 'footprint' ? 'active' : ''}}" data-tab="footprint" bindtap="setActiveTab">👣 我的足迹</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{activeTab === 'overview'}}">
|
||||
<view class="menu">
|
||||
<view class="menu-item" bindtap="goPurchases">
|
||||
<text class="menu-icon">📦</text>
|
||||
<text class="menu-text">我的订单</text>
|
||||
<text class="menu-extra">{{completedOrders}}笔</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="goReferral">
|
||||
<view class="menu-icon-circle gold">🎁</view>
|
||||
<text class="menu-text">推广中心</text>
|
||||
<text class="menu-extra brand">90%佣金</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="goAbout">
|
||||
<view class="menu-icon-circle brand">ℹ</view>
|
||||
<text class="menu-text">关于作者</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="goSettings">
|
||||
<view class="menu-icon-circle gray">⚙</view>
|
||||
<text class="menu-text">设置</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:if="{{activeTab === 'footprint'}}">
|
||||
<view class="footprint-section">
|
||||
<view class="footprint-card">
|
||||
<view class="footprint-title-row">
|
||||
<text class="footprint-icon">📊</text>
|
||||
<text class="footprint-title">阅读统计</text>
|
||||
</view>
|
||||
<view class="footprint-stats">
|
||||
<view class="fp-stat">
|
||||
<text class="fp-stat-icon">📖</text>
|
||||
<text class="fp-stat-value brand-color">{{purchasedCount}}</text>
|
||||
<text class="fp-stat-label">已读章节</text>
|
||||
</view>
|
||||
<view class="fp-stat">
|
||||
<text class="fp-stat-icon">⏱</text>
|
||||
<text class="fp-stat-value">{{totalReadTime}}</text>
|
||||
<text class="fp-stat-label">阅读分钟</text>
|
||||
</view>
|
||||
<view class="fp-stat">
|
||||
<text class="fp-stat-icon">👥</text>
|
||||
<text class="fp-stat-value">{{matchHistoryCount}}</text>
|
||||
<text class="fp-stat-label">匹配伙伴</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footprint-card">
|
||||
<view class="footprint-title-row">
|
||||
<text class="footprint-icon">📖</text>
|
||||
<text class="footprint-title">最近阅读</text>
|
||||
</view>
|
||||
<block wx:if="{{recentChapters.length > 0}}">
|
||||
<view class="recent-item" wx:for="{{recentChapters}}" wx:key="*this" data-id="{{item}}" bindtap="goToRead">
|
||||
<text class="recent-index">{{index + 1}}</text>
|
||||
<text class="recent-id">章节 {{item}}</text>
|
||||
<text class="recent-link">继续阅读</text>
|
||||
</view>
|
||||
</block>
|
||||
<view class="footprint-empty" wx:else>
|
||||
<text class="empty-desc">暂无阅读记录</text>
|
||||
<view class="empty-btn" bindtap="goChapters">去阅读 →</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footprint-card" wx:if="{{matchEnabled}}">
|
||||
<view class="footprint-title-row">
|
||||
<text class="footprint-icon">👥</text>
|
||||
<text class="footprint-title">匹配记录</text>
|
||||
</view>
|
||||
<view class="footprint-empty">
|
||||
<text class="empty-desc">暂无匹配记录</text>
|
||||
<view class="empty-btn" bindtap="goMatch">去匹配 →</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<view class="mask" wx:if="{{showBindModal}}" catchtap="closeBindModal">
|
||||
<view class="modal bind-modal" catchtap="">
|
||||
<view class="modal-head">
|
||||
<text class="modal-head-title">绑定{{bindType === 'phone' ? '手机号' : (bindType === 'wechat' ? '微信号' : '支付宝')}}</text>
|
||||
<view class="modal-close" bindtap="closeBindModal">×</view>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<input class="bind-input" placeholder="{{bindType === 'phone' ? '请输入11位手机号' : (bindType === 'wechat' ? '请输入微信号' : '请输入支付宝账号')}}" value="{{bindValue}}" bindinput="onBindInput" />
|
||||
<text class="bind-error" wx:if="{{bindError}}">{{bindError}}</text>
|
||||
<text class="bind-hint">{{bindType === 'phone' ? '绑定手机号后可用于找伙伴匹配' : (bindType === 'wechat' ? '绑定微信号后可用于找伙伴匹配和好友添加' : '绑定支付宝后可用于提现收益')}}</text>
|
||||
<view class="btn-primary {{!bindValue || isBinding ? 'disabled' : ''}}" bindtap="submitBind">{{isBinding ? '绑定中...' : '确认绑定'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
@@ -1,88 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; padding-bottom: 200rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { text-align: center; padding: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.header-title { font-size: 36rpx; font-weight: 500; color: #00CED1; }
|
||||
.card { margin: 32rpx; border-radius: 32rpx; padding: 32rpx; border: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; }
|
||||
.user-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-color: rgba(0,206,209,0.2); }
|
||||
.user-row { display: flex; align-items: center; gap: 24rpx; margin-bottom: 32rpx; }
|
||||
.avatar-placeholder { width: 128rpx; height: 128rpx; border-radius: 50%; border: 4rpx dashed rgba(0,206,209,0.5); background: linear-gradient(135deg, rgba(0,206,209,0.1) 0%, transparent 100%); display: flex; align-items: center; justify-content: center; font-size: 64rpx; }
|
||||
.avatar { width: 128rpx; height: 128rpx; border-radius: 50%; border: 4rpx solid #00CED1; background: linear-gradient(135deg, rgba(0,206,209,0.2) 0%, transparent 100%); display: flex; align-items: center; justify-content: center; font-size: 48rpx; font-weight: 700; color: #00CED1; }
|
||||
.user-info { flex: 1; }
|
||||
.btn-login { font-size: 36rpx; font-weight: 600; color: #00CED1; margin-bottom: 8rpx; }
|
||||
.user-desc { font-size: 28rpx; color: rgba(255,255,255,0.3); }
|
||||
.user-name { font-size: 36rpx; font-weight: 600; color: #fff; display: block; }
|
||||
.user-id { font-size: 24rpx; color: rgba(255,255,255,0.3); }
|
||||
.tag { padding: 8rpx 24rpx; border-radius: 32rpx; background: rgba(0,206,209,0.2); border: 2rpx solid rgba(0,206,209,0.3); font-size: 22rpx; color: #00CED1; }
|
||||
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.stat { text-align: center; padding: 16rpx; border-radius: 16rpx; background: rgba(255,255,255,0.05); }
|
||||
.stat-num { font-size: 40rpx; font-weight: 700; display: block; }
|
||||
.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.brand-color { color: #00CED1; }
|
||||
.gold-color { color: #FFD700; }
|
||||
.referral-card { background: linear-gradient(90deg, rgba(255,215,0,0.1) 0%, #1c1c1e 100%); border-color: rgba(255,215,0,0.2); display: flex; align-items: center; justify-content: space-between; }
|
||||
.referral-left { display: flex; align-items: center; gap: 24rpx; }
|
||||
.referral-icon { width: 80rpx; height: 80rpx; border-radius: 50%; background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); display: flex; align-items: center; justify-content: center; font-size: 40rpx; }
|
||||
.referral-title { font-size: 30rpx; color: #fff; display: block; }
|
||||
.referral-desc { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.btn-ref { padding: 16rpx 32rpx; border-radius: 16rpx; background: rgba(255,215,0,0.2); font-size: 28rpx; color: #FFD700; }
|
||||
.menu { margin: 32rpx; border-radius: 32rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); overflow: hidden; }
|
||||
.menu-item { display: flex; align-items: center; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.menu-item:last-child { border-bottom: none; }
|
||||
.menu-icon { font-size: 40rpx; margin-right: 24rpx; }
|
||||
.menu-icon-circle { width: 48rpx; height: 48rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; font-size: 24rpx; }
|
||||
.menu-icon-circle.brand { background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; }
|
||||
.menu-icon-circle.gold { background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); color: #000; }
|
||||
.menu-icon-circle.gray { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.6); }
|
||||
.menu-text { flex: 1; font-size: 30rpx; color: #fff; }
|
||||
.arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); }
|
||||
.menu-extra { font-size: 28rpx; color: rgba(255,255,255,0.4); margin-right: 16rpx; }
|
||||
.menu-extra.brand { color: #FFD700; font-weight: 500; }
|
||||
|
||||
.earnings-card { margin: 32rpx; border-radius: 32rpx; padding: 32rpx; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); border: 2rpx solid rgba(0,206,209,0.2); box-sizing: border-box; }
|
||||
.earnings-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; gap: 16rpx; min-width: 0; }
|
||||
.earnings-title-row { display: flex; align-items: center; gap: 12rpx; flex-shrink: 0; min-width: 0; }
|
||||
.earnings-icon { font-size: 36rpx; flex-shrink: 0; }
|
||||
.earnings-title { font-size: 30rpx; color: #fff; font-weight: 500; white-space: nowrap; }
|
||||
.earnings-link { font-size: 24rpx; color: #00CED1; flex-shrink: 0; white-space: nowrap; }
|
||||
.earnings-row { display: flex; align-items: flex-end; gap: 48rpx; margin-bottom: 24rpx; }
|
||||
.earnings-item { min-width: 0; }
|
||||
.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 8rpx; }
|
||||
.earnings-value { font-size: 36rpx; font-weight: 700; color: #fff; }
|
||||
.earnings-value.gold { font-size: 60rpx; background: linear-gradient(90deg, #FFD700 0%, #FFA500 100%); -webkit-background-clip: text; color: transparent; }
|
||||
.btn-earnings { display: block; width: 100%; padding: 24rpx; border-radius: 24rpx; background: linear-gradient(90deg, rgba(255,215,0,0.8) 0%, rgba(255,165,0,0.8) 100%); color: #000; font-size: 28rpx; font-weight: 700; text-align: center; box-sizing: border-box; }
|
||||
|
||||
.tabs { display: flex; gap: 16rpx; margin: 0 32rpx 24rpx; }
|
||||
.tab { flex: 1; padding: 24rpx; border-radius: 24rpx; background: #1c1c1e; color: rgba(255,255,255,0.6); font-size: 28rpx; text-align: center; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.tab.active { background: rgba(0,206,209,0.2); color: #00CED1; border-color: rgba(0,206,209,0.3); }
|
||||
|
||||
.footprint-section { padding: 0 32rpx 32rpx; }
|
||||
.footprint-card { padding: 32rpx; border-radius: 32rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 24rpx; }
|
||||
.footprint-title-row { display: flex; align-items: center; gap: 12rpx; margin-bottom: 24rpx; }
|
||||
.footprint-icon { font-size: 32rpx; }
|
||||
.footprint-title { font-size: 30rpx; color: #fff; font-weight: 500; }
|
||||
.footprint-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24rpx; }
|
||||
.fp-stat { text-align: center; padding: 24rpx; border-radius: 24rpx; background: rgba(255,255,255,0.05); }
|
||||
.fp-stat-icon { font-size: 36rpx; display: block; margin-bottom: 8rpx; }
|
||||
.fp-stat-value { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.fp-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.4); }
|
||||
.recent-item { display: flex; align-items: center; padding: 24rpx; border-radius: 24rpx; background: rgba(255,255,255,0.05); margin-bottom: 16rpx; }
|
||||
.recent-index { font-size: 28rpx; color: rgba(255,255,255,0.3); margin-right: 24rpx; }
|
||||
.recent-id { flex: 1; font-size: 28rpx; color: #fff; }
|
||||
.recent-link { font-size: 24rpx; color: #00CED1; }
|
||||
.footprint-empty { text-align: center; padding: 48rpx; }
|
||||
.empty-desc { font-size: 28rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 16rpx; }
|
||||
.empty-btn { font-size: 28rpx; color: #00CED1; }
|
||||
|
||||
.mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 48rpx; box-sizing: border-box; }
|
||||
.modal { width: 100%; max-width: 600rpx; background: #1c1c1e; border-radius: 32rpx; overflow: hidden; }
|
||||
.bind-modal .modal-head { display: flex; align-items: center; justify-content: space-between; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.modal-head-title { font-size: 36rpx; font-weight: 600; color: #fff; }
|
||||
.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 40rpx; color: rgba(255,255,255,0.6); }
|
||||
.bind-modal .modal-body { padding: 32rpx; }
|
||||
.bind-input { width: 100%; padding: 24rpx 32rpx; border-radius: 24rpx; background: rgba(0,0,0,0.3); border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 28rpx; box-sizing: border-box; margin-bottom: 24rpx; }
|
||||
.bind-error { font-size: 24rpx; color: #f87171; display: block; margin-bottom: 16rpx; }
|
||||
.bind-hint { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 24rpx; }
|
||||
.btn-primary { width: 100%; padding: 24rpx; border-radius: 24rpx; background: #00CED1; color: #000; font-size: 30rpx; font-weight: 500; text-align: center; }
|
||||
.btn-primary.disabled { opacity: 0.5; }
|
||||
|
||||
.bottom-space { height: 40rpx; }
|
||||
@@ -1,105 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
function flattenSections(bookData) {
|
||||
if (!bookData || !Array.isArray(bookData)) return []
|
||||
const out = []
|
||||
bookData.forEach(part => {
|
||||
if (!part.chapters || !Array.isArray(part.chapters)) return
|
||||
part.chapters.forEach(ch => {
|
||||
if (!ch.sections || !Array.isArray(ch.sections)) return
|
||||
ch.sections.forEach(sec => {
|
||||
out.push({ id: sec.id, title: sec.title || sec.id })
|
||||
})
|
||||
})
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
hasFullBook: false,
|
||||
purchasedSections: [],
|
||||
purchasedSectionList: [],
|
||||
purchasedCount: 0,
|
||||
totalSections: 62,
|
||||
sectionList: [],
|
||||
loading: true
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
this.syncUser()
|
||||
this.loadBookData()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncUser()
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const isLoggedIn = !!app.globalData.isLoggedIn
|
||||
const user = app.globalData.userInfo || null
|
||||
const hasFullBook = !!app.globalData.hasFullBook
|
||||
const purchasedSections = app.globalData.purchasedSections || []
|
||||
const total = this.data.totalSections || 62
|
||||
const purchasedCount = hasFullBook ? total : purchasedSections.length
|
||||
const sectionList = this.data.sectionList || []
|
||||
const purchasedSectionList = sectionList.filter(s => purchasedSections.indexOf(s.id) >= 0)
|
||||
this.setData({
|
||||
isLoggedIn,
|
||||
user,
|
||||
hasFullBook,
|
||||
purchasedSections,
|
||||
purchasedCount,
|
||||
purchasedSectionList
|
||||
})
|
||||
},
|
||||
|
||||
loadBookData() {
|
||||
const bookData = app.globalData.bookData
|
||||
if (bookData && Array.isArray(bookData)) {
|
||||
const sectionList = flattenSections(bookData)
|
||||
const totalSections = sectionList.length || 62
|
||||
this.setData({ sectionList, totalSections, loading: false })
|
||||
this.syncUser()
|
||||
return
|
||||
}
|
||||
app.request('/api/book/all-chapters').then(res => {
|
||||
const raw = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
|
||||
const totalSections = res.totalSections || res.total || raw.length || 62
|
||||
let sectionList = []
|
||||
if (Array.isArray(raw)) {
|
||||
raw.forEach(s => {
|
||||
sectionList.push({
|
||||
id: s.id,
|
||||
title: s.sectionTitle || s.title || s.section_title || s.id
|
||||
})
|
||||
})
|
||||
}
|
||||
if (sectionList.length === 0 && raw.length > 0) {
|
||||
sectionList = raw.map(s => ({ id: s.id, title: s.sectionTitle || s.title || s.id }))
|
||||
}
|
||||
this.setData({ sectionList, totalSections, loading: false })
|
||||
this.syncUser()
|
||||
}).catch(() => this.setData({ loading: false }))
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
||||
},
|
||||
|
||||
goChapters() {
|
||||
wx.switchTab({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (id) wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="header-title">我的订单</text>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!isLoggedIn}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">请先登录</text>
|
||||
<view class="btn-primary" bindtap="goBack">返回我的</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{loading}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">加载中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="stats-card">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{purchasedCount}}</text>
|
||||
<text class="stat-label">已购买章节</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand">{{hasFullBook ? '全书' : (purchasedCount + '/' + totalSections)}}</text>
|
||||
<text class="stat-label">{{hasFullBook ? '已解锁' : '进度'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{hasFullBook}}">
|
||||
<view class="fullbook-card">
|
||||
<text class="fullbook-icon">✓</text>
|
||||
<text class="fullbook-title">您已购买整本书</text>
|
||||
<text class="fullbook-desc">全部内容已解锁,可随时阅读</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{purchasedCount === 0}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-icon">📖</text>
|
||||
<text class="empty-desc">您还没有购买任何章节</text>
|
||||
<view class="btn-primary" bindtap="goChapters">去浏览章节</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="section-header">已购买的章节</view>
|
||||
<view class="section-list">
|
||||
<view
|
||||
class="section-row"
|
||||
wx:for="{{purchasedSectionList}}"
|
||||
wx:key="id"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="goToRead"
|
||||
>
|
||||
<text class="section-check">✓</text>
|
||||
<text class="section-id">{{item.id}}</text>
|
||||
<text class="section-title">{{item.title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
@@ -1,29 +0,0 @@
|
||||
page { background: #000; color: #fff; }
|
||||
.page { min-height: 100vh; padding-bottom: 80rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
|
||||
.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
|
||||
.stats-card { display: grid; grid-template-columns: 1fr 1fr; gap: 24rpx; margin: 32rpx; padding: 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.stat-item { text-align: center; }
|
||||
.stat-value { font-size: 56rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.stat-value.brand { color: #00CED1; }
|
||||
.stat-label { font-size: 24rpx; color: rgba(255,255,255,0.4); }
|
||||
|
||||
.fullbook-card { margin: 32rpx; padding: 48rpx; border-radius: 24rpx; background: linear-gradient(90deg, rgba(0,206,209,0.15) 0%, #1c1c1e 100%); border: 2rpx solid rgba(0,206,209,0.3); text-align: center; }
|
||||
.fullbook-icon { width: 96rpx; height: 96rpx; border-radius: 50%; background: #00CED1; color: #000; font-size: 48rpx; font-weight: 700; display: flex; align-items: center; justify-content: center; margin: 0 auto 24rpx; display: block; line-height: 96rpx; text-align: center; }
|
||||
.fullbook-title { font-size: 36rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 16rpx; }
|
||||
.fullbook-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
|
||||
.empty-wrap { padding: 80rpx 48rpx; text-align: center; }
|
||||
.empty-icon { font-size: 96rpx; display: block; margin-bottom: 24rpx; opacity: 0.5; }
|
||||
.empty-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 32rpx; }
|
||||
.btn-primary { display: inline-block; padding: 24rpx 64rpx; border-radius: 48rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; }
|
||||
|
||||
.section-header { font-size: 24rpx; color: rgba(255,255,255,0.4); margin: 0 32rpx 24rpx; }
|
||||
.section-list { padding: 0 32rpx; }
|
||||
.section-row { display: flex; align-items: center; gap: 24rpx; padding: 28rpx 24rpx; border-radius: 16rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); margin-bottom: 16rpx; }
|
||||
.section-check { font-size: 28rpx; color: #00CED1; flex-shrink: 0; }
|
||||
.section-id { font-size: 24rpx; color: rgba(255,255,255,0.5); font-family: monospace; flex-shrink: 0; width: 80rpx; }
|
||||
.section-title { flex: 1; font-size: 28rpx; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
@@ -1,207 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
const FULL_BOOK_PRICE = 9.9
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
id: '',
|
||||
title: '',
|
||||
partTitle: '',
|
||||
chapterTitle: '',
|
||||
content: '',
|
||||
contentNodes: '',
|
||||
contentType: 'text',
|
||||
loading: true,
|
||||
needPurchase: false,
|
||||
canAccess: false,
|
||||
error: '',
|
||||
price: 1,
|
||||
fullBookPrice: FULL_BOOK_PRICE,
|
||||
totalSections: 62,
|
||||
purchasedCount: 0,
|
||||
showFullBookOption: false,
|
||||
prevSection: null,
|
||||
nextSection: null,
|
||||
showShareModal: false,
|
||||
shareCopied: false,
|
||||
referralCode: '',
|
||||
shareLink: ''
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const id = (options && options.id) ? decodeURIComponent(options.id) : ''
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({
|
||||
statusBarHeight,
|
||||
navBarHeight,
|
||||
id
|
||||
})
|
||||
this.syncUser()
|
||||
if (id) {
|
||||
this.loadAllSections().then(() => this.loadChapter(id))
|
||||
} else {
|
||||
this.setData({ loading: false, error: '缺少章节 id' })
|
||||
}
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const purchasedSections = app.globalData.purchasedSections || []
|
||||
const hasFullBook = !!app.globalData.hasFullBook
|
||||
const total = this.data.totalSections || 62
|
||||
const purchasedCount = hasFullBook ? total : purchasedSections.length
|
||||
const user = app.globalData.userInfo || {}
|
||||
this.setData({
|
||||
purchasedCount,
|
||||
showFullBookOption: purchasedSections.length >= 3,
|
||||
referralCode: user.referralCode || ''
|
||||
})
|
||||
},
|
||||
|
||||
allSectionsList: [],
|
||||
|
||||
loadAllSections() {
|
||||
return app.request('/api/book/all-chapters').then((res) => {
|
||||
const list = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
|
||||
const total = res.totalSections || res.total || list.length
|
||||
const ids = list.map(s => s.id)
|
||||
this.allSectionsList = ids
|
||||
this.setData({ totalSections: total })
|
||||
this.syncUser()
|
||||
return list
|
||||
}).catch(() => [])
|
||||
},
|
||||
|
||||
getPrevNext(currentId) {
|
||||
const list = this.allSectionsList
|
||||
if (!list || !list.length) return { prev: null, next: null }
|
||||
const i = list.indexOf(currentId)
|
||||
return {
|
||||
prev: i > 0 ? { id: list[i - 1], title: '' } : null,
|
||||
next: i >= 0 && i < list.length - 1 ? { id: list[i + 1], title: '' } : null
|
||||
}
|
||||
},
|
||||
|
||||
loadChapter(id) {
|
||||
this.setData({ loading: true, error: '' })
|
||||
app.request('/api/book/chapter/' + encodeURIComponent(id))
|
||||
.then((res) => {
|
||||
if (!res || !res.success) {
|
||||
this.setData({ loading: false, error: res && res.error ? res.error : '加载失败' })
|
||||
return
|
||||
}
|
||||
const hasFullBook = !!app.globalData.hasFullBook
|
||||
const purchasedSections = app.globalData.purchasedSections || []
|
||||
const isPurchased = hasFullBook || purchasedSections.indexOf(res.id) >= 0
|
||||
const canAccess = !!res.isFree || isPurchased
|
||||
const raw = res.content || ''
|
||||
const isHtml = typeof raw === 'string' && (raw.indexOf('<') >= 0 && raw.indexOf('>') >= 0)
|
||||
const contentType = isHtml ? 'html' : 'text'
|
||||
const contentNodes = isHtml ? raw : null
|
||||
const content = isHtml ? '' : raw
|
||||
const lines = content.split('\n').filter(l => l.trim())
|
||||
const previewLineCount = Math.max(1, Math.ceil(lines.length * 0.2))
|
||||
const previewContent = lines.slice(0, previewLineCount).join('\n')
|
||||
const prevNext = this.getPrevNext(res.id)
|
||||
this.syncUser()
|
||||
this.setData({
|
||||
title: res.title || res.sectionTitle || '',
|
||||
partTitle: res.partTitle || '',
|
||||
chapterTitle: res.chapterTitle || '',
|
||||
content,
|
||||
contentNodes,
|
||||
contentType,
|
||||
needPurchase: !!res.needPurchase && !canAccess,
|
||||
canAccess,
|
||||
loading: false,
|
||||
error: '',
|
||||
price: res.price != null ? res.price : 1,
|
||||
previewContent,
|
||||
prevSection: prevNext.prev,
|
||||
nextSection: prevNext.next
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setData({
|
||||
loading: false,
|
||||
error: err && err.message ? err.message : '网络错误'
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
|
||||
},
|
||||
|
||||
goChapters() {
|
||||
wx.switchTab({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
openShare() {
|
||||
const link = this.getShareLink()
|
||||
this.setData({ showShareModal: true, shareCopied: false, shareLink: link })
|
||||
},
|
||||
|
||||
closeShare() {
|
||||
this.setData({ showShareModal: false })
|
||||
},
|
||||
|
||||
getShareLink() {
|
||||
const baseUrl = app.globalData.baseUrl || 'https://soul.quwanzhi.com'
|
||||
const ref = this.data.referralCode ? '?ref=' + this.data.referralCode : ''
|
||||
return baseUrl + '/read/' + this.data.id + ref
|
||||
},
|
||||
|
||||
copyLink() {
|
||||
const link = this.getShareLink()
|
||||
wx.setClipboardData({
|
||||
data: link,
|
||||
success: () => this.setData({ shareCopied: true })
|
||||
})
|
||||
},
|
||||
|
||||
copyWechatText() {
|
||||
const link = this.getShareLink()
|
||||
const text = '📚 推荐阅读《' + this.data.title + '》\n\n' + (this.data.content || '').slice(0, 100) + '...\n\n👉 点击阅读:' + link
|
||||
wx.setClipboardData({
|
||||
data: text,
|
||||
success: () => wx.showToast({ title: '已复制,请粘贴到微信发送', icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
copyMomentText() {
|
||||
const link = this.getShareLink()
|
||||
const text = '📚 ' + this.data.title + '\n' + link
|
||||
wx.setClipboardData({
|
||||
data: text,
|
||||
success: () => wx.showToast({ title: '朋友圈文案已复制', icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
goReferral() {
|
||||
this.setData({ showShareModal: false })
|
||||
wx.navigateTo({ url: '/pages/referral/referral' })
|
||||
},
|
||||
|
||||
goPrev() {
|
||||
const prev = this.data.prevSection
|
||||
if (prev && prev.id) wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(prev.id) })
|
||||
},
|
||||
|
||||
goNext() {
|
||||
const next = this.data.nextSection
|
||||
if (next && next.id) wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(next.id) })
|
||||
},
|
||||
|
||||
purchaseSection() {
|
||||
wx.showToast({ title: '请在小程序内完成支付', icon: 'none' })
|
||||
wx.navigateTo({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
purchaseFullBook() {
|
||||
wx.showToast({ title: '请在小程序内完成支付', icon: 'none' })
|
||||
wx.navigateTo({ url: '/pages/chapters/chapters' })
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "阅读",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="height: {{navBarHeight || (statusBarHeight + 44)}}px; padding-top: {{statusBarHeight || 44}}px; box-sizing: border-box;">
|
||||
<view class="nav-inner safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<view class="nav-center">
|
||||
<text class="content-part" wx:if="{{partTitle}}">{{partTitle}}</text>
|
||||
<text class="content-chapter" wx:if="{{chapterTitle}}">{{chapterTitle}}</text>
|
||||
</view>
|
||||
<view class="nav-share" bindtap="openShare">分享</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{loading}}">
|
||||
<view class="loading-wrap">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{error}}">
|
||||
<view class="error-wrap">
|
||||
<text class="error-text">{{error}}</text>
|
||||
<view class="btn-primary" bindtap="goBack">返回</view>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{!canAccess && needPurchase}}">
|
||||
<scroll-view class="content-scroll" scroll-y>
|
||||
<view class="content-head">
|
||||
<text class="section-id">{{id}}</text>
|
||||
<text class="tag-free" wx:if="{{false}}">免费</text>
|
||||
<text class="content-title">{{title}}</text>
|
||||
</view>
|
||||
<view class="content-body preview">
|
||||
<text class="content-text">{{previewContent}}</text>
|
||||
</view>
|
||||
<view class="paywall-card">
|
||||
<view class="paywall-icon">🔒</view>
|
||||
<text class="paywall-title">解锁完整内容</text>
|
||||
<text class="paywall-desc">已阅读20%,购买后继续阅读</text>
|
||||
<view class="paywall-btn" bindtap="purchaseSection">
|
||||
<text>购买本章</text>
|
||||
<text class="price">¥{{price}}</text>
|
||||
</view>
|
||||
<view class="paywall-btn primary" wx:if="{{showFullBookOption}}" bindtap="purchaseFullBook">
|
||||
<text>解锁全部 {{totalSections}} 章</text>
|
||||
<text class="price">¥{{fullBookPrice}}</text>
|
||||
</view>
|
||||
<text class="paywall-hint">分享给好友购买,你可获得90%佣金</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<scroll-view class="content-scroll" scroll-y>
|
||||
<view class="content-head">
|
||||
<text class="section-id">{{id}}</text>
|
||||
<text class="tag-free" wx:if="{{!needPurchase}}">免费</text>
|
||||
<text class="content-title">{{title}}</text>
|
||||
</view>
|
||||
<view class="content-body">
|
||||
<text class="content-text" wx:if="{{contentType === 'text'}}">{{content}}</text>
|
||||
<rich-text wx:else nodes="{{contentNodes}}"></rich-text>
|
||||
</view>
|
||||
<view class="nav-footer">
|
||||
<view class="prev-next-row">
|
||||
<view class="prev-next prev" wx:if="{{prevSection}}" bindtap="goPrev">
|
||||
<text class="pn-label">上一篇</text>
|
||||
<text class="pn-title">{{prevSection.id}}</text>
|
||||
</view>
|
||||
<view class="prev-next next" wx:if="{{nextSection}}" bindtap="goNext">
|
||||
<text class="pn-label">下一篇</text>
|
||||
<text class="pn-title">{{nextSection.id}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="share-tip" bindtap="openShare">
|
||||
<text class="share-tip-title">觉得不错?分享给好友</text>
|
||||
<text class="share-tip-desc">好友购买你获得90%佣金</text>
|
||||
<view class="btn-share">分享赚钱</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
|
||||
<view class="mask" wx:if="{{showShareModal}}" catchtap="closeShare">
|
||||
<view class="modal share-modal" catchtap="">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">分享文章</text>
|
||||
<view class="modal-close" bindtap="closeShare">×</view>
|
||||
</view>
|
||||
<view class="share-link-wrap">
|
||||
<text class="share-link-label">你的专属分享链接</text>
|
||||
<text class="share-link">{{shareLink}}</text>
|
||||
<text class="share-code" wx:if="{{referralCode}}">邀请码: {{referralCode}} · 好友购买你获得90%佣金</text>
|
||||
</view>
|
||||
<view class="share-btns">
|
||||
<view class="share-btn" bindtap="copyLink">
|
||||
<view class="share-btn-icon">{{shareCopied ? '✓' : '复制'}}</view>
|
||||
<text class="share-btn-label">{{shareCopied ? '已复制' : '复制链接'}}</text>
|
||||
</view>
|
||||
<view class="share-btn" bindtap="copyWechatText">
|
||||
<view class="share-btn-icon wechat">微</view>
|
||||
<text class="share-btn-label">微信好友</text>
|
||||
</view>
|
||||
<view class="share-btn" bindtap="copyMomentText">
|
||||
<view class="share-btn-icon wechat">朋</view>
|
||||
<text class="share-btn-label">朋友圈</text>
|
||||
</view>
|
||||
<view class="share-btn" bindtap="goReferral">
|
||||
<view class="share-btn-icon gold">海报</view>
|
||||
<text class="share-btn-label">生成海报</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,64 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; display: flex; flex-direction: column; }
|
||||
.nav-bar { background: rgba(0,0,0,0.9); border-bottom: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; display: flex; flex-direction: column; justify-content: flex-end; }
|
||||
.nav-inner { display: flex; align-items: center; padding: 0 24rpx; height: 88rpx; min-height: 44px; flex-shrink: 0; }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; padding: 16rpx 0; }
|
||||
.nav-center { flex: 1; text-align: center; padding: 0 16rpx; }
|
||||
.nav-center .content-part { font-size: 20rpx; color: rgba(255,255,255,0.4); margin-bottom: 4rpx; }
|
||||
.nav-center .content-chapter { font-size: 24rpx; color: rgba(255,255,255,0.5); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.nav-share { font-size: 28rpx; color: rgba(255,255,255,0.6); padding: 16rpx 0; }
|
||||
.section-id { font-size: 28rpx; color: #00CED1; background: rgba(0,206,209,0.1); padding: 8rpx 24rpx; border-radius: 32rpx; margin-right: 16rpx; }
|
||||
.tag-free { font-size: 22rpx; color: #00CED1; background: rgba(0,206,209,0.1); padding: 4rpx 12rpx; border-radius: 8rpx; }
|
||||
.content-body.preview { opacity: 0.9; }
|
||||
.paywall-card { margin: 32rpx; padding: 48rpx; border-radius: 32rpx; background: linear-gradient(180deg, #1c1c1e 0%, #2c2c2e 100%); border: 2rpx solid rgba(0,206,209,0.2); text-align: center; }
|
||||
.paywall-icon { font-size: 64rpx; display: block; margin-bottom: 32rpx; }
|
||||
.paywall-card .paywall-title { font-size: 40rpx; font-weight: 600; color: #fff; margin-bottom: 16rpx; }
|
||||
.paywall-card .paywall-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); margin-bottom: 32rpx; }
|
||||
.paywall-btn { width: 100%; padding: 28rpx 32rpx; border-radius: 24rpx; background: #2c2c2e; border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 30rpx; margin-bottom: 24rpx; display: flex; justify-content: space-between; align-items: center; }
|
||||
.paywall-btn.primary { background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); color: #fff; }
|
||||
.paywall-btn .price { color: #00CED1; font-weight: 600; }
|
||||
.paywall-btn.primary .price { color: #fff; }
|
||||
.paywall-hint { font-size: 24rpx; color: rgba(255,255,255,0.4); }
|
||||
.nav-footer { padding: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.prev-next-row { display: flex; gap: 24rpx; margin-bottom: 32rpx; }
|
||||
.prev-next { flex: 1; max-width: 48%; padding: 24rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.prev-next.next { background: linear-gradient(90deg, rgba(0,206,209,0.1) 0%, rgba(32,178,170,0.1) 100%); border-color: rgba(0,206,209,0.2); }
|
||||
.pn-label { font-size: 20rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 8rpx; }
|
||||
.prev-next.next .pn-label { color: #00CED1; }
|
||||
.pn-title { font-size: 24rpx; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
|
||||
.share-tip { padding: 32rpx; border-radius: 24rpx; background: linear-gradient(90deg, rgba(255,215,0,0.1) 0%, transparent 100%); border: 2rpx solid rgba(255,215,0,0.2); display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; }
|
||||
.share-tip-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; width: 100%; margin-bottom: 8rpx; }
|
||||
.share-tip-desc { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 16rpx; }
|
||||
.btn-share { padding: 16rpx 32rpx; border-radius: 16rpx; background: #FFD700; color: #000; font-size: 28rpx; font-weight: 500; }
|
||||
.mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 100; display: flex; align-items: flex-end; justify-content: center; padding: 0; box-sizing: border-box; }
|
||||
.modal { width: 100%; max-height: 80vh; background: #1c1c1e; border-radius: 32rpx 32rpx 0 0; padding: 48rpx; padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); }
|
||||
.share-modal .modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32rpx; }
|
||||
.modal-title { font-size: 36rpx; font-weight: 600; color: #fff; }
|
||||
.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 40rpx; color: rgba(255,255,255,0.5); }
|
||||
.share-link-wrap { padding: 32rpx; border-radius: 24rpx; background: rgba(0,0,0,0.3); border: 2rpx solid rgba(255,255,255,0.1); margin-bottom: 32rpx; }
|
||||
.share-link-label { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 16rpx; }
|
||||
.share-link { font-size: 24rpx; color: #00CED1; word-break: break-all; display: block; }
|
||||
.share-code { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; margin-top: 16rpx; }
|
||||
.share-btns { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24rpx; }
|
||||
.share-btn { display: flex; flex-direction: column; align-items: center; gap: 16rpx; padding: 24rpx; border-radius: 24rpx; background: rgba(255,255,255,0.05); }
|
||||
.share-btn-icon { width: 96rpx; height: 96rpx; border-radius: 50%; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #00CED1; }
|
||||
.share-btn-icon.wechat { background: rgba(7,193,96,0.2); color: #07C160; }
|
||||
.share-btn-icon.gold { background: rgba(255,215,0,0.2); color: #FFD700; }
|
||||
.share-btn-label { font-size: 24rpx; color: rgba(255,255,255,0.5); }
|
||||
.loading-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 80rpx; }
|
||||
.loading-spinner { width: 64rpx; height: 64rpx; border: 6rpx solid rgba(0,206,209,0.3); border-top-color: #00CED1; border-radius: 50%; animation: spin 0.8s linear infinite; }
|
||||
.loading-text { margin-top: 24rpx; font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.error-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 80rpx; }
|
||||
.error-text { font-size: 30rpx; color: rgba(255,255,255,0.6); margin-bottom: 32rpx; }
|
||||
.paywall { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 80rpx; }
|
||||
.paywall-title { font-size: 36rpx; font-weight: 600; color: #fff; margin-bottom: 16rpx; text-align: center; }
|
||||
.paywall-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); margin-bottom: 48rpx; }
|
||||
.btn-primary { padding: 24rpx 64rpx; border-radius: 48rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 32rpx; font-weight: 600; }
|
||||
.content-scroll { flex: 1; height: 0; }
|
||||
.content-head { padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.content-part { font-size: 24rpx; color: #00CED1; display: block; margin-bottom: 8rpx; }
|
||||
.content-chapter { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 8rpx; }
|
||||
.content-title { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.content-body { padding: 32rpx; font-size: 30rpx; line-height: 1.8; color: rgba(255,255,255,0.9); }
|
||||
.content-body .content-text { white-space: pre-wrap; word-break: break-all; display: block; }
|
||||
.content-body rich-text { display: block; white-space: pre-wrap; word-break: break-all; }
|
||||
@@ -1,104 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
totalEarnings: '0.00',
|
||||
pendingEarnings: '0.00',
|
||||
referralCode: '',
|
||||
distributorShare: 90,
|
||||
canWithdraw: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
this.syncUser()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncUser()
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const isLoggedIn = !!app.globalData.isLoggedIn
|
||||
const user = app.globalData.userInfo || null
|
||||
if (!user) {
|
||||
this.setData({ isLoggedIn: false, user: null })
|
||||
return
|
||||
}
|
||||
const total = Number(user.earnings != null ? user.earnings : 0)
|
||||
const totalEarnings = total.toFixed(2)
|
||||
const pendingEarnings = Number(user.pendingEarnings != null ? user.pendingEarnings : 0).toFixed(2)
|
||||
const referralCode = user.referralCode || ''
|
||||
this.setData({
|
||||
isLoggedIn: true,
|
||||
user,
|
||||
totalEarnings,
|
||||
pendingEarnings,
|
||||
referralCode,
|
||||
canWithdraw: total >= 10
|
||||
})
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
||||
},
|
||||
|
||||
copyLink() {
|
||||
const user = app.globalData.userInfo
|
||||
if (!user || !user.referralCode) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const baseUrl = app.globalData.baseUrl || 'https://soul.quwanzhi.com'
|
||||
const link = baseUrl + '?ref=' + user.referralCode
|
||||
wx.setClipboardData({
|
||||
data: link,
|
||||
success: () => wx.showToast({ title: '链接已复制', icon: 'success' })
|
||||
})
|
||||
},
|
||||
|
||||
shareToMoments() {
|
||||
const user = app.globalData.userInfo
|
||||
if (!user || !user.referralCode) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const baseUrl = app.globalData.baseUrl || 'https://soul.quwanzhi.com'
|
||||
const link = baseUrl + '?ref=' + user.referralCode
|
||||
const text = `📖 推荐一本好书《一场SOUL的创业实验场》
|
||||
|
||||
这是卡若每天早上6-9点在Soul派对房分享的真实商业故事,55个真实案例,讲透创业的底层逻辑。
|
||||
|
||||
👉 点击阅读: ${link}
|
||||
|
||||
#创业 #商业思维 #Soul派对`
|
||||
wx.setClipboardData({
|
||||
data: text,
|
||||
success: () => wx.showToast({ title: '朋友圈文案已复制', icon: 'success' })
|
||||
})
|
||||
},
|
||||
|
||||
applyWithdraw() {
|
||||
const user = app.globalData.userInfo
|
||||
if (!user) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const total = Number(user.earnings != null ? user.earnings : 0)
|
||||
if (total < 10) {
|
||||
wx.showToast({ title: '满10元可提现', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.showToast({
|
||||
title: '请在小程序内联系客服或使用提现功能',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "推广中心",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="header-title">推广中心</text>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!isLoggedIn}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-desc">请先登录</text>
|
||||
<view class="btn-primary" bindtap="goBack">返回我的</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="earnings-card">
|
||||
<view class="earnings-head">
|
||||
<view class="earnings-title-row">
|
||||
<text class="earnings-icon">💰</text>
|
||||
<view>
|
||||
<text class="earnings-label">累计收益</text>
|
||||
<text class="earnings-rate">{{distributorShare}}% 返利</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="earnings-right">
|
||||
<text class="earnings-total">¥{{totalEarnings}}</text>
|
||||
<text class="earnings-pending">待结算: ¥{{pendingEarnings}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-withdraw {{canWithdraw ? '' : 'disabled'}}" bindtap="applyWithdraw">
|
||||
{{canWithdraw ? '申请提现' : '满10元可提现'}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="code-card">
|
||||
<view class="code-row">
|
||||
<text class="code-label">我的邀请码</text>
|
||||
<text class="code-value">{{referralCode}}</text>
|
||||
</view>
|
||||
<text class="code-desc">好友通过你的链接购买立省5%,你获得{{distributorShare}}%收益</text>
|
||||
</view>
|
||||
|
||||
<view class="action-list">
|
||||
<view class="action-item" bindtap="copyLink">
|
||||
<view class="action-icon">🔗</view>
|
||||
<view class="action-text">
|
||||
<text class="action-title">复制邀请链接</text>
|
||||
<text class="action-desc">分享给好友购买</text>
|
||||
</view>
|
||||
<text class="action-arrow">›</text>
|
||||
</view>
|
||||
<view class="action-item" bindtap="shareToMoments">
|
||||
<view class="action-icon wechat">💬</view>
|
||||
<view class="action-text">
|
||||
<text class="action-title">分享到朋友圈</text>
|
||||
<text class="action-desc">复制文案发朋友圈</text>
|
||||
</view>
|
||||
<text class="action-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -1,38 +0,0 @@
|
||||
page { background: #000; color: #fff; }
|
||||
.page { min-height: 100vh; padding-bottom: 80rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
|
||||
.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
|
||||
.empty-wrap { padding: 80rpx 48rpx; text-align: center; }
|
||||
.empty-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 32rpx; }
|
||||
.btn-primary { display: inline-block; padding: 24rpx 64rpx; border-radius: 48rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; }
|
||||
|
||||
.earnings-card { margin: 32rpx; padding: 32rpx; border-radius: 32rpx; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); border: 2rpx solid rgba(0,206,209,0.2); box-sizing: border-box; }
|
||||
.earnings-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; gap: 24rpx; min-width: 0; }
|
||||
.earnings-title-row { display: flex; align-items: center; gap: 16rpx; min-width: 0; }
|
||||
.earnings-icon { font-size: 40rpx; flex-shrink: 0; }
|
||||
.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; }
|
||||
.earnings-rate { font-size: 24rpx; color: #00CED1; display: block; margin-top: 4rpx; }
|
||||
.earnings-right { text-align: right; flex-shrink: 0; }
|
||||
.earnings-total { font-size: 56rpx; font-weight: 700; color: #fff; display: block; }
|
||||
.earnings-pending { font-size: 24rpx; color: rgba(255,255,255,0.5); }
|
||||
.btn-withdraw { width: 100%; padding: 24rpx; border-radius: 24rpx; background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 30rpx; font-weight: 600; text-align: center; margin-top: 16rpx; box-sizing: border-box; }
|
||||
.btn-withdraw.disabled { opacity: 0.5; background: #2c2c2e; color: rgba(255,255,255,0.5); }
|
||||
|
||||
.code-card { margin: 32rpx; padding: 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.code-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx; }
|
||||
.code-label { font-size: 30rpx; color: #fff; font-weight: 500; }
|
||||
.code-value { font-size: 28rpx; color: #00CED1; font-family: monospace; background: rgba(0,206,209,0.1); padding: 12rpx 24rpx; border-radius: 16rpx; }
|
||||
.code-desc { font-size: 24rpx; color: rgba(255,255,255,0.5); }
|
||||
|
||||
.action-list { margin: 32rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); overflow: hidden; }
|
||||
.action-item { display: flex; align-items: center; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.action-item:last-child { border-bottom: none; }
|
||||
.action-icon { width: 80rpx; height: 80rpx; border-radius: 20rpx; background: rgba(0,206,209,0.15); display: flex; align-items: center; justify-content: center; font-size: 40rpx; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.action-icon.wechat { background: rgba(7,193,96,0.15); }
|
||||
.action-text { flex: 1; min-width: 0; }
|
||||
.action-title { font-size: 30rpx; color: #fff; font-weight: 500; display: block; }
|
||||
.action-desc { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-top: 8rpx; }
|
||||
.action-arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); }
|
||||
@@ -1,69 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
query: '',
|
||||
results: [],
|
||||
keywords: [],
|
||||
isLoading: false,
|
||||
hotKeywords: ['私域', '流量', '赚钱', '电商', 'AI', '社群']
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
onInput(e) {
|
||||
const query = (e.detail && e.detail.value) || ''
|
||||
this.setData({ query })
|
||||
if (!query.trim()) {
|
||||
this.setData({ results: [], keywords: [] })
|
||||
return
|
||||
}
|
||||
this.debounceSearch(query)
|
||||
},
|
||||
|
||||
_searchTimer: null,
|
||||
debounceSearch(query) {
|
||||
if (this._searchTimer) clearTimeout(this._searchTimer)
|
||||
this._searchTimer = setTimeout(() => this.doSearch(query), 300)
|
||||
},
|
||||
|
||||
doSearch(q) {
|
||||
if (!q || !q.trim()) return
|
||||
this.setData({ isLoading: true })
|
||||
app.request('/api/search?q=' + encodeURIComponent(q.trim()) + '&type=all')
|
||||
.then((res) => {
|
||||
const results = (res && res.data && res.data.results) ? res.data.results : []
|
||||
const keywords = (res && res.data && res.data.keywords) ? res.data.keywords : []
|
||||
this.setData({ results, keywords, isLoading: false })
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({ results: [], keywords: [], isLoading: false })
|
||||
})
|
||||
},
|
||||
|
||||
onKeywordTap(e) {
|
||||
const keyword = e.currentTarget.dataset.keyword
|
||||
this.setData({ query: keyword })
|
||||
this.doSearch(keyword)
|
||||
},
|
||||
|
||||
clearQuery() {
|
||||
this.setData({ query: '', results: [], keywords: [] })
|
||||
},
|
||||
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (!id) return
|
||||
wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "搜索",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="height: {{navBarHeight || (statusBarHeight + 44)}}px; padding-top: {{statusBarHeight || 44}}px; box-sizing: border-box;">
|
||||
<view class="nav-inner safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="nav-title">搜索</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="search-box">
|
||||
<view class="search-input-wrap">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input class="search-input" placeholder="搜索章节标题或内容..." value="{{query}}" bindinput="onInput" focus="{{true}}" />
|
||||
<view class="search-clear" wx:if="{{query}}" bindtap="clearQuery">×</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="result-area" scroll-y>
|
||||
<block wx:if="{{!query}}">
|
||||
<view class="hot-section">
|
||||
<text class="hot-label">热门搜索</text>
|
||||
<view class="hot-tags">
|
||||
<view class="hot-tag" wx:for="{{hotKeywords}}" wx:key="*this" data-keyword="{{item}}" bindtap="onKeywordTap">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{isLoading}}">
|
||||
<view class="loading-wrap">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">搜索中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{results.length > 0}}">
|
||||
<view class="result-header">找到 {{results.length}} 个结果</view>
|
||||
<view class="result-list">
|
||||
<view class="result-item" wx:for="{{results}}" wx:key="id" data-id="{{item.id}}" bindtap="goToRead">
|
||||
<view class="result-icon">📄</view>
|
||||
<view class="result-body">
|
||||
<view class="result-meta">
|
||||
<text class="result-id">{{item.id}}</text>
|
||||
<text class="tag-free" wx:if="{{item.isFree}}">免费</text>
|
||||
<text class="tag-content" wx:if="{{item.matchType === 'content'}}">内容匹配</text>
|
||||
</view>
|
||||
<text class="result-title">{{item.title}}</text>
|
||||
<text class="result-snippet" wx:if="{{item.snippet}}">{{item.snippet}}</text>
|
||||
<text class="result-path" wx:if="{{item.partTitle}}">{{item.partTitle}} · {{item.chapterTitle}}</text>
|
||||
</view>
|
||||
<text class="result-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="keywords-section" wx:if="{{keywords.length > 0}}">
|
||||
<text class="keywords-label">相关标签</text>
|
||||
<view class="keywords-tags">
|
||||
<view class="keyword-tag" wx:for="{{keywords}}" wx:for-item="kw" wx:key="*this" wx:if="{{kw}}" data-keyword="{{kw}}" bindtap="onKeywordTap">#{{kw}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{query && !isLoading}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-icon">🔍</text>
|
||||
<text class="empty-text">未找到相关内容</text>
|
||||
<text class="empty-hint">试试其他关键词</text>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@@ -1,40 +0,0 @@
|
||||
.page { min-height: 100vh; background: #000; display: flex; flex-direction: column; }
|
||||
.nav-bar { background: #1c1c1e; border-bottom: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; display: flex; flex-direction: column; justify-content: flex-end; }
|
||||
.nav-inner { display: flex; align-items: center; padding: 0 24rpx; height: 88rpx; min-height: 44px; flex-shrink: 0; }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; padding: 16rpx 0; }
|
||||
.nav-title { flex: 1; text-align: center; font-size: 34rpx; color: #fff; }
|
||||
.search-box { padding: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.search-input-wrap { display: flex; align-items: center; padding: 24rpx 32rpx; background: #2c2c2e; border-radius: 24rpx; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.search-icon { font-size: 32rpx; margin-right: 16rpx; opacity: 0.6; }
|
||||
.search-input { flex: 1; font-size: 28rpx; color: #fff; }
|
||||
.search-clear { font-size: 40rpx; color: rgba(255,255,255,0.5); padding: 0 16rpx; }
|
||||
.result-area { flex: 1; height: 0; }
|
||||
.hot-section { padding: 32rpx; }
|
||||
.hot-label { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 24rpx; }
|
||||
.hot-tags { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.hot-tag { padding: 16rpx 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); background: #2c2c2e; border-radius: 32rpx; }
|
||||
.loading-wrap { padding: 80rpx; text-align: center; }
|
||||
.loading-spinner { width: 48rpx; height: 48rpx; border: 4rpx solid rgba(0,206,209,0.3); border-top-color: #00CED1; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 24rpx; }
|
||||
.loading-text { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.result-header { padding: 24rpx 32rpx; font-size: 24rpx; color: rgba(255,255,255,0.4); border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.result-list { }
|
||||
.result-item { display: flex; align-items: flex-start; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.result-icon { width: 64rpx; height: 64rpx; border-radius: 16rpx; background: rgba(0,206,209,0.1); display: flex; align-items: center; justify-content: center; font-size: 32rpx; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.result-body { flex: 1; min-width: 0; }
|
||||
.result-meta { display: flex; align-items: center; gap: 16rpx; margin-bottom: 8rpx; }
|
||||
.result-id { font-size: 24rpx; color: #00CED1; font-family: monospace; }
|
||||
.tag-free { font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 6rpx; background: rgba(0,206,209,0.1); color: #00CED1; }
|
||||
.tag-content { font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 6rpx; background: rgba(123,97,255,0.1); color: #7B61FF; }
|
||||
.result-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; margin-bottom: 4rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.result-snippet { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 4rpx; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||||
.result-path { font-size: 22rpx; color: rgba(255,255,255,0.3); }
|
||||
.result-arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); margin-left: 16rpx; }
|
||||
.keywords-section { padding: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.keywords-label { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 16rpx; }
|
||||
.keywords-tags { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.keyword-tag { font-size: 24rpx; color: rgba(255,255,255,0.5); padding: 8rpx 16rpx; background: #2c2c2e; border-radius: 8rpx; }
|
||||
.empty-wrap { padding: 80rpx; text-align: center; }
|
||||
.empty-icon { font-size: 80rpx; display: block; margin-bottom: 24rpx; opacity: 0.5; }
|
||||
.empty-text { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; }
|
||||
.empty-hint { font-size: 24rpx; color: rgba(255,255,255,0.3); margin-top: 8rpx; display: block; }
|
||||
@@ -1,120 +0,0 @@
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
user: null,
|
||||
showBindModal: false,
|
||||
bindType: 'phone',
|
||||
bindValue: '',
|
||||
isBinding: false,
|
||||
bindError: ''
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
this.syncUser()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncUser()
|
||||
},
|
||||
|
||||
syncUser() {
|
||||
const user = app.globalData.userInfo || null
|
||||
this.setData({ user })
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
||||
},
|
||||
|
||||
goAddresses() {
|
||||
wx.navigateTo({ url: '/pages/address-list/address-list' })
|
||||
},
|
||||
|
||||
openBindModal(e) {
|
||||
const type = e.currentTarget.dataset.type
|
||||
const user = this.data.user
|
||||
let bindValue = ''
|
||||
if (type === 'phone' && user && user.phone) bindValue = user.phone
|
||||
if (type === 'wechat' && user && user.wechat) bindValue = user.wechat
|
||||
if (type === 'alipay' && user && user.alipay) bindValue = user.alipay
|
||||
this.setData({
|
||||
showBindModal: true,
|
||||
bindType: type,
|
||||
bindValue,
|
||||
bindError: ''
|
||||
})
|
||||
},
|
||||
|
||||
closeBindModal() {
|
||||
if (!this.data.isBinding) this.setData({ showBindModal: false, bindValue: '', bindError: '' })
|
||||
},
|
||||
|
||||
onBindInput(e) {
|
||||
this.setData({ bindValue: (e.detail && e.detail.value) || '', bindError: '' })
|
||||
},
|
||||
|
||||
submitBind() {
|
||||
const { bindType, bindValue, user } = this.data
|
||||
if (!bindValue || !bindValue.trim()) {
|
||||
this.setData({ bindError: '请输入内容' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'phone' && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
this.setData({ bindError: '请输入正确的手机号' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'wechat' && bindValue.length < 6) {
|
||||
this.setData({ bindError: '微信号至少6位' })
|
||||
return
|
||||
}
|
||||
if (bindType === 'alipay' && !bindValue.includes('@') && !/^1[3-9]\d{9}$/.test(bindValue)) {
|
||||
this.setData({ bindError: '请输入正确的支付宝账号' })
|
||||
return
|
||||
}
|
||||
this.setData({ isBinding: true, bindError: '' })
|
||||
app.request('/api/user/update', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: user && user.id,
|
||||
[bindType]: bindValue
|
||||
}
|
||||
}).then(() => {
|
||||
const newUser = { ...user, [bindType]: bindValue }
|
||||
app.globalData.userInfo = newUser
|
||||
wx.setStorageSync('userInfo', newUser)
|
||||
this.setData({
|
||||
user: newUser,
|
||||
showBindModal: false,
|
||||
bindValue: '',
|
||||
isBinding: false
|
||||
})
|
||||
wx.showToast({ title: '绑定成功', icon: 'success' })
|
||||
}).catch(() => {
|
||||
this.setData({ bindError: '绑定失败,请重试', isBinding: false })
|
||||
})
|
||||
},
|
||||
|
||||
logout() {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '确定退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
app.globalData.userInfo = null
|
||||
app.globalData.isLoggedIn = false
|
||||
app.globalData.purchasedSections = []
|
||||
app.globalData.hasFullBook = false
|
||||
wx.removeStorageSync('userInfo')
|
||||
wx.removeStorageSync('token')
|
||||
wx.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "设置",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<view class="page">
|
||||
<view class="nav-placeholder" style="height: {{navBarHeight || (statusBarHeight + 44)}}px;"></view>
|
||||
<view class="header safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="header-title">设置</text>
|
||||
</view>
|
||||
|
||||
<view class="main">
|
||||
<view class="card">
|
||||
<view class="card-head">
|
||||
<text class="card-icon">🛡</text>
|
||||
<view>
|
||||
<text class="card-title">账号绑定</text>
|
||||
<text class="card-desc">绑定后可用于提现和找伙伴功能</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bind-item" data-type="phone" bindtap="openBindModal">
|
||||
<view class="bind-left">
|
||||
<view class="bind-icon {{user.phone ? 'bound' : ''}}">📱</view>
|
||||
<view>
|
||||
<text class="bind-label">手机号</text>
|
||||
<text class="bind-value">{{user.phone || '未绑定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="bind-action" wx:if="{{user.phone}}">✓</text>
|
||||
<text class="bind-action brand" wx:else>去绑定</text>
|
||||
</view>
|
||||
|
||||
<view class="bind-item" data-type="wechat" bindtap="openBindModal">
|
||||
<view class="bind-left">
|
||||
<view class="bind-icon wechat {{user.wechat ? 'bound' : ''}}">💬</view>
|
||||
<view>
|
||||
<text class="bind-label">微信号</text>
|
||||
<text class="bind-value">{{user.wechat || '未绑定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="bind-action" wx:if="{{user.wechat}}">✓</text>
|
||||
<text class="bind-action green" wx:else>去绑定</text>
|
||||
</view>
|
||||
|
||||
<view class="bind-item" data-type="alipay" bindtap="openBindModal">
|
||||
<view class="bind-left">
|
||||
<view class="bind-icon alipay {{user.alipay ? 'bound' : ''}}">💳</view>
|
||||
<view>
|
||||
<text class="bind-label">支付宝</text>
|
||||
<text class="bind-value">{{user.alipay || '未绑定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="bind-action" wx:if="{{user.alipay}}">✓</text>
|
||||
<text class="bind-action blue" wx:else>去绑定</text>
|
||||
</view>
|
||||
|
||||
<view class="bind-item" bindtap="goAddresses">
|
||||
<view class="bind-left">
|
||||
<view class="bind-icon addr">📍</view>
|
||||
<view>
|
||||
<text class="bind-label">收货地址</text>
|
||||
<text class="bind-value">管理收货地址,用于发货与邮寄</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="bind-action brand">管理</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="hint" wx:if="{{!user.wechat && !user.alipay}}">
|
||||
<text>提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能</text>
|
||||
</view>
|
||||
|
||||
<view class="btn-logout" bindtap="logout">退出登录</view>
|
||||
</view>
|
||||
|
||||
<view class="mask" wx:if="{{showBindModal}}" catchtap="closeBindModal">
|
||||
<view class="modal" catchtap="">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">绑定{{bindType === 'phone' ? '手机号' : (bindType === 'wechat' ? '微信号' : '支付宝')}}</text>
|
||||
<view class="modal-close" bindtap="closeBindModal">×</view>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<input
|
||||
class="bind-input"
|
||||
placeholder="{{bindType === 'phone' ? '请输入11位手机号' : (bindType === 'wechat' ? '请输入微信号' : '请输入支付宝账号')}}"
|
||||
value="{{bindValue}}"
|
||||
bindinput="onBindInput"
|
||||
/>
|
||||
<text class="bind-err" wx:if="{{bindError}}">{{bindError}}</text>
|
||||
<view class="btn-primary {{!bindValue || isBinding ? 'disabled' : ''}}" bindtap="submitBind">
|
||||
{{isBinding ? '绑定中...' : '确认绑定'}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,44 +0,0 @@
|
||||
page { background: #000; color: #fff; }
|
||||
.page { min-height: 100vh; padding-bottom: 80rpx; box-sizing: border-box; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
|
||||
.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
|
||||
|
||||
.main { padding: 32rpx; }
|
||||
.card { border-radius: 32rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); overflow: hidden; margin-bottom: 24rpx; }
|
||||
.card-head { display: flex; align-items: flex-start; gap: 24rpx; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.card-icon { font-size: 36rpx; }
|
||||
.card-title { font-size: 30rpx; color: #fff; font-weight: 500; display: block; }
|
||||
.card-desc { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-top: 8rpx; }
|
||||
|
||||
.bind-item { display: flex; align-items: center; justify-content: space-between; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.bind-item:last-child { border-bottom: none; }
|
||||
.bind-left { display: flex; align-items: center; gap: 24rpx; flex: 1; min-width: 0; }
|
||||
.bind-icon { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 32rpx; flex-shrink: 0; }
|
||||
.bind-icon.bound { background: rgba(0,206,209,0.2); }
|
||||
.bind-icon.wechat.bound { background: rgba(7,193,96,0.2); }
|
||||
.bind-icon.alipay.bound { background: rgba(22,119,255,0.2); }
|
||||
.bind-icon.addr { background: rgba(249,115,22,0.2); }
|
||||
.bind-label { font-size: 28rpx; color: #fff; display: block; }
|
||||
.bind-value { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-top: 4rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.bind-action { font-size: 24rpx; color: rgba(255,255,255,0.5); }
|
||||
.bind-action.brand { color: #00CED1; }
|
||||
.bind-action.green { color: #07C160; }
|
||||
.bind-action.blue { color: #1677FF; }
|
||||
|
||||
.hint { padding: 24rpx 32rpx; border-radius: 24rpx; background: rgba(249,115,22,0.1); border: 2rpx solid rgba(249,115,22,0.2); margin-bottom: 24rpx; }
|
||||
.hint text { font-size: 24rpx; color: rgba(249,115,22,0.9); }
|
||||
|
||||
.btn-logout { width: 100%; padding: 28rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(248,113,113,0.4); color: #f87171; font-size: 30rpx; font-weight: 500; text-align: center; }
|
||||
|
||||
.mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 48rpx; box-sizing: border-box; }
|
||||
.modal { width: 100%; max-width: 600rpx; background: #1c1c1e; border-radius: 32rpx; overflow: hidden; }
|
||||
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.1); }
|
||||
.modal-title { font-size: 36rpx; font-weight: 600; color: #fff; }
|
||||
.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 40rpx; color: rgba(255,255,255,0.6); }
|
||||
.modal-body { padding: 32rpx; }
|
||||
.bind-input { width: 100%; padding: 24rpx 32rpx; border-radius: 24rpx; background: rgba(0,0,0,0.3); border: 2rpx solid rgba(255,255,255,0.1); color: #fff; font-size: 28rpx; box-sizing: border-box; margin-bottom: 24rpx; }
|
||||
.bind-err { font-size: 24rpx; color: #f87171; display: block; margin-bottom: 16rpx; }
|
||||
.btn-primary { width: 100%; padding: 24rpx; border-radius: 24rpx; background: #00CED1; color: #000; font-size: 30rpx; font-weight: 500; text-align: center; }
|
||||
.btn-primary.disabled { opacity: 0.5; }
|
||||
Reference in New Issue
Block a user