初始提交:一场soul的创业实验-永平 网站与小程序

Made-with: Cursor
This commit is contained in:
卡若
2026-03-07 22:58:43 +08:00
commit b7c35a89b0
513 changed files with 89020 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
import accessManager from '../../utils/chapterAccessManager'
const app = getApp()
Page({
data: {
statusBarHeight: 44,
isVip: false,
daysRemaining: 0,
expireDateStr: '',
price: 1980,
originalPrice: 6980,
/* 按 premium_membership_landing_v1 设计稿 */
contentRights: [
{ title: '解锁全部章节', desc: '365天全案精读', icon: '📖' },
{ title: '案例库', desc: '100+创业实战案例', icon: '📚' },
{ title: '智能纪要', desc: 'AI每日精华推送', icon: '💡' },
{ title: '会议纪要库', desc: '往期完整沉淀', icon: '📁' }
],
socialRights: [
{ title: '匹配创业伙伴', desc: '精准人脉匹配', icon: '👥' },
{ title: '创业老板排行', desc: '项目曝光展示', icon: '📊' },
{ title: '链接资源', desc: '深度私域资源池', icon: '🔗' },
{ title: '专属VIP标识', desc: '金色尊享光圈', icon: '✓' }
],
purchasing: false
},
onLoad() {
wx.showShareMenu({ withShareTimeline: true })
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.loadVipInfo()
},
async loadVipInfo() {
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true })
if (res?.success) {
const d = res.data
let expStr = ''
if (d.expireDate) {
const dt = new Date(d.expireDate)
expStr = `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`
}
this.setData({
isVip: d.isVip,
daysRemaining: d.daysRemaining,
expireDateStr: expStr,
price: d.price || 1980
})
}
} catch (e) { console.log('[VIP] 加载失败', e) }
},
async handlePurchase() {
let userId = app.globalData.userInfo?.id
let openId = app.globalData.openId || app.globalData.userInfo?.open_id
if (!userId || !openId) {
wx.showLoading({ title: '登录中...', mask: true })
try {
await app.login()
userId = app.globalData.userInfo?.id
openId = app.globalData.openId || app.globalData.userInfo?.open_id
wx.hideLoading()
if (!userId || !openId) {
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
return
}
} catch (e) {
wx.hideLoading()
wx.showToast({ title: '登录失败', icon: 'none' })
return
}
}
this.setData({ purchasing: true })
try {
const payRes = await app.request('/api/miniprogram/pay', {
method: 'POST',
data: {
openId,
userId,
productType: 'vip',
productId: 'vip_annual',
amount: this.data.price,
description: '卡若创业派对VIP年度会员365天'
}
})
if (payRes?.success && payRes.data?.payParams) {
wx.requestPayment({
...payRes.data.payParams,
success: async () => {
wx.showToast({ title: 'VIP开通成功', icon: 'success' })
await this._onVipPaymentSuccess()
},
fail: () => wx.showToast({ title: '支付取消', icon: 'none' })
})
} else {
wx.showToast({ title: payRes?.error || '支付参数获取失败', icon: 'none' })
}
} catch (e) {
console.error('[VIP] 购买失败', e)
wx.showToast({ title: '购买失败,请稍后重试', icon: 'none' })
} finally { this.setData({ purchasing: false }) }
},
async _onVipPaymentSuccess() {
wx.showLoading({ title: '同步权益中...', mask: true })
try {
await new Promise(r => setTimeout(r, 1500))
await accessManager.refreshUserPurchaseStatus()
await this.loadVipInfo()
app.globalData.hasFullBook = true
const userInfo = app.globalData.userInfo || {}
userInfo.hasFullBook = true
app.globalData.userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
const pages = getCurrentPages()
pages.forEach(p => {
if (typeof p.initUserStatus === 'function') p.initUserStatus()
else if (typeof p.updateUserStatus === 'function') p.updateUserStatus()
})
} catch (e) {
console.error('[VIP] 支付后同步失败:', e)
}
wx.hideLoading()
},
goBack() { getApp().goBackOrToHome() },
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - VIP会员',
path: ref ? `/pages/vip/vip?ref=${ref}` : '/pages/vip/vip'
}
},
onShareTimeline() {
const ref = app.getMyReferralCode()
return { title: 'Soul创业派对 - VIP会员', query: ref ? `ref=${ref}` : '' }
}
})

View File

@@ -0,0 +1 @@
{ "usingComponents": {}, "navigationStyle": "custom" }

View File

@@ -0,0 +1,55 @@
<!-- VIP会员页 - 按 premium_membership_landing_v1 设计稿 -->
<view class="page">
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<view class="nav-back" bindtap="goBack">
<text class="back-icon"></text>
</view>
<text class="nav-title">卡若创业派对</text>
<view class="nav-placeholder-r"></view>
</view>
<view style="height: {{statusBarHeight + 44}}px;"></view>
<!-- 会员宣传区(已去掉 VIP PREMIUM 标签) -->
<view class="vip-hero {{isVip ? 'vip-hero-active' : ''}}">
<view class="vip-hero-title">加入卡若的</view>
<view class="vip-hero-title gold">创业派对 会员</view>
<text class="vip-hero-sub" wx:if="{{isVip}}">有效期至 {{expireDateStr}}(剩余{{daysRemaining}}天)</text>
<text class="vip-hero-sub" wx:else>一次加入 尊享终身陪伴与成长</text>
</view>
<!-- 双列权益:内容权益 + 社交权益 -->
<view class="rights-grid">
<view class="rights-col">
<view class="rights-col-header">
<text class="rights-dot rights-dot-teal"></text>
<text class="rights-col-title">内容权益</text>
</view>
<view class="benefit-card" wx:for="{{contentRights}}" wx:key="title">
<text class="benefit-icon">{{item.icon || '✓'}}</text>
<view class="benefit-info">
<text class="benefit-title">{{item.title}}</text>
<text class="benefit-desc">{{item.desc}}</text>
</view>
</view>
</view>
<view class="rights-col">
<view class="rights-col-header">
<text class="rights-dot rights-dot-gold"></text>
<text class="rights-col-title rights-col-title-gold">社交权益</text>
</view>
<view class="benefit-card" wx:for="{{socialRights}}" wx:key="title">
<text class="benefit-icon benefit-icon-gold">{{item.icon || '✓'}}</text>
<view class="benefit-info">
<text class="benefit-title">{{item.title}}</text>
<text class="benefit-desc">{{item.desc}}</text>
</view>
</view>
</view>
</view>
<!-- 底部固定购买按钮(非 VIP 时显示,用 view 避让 button 默认 margin -->
<view class="buy-footer" wx:if="{{!isVip}}">
<view class="buy-btn-fixed {{purchasing ? 'buy-btn-disabled' : ''}}" bindtap="handlePurchase">
{{purchasing ? "处理中..." : "¥" + price + "/年 加入创业派对"}}
</view>
</view>
<view class="bottom-space"></view>
</view>

View File

@@ -0,0 +1,67 @@
.page { background: #000; min-height: 100vh; color: #fff; }
.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 24rpx; background: rgba(0,0,0,0.9); }
.nav-back { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
.back-icon { font-size: 44rpx; color: #fff; }
.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; }
.nav-placeholder-r { width: 60rpx; }
.vip-hero { margin: 24rpx; padding: 48rpx 32rpx; border-radius: 24rpx; background: linear-gradient(135deg, rgba(0,206,209,0.08), rgba(255,215,0,0.06)); border: 1rpx solid rgba(0,206,209,0.2); }
.vip-hero-active { border-color: rgba(255,215,0,0.4); background: linear-gradient(135deg, rgba(255,215,0,0.15), rgba(0,206,209,0.08)); }
.vip-hero-tag { display: inline-block; background: rgba(0,206,209,0.15); color: #00CED1; font-size: 22rpx; padding: 6rpx 16rpx; border-radius: 16rpx; margin-bottom: 20rpx; }
.vip-hero-title { display: block; font-size: 44rpx; font-weight: bold; color: #fff; }
.gold { color: #FFD700; }
.vip-hero-sub { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; }
/* 双列权益 - 按 premium_membership_landing_v1 */
.rights-grid { display: flex; gap: 24rpx; margin: 24rpx; }
.rights-col { flex: 1; min-width: 0; }
.rights-col-header { display: flex; align-items: center; gap: 12rpx; margin-bottom: 16rpx; padding-left: 8rpx; }
.rights-dot { width: 8rpx; height: 24rpx; border-radius: 4rpx; }
.rights-dot-teal { background: #4FD1C5; }
.rights-dot-gold { background: #FFBD2E; }
.rights-col-title { font-size: 24rpx; font-weight: bold; color: #4FD1C5; letter-spacing: 2rpx; }
.rights-col-title-gold { color: #FFBD2E; }
.benefit-card { display: flex; flex-direction: column; gap: 16rpx; padding: 24rpx; margin-bottom: 16rpx; background: #141414; border: 1rpx solid rgba(255,255,255,0.05); border-radius: 24rpx; }
.benefit-icon { font-size: 36rpx; color: #4FD1C5; }
.benefit-icon-gold { color: #FFBD2E; }
.benefit-info { display: flex; flex-direction: column; }
.benefit-title { font-size: 26rpx; font-weight: bold; color: #fff; }
.benefit-desc { font-size: 20rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; line-height: 1.4; }
/* 底部固定购买按钮 - 宽度拉满屏幕(用 view 替代 button 避让默认 margin */
.buy-footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 24rpx 20rpx; padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); background: rgba(0,0,0,0.95); border-top: 1rpx solid rgba(255,255,255,0.05); z-index: 50; box-sizing: border-box; }
.buy-btn-fixed {
width: 100%;
height: 96rpx;
display: flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, #FFD700, #FFB000); color: #000;
font-size: 32rpx; font-weight: bold; border-radius: 48rpx;
box-shadow: 0 8rpx 32rpx rgba(255,188,46,0.2);
}
.buy-btn-disabled { opacity: 0.6; pointer-events: none; }
.bottom-spacer { height: 180rpx; }
.profile-card { margin: 24rpx; padding: 32rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.08); border-radius: 20rpx; }
.profile-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.9); display: block; margin-bottom: 24rpx; }
.avatar-row { display: flex; align-items: center; margin-bottom: 24rpx; }
.avatar-label { font-size: 26rpx; color: rgba(255,255,255,0.6); width: 140rpx; flex-shrink: 0; }
.avatar-slot { width: 128rpx; height: 128rpx; border-radius: 50%; background: rgba(255,255,255,0.06); border: 2rpx dashed rgba(255,255,255,0.2); overflow: hidden; display: flex; align-items: center; justify-content: center; }
.avatar-slot .avatar-img { width: 100%; height: 100%; }
.avatar-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(255,255,255,0.4); }
.avatar-add { font-size: 48rpx; line-height: 1; }
.avatar-hint { font-size: 20rpx; margin-top: 8rpx; }
.form-group { margin-bottom: 24rpx; }
.form-label { font-size: 26rpx; color: rgba(255,255,255,0.6); display: block; margin-bottom: 10rpx; }
.form-input { box-sizing: border-box; width: 100%; min-height: 76rpx; background: rgba(255,255,255,0.06); border: 1rpx solid rgba(255,255,255,0.1); border-radius: 12rpx; padding: 16rpx 24rpx; }
.form-input input { width: 100%; font-size: 28rpx; color: #fff; line-height: 1.5; background: transparent; }
.form-placeholder { color: rgba(255,255,255,0.35); }
.save-btn-wrap { margin-top: 32rpx; width: 100%; display: flex; justify-content: center; }
.save-btn { display: block; margin: 0; padding: 0; width: 100%; height: 88rpx; line-height: 88rpx; text-align: center; background: linear-gradient(135deg, #00CED1, #20B2AA); color: #000; font-size: 32rpx; font-weight: 600; border-radius: 44rpx; border: none; box-sizing: border-box; }
.save-btn::after { border: none; margin: 0; padding: 0; }
.author-section { margin: 32rpx 24rpx; padding: 24rpx; border-top: 1rpx solid rgba(255,255,255,0.08); }
.author-row { display: flex; justify-content: space-between; padding: 8rpx 0; }
.author-label { font-size: 24rpx; color: rgba(255,255,255,0.4); }
.author-name { font-size: 24rpx; color: rgba(255,255,255,0.7); }
.bottom-space { height: 120rpx; }