Merge branch 'soul-content' into yongpxu-soul

This commit is contained in:
乘风
2026-01-30 14:12:03 +08:00
28 changed files with 2929 additions and 230 deletions

View File

@@ -7,9 +7,13 @@
const app = getApp()
// 默认匹配类型配置
// 找伙伴:真正的匹配功能,匹配数据库中的真实用户
// 资源对接:需要登录+购买章节才能使用填写2项信息我能帮到你什么、我需要什么帮助
// 导师顾问:跳转到存客宝添加微信
// 团队招募:跳转到存客宝添加微信
let MATCH_TYPES = [
{ id: 'partner', label: '找伙伴', matchLabel: '找伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false },
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true, requirePurchase: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true }
]
@@ -54,6 +58,11 @@ Page({
joinError: '',
needBindFirst: false,
// 资源对接表单
canHelp: '',
needHelp: '',
goodAt: '',
// 解锁弹窗
showUnlockModal: false,
@@ -180,6 +189,40 @@ Page({
handleMatchClick() {
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
// 资源对接类型需要登录+购买章节才能使用
if (currentType && currentType.id === 'investor') {
// 检查是否登录
if (!this.data.isLoggedIn) {
wx.showModal({
title: '需要登录',
content: '请先登录后再使用资源对接功能',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
wx.switchTab({ url: '/pages/my/my' })
}
}
})
return
}
// 检查是否购买过章节
const hasPurchased = app.globalData.purchasedSections?.length > 0 || app.globalData.hasFullBook
if (!hasPurchased) {
wx.showModal({
title: '需要购买章节',
content: '购买任意章节后即可使用资源对接功能',
confirmText: '去购买',
success: (res) => {
if (res.confirm) {
wx.switchTab({ url: '/pages/catalog/catalog' })
}
}
})
return
}
}
// 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募)
if (currentType && currentType.showJoinAfterMatch) {
// 先检查是否已绑定联系方式
@@ -417,6 +460,17 @@ Page({
joinError: ''
})
},
// 资源对接表单输入
onCanHelpInput(e) {
this.setData({ canHelp: e.detail.value })
},
onNeedHelpInput(e) {
this.setData({ needHelp: e.detail.value })
},
onGoodAtInput(e) {
this.setData({ goodAt: e.detail.value })
},
// 微信号输入
onWechatInput(e) {
@@ -428,11 +482,11 @@ Page({
// 提交加入
async handleJoinSubmit() {
const { contactType, phoneNumber, wechatId, joinType, isJoining } = this.data
const { contactType, phoneNumber, wechatId, joinType, isJoining, canHelp, needHelp } = this.data
if (isJoining) return
// 验证
// 验证联系方式
if (contactType === 'phone') {
if (!phoneNumber || phoneNumber.length !== 11) {
this.setData({ joinError: '请输入正确的11位手机号' })
@@ -445,6 +499,18 @@ Page({
}
}
// 资源对接需要填写两项信息
if (joinType === 'investor') {
if (!canHelp || canHelp.trim().length < 2) {
this.setData({ joinError: '请填写"我能帮到你什么"' })
return
}
if (!needHelp || needHelp.trim().length < 2) {
this.setData({ joinError: '请填写"我需要什么帮助"' })
return
}
}
this.setData({ isJoining: true, joinError: '' })
try {
@@ -454,7 +520,10 @@ Page({
type: joinType,
phone: contactType === 'phone' ? phoneNumber : '',
wechat: contactType === 'wechat' ? wechatId : '',
userId: app.globalData.userInfo?.id || ''
userId: app.globalData.userInfo?.id || '',
// 资源对接专属字段
canHelp: joinType === 'investor' ? canHelp : '',
needHelp: joinType === 'investor' ? needHelp : ''
}
})

View File

@@ -207,7 +207,21 @@
</view>
</view>
<!-- 输入区域 -->
<!-- 资源对接专用输入(只有两项:我能帮到你什么、我需要什么帮助) -->
<block wx:if="{{joinType === 'investor'}}">
<view class="resource-form">
<view class="form-item">
<text class="form-label">我能帮到你什么 <text class="required">*</text></text>
<input class="form-input-new" placeholder="例如:私域运营、品牌策划、流量资源..." value="{{canHelp}}" bindinput="onCanHelpInput" maxlength="100"/>
</view>
<view class="form-item">
<text class="form-label">我需要什么帮助 <text class="required">*</text></text>
<input class="form-input-new" placeholder="例如:技术支持、资金、人脉..." value="{{needHelp}}" bindinput="onNeedHelpInput" maxlength="100"/>
</view>
</view>
</block>
<!-- 联系方式输入区域 -->
<view class="input-area">
<view class="input-wrapper">
<text class="input-prefix">{{contactType === 'phone' ? '+86' : '@'}}</text>

View File

@@ -1175,3 +1175,28 @@
border-radius: 40rpx;
border: 1rpx solid rgba(255, 255, 255, 0.2);
}
/* 资源对接表单 */
.resource-form {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-bottom: 24rpx;
}
.resource-form .form-item {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.resource-form .form-label {
font-size: 26rpx;
color: rgba(255,255,255,0.6);
}
.resource-form .form-input-new {
background: #1c1c1e;
border: 2rpx solid rgba(0,206,209,0.3);
border-radius: 16rpx;
padding: 20rpx;
font-size: 28rpx;
color: #fff;
}

View File

@@ -77,10 +77,14 @@ Page({
const userId = userInfo.id || ''
const userIdShort = userId.length > 20 ? userId.slice(0, 10) + '...' + userId.slice(-6) : userId
// 获取微信号(优先显示)
const userWechat = wx.getStorageSync('user_wechat') || userInfo.wechat || ''
this.setData({
isLoggedIn: true,
userInfo,
userIdShort,
userWechat,
purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0),
referralCount: userInfo.referralCount || 0,
earnings: userInfo.earnings || 0,

View File

@@ -42,7 +42,7 @@
<text class="edit-icon-small">✎</text>
</view>
<view class="user-id-row" bindtap="copyUserId">
<text class="user-id">ID: {{userIdShort}}</text>
<text class="user-id">{{userWechat ? '微信: ' + userWechat : 'ID: ' + userIdShort}}</text>
<text class="copy-icon">📋</text>
</view>
</view>

View File

@@ -67,6 +67,7 @@
align-items: center;
gap: 24rpx;
margin-bottom: 32rpx;
width: 100%;
}
/* 头像容器 */
@@ -82,13 +83,18 @@
flex-shrink: 0;
width: 120rpx;
height: 120rpx;
min-width: 120rpx;
min-height: 120rpx;
padding: 0;
margin: 0;
background: transparent;
background: transparent !important;
border: none;
line-height: normal;
border-radius: 50%;
overflow: visible;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-btn-simple::after { border: none; }
@@ -167,14 +173,16 @@
display: flex;
flex-direction: column;
justify-content: center;
gap: 8rpx;
gap: 10rpx;
min-width: 0;
padding-top: 4rpx;
}
.user-name-row {
display: flex;
align-items: center;
gap: 8rpx;
gap: 10rpx;
line-height: 1.2;
}
.user-name {
@@ -184,7 +192,8 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 280rpx;
max-width: 320rpx;
line-height: 1.3;
}
.edit-name-icon {

View File

@@ -185,7 +185,7 @@ Page({
// 生成推广海报
async generatePoster() {
wx.showLoading({ title: '生成中...' })
wx.showLoading({ title: '生成中...', mask: true })
this.setData({ showPosterModal: true, isGeneratingPoster: true })
try {
@@ -196,16 +196,29 @@ Page({
// 获取小程序码(带推荐人参数)
let qrcodeImage = null
try {
// scene格式ref=用户ID前20位
const scene = userId ? `ref=${userId.slice(0,20)}` : 'ref=soul'
console.log('[Poster] 请求小程序码, scene:', scene)
const qrRes = await app.request('/api/miniprogram/qrcode', {
method: 'POST',
data: { scene, page: 'pages/index/index', width: 280 }
data: {
scene,
page: 'pages/index/index',
width: 280
}
})
if (qrRes.success && qrRes.image) {
console.log('[Poster] 小程序码响应:', qrRes?.success, qrRes?.image?.length)
if (qrRes && qrRes.success && qrRes.image) {
qrcodeImage = qrRes.image
console.log('[Poster] 小程序码获取成功')
} else {
console.log('[Poster] 响应无效:', qrRes)
}
} catch (e) {
console.log('[Poster] 获取小程序码失败,使用占位符')
console.error('[Poster] 获取小程序码失败:', e)
}
// 海报尺寸 300x450

View File

@@ -64,38 +64,63 @@ Page({
},
// 一键获取收货地址
async getAddress() {
try {
const res = await wx.chooseAddress()
if (res) {
const fullAddress = `${res.provinceName}${res.cityName}${res.countyName}${res.detailInfo}`
getAddress() {
wx.chooseAddress({
success: (res) => {
console.log('[Settings] 获取地址成功:', res)
const fullAddress = `${res.provinceName || ''}${res.cityName || ''}${res.countyName || ''}${res.detailInfo || ''}`
wx.setStorageSync('user_address', fullAddress)
this.setData({ address: fullAddress })
// 更新用户信息
if (app.globalData.userInfo) {
app.globalData.userInfo.address = fullAddress
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
// 同步到服务器
this.syncProfileToServer()
wx.showToast({ title: '地址已获取', icon: 'success' })
}
} catch (e) {
console.log('[Settings] 获取地址失败:', e)
if (e.errMsg?.includes('auth deny')) {
wx.showModal({
title: '需要授权',
content: '请允许获取收货地址',
confirmText: '去设置',
success: (res) => {
if (res.confirm) wx.openSetting()
if (fullAddress.trim()) {
wx.setStorageSync('user_address', fullAddress)
this.setData({ address: fullAddress })
// 更新用户信息
if (app.globalData.userInfo) {
app.globalData.userInfo.address = fullAddress
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
})
// 同步到服务器
this.syncAddressToServer(fullAddress)
wx.showToast({ title: '地址已获取', icon: 'success' })
}
},
fail: (e) => {
console.log('[Settings] 获取地址失败:', e)
if (e.errMsg?.includes('cancel')) {
// 用户取消,不提示
return
}
if (e.errMsg?.includes('auth deny') || e.errMsg?.includes('authorize')) {
wx.showModal({
title: '需要授权',
content: '请在设置中允许获取收货地址',
confirmText: '去设置',
success: (res) => {
if (res.confirm) wx.openSetting()
}
})
} else {
wx.showToast({ title: '获取失败,请重试', icon: 'none' })
}
}
})
},
// 同步地址到服务器
async syncAddressToServer(address) {
try {
const userId = app.globalData.userInfo?.id
if (!userId) return
await app.request('/api/user/update', {
method: 'POST',
data: { userId, address }
})
console.log('[Settings] 地址已同步到服务器')
} catch (e) {
console.log('[Settings] 同步地址失败:', e)
}
},
@@ -156,22 +181,37 @@ Page({
})
},
// 绑定微信号
bindWechat() {
this.setData({
showBindModal: true,
bindType: 'wechat',
bindValue: ''
})
// 微信号输入
onWechatInput(e) {
this.setData({ wechatId: e.detail.value })
},
// 绑定支付宝
bindAlipay() {
this.setData({
showBindModal: true,
bindType: 'alipay',
bindValue: ''
})
// 保存微信号
async saveWechat() {
const { wechatId } = this.data
if (!wechatId || wechatId.length < 6) return
wx.setStorageSync('user_wechat', wechatId)
// 更新用户信息
if (app.globalData.userInfo) {
app.globalData.userInfo.wechat = wechatId
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
// 同步到服务器
try {
await app.request('/api/user/update', {
method: 'POST',
data: {
userId: app.globalData.userInfo?.id,
wechat: wechatId
}
})
wx.showToast({ title: '微信号已保存', icon: 'success' })
} catch (e) {
console.log('保存微信号失败', e)
}
},
// 输入绑定值
@@ -319,10 +359,11 @@ Page({
wx.showLoading({ title: '获取中...', mask: true })
// 调用服务器解密手机号
// 调用服务器解密手机号传入userId以便同步到数据库
const userId = app.globalData.userInfo?.id
const res = await app.request('/api/miniprogram/phone', {
method: 'POST',
data: { code }
data: { code, userId }
})
wx.hideLoading()

View File

@@ -38,33 +38,23 @@
</view>
</view>
<!-- 微信号 -->
<view class="bind-item" bindtap="bindWechat">
<!-- 微信号 - 简化输入 -->
<view class="bind-item">
<view class="bind-left">
<view class="bind-icon wechat-icon">💬</view>
<view class="bind-info">
<text class="bind-label">微信号</text>
<text class="bind-value">{{wechatId || '未绑定'}}</text>
<input
class="bind-input"
placeholder="输入微信号"
value="{{wechatId}}"
bindinput="onWechatInput"
bindblur="saveWechat"
/>
</view>
</view>
<view class="bind-right">
<text class="bind-check" wx:if="{{wechatId}}">✓</text>
<text class="bind-btn" wx:else>去绑定</text>
</view>
</view>
<!-- 支付宝 -->
<view class="bind-item" bindtap="bindAlipay">
<view class="bind-left">
<view class="bind-icon alipay-icon">💳</view>
<view class="bind-info">
<text class="bind-label">支付宝</text>
<text class="bind-value">{{alipayAccount || '未绑定'}}</text>
</view>
</view>
<view class="bind-right">
<text class="bind-check" wx:if="{{alipayAccount}}">✓</text>
<text class="bind-btn" wx:else>去绑定</text>
</view>
</view>
@@ -86,12 +76,12 @@
</view>
<!-- 自动提现设置 -->
<view class="bind-card auto-withdraw-card" wx:if="{{isLoggedIn && (wechatId || alipayAccount)}}">
<view class="bind-card auto-withdraw-card" wx:if="{{isLoggedIn && wechatId}}">
<view class="card-header">
<text class="card-icon">💰</text>
<view class="card-title-wrap">
<text class="card-title">自动提现</text>
<text class="card-desc">收益自动打款到您的账户</text>
<text class="card-desc">收益自动打款到微信零钱</text>
</view>
</view>
@@ -104,11 +94,11 @@
<view class="withdraw-info" wx:if="{{autoWithdrawEnabled}}">
<view class="info-item">
<text class="info-label">提现方式</text>
<text class="info-value">{{alipayAccount ? '支付宝' : '微信零钱'}}</text>
<text class="info-value">微信零钱</text>
</view>
<view class="info-item">
<text class="info-label">提现账户</text>
<text class="info-value">{{alipayAccount || wechatId}}</text>
<text class="info-value">{{wechatId}}</text>
</view>
<text class="withdraw-tip">收益将在每笔订单完成后自动打款</text>
</view>
@@ -116,8 +106,8 @@
</view>
<!-- 提现提示 -->
<view class="tip-banner" wx:if="{{isLoggedIn && !wechatId && !alipayAccount}}">
<text class="tip-text">提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能</text>
<view class="tip-banner" wx:if="{{isLoggedIn && !wechatId}}">
<text class="tip-text">提示:绑定微信号才能使用提现功能</text>
</view>
<view class="logout-btn" wx:if="{{isLoggedIn}}" bindtap="handleLogout">退出登录</view>

View File

@@ -25,9 +25,13 @@
.bind-icon.phone-icon { background: rgba(0,206,209,0.2); }
.bind-icon.wechat-icon { background: rgba(158,158,158,0.2); }
.bind-icon.alipay-icon { background: rgba(158,158,158,0.2); }
.bind-info { display: flex; flex-direction: column; gap: 4rpx; }
.bind-info { display: flex; flex-direction: column; gap: 4rpx; flex: 1; }
.bind-label { font-size: 28rpx; color: #fff; font-weight: 500; }
.bind-value { font-size: 24rpx; color: rgba(255,255,255,0.5); }
.address-text { max-width: 360rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bind-icon.address-icon { background: rgba(255,165,0,0.2); }
.required { color: #FF6B6B; font-size: 24rpx; }
.bind-input { font-size: 24rpx; color: #00CED1; background: transparent; padding: 8rpx 0; }
.bind-right { display: flex; align-items: center; }
.bind-check { color: #00CED1; font-size: 32rpx; }
.bind-btn { color: #00CED1; font-size: 26rpx; }