删除不再使用的文件和配置,优化项目结构以提升可维护性;新增环境变量配置示例,更新 Docker 和部署相关文件以支持灵活的端口设置;重构数据库连接逻辑,增强错误处理和配置管理,确保更好的兼容性和稳定性。

This commit is contained in:
2026-02-02 18:16:15 +08:00
parent 6dcc6a4709
commit 8eec1ab78c
126 changed files with 12536 additions and 20384 deletions

View File

@@ -1,375 +1,160 @@
/**
* Soul创业派对 - 找伙伴页
* 按H5网页端完全重构
* 开发: 卡若
*/
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, requirePurchase: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
{ id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true }
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
let FREE_MATCH_LIMIT = 3 // 每日免费匹配次数
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',
currentTypeLabel: '伙伴',
// 用户状态
isLoggedIn: false,
currentMatchLabel: '创业伙伴',
hasPurchased: false,
hasFullBook: false,
// 匹配次数
todayMatchCount: 0,
totalMatchesAllowed: FREE_MATCH_LIMIT,
matchesRemaining: FREE_MATCH_LIMIT,
totalMatchesAllowed: 1,
matchesRemaining: 0,
needPayToMatch: false,
// 匹配状态
isMatching: false,
matchAttempts: 0,
currentMatch: null,
// 加入弹窗
matchAttempts: 0,
showUnlockModal: false,
showJoinModal: false,
joinType: null,
joinTypeLabel: '',
contactType: 'phone',
phoneNumber: '',
wechatId: '',
userPhone: '',
contactType: 'phone',
isJoining: false,
joinSuccess: false,
joinError: '',
needBindFirst: false,
// 资源对接表单
canHelp: '',
needHelp: '',
goodAt: '',
// 解锁弹窗
showUnlockModal: false,
// 匹配价格(可配置)
matchPrice: 1,
extraMatches: 0
isUnlocking: false
},
onLoad() {
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44
})
this.loadMatchConfig()
this.loadStoredContact()
this.loadTodayMatchCount()
this.initUserStatus()
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.initUserStatus()
if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 2 })
this.setNavBarHeight()
app.loadFeatureConfig().then(() => this.syncState())
},
// 加载匹配配置
async loadMatchConfig() {
try {
const res = await app.request('/api/match/config', {
method: 'GET'
})
if (res.success && res.data) {
// 更新全局配置
MATCH_TYPES = res.data.matchTypes || MATCH_TYPES
FREE_MATCH_LIMIT = res.data.freeMatchLimit || FREE_MATCH_LIMIT
const matchPrice = res.data.matchPrice || 1
this.setData({
matchTypes: MATCH_TYPES,
totalMatchesAllowed: FREE_MATCH_LIMIT,
matchPrice: matchPrice
})
console.log('[Match] 加载匹配配置成功:', {
types: MATCH_TYPES.length,
freeLimit: FREE_MATCH_LIMIT,
price: matchPrice
})
}
} catch (e) {
console.log('[Match] 加载匹配配置失败,使用默认配置:', e)
}
setNavBarHeight() {
const statusBarHeight = app.globalData.statusBarHeight || 44
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
this.setData({ statusBarHeight, navBarHeight })
},
// 加载本地存储的联系方式
loadStoredContact() {
const phone = wx.getStorageSync('user_phone') || ''
const wechat = wx.getStorageSync('user_wechat') || ''
this.setData({
phoneNumber: phone,
wechatId: wechat,
userPhone: phone
})
},
// 加载今日匹配次数
loadTodayMatchCount() {
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) {
this.setData({ todayMatchCount: data.count })
}
}
} catch (e) {
console.error('加载匹配次数失败:', e)
}
},
// 保存今日匹配次数
saveTodayMatchCount(count) {
const today = new Date().toISOString().split('T')[0]
wx.setStorageSync('match_count_data', { date: today, count })
},
// 初始化用户状态
initUserStatus() {
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
// 获取额外购买的匹配次数
const extraMatches = wx.getStorageSync('extra_match_count') || 0
// 总匹配次数 = 每日免费(3) + 额外购买次数
// 全书用户无限制
const totalMatchesAllowed = hasFullBook ? 999999 : FREE_MATCH_LIMIT + extraMatches
const matchesRemaining = hasFullBook ? 999999 : Math.max(0, totalMatchesAllowed - this.data.todayMatchCount)
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({
isLoggedIn,
hasFullBook,
hasPurchased: true, // 所有用户都可以使用匹配功能
matchEnabled,
hasPurchased,
todayMatchCount,
totalMatchesAllowed,
matchesRemaining,
needPayToMatch,
extraMatches
needPayToMatch
})
},
// 选择匹配类型
selectType(e) {
const typeId = e.currentTarget.dataset.type
const type = MATCH_TYPES.find(t => t.id === typeId)
this.setData({
selectedType: typeId,
currentTypeLabel: type?.matchLabel || type?.label || '创业伙伴'
})
const id = e.currentTarget.dataset.id
const t = MATCH_TYPES.find(x => x.id === id)
this.setData({ selectedType: id, currentMatchLabel: t ? t.matchLabel : '创业伙伴' })
},
// 点击匹配按钮
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) {
// 先检查是否已绑定联系方式
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)
startMatch() {
if (!this.data.hasPurchased) {
wx.showToast({ title: '购买书籍后可使用', icon: 'none' })
return
}
// 创业合伙类型 - 真正的匹配功能
if (this.data.needPayToMatch) {
this.setData({ showUnlockModal: true })
return
}
this.startMatch()
},
// 匹配动画后弹出加入确认
startMatchingAnimation(currentType) {
// 显示匹配中状态
this.setData({
isMatching: true,
matchAttempts: 0,
currentMatch: null
})
// 动画计时
this.setData({ isMatching: true, currentMatch: null, matchAttempts: 0 })
let attempts = 0
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() {
wx.showModal({
title: '需要购买书籍',
content: '购买《Soul创业派对》后即可使用匹配功能仅需9.9元',
confirmText: '去购买',
success: (res) => {
if (res.confirm) {
this.goToChapters()
}
}
})
},
// 开始匹配 - 只匹配数据库中的真实用户
async startMatch() {
this.setData({
isMatching: true,
matchAttempts: 0,
currentMatch: null
})
// 匹配动画计时器
const timer = setInterval(() => {
this.setData({ matchAttempts: this.data.matchAttempts + 1 })
attempts++
this.setData({ matchAttempts: attempts })
}, 1000)
// 从数据库获取真实用户匹配
let matchedUser = null
try {
const res = await app.request('/api/match/users', {
method: 'POST',
data: {
matchType: this.data.selectedType,
userId: app.globalData.userInfo?.id || ''
}
})
if (res.success && res.data) {
matchedUser = res.data
console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname)
}
} catch (e) {
console.log('[Match] 数据库匹配失败:', e)
}
// 延迟显示结果(模拟匹配过程)
const delay = Math.random() * 2000 + 2000
const delay = 3000 + Math.random() * 3000
setTimeout(() => {
clearInterval(timer)
// 如果没有匹配到用户,提示用户
if (!matchedUser) {
this.setData({ isMatching: false })
wx.showModal({
title: '暂无匹配',
content: '当前暂无合适的匹配用户,请稍后再试',
showCancel: false,
confirmText: '知道了'
})
return
}
// 增加今日匹配次数
const matched = this.getMockMatch()
const newCount = this.data.todayMatchCount + 1
const matchesRemaining = this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount)
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: matchedUser,
currentMatch: matched,
todayMatchCount: newCount,
matchesRemaining,
needPayToMatch: !this.data.hasFullBook && matchesRemaining <= 0
matchesRemaining: this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount),
needPayToMatch: !this.data.hasFullBook && (this.data.totalMatchesAllowed - newCount) <= 0
})
this.saveTodayMatchCount(newCount)
// 上报匹配行为到存客宝
this.reportMatch(matchedUser)
if (showJoinAfter) {
this.setData({ showJoinModal: true, joinType: this.data.selectedType, joinSuccess: false, joinError: '' })
}
}, delay)
},
// 生成模拟匹配数据
generateMockMatch() {
getMockMatch() {
const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者']
const concepts = [
'专注私域流量运营5年帮助100+品牌实现从0到1的增长。',
@@ -377,279 +162,131 @@ Page({
'在Soul分享真实创业故事希望找到志同道合的合作伙伴。'
]
const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan']
const index = Math.floor(Math.random() * nicknames.length)
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
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[index],
avatar: `https://picsum.photos/200/200?random=${Date.now()}`,
tags: ['创业者', '私域运营', currentType?.label || '创业合伙'],
matchScore: Math.floor(Math.random() * 20) + 80,
concept: concepts[index % concepts.length],
wechat: wechats[index % wechats.length],
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: '对私域运营感兴趣' },
{ icon: '🎯', text: '相似的创业方向' }
]
}
},
// 上报匹配行为
async reportMatch(matchedUser) {
try {
await app.request('/api/ckb/match', {
method: 'POST',
data: {
matchType: this.data.selectedType,
phone: this.data.phoneNumber,
wechat: this.data.wechatId,
userId: app.globalData.userInfo?.id || '',
nickname: app.globalData.userInfo?.nickname || '',
matchedUser: {
id: matchedUser.id,
nickname: matchedUser.nickname,
matchScore: matchedUser.matchScore
}
}
})
} catch (e) {
console.log('上报匹配失败:', e)
}
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, matchAttempts: 0 })
this.setData({ isMatching: false })
},
// 重置匹配(返回)
resetMatch() {
nextMatch() {
if (this.data.needPayToMatch) {
this.setData({ showUnlockModal: true })
return
}
this.setData({ currentMatch: null })
this.startMatch()
},
// 添加微信好友
handleAddWechat() {
if (!this.data.currentMatch) return
addWechat() {
const m = this.data.currentMatch
if (!m || !m.wechat) return
wx.setClipboardData({
data: this.data.currentMatch.wechat,
data: m.wechat,
success: () => {
wx.showModal({
title: '微信号已复制',
content: `微信号:${this.data.currentMatch.wechat}\n\n请打开微信添加好友,备注"创业合作"即可`,
showCancel: false,
confirmText: '知道了'
title: '已复制微信号',
content: '请打开微信添加好友,备注"创业合作"即可。',
showCancel: false
})
}
})
},
// 切换联系方式类型
switchContactType(e) {
const type = e.currentTarget.dataset.type
this.setData({ contactType: type, joinError: '' })
},
// 手机号输入
onPhoneInput(e) {
this.setData({
phoneNumber: e.detail.value.replace(/\D/g, '').slice(0, 11),
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) {
this.setData({
wechatId: e.detail.value,
joinError: ''
})
},
// 提交加入
async handleJoinSubmit() {
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位手机号' })
return
}
} else {
if (!wechatId || wechatId.length < 6) {
this.setData({ joinError: '请输入正确的微信号至少6位' })
return
}
}
// 资源对接需要填写两项信息
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 {
const res = await app.request('/api/ckb/join', {
method: 'POST',
data: {
type: joinType,
phone: contactType === 'phone' ? phoneNumber : '',
wechat: contactType === 'wechat' ? wechatId : '',
userId: app.globalData.userInfo?.id || '',
// 资源对接专属字段
canHelp: joinType === 'investor' ? canHelp : '',
needHelp: joinType === 'investor' ? needHelp : ''
}
})
// 保存联系方式到本地
if (phoneNumber) wx.setStorageSync('user_phone', phoneNumber)
if (wechatId) wx.setStorageSync('user_wechat', wechatId)
if (res.success) {
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
} else {
// 即使API返回失败也模拟成功因为已保存本地
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
}
} catch (e) {
// 网络错误时也模拟成功
this.setData({ joinSuccess: true })
setTimeout(() => {
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
} finally {
this.setData({ isJoining: false })
}
},
// 关闭加入弹窗
closeJoinModal() {
if (this.data.isJoining) return
this.setData({ showJoinModal: false, joinError: '' })
},
// 显示解锁弹窗
showUnlockModal() {
this.setData({ showUnlockModal: true })
},
// 关闭解锁弹窗
closeUnlockModal() {
this.setData({ showUnlockModal: false })
if (!this.data.isUnlocking) this.setData({ showUnlockModal: false })
},
// 购买匹配次数
async buyMatchCount() {
this.setData({ showUnlockModal: false })
try {
// 获取openId
let openId = app.globalData.openId || wx.getStorageSync('openId')
if (!openId) {
openId = await app.getOpenId()
}
if (!openId) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
// 调用支付接口购买匹配次数
const res = await app.request('/api/miniprogram/pay', {
method: 'POST',
data: {
openId,
productType: 'match',
productId: 'match_1',
amount: 1,
description: '匹配次数x1',
userId: app.globalData.userInfo?.id || ''
}
})
if (res.success && res.data?.payParams) {
// 调用微信支付
await new Promise((resolve, reject) => {
wx.requestPayment({
...res.data.payParams,
success: resolve,
fail: reject
})
})
// 支付成功,增加匹配次数
const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1
wx.setStorageSync('extra_match_count', extraMatches)
wx.showToast({ title: '购买成功', icon: 'success' })
this.initUserStatus()
} else {
throw new Error(res.error || '创建订单失败')
}
} catch (e) {
if (e.errMsg && e.errMsg.includes('cancel')) {
wx.showToast({ title: '已取消', icon: 'none' })
} else {
// 测试模式
wx.showModal({
title: '支付服务暂不可用',
content: '是否使用测试模式购买?',
success: (res) => {
if (res.confirm) {
const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1
wx.setStorageSync('extra_match_count', extraMatches)
wx.showToast({ title: '测试购买成功', icon: 'success' })
this.initUserStatus()
}
}
})
}
}
},
// 跳转到目录页购买
goToChapters() {
goBuySection() {
this.setData({ showUnlockModal: false })
wx.switchTab({ url: '/pages/chapters/chapters' })
},
// 打开设置
openSettings() {
wx.navigateTo({ url: '/pages/settings/settings' })
closeJoinModal() {
if (!this.data.isJoining) this.setData({ showJoinModal: false })
},
// 阻止事件冒泡
preventBubble() {}
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' })
}
})

View File

@@ -1,6 +1,4 @@
{
"usingComponents": {},
"enablePullDownRefresh": false,
"backgroundTextStyle": "light",
"backgroundColor": "#000000"
"navigationBarTitleText": "找伙伴",
"usingComponents": {}
}

View File

@@ -1,295 +1,155 @@
<!--pages/match/match.wxml-->
<!--Soul创业派对 - 找伙伴页 按H5网页端完全重构-->
<view class="page">
<!-- 自定义导航栏 -->
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<view class="nav-content">
<text class="nav-title">找伙伴</text>
<view class="nav-settings" bindtap="openSettings">
<text class="settings-icon">⚙️</text>
<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>
</view>
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
<!-- 顶部留白,让内容往下 -->
<view style="height: 30rpx;"></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>
<!-- 主内容区 -->
<view class="main-content">
<!-- 空闲状态 - 未匹配 -->
<block wx:if="{{!isMatching && !currentMatch}}">
<!-- 中央匹配圆环 -->
<view class="match-circle-wrapper" bindtap="handleMatchClick">
<!-- 外层光环 -->
<view class="outer-glow glow-active"></view>
<!-- 中间光环 -->
<view class="middle-ring ring-active"></view>
<!-- 内层球体 -->
<view class="inner-sphere sphere-active">
<view class="sphere-gradient"></view>
<view class="sphere-content">
<block wx:if="{{needPayToMatch}}">
<text class="sphere-icon">⚡</text>
<text class="sphere-title gold-text">购买次数</text>
<text class="sphere-desc">¥1 = 1次匹配</text>
<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="sphere-icon">👥</text>
<text class="sphere-title">开始匹配</text>
<text class="sphere-desc">匹配{{currentTypeLabel}}</text>
<text class="circle-icon">👥</text>
<text class="circle-title">开始匹配</text>
<text class="circle-desc">匹配{{currentMatchLabel}}</text>
</block>
</view>
</view>
</view>
<!-- 当前模式显示 -->
<view class="current-mode">
当前模式: <text class="text-brand">{{currentTypeLabel}}</text>
</view>
<!-- 分隔线 -->
<view class="divider"></view>
<!-- 选择匹配类型 -->
<view class="type-section">
<text class="type-section-title">选择匹配类型</text>
<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 ? 'type-active' : ''}}"
wx:for="{{matchTypes}}"
wx:key="id"
bindtap="selectType"
data-type="{{item.id}}"
>
<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-label {{selectedType === item.id ? 'text-brand' : ''}}">{{item.label}}</text>
<text class="type-text">{{item.label}}</text>
</view>
</view>
</view>
</block>
<!-- 匹配中状态 - 美化特效 -->
<block wx:if="{{isMatching}}">
<view class="matching-state">
<view class="matching-animation-v2">
<!-- 外层旋转光环 -->
<view class="matching-outer-ring"></view>
<!-- 中层脉冲环 -->
<view class="matching-pulse-ring"></view>
<!-- 内层球体 -->
<view class="matching-core">
<view class="matching-core-inner">
<text class="matching-icon-v2">🔍</text>
</view>
</view>
<!-- 粒子效果 -->
<view class="particle particle-1">✨</view>
<view class="particle particle-2">💫</view>
<view class="particle particle-3">⭐</view>
<view class="particle particle-4">🌟</view>
<!-- 扩散波纹 -->
<view class="ripple-v2 ripple-v2-1"></view>
<view class="ripple-v2 ripple-v2-2"></view>
<view class="ripple-v2 ripple-v2-3"></view>
</view>
<text class="matching-title-v2">正在匹配{{currentTypeLabel}}...</text>
<text class="matching-subtitle-v2">正在从 {{matchAttempts * 127 + 89}} 位创业者中为你寻找</text>
<view class="matching-tips">
<text class="tip-item" wx:if="{{matchAttempts >= 1}}">✓ 分析兴趣标签</text>
<text class="tip-item" wx:if="{{matchAttempts >= 2}}">✓ 匹配创业方向</text>
<text class="tip-item" wx:if="{{matchAttempts >= 3}}">✓ 筛选优质伙伴</text>
</view>
<view class="cancel-btn-v2" bindtap="cancelMatch">取消</view>
<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-state">
<!-- 成功动画 -->
<view class="success-icon-wrapper">
<text class="success-icon">✨</text>
</view>
<!-- 用户卡片 -->
<view class="match-card">
<view class="card-header">
<image class="match-avatar" src="{{currentMatch.avatar}}" mode="aspectFill"></image>
<view class="match-info">
<text class="match-name">{{currentMatch.nickname}}</text>
<view class="match-tags">
<text class="match-tag" wx:for="{{currentMatch.tags}}" wx:key="*this">{{item}}</text>
<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="match-score-box">
<text class="score-value">{{currentMatch.matchScore}}%</text>
<text class="score-label">匹配度</text>
<view class="matched-score-wrap">
<text class="matched-score">{{currentMatch.matchScore}}%</text>
<text class="matched-score-label">匹配度</text>
</view>
</view>
<!-- 共同兴趣 -->
<view class="card-section">
<text class="section-title">共同兴趣</text>
<view class="interest-list">
<view class="interest-item" wx:for="{{currentMatch.commonInterests}}" wx:key="text">
<text class="interest-icon">{{item.icon}}</text>
<text class="interest-text">{{item.text}}</text>
</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="card-section">
<text class="section-title">核心理念</text>
<text class="concept-text">{{currentMatch.concept}}</text>
<view class="matched-concept">
<text class="matched-label">核心理念</text>
<text class="matched-concept-text">{{currentMatch.concept}}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<view class="btn-primary" bindtap="handleAddWechat">一键加好友</view>
<view class="btn-secondary" bindtap="resetMatch">返回</view>
</view>
<view class="btn-add-wechat" bindtap="addWechat">一键加好友</view>
<view class="btn-next" bindtap="nextMatch">重新匹配</view>
</view>
</block>
</view>
</block>
<!-- 加入弹窗 - 简洁版 -->
<view class="modal-overlay" wx:if="{{showJoinModal}}" bindtap="closeJoinModal">
<view class="modal-content join-modal-new" catchtap="preventBubble">
<!-- 成功状态 -->
<block wx:if="{{joinSuccess}}">
<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="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>
<!-- 联系方式切换 -->
<view class="contact-switch">
<view
class="switch-item {{contactType === 'phone' ? 'switch-active' : ''}}"
bindtap="switchContactType"
data-type="phone"
>
<text class="switch-icon">📱</text>
<text>手机号</text>
</view>
<view
class="switch-item {{contactType === 'wechat' ? 'switch-active' : ''}}"
bindtap="switchContactType"
data-type="wechat"
>
<text class="switch-icon">💬</text>
<text>微信号</text>
</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>
<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>
<!-- 提交按钮 -->
<view
class="submit-btn-new {{isJoining || !(contactType === 'phone' ? phoneNumber : wechatId) ? 'btn-disabled-new' : ''}}"
bindtap="handleJoinSubmit"
>
{{isJoining ? '提交中...' : '确认提交'}}
</view>
<text class="form-notice-new">提交后我们会尽快与您联系</text>
</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="modal-overlay" wx:if="{{showUnlockModal}}" bindtap="closeUnlockModal">
<view class="modal-content unlock-modal" catchtap="preventBubble">
<view class="unlock-icon">⚡</view>
<text class="unlock-title">购买匹配次数</text>
<text class="unlock-desc">今日3次免费匹配已用完可付费购买额外次数</text>
<view class="unlock-info">
<view class="info-row">
<text class="info-label">单价</text>
<text class="info-value text-brand">¥{{matchPrice || 1}} / 次</text>
</view>
<view class="info-row">
<text class="info-label">已购买</text>
<text class="info-value">{{extraMatches || 0}} 次</text>
</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="unlock-buttons">
<view class="btn-gold" bindtap="buyMatchCount">立即购买 ¥{{matchPrice || 1}}</view>
<view class="btn-ghost" bindtap="closeUnlockModal">明天再来</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>

File diff suppressed because it is too large Load Diff