fix: 修复小程序多个问题

1. 修复章节内容加载卡住问题 - 更新API返回格式和章节映射
2. 修复找伙伴页面 - 去掉今日剩余次数显示
3. 添加匹配动画 - 1-3秒转动动画后再弹出加入弹窗
4. 加入资源对接需要先绑定手机号或微信号
5. 重构加入弹窗UI - 更简洁美观
6. 修复手机号输入框问题
7. 关于作者页面 - 内容与真实章节数据保持一致(62个案例)
This commit is contained in:
卡若
2026-01-25 09:27:32 +08:00
parent e16dce118e
commit 4f11fe25f9
7 changed files with 547 additions and 180 deletions

View File

@@ -1,5 +1,6 @@
/**
* Soul创业实验 - 关于作者页
* 开发: 卡若
*/
const app = getApp()
@@ -9,17 +10,34 @@ Page({
author: {
name: '卡若',
avatar: 'K',
title: 'Soul派对房主理人',
bio: '每天早上6点到9点在Soul派对房分享真实的创业故事。专注私域运营与项目变现用"云阿米巴"模式帮助创业者构建可持续的商业体系。',
title: 'Soul派对房主理人 · 私域运营专家',
bio: '每天早上6点到9点在Soul派对房分享真实的创业故事。专注私域运营与项目变现用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例涵盖电商、内容、传统行业等多个领域。',
stats: [
{ label: '派对房分享', value: '500+' },
{ label: '直播天数', value: '365+' },
{ label: '商业案例', value: '62+' }
{ label: '商业案例', value: '62' },
{ label: '连续直播', value: '365' },
{ label: '派对分享', value: '1000+' }
],
contact: {
wechat: '28533368',
phone: '15880802661'
}
},
highlights: [
'5年私域运营经验',
'帮助100+品牌从0到1增长',
'连续创业者,擅长商业模式设计'
]
},
bookInfo: {
title: '一场Soul的创业实验',
totalChapters: 62,
parts: [
{ name: '真实的人', chapters: 10 },
{ name: '真实的行业', chapters: 15 },
{ name: '真实的错误', chapters: 9 },
{ name: '真实的赚钱', chapters: 20 },
{ name: '真实的社会', chapters: 9 }
],
price: 9.9
}
},
@@ -27,6 +45,26 @@ Page({
this.setData({
statusBarHeight: app.globalData.statusBarHeight
})
this.loadBookStats()
},
// 加载书籍统计
async loadBookStats() {
try {
const res = await app.request('/api/book/stats')
if (res && res.success) {
this.setData({
'bookInfo.totalChapters': res.data?.totalChapters || 62,
'author.stats': [
{ label: '商业案例', value: String(res.data?.totalChapters || 62) },
{ label: '连续直播', value: '365天' },
{ label: '派对分享', value: '1000+' }
]
})
}
} catch (e) {
console.log('[About] 加载书籍统计失败,使用默认值')
}
},
// 复制微信号

View File

@@ -8,20 +8,56 @@
<view style="height: {{statusBarHeight + 44}}px;"></view>
<view class="content">
<!-- 作者信息卡片 -->
<view class="author-card">
<view class="author-avatar">{{author.avatar}}</view>
<text class="author-name">{{author.name}}</text>
<text class="author-title">{{author.title}}</text>
<text class="author-bio">{{author.bio}}</text>
<!-- 统计数据 -->
<view class="stats-row">
<view class="stat-item" wx:for="{{author.stats}}" wx:key="label">
<text class="stat-value">{{item.value}}</text>
<text class="stat-label">{{item.label}}</text>
</view>
</view>
<!-- 亮点标签 -->
<view class="highlights" wx:if="{{author.highlights}}">
<view class="highlight-tag" wx:for="{{author.highlights}}" wx:key="*this">
<text class="tag-icon">✓</text>
<text>{{item}}</text>
</view>
</view>
</view>
<!-- 书籍信息 -->
<view class="book-info-card" wx:if="{{bookInfo}}">
<text class="card-title">📚 {{bookInfo.title}}</text>
<view class="book-stats">
<view class="book-stat">
<text class="book-stat-value">{{bookInfo.totalChapters}}</text>
<text class="book-stat-label">篇章节</text>
</view>
<view class="book-stat">
<text class="book-stat-value">5</text>
<text class="book-stat-label">大篇章</text>
</view>
<view class="book-stat">
<text class="book-stat-value">¥{{bookInfo.price}}</text>
<text class="book-stat-label">全书价格</text>
</view>
</view>
<view class="parts-list">
<view class="part-item" wx:for="{{bookInfo.parts}}" wx:key="name">
<text class="part-name">{{item.name}}</text>
<text class="part-chapters">{{item.chapters}}节</text>
</view>
</view>
</view>
<!-- 联系方式 -->
<view class="contact-card">
<text class="card-title">联系作者</text>
<view class="contact-item" bindtap="copyWechat">

View File

@@ -22,3 +22,19 @@
.contact-label { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; }
.contact-value { font-size: 28rpx; color: #fff; }
.contact-btn { padding: 12rpx 24rpx; background: rgba(0,206,209,0.2); color: #00CED1; font-size: 24rpx; border-radius: 16rpx; }
/* 亮点标签 */
.highlights { display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); justify-content: center; }
.highlight-tag { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; background: rgba(0,206,209,0.15); border-radius: 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); }
.tag-icon { color: #00CED1; font-size: 22rpx; }
/* 书籍信息卡片 */
.book-info-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; }
.book-stats { display: flex; justify-content: space-around; padding: 24rpx 0; margin: 16rpx 0; background: rgba(0,0,0,0.3); border-radius: 16rpx; }
.book-stat { text-align: center; }
.book-stat-value { font-size: 36rpx; font-weight: 700; color: #FFD700; display: block; }
.book-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); }
.parts-list { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 16rpx; }
.part-item { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: rgba(255,255,255,0.05); border-radius: 12rpx; }
.part-name { font-size: 24rpx; color: rgba(255,255,255,0.8); }
.part-chapters { font-size: 22rpx; color: #00CED1; }

View File

@@ -52,6 +52,7 @@ Page({
isJoining: false,
joinSuccess: false,
joinError: '',
needBindFirst: false,
// 解锁弹窗
showUnlockModal: false
@@ -172,15 +173,27 @@ Page({
handleMatchClick() {
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
// 如果是需要填写联系方式的类型,直接弹出加入弹窗
// 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募)
if (currentType && currentType.showJoinAfterMatch) {
this.setData({
showJoinModal: true,
joinType: currentType.id,
joinTypeLabel: currentType.matchLabel || currentType.label,
joinSuccess: false,
joinError: ''
})
// 先检查是否已绑定联系方式
const hasPhone = !!this.data.phoneNumber
const hasWechat = !!this.data.wechatId
if (!hasPhone && !hasWechat) {
// 没有绑定联系方式,先显示绑定提示
this.setData({
showJoinModal: true,
joinType: currentType.id,
joinTypeLabel: currentType.matchLabel || currentType.label,
joinSuccess: false,
joinError: '',
needBindFirst: true
})
return
}
// 已绑定联系方式先显示匹配动画1-3秒再弹出确认
this.startMatchingAnimation(currentType)
return
}
@@ -192,6 +205,36 @@ Page({
this.startMatch()
},
// 匹配动画后弹出加入确认
startMatchingAnimation(currentType) {
// 显示匹配中状态
this.setData({
isMatching: true,
matchAttempts: 0,
currentMatch: null
})
// 动画计时
const timer = setInterval(() => {
this.setData({ matchAttempts: this.data.matchAttempts + 1 })
}, 500)
// 1-3秒随机延迟后显示弹窗
const delay = Math.random() * 2000 + 1000
setTimeout(() => {
clearInterval(timer)
this.setData({
isMatching: false,
showJoinModal: true,
joinType: currentType.id,
joinTypeLabel: currentType.matchLabel || currentType.label,
joinSuccess: false,
joinError: '',
needBindFirst: false
})
}, delay)
},
// 显示购买提示
showPurchaseTip() {

View File

@@ -12,22 +12,11 @@
</view>
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
<!-- 匹配次数显示 - 所有用户可见 -->
<view class="match-count-bar">
<view class="count-left">
<text class="count-icon {{matchesRemaining <= 0 && !hasFullBook ? 'icon-warning' : ''}}">⚡</text>
<text class="count-text">
{{hasFullBook ? '无限匹配机会' : matchesRemaining <= 0 ? '今日免费次数已用完' : '今日剩余'}}
</text>
</view>
<view class="count-right">
<text class="count-value {{matchesRemaining > 0 || hasFullBook ? 'text-brand' : 'text-red'}}">
{{hasFullBook ? '∞' : matchesRemaining + '次'}}
</text>
<view class="unlock-mini-btn" wx:if="{{matchesRemaining <= 0 && !hasFullBook}}" bindtap="showUnlockModal">
¥1购买1次
</view>
</view>
<!-- 匹配提示条 - 简化显示 -->
<view class="match-tip-bar" wx:if="{{matchesRemaining <= 0 && !hasFullBook}}">
<text class="tip-icon">⚡</text>
<text class="tip-text">今日免费次数已用完</text>
<view class="tip-btn" bindtap="showUnlockModal">购买次数</view>
</view>
<!-- 主内容区 -->
@@ -153,82 +142,91 @@
</block>
</view>
<!-- 加入弹窗 - 填写手机号或微信号 -->
<!-- 加入弹窗 - 简洁版 -->
<view class="modal-overlay" wx:if="{{showJoinModal}}" bindtap="closeJoinModal">
<view class="modal-content join-modal" catchtap="preventBubble">
<view class="modal-content join-modal-new" catchtap="preventBubble">
<!-- 成功状态 -->
<block wx:if="{{joinSuccess}}">
<view class="join-success">
<view class="success-check">✅</view>
<text class="success-title">加入成功!</text>
<text class="success-desc">我们会尽快与您联系</text>
<view class="join-success-new">
<view class="success-icon-big">✅</view>
<text class="success-title-new">提交成功</text>
<text class="success-desc-new">工作人员将在24小时内与您联系</text>
</view>
</block>
<!-- 表单状态 -->
<block wx:else>
<view class="modal-header">
<text class="modal-title">加入{{joinTypeLabel}}</text>
<view class="close-btn" bindtap="closeJoinModal">✕</view>
<!-- 头部 -->
<view class="join-header">
<view class="join-icon-wrap">
<text class="join-icon">{{joinType === 'investor' ? '👥' : joinType === 'mentor' ? '❤️' : '🎮'}}</text>
</view>
<text class="join-title">加入{{joinTypeLabel}}</text>
<text class="join-subtitle" wx:if="{{needBindFirst}}">请先绑定联系方式</text>
<text class="join-subtitle" wx:else>填写联系方式,专人对接</text>
<view class="close-btn-new" bindtap="closeJoinModal">✕</view>
</view>
<text class="form-tip">
{{userPhone ? '已检测到您的绑定信息,可直接提交或修改' : '请填写您的联系方式以便我们联系您'}}
</text>
<!-- 联系方式类型切换 -->
<view class="contact-tabs">
<!-- 联系方式切换 -->
<view class="contact-switch">
<view
class="contact-tab {{contactType === 'phone' ? 'tab-active-phone' : ''}}"
class="switch-item {{contactType === 'phone' ? 'switch-active' : ''}}"
bindtap="switchContactType"
data-type="phone"
>手机号</view>
>
<text class="switch-icon">📱</text>
<text>手机号</text>
</view>
<view
class="contact-tab {{contactType === 'wechat' ? 'tab-active-wechat' : ''}}"
class="switch-item {{contactType === 'wechat' ? 'switch-active' : ''}}"
bindtap="switchContactType"
data-type="wechat"
>微信号</view>
>
<text class="switch-icon">💬</text>
<text>微信号</text>
</view>
</view>
<!-- 输入 -->
<view class="input-group">
<text class="input-label">{{contactType === 'phone' ? '手机号' : '微信号'}}</text>
<input
wx:if="{{contactType === 'phone'}}"
type="number"
class="form-input"
placeholder="请输入11位手机号"
placeholder-class="input-placeholder"
value="{{phoneNumber}}"
bindinput="onPhoneInput"
maxlength="11"
disabled="{{isJoining}}"
/>
<input
wx:else
type="text"
class="form-input"
placeholder="请输入微信号"
placeholder-class="input-placeholder"
value="{{wechatId}}"
bindinput="onWechatInput"
disabled="{{isJoining}}"
/>
<!-- 输入区域 -->
<view class="input-area">
<view class="input-wrapper">
<text class="input-prefix">{{contactType === 'phone' ? '+86' : '@'}}</text>
<input
wx:if="{{contactType === 'phone'}}"
type="number"
class="input-field"
placeholder="请输入11位手机号"
placeholder-class="input-placeholder-new"
value="{{phoneNumber}}"
bindinput="onPhoneInput"
maxlength="11"
disabled="{{isJoining}}"
focus="{{contactType === 'phone'}}"
/>
<input
wx:else
type="text"
class="input-field"
placeholder="请输入微信号"
placeholder-class="input-placeholder-new"
value="{{wechatId}}"
bindinput="onWechatInput"
disabled="{{isJoining}}"
focus="{{contactType === 'wechat'}}"
/>
</view>
<text class="error-msg" wx:if="{{joinError}}">{{joinError}}</text>
</view>
<!-- 错误提示 -->
<text class="error-text" wx:if="{{joinError}}">{{joinError}}</text>
<!-- 提交按钮 -->
<view
class="btn-primary submit-btn {{isJoining || !(contactType === 'phone' ? phoneNumber : wechatId) ? 'btn-disabled' : ''}}"
class="submit-btn-new {{isJoining || !(contactType === 'phone' ? phoneNumber : wechatId) ? 'btn-disabled-new' : ''}}"
bindtap="handleJoinSubmit"
>
<text wx:if="{{isJoining}}">提交中...</text>
<text wx:else>确认加入</text>
{{isJoining ? '提交中...' : '确认提交'}}
</view>
<text class="form-notice">提交即表示同意我们的服务条款</text>
<text class="form-notice-new">提交后我们会尽快与您联系</text>
</block>
</view>
</view>

View File

@@ -52,67 +52,35 @@
width: 100%;
}
/* ===== 匹配次数条 ===== */
.match-count-bar {
/* ===== 匹配提示条 - 简化版 ===== */
.match-tip-bar {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 32rpx;
padding: 24rpx 32rpx;
background: #1c1c1e;
border-radius: 24rpx;
border: 2rpx solid rgba(255, 255, 255, 0.05);
}
.count-left {
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
}
.count-icon {
font-size: 28rpx;
width: 32rpx;
text-align: center;
}
.count-icon.icon-warning {
color: #FFD700;
}
.count-text {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.7);
}
.count-right {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 16rpx;
margin: 24rpx 32rpx;
padding: 20rpx 32rpx;
background: rgba(255, 215, 0, 0.1);
border-radius: 16rpx;
border: 1rpx solid rgba(255, 215, 0, 0.2);
}
.count-value {
font-size: 32rpx;
font-weight: 600;
.tip-icon {
font-size: 28rpx;
}
.count-text {
.tip-text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
}
.count-right {
display: flex;
align-items: center;
gap: 16rpx;
}
.count-value {
font-size: 32rpx;
font-weight: 700;
.tip-btn {
padding: 10rpx 24rpx;
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
color: #000;
font-size: 24rpx;
font-weight: 500;
border-radius: 20rpx;
}
.text-brand {
@@ -135,15 +103,6 @@
color: #FFD700;
}
.unlock-mini-btn {
padding: 12rpx 24rpx;
background: rgba(255, 215, 0, 0.2);
color: #FFD700;
font-size: 22rpx;
font-weight: 500;
border-radius: 24rpx;
}
/* ===== 主内容区 ===== */
.main-content {
padding: 0 32rpx;
@@ -737,7 +696,182 @@
margin-top: 24rpx;
}
/* ===== 加入成功 ===== */
/* ===== 新版加入弹窗 ===== */
.join-modal-new {
padding: 0;
border-radius: 32rpx;
overflow: hidden;
}
.join-header {
position: relative;
padding: 48rpx 40rpx 32rpx;
background: linear-gradient(135deg, rgba(0, 206, 209, 0.15) 0%, rgba(123, 97, 255, 0.1) 100%);
text-align: center;
}
.join-icon-wrap {
width: 100rpx;
height: 100rpx;
margin: 0 auto 20rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.join-icon {
font-size: 48rpx;
}
.join-title {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.join-subtitle {
display: block;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
}
.close-btn-new {
position: absolute;
top: 24rpx;
right: 24rpx;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
}
.contact-switch {
display: flex;
gap: 16rpx;
padding: 24rpx 40rpx;
}
.switch-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
padding: 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
border: 2rpx solid transparent;
}
.switch-item.switch-active {
background: rgba(0, 206, 209, 0.15);
color: #00CED1;
border-color: rgba(0, 206, 209, 0.3);
}
.switch-icon {
font-size: 32rpx;
}
.input-area {
padding: 0 40rpx 24rpx;
}
.input-wrapper {
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.3);
border: 2rpx solid rgba(255, 255, 255, 0.1);
border-radius: 20rpx;
overflow: hidden;
}
.input-prefix {
padding: 0 24rpx;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.5);
border-right: 1rpx solid rgba(255, 255, 255, 0.1);
}
.input-field {
flex: 1;
padding: 28rpx 24rpx;
font-size: 32rpx;
color: #ffffff;
}
.input-placeholder-new {
color: rgba(255, 255, 255, 0.3);
}
.error-msg {
display: block;
font-size: 24rpx;
color: #ff4444;
margin-top: 12rpx;
padding-left: 8rpx;
}
.submit-btn-new {
margin: 8rpx 40rpx 24rpx;
padding: 28rpx;
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
color: #ffffff;
font-size: 32rpx;
font-weight: 600;
text-align: center;
border-radius: 20rpx;
}
.btn-disabled-new {
opacity: 0.5;
}
.form-notice-new {
display: block;
text-align: center;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.3);
padding-bottom: 32rpx;
}
/* ===== 新版加入成功 ===== */
.join-success-new {
padding: 64rpx 40rpx;
text-align: center;
}
.success-icon-big {
font-size: 96rpx;
display: block;
margin-bottom: 24rpx;
}
.success-title-new {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 12rpx;
}
.success-desc-new {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
}
/* ===== 旧版加入成功 (保留兼容) ===== */
.join-success {
padding: 48rpx;
text-align: center;