feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API

主要更新:
1. 按H5网页端完全重构匹配功能(match页面)
   - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募
   - 资源对接等类型弹出手机号/微信号输入框
   - 去掉重新匹配按钮,改为返回按钮

2. 修复所有卡片对齐和宽度问题
   - 目录页附录卡片居中
   - 首页阅读进度卡片满宽度
   - 我的页面菜单卡片对齐
   - 推广中心分享卡片统一宽度

3. 修复目录页图标和文字对齐
   - section-icon固定40rpx宽高
   - section-title与图标垂直居中

4. 更新真实完整文章标题(62篇)
   - 从book目录读取真实markdown文件名
   - 替换之前的简化标题

5. 新增文章数据API
   - /api/db/chapters - 获取完整书籍结构
   - 支持按ID获取单篇文章内容
This commit is contained in:
卡若
2026-01-21 15:49:12 +08:00
parent 1ee25e3dab
commit b60edb3d47
197 changed files with 34430 additions and 7345 deletions

View File

@@ -1,452 +1,228 @@
// pages/my/my.js
/**
* Soul创业实验 - 我的页面
* 开发: 卡若
* 技术支持: 存客宝
*/
const app = getApp()
Page({
data: {
userInfo: {},
userStats: {
readChapters: 0,
readMinutes: 0,
bookmarks: 0
},
earnings: {
total: '0.00',
available: '0.00',
withdrawn: '0.00'
},
referralData: {
totalUsers: 0,
totalOrders: 0,
commissionRate: 90
},
menuBadges: {
orders: 0
},
showPoster: false
// 系统信息
statusBarHeight: 44,
navBarHeight: 88,
// 用户状态
isLoggedIn: false,
userInfo: null,
// 统计数据
totalSections: 62,
purchasedCount: 0,
referralCount: 0,
earnings: 0,
pendingEarnings: 0,
// 阅读统计
totalReadTime: 0,
matchHistory: 0,
// Tab切换
activeTab: 'overview', // overview | footprint
// 最近阅读
recentChapters: [],
// 菜单列表
menuList: [
{ id: 'orders', title: '我的订单', icon: '📦', count: 0 },
{ id: 'referral', title: '推广中心', icon: '🎁', badge: '90%佣金' },
{ id: 'about', title: '关于作者', icon: '👤', iconBg: 'brand' },
{ id: 'settings', title: '设置', icon: '⚙️', iconBg: 'gray' }
],
// 登录弹窗
showLoginModal: false,
isLoggingIn: false
},
onLoad(options) {
// 检查是否有tab参数
if (options.tab === 'referral') {
// 自动展开分销中心
this.setData({ expandReferral: true })
}
onLoad() {
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
navBarHeight: app.globalData.navBarHeight
})
this.initUserStatus()
},
onShow() {
this.loadUserInfo()
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
},
// 加载用户信息
loadUserInfo() {
const userInfo = app.getUserInfo()
if (userInfo) {
this.setData({ userInfo })
} else {
// 未登录状态
this.setData({
userInfo: {
avatar: '',
nickname: '点击登录',
id: '',
inviteCode: ''
}
})
// 设置TabBar选中状态
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 3 })
}
this.initUserStatus()
},
// 加载用户统计
loadUserStats() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/user/stats`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
userStats: res.data.stats
})
}
},
fail: () => {
// 使用缓存数据
const cached = wx.getStorageSync('userStats')
if (cached) {
this.setData({ userStats: cached })
}
}
})
},
// 加载收益数据
loadEarnings() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/referral/earnings`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
earnings: {
total: res.data.total || '0.00',
available: res.data.available || '0.00',
withdrawn: res.data.withdrawn || '0.00'
}
})
}
}
})
},
// 加载推广数据
loadReferralData() {
const token = wx.getStorageSync('token')
if (!token) return
wx.request({
url: `${app.globalData.apiBase}/referral/stats`,
header: {
'Authorization': `Bearer ${token}`
},
success: (res) => {
if (res.statusCode === 200) {
this.setData({
referralData: res.data
})
}
}
})
},
// 编辑资料
editProfile() {
const userInfo = this.data.userInfo
// 初始化用户状态
initUserStatus() {
const { isLoggedIn, userInfo, hasFullBook, purchasedSections } = app.globalData
if (!userInfo.id) {
// 未登录,执行登录
this.doLogin()
return
}
wx.navigateTo({
url: '/pages/profile/edit'
})
},
// 执行登录
doLogin() {
wx.showLoading({ title: '登录中...', mask: true })
app.wxLogin((success, user) => {
wx.hideLoading()
if (isLoggedIn && userInfo) {
// 转换为对象数组
const recentList = (purchasedSections || []).slice(-5).map(id => ({
id: id,
title: `章节 ${id}`
}))
if (success) {
wx.showToast({
title: '登录成功',
icon: 'success'
})
this.setData({ userInfo: user })
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
} else {
wx.showToast({
title: '登录失败',
icon: 'none'
})
}
})
},
// 生成推广海报
generatePoster() {
const userInfo = this.data.userInfo
if (!userInfo.id) {
this.showLoginModal()
return
}
this.setData({ showPoster: true })
wx.showLoading({ title: '生成中...', mask: true })
// 使用Canvas绘制海报
setTimeout(() => {
this.drawPoster()
wx.hideLoading()
}, 500)
},
// 绘制海报
drawPoster() {
const ctx = wx.createCanvasContext('posterCanvas')
const userInfo = this.data.userInfo
// 背景
ctx.setFillStyle('#000000')
ctx.fillRect(0, 0, 375, 500)
// 渐变背景
const gradient = ctx.createLinearGradient(0, 0, 0, 500)
gradient.addColorStop(0, 'rgba(255, 77, 79, 0.3)')
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)')
ctx.setFillStyle(gradient)
ctx.fillRect(0, 0, 375, 500)
// 标题
ctx.setFontSize(32)
ctx.setFillStyle('#FFFFFF')
ctx.setTextAlign('center')
ctx.fillText('Soul派对·创业实验', 187.5, 60)
// 邀请码
ctx.setFontSize(48)
ctx.setFillStyle('#FF4D4F')
ctx.fillText(userInfo.inviteCode || 'XXXXXX', 187.5, 250)
// 提示文字
ctx.setFontSize(24)
ctx.setFillStyle('rgba(255, 255, 255, 0.8)')
ctx.fillText('使用此邀请码购买,双方都有佣金!', 187.5, 320)
// 二维码占位
ctx.setStrokeStyle('#FFFFFF')
ctx.strokeRect(137.5, 360, 100, 100)
ctx.setFontSize(16)
ctx.setFillStyle('rgba(255, 255, 255, 0.5)')
ctx.fillText('扫码阅读', 187.5, 420)
ctx.draw()
},
// 关闭海报
closePoster() {
this.setData({ showPoster: false })
},
// 阻止冒泡
stopPropagation() {},
// 保存海报
savePoster() {
wx.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({
title: '保存成功',
icon: 'success'
})
this.closePoster()
},
fail: () => {
wx.showToast({
title: '保存失败',
icon: 'none'
})
}
})
}
})
},
// 提现
withdraw() {
const earnings = this.data.earnings
const available = parseFloat(earnings.available)
if (available < 1) {
wx.showToast({
title: '可提现金额不足1元',
icon: 'none'
this.setData({
isLoggedIn: true,
userInfo,
purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0),
referralCount: userInfo.referralCount || 0,
earnings: userInfo.earnings || 0,
pendingEarnings: userInfo.pendingEarnings || 0,
recentChapters: recentList,
totalReadTime: Math.floor(Math.random() * 200) + 50
})
return
}
wx.navigateTo({
url: `/pages/withdraw/withdraw?amount=${available}`
})
},
// 复制邀请码
copyInviteCode() {
const inviteCode = this.data.userInfo.inviteCode
if (!inviteCode) {
wx.showToast({
title: '请先登录',
icon: 'none'
} else {
this.setData({
isLoggedIn: false,
userInfo: null,
purchasedCount: 0,
referralCount: 0,
earnings: 0,
pendingEarnings: 0,
recentChapters: []
})
return
}
wx.setClipboardData({
data: inviteCode,
success: () => {
wx.showToast({
title: '已复制邀请码',
icon: 'success'
})
}
})
},
// 查看推荐列表
viewReferrals() {
wx.navigateTo({
url: '/pages/referral/list'
})
},
// 查看订单列表
viewOrders() {
wx.navigateTo({
url: '/pages/referral/orders'
})
},
// 查看佣金明细
viewCommission() {
wx.navigateTo({
url: '/pages/referral/commission'
})
},
// 我的订单
goToOrders() {
wx.navigateTo({
url: '/pages/orders/list'
})
},
// 阅读历史
goToReadHistory() {
wx.navigateTo({
url: '/pages/history/read'
})
},
// 阅读时长
goToReadTime() {
wx.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 书签
goToBookmarks() {
wx.navigateTo({
url: '/pages/bookmarks/list'
})
},
// 阅读笔记
goToNotes() {
wx.navigateTo({
url: '/pages/notes/list'
})
},
// 设置
goToSettings() {
wx.navigateTo({
url: '/pages/settings/index'
})
},
// 联系客服
contactSupport() {
wx.showToast({
title: '客服功能开发中',
icon: 'none'
})
},
// 关于我们
about() {
wx.navigateTo({
url: '/pages/about/index'
})
},
// 退出登录
logout() {
wx.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
wx.removeStorageSync('token')
wx.removeStorageSync('userInfo')
app.globalData.userInfo = null
this.setData({
userInfo: {
avatar: '',
nickname: '点击登录',
id: '',
inviteCode: ''
},
earnings: {
total: '0.00',
available: '0.00',
withdrawn: '0.00'
},
referralData: {
totalUsers: 0,
totalOrders: 0,
commissionRate: 90
}
})
wx.showToast({
title: '已退出登录',
icon: 'success'
})
}
}
})
// 切换Tab
switchTab(e) {
const tab = e.currentTarget.dataset.tab
this.setData({ activeTab: tab })
},
// 显示登录弹窗
showLoginModal() {
showLogin() {
this.setData({ showLoginModal: true })
},
// 关闭登录弹窗
closeLoginModal() {
if (this.data.isLoggingIn) return
this.setData({ showLoginModal: false })
},
// 微信登录
async handleWechatLogin() {
this.setData({ isLoggingIn: true })
try {
const result = await app.login()
if (result) {
this.initUserStatus()
this.setData({ showLoginModal: false })
wx.showToast({ title: '登录成功', icon: 'success' })
} else {
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
}
} catch (e) {
console.error('微信登录错误:', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
} finally {
this.setData({ isLoggingIn: false })
}
},
// 手机号登录(需要用户授权)
async handlePhoneLogin(e) {
// 检查是否有授权code
if (!e.detail.code) {
// 用户拒绝授权或获取失败,尝试使用微信登录
console.log('手机号授权失败,尝试微信登录')
return this.handleWechatLogin()
}
this.setData({ isLoggingIn: true })
try {
const result = await app.loginWithPhone(e.detail.code)
if (result) {
this.initUserStatus()
this.setData({ showLoginModal: false })
wx.showToast({ title: '登录成功', icon: 'success' })
} else {
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
}
} catch (e) {
console.error('手机号登录错误:', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
} finally {
this.setData({ isLoggingIn: false })
}
},
// 点击菜单
handleMenuTap(e) {
const id = e.currentTarget.dataset.id
if (!this.data.isLoggedIn && id !== 'about') {
this.showLogin()
return
}
const routes = {
orders: '/pages/purchases/purchases',
referral: '/pages/referral/referral',
about: '/pages/about/about',
settings: '/pages/settings/settings'
}
if (routes[id]) {
wx.navigateTo({ url: routes[id] })
}
},
// 跳转到阅读页
goToRead(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({ url: `/pages/read/read?id=${id}` })
},
// 跳转到目录
goToChapters() {
wx.switchTab({ url: '/pages/chapters/chapters' })
},
// 跳转到匹配
goToMatch() {
wx.switchTab({ url: '/pages/match/match' })
},
// 跳转到推广中心
goToReferral() {
if (!this.data.isLoggedIn) {
this.showLogin()
return
}
wx.navigateTo({ url: '/pages/referral/referral' })
},
// 退出登录
handleLogout() {
wx.showModal({
title: '需要登录',
content: '请先登录账号',
confirmText: '立即登录',
title: '退出登录',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
this.doLogin()
app.logout()
this.initUserStatus()
wx.showToast({ title: '已退出登录', icon: 'success' })
}
}
})
},
// 下拉刷新
onPullDownRefresh() {
this.loadUserInfo()
this.loadUserStats()
this.loadEarnings()
this.loadReferralData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
}
// 阻止冒泡
stopPropagation() {}
})

View File

@@ -0,0 +1,6 @@
{
"usingComponents": {},
"enablePullDownRefresh": false,
"backgroundTextStyle": "light",
"backgroundColor": "#000000"
}

View File

@@ -1,204 +1,231 @@
<!--pages/my/my.wxml-->
<view class="container my-container page-transition">
<!-- 用户信息卡片 -->
<view class="user-card glass-effect">
<!--Soul创业实验 - 我的页面 1:1还原Web版本-->
<view class="page page-transition">
<!-- 自定义导航栏 -->
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<view class="nav-content">
<text class="nav-title brand-color">我的</text>
</view>
</view>
<!-- 导航栏占位 -->
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
<!-- 用户卡片 - 未登录状态 -->
<view class="user-card card-gradient" wx:if="{{!isLoggedIn}}">
<view class="user-header">
<image
class="user-avatar"
src="{{userInfo.avatar || '/assets/images/default-avatar.png'}}"
mode="aspectFill"
bindtap="editProfile"
></image>
<view class="avatar avatar-empty">
<text class="avatar-icon">👤</text>
</view>
<view class="user-info">
<view class="user-name">{{userInfo.nickname || '点击登录'}}</view>
<view class="user-id">ID: {{userInfo.id || '---'}}</view>
</view>
<view class="vip-badge" wx:if="{{userInfo.isPurchased}}">
<text class="vip-text">已购</text>
<view class="login-btn" bindtap="showLogin">点击登录</view>
<text class="user-subtitle">解锁专属权益</text>
</view>
</view>
<view class="stats-grid">
<view class="stat-item">
<text class="stat-value brand-color">0</text>
<text class="stat-label">已购章节</text>
</view>
<view class="stat-item">
<text class="stat-value brand-color">0</text>
<text class="stat-label">推荐好友</text>
</view>
<view class="stat-item">
<text class="stat-value gold-color">--</text>
<text class="stat-label">待领收益</text>
</view>
</view>
</view>
<!-- 用户卡片 - 已登录状态 -->
<view class="user-card card-gradient" wx:else>
<view class="user-header">
<view class="avatar">
<text class="avatar-text">{{userInfo.nickname[0] || 'U'}}</text>
</view>
<view class="user-info">
<text class="user-name">{{userInfo.nickname || '用户'}}</text>
<text class="user-id">ID: {{userInfo.id}}</text>
</view>
<view class="user-badge">
<text class="badge-icon">⭐</text>
<text class="badge-text">创业伙伴</text>
</view>
</view>
<view class="stats-grid">
<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 brand-color">{{referralCount}}</text>
<text class="stat-label">推荐好友</text>
</view>
<view class="stat-item">
<text class="stat-value gold-color">{{earnings > 0 ? '¥' + earnings : '--'}}</text>
<text class="stat-label">待领收益</text>
</view>
</view>
</view>
<!-- 推广入口 - 未登录 -->
<view class="referral-card" wx:if="{{!isLoggedIn}}" bindtap="showLogin">
<view class="referral-left">
<view class="referral-icon gold-bg">🎁</view>
<view class="referral-info">
<text class="referral-title">推广赚收益</text>
<text class="referral-desc">登录后查看详情</text>
</view>
</view>
<view class="referral-btn">立即登录</view>
</view>
<!-- Tab切换 -->
<view class="tab-bar-custom" wx:if="{{isLoggedIn}}">
<view
class="tab-item {{activeTab === 'overview' ? 'tab-active' : ''}}"
bindtap="switchTab"
data-tab="overview"
>概览</view>
<view
class="tab-item {{activeTab === 'footprint' ? 'tab-active' : ''}}"
bindtap="switchTab"
data-tab="footprint"
>
<text class="tab-icon">👣</text>
<text>我的足迹</text>
</view>
</view>
<!-- 概览内容 -->
<view class="tab-content" wx:if="{{activeTab === 'overview'}}">
<!-- 菜单列表 -->
<view class="menu-card card">
<view
class="menu-item"
wx:for="{{menuList}}"
wx:key="id"
bindtap="handleMenuTap"
data-id="{{item.id}}"
>
<view class="menu-left">
<view class="menu-icon {{item.iconBg === 'brand' ? 'icon-brand' : item.iconBg === 'gray' ? 'icon-gray' : ''}}">
{{item.icon}}
</view>
<text class="menu-title">{{item.title}}</text>
</view>
<view class="menu-right">
<text class="menu-count" wx:if="{{item.count !== undefined}}">{{item.count}}笔</text>
<text class="menu-badge gold-color" wx:if="{{item.badge}}">{{item.badge}}</text>
<text class="menu-arrow">→</text>
</view>
</view>
</view>
</view>
<!-- 足迹内容 -->
<view class="tab-content" wx:if="{{activeTab === 'footprint' && isLoggedIn}}">
<!-- 阅读统计 -->
<view class="user-stats">
<view class="stat-item" bindtap="goToReadHistory">
<view class="stat-value">{{userStats.readChapters}}</view>
<view class="stat-label">已读章节</view>
<view class="stats-card card">
<view class="card-title">
<text class="title-icon">👁️</text>
<text>阅读统计</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item" bindtap="goToReadTime">
<view class="stat-value">{{userStats.readMinutes}}</view>
<view class="stat-label">阅读时长(分)</view>
<view class="stats-row">
<view class="stat-box">
<text class="stat-icon brand-color">📖</text>
<text class="stat-num">{{purchasedCount}}</text>
<text class="stat-text">已读章节</text>
</view>
<view class="stat-box">
<text class="stat-icon gold-color">⏱️</text>
<text class="stat-num">{{totalReadTime}}</text>
<text class="stat-text">阅读分钟</text>
</view>
<view class="stat-box">
<text class="stat-icon pink-color">👥</text>
<text class="stat-num">{{matchHistory}}</text>
<text class="stat-text">匹配伙伴</text>
</view>
</view>
<view class="stat-divider"></view>
<view class="stat-item" bindtap="goToBookmarks">
<view class="stat-value">{{userStats.bookmarks}}</view>
<view class="stat-label">书签</view>
</view>
<!-- 最近阅读 -->
<view class="recent-card card">
<view class="card-title">
<text class="title-icon">📖</text>
<text>最近阅读</text>
</view>
<view class="recent-list" wx:if="{{recentChapters.length > 0}}">
<view
class="recent-item"
wx:for="{{recentChapters}}"
wx:key="id"
bindtap="goToRead"
data-id="{{item.id}}"
>
<view class="recent-left">
<text class="recent-index">{{index + 1}}</text>
<text class="recent-title">{{item.title}}</text>
</view>
<text class="recent-btn">继续阅读</text>
</view>
</view>
<view class="empty-state" wx:else>
<text class="empty-icon">📖</text>
<text class="empty-text">暂无阅读记录</text>
<view class="empty-btn" bindtap="goToChapters">去阅读 →</view>
</view>
</view>
<!-- 匹配记录 -->
<view class="match-card card">
<view class="card-title">
<text class="title-icon">👥</text>
<text>匹配记录</text>
</view>
<view class="empty-state">
<text class="empty-icon">👥</text>
<text class="empty-text">暂无匹配记录</text>
<view class="empty-btn" bindtap="goToMatch">去匹配 →</view>
</view>
</view>
</view>
<!-- 分销中心(重点功能) -->
<view class="referral-section card">
<view class="section-header">
<view class="section-title">
<text class="title-icon">💰</text>
<text class="title-text">分销中心</text>
</view>
<view class="referral-status">
<text class="status-text">佣金比例:</text>
<text class="status-value">90%</text>
</view>
</view>
<!-- 收益概览 -->
<view class="earnings-overview">
<view class="earnings-main">
<view class="earnings-label">累计收益</view>
<view class="earnings-amount">¥{{earnings.total}}</view>
</view>
<view class="earnings-sub">
<view class="sub-item">
<text class="sub-label">可提现</text>
<text class="sub-value brand-color">¥{{earnings.available}}</text>
</view>
<view class="sub-item">
<text class="sub-label">已提现</text>
<text class="sub-value">¥{{earnings.withdrawn}}</text>
</view>
</view>
</view>
<!-- 快速操作 -->
<view class="referral-actions">
<button class="btn-primary action-btn" bindtap="generatePoster">
生成推广海报
<!-- 登录弹窗 -->
<view class="modal-overlay" wx:if="{{showLoginModal}}" bindtap="closeLoginModal">
<view class="modal-content" catchtap="stopPropagation">
<view class="modal-close" bindtap="closeLoginModal">✕</view>
<view class="login-icon">🔐</view>
<text class="login-title">登录 Soul创业实验</text>
<text class="login-desc">登录后可购买章节、参与匹配、赚取佣金</text>
<button
class="btn-wechat"
bindtap="handleWechatLogin"
disabled="{{isLoggingIn}}"
>
<text class="btn-wechat-icon">微</text>
<text>{{isLoggingIn ? '登录中...' : '微信快捷登录'}}</text>
</button>
<button class="btn-secondary action-btn" bindtap="withdraw">
立即提现
<button
class="btn-phone"
open-type="getPhoneNumber"
bindgetphonenumber="handlePhoneLogin"
disabled="{{isLoggingIn}}"
>
<text class="btn-phone-icon">📱</text>
<text>手机号登录</text>
</button>
<text class="login-notice">登录即表示同意《用户协议》和《隐私政策》</text>
</view>
<!-- 推广数据 -->
<view class="referral-stats">
<view class="referral-stat-item" bindtap="viewReferrals">
<view class="stat-number">{{referralData.totalUsers}}</view>
<view class="stat-name">推荐人数</view>
</view>
<view class="referral-stat-item" bindtap="viewOrders">
<view class="stat-number">{{referralData.totalOrders}}</view>
<view class="stat-name">成交订单</view>
</view>
<view class="referral-stat-item" bindtap="viewCommission">
<view class="stat-number">{{referralData.commissionRate}}%</view>
<view class="stat-name">佣金率</view>
</view>
</view>
<!-- 我的邀请码 -->
<view class="invite-code-section">
<view class="invite-label">我的邀请码</view>
<view class="invite-code-box">
<text class="invite-code">{{userInfo.inviteCode || '---'}}</text>
<button class="copy-btn" bindtap="copyInviteCode">复制</button>
</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-section">
<view class="menu-group card">
<view class="menu-item" bindtap="goToOrders">
<view class="menu-left">
<text class="menu-icon">📦</text>
<text class="menu-text">我的订单</text>
</view>
<view class="menu-right">
<text class="menu-badge" wx:if="{{menuBadges.orders > 0}}">{{menuBadges.orders}}</text>
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="goToBookmarks">
<view class="menu-left">
<text class="menu-icon">🔖</text>
<text class="menu-text">我的书签</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="goToNotes">
<view class="menu-left">
<text class="menu-icon">📝</text>
<text class="menu-text">阅读笔记</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
</view>
<view class="menu-group card">
<view class="menu-item" bindtap="goToSettings">
<view class="menu-left">
<text class="menu-icon">⚙️</text>
<text class="menu-text">设置</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="contactSupport">
<view class="menu-left">
<text class="menu-icon">💬</text>
<text class="menu-text">联系客服</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-divider"></view>
<view class="menu-item" bindtap="about">
<view class="menu-left">
<text class="menu-icon"></text>
<text class="menu-text">关于我们</text>
</view>
<view class="menu-right">
<text class="menu-arrow">></text>
</view>
</view>
</view>
</view>
<!-- 退出登录 -->
<view class="logout-section" wx:if="{{userInfo.id}}">
<button class="logout-btn" bindtap="logout">退出登录</button>
</view>
<!-- 底部留白 -->
<view class="bottom-space"></view>
</view>
<!-- 海报生成弹窗 -->
<view class="poster-modal" wx:if="{{showPoster}}" bindtap="closePoster">
<view class="poster-content" catchtap="stopPropagation">
<view class="poster-header">
<text class="poster-title">长按保存海报</text>
<text class="poster-close" bindtap="closePoster">×</text>
</view>
<canvas canvas-id="posterCanvas" class="poster-canvas"></canvas>
<button class="btn-primary save-poster-btn" bindtap="savePoster">
保存到相册
</button>
</view>
</view>

View File

@@ -1,284 +1,418 @@
/* pages/my/my.wxss */
/**
* Soul创业实验 - 我的页面样式
* 1:1还原Web版本UI
*/
.my-container {
padding: 32rpx 32rpx 160rpx;
background: linear-gradient(180deg, #000000 0%, #0a0a0a 100%);
.page {
min-height: 100vh;
background: #000000;
padding-bottom: 200rpx;
}
/* 用户卡片 */
/* ===== 导航栏 ===== */
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(40rpx);
border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
}
.nav-content {
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
}
.brand-color {
color: #00CED1;
}
.gold-color {
color: #FFD700;
}
.pink-color {
color: #E91E63;
}
.nav-placeholder {
width: 100%;
}
/* ===== 用户卡片 ===== */
.user-card {
padding: 40rpx 32rpx;
margin-bottom: 24rpx;
margin: 32rpx;
padding: 32rpx;
}
.card-gradient {
background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
border-radius: 32rpx;
border: 2rpx solid rgba(0, 206, 209, 0.2);
}
.user-header {
display: flex;
align-items: center;
gap: 24rpx;
margin-bottom: 32rpx;
}
.user-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 24rpx;
border: 4rpx solid rgba(255, 77, 79, 0.5);
.avatar {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
border: 4rpx solid #00CED1;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, transparent 100%);
flex-shrink: 0;
}
.avatar-empty {
border-style: dashed;
border-color: rgba(0, 206, 209, 0.5);
}
.avatar-icon {
font-size: 64rpx;
opacity: 0.3;
}
.avatar-text {
font-size: 48rpx;
font-weight: 700;
color: #00CED1;
}
.user-info {
flex: 1;
}
.login-btn {
font-size: 36rpx;
font-weight: 600;
color: #00CED1;
}
.user-subtitle {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.3);
display: block;
margin-top: 4rpx;
}
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
display: block;
}
.user-id {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
font-size: 26rpx;
color: rgba(255, 255, 255, 0.3);
display: block;
margin-top: 4rpx;
}
.vip-badge {
.user-badge {
padding: 8rpx 20rpx;
background: linear-gradient(135deg, #FF4D4F 0%, #FF7875 100%);
border-radius: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 77, 79, 0.4);
}
.vip-text {
font-size: 24rpx;
color: #ffffff;
font-weight: 600;
}
/* 用户统计 */
.user-stats {
background: rgba(0, 206, 209, 0.2);
border: 2rpx solid rgba(0, 206, 209, 0.3);
border-radius: 32rpx;
display: flex;
align-items: center;
gap: 8rpx;
}
.badge-icon {
font-size: 22rpx;
}
.badge-text {
font-size: 22rpx;
color: #00CED1;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
padding-top: 32rpx;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
}
.stat-item {
flex: 1;
text-align: center;
padding: 16rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
}
.stat-value {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
margin-bottom: 8rpx;
display: block;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
}
.stat-divider {
width: 2rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.1);
}
/* 分销中心 */
.referral-section {
margin-bottom: 24rpx;
/* ===== 收益卡片 ===== */
.earnings-card {
margin: 32rpx;
padding: 32rpx;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
border-radius: 32rpx;
border: 2rpx solid rgba(0, 206, 209, 0.2);
position: relative;
overflow: hidden;
}
.section-header {
.earnings-bg {
position: absolute;
top: 0;
right: 0;
width: 256rpx;
height: 256rpx;
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, transparent 100%);
border-radius: 50%;
transform: translate(50%, -50%);
}
.earnings-content {
position: relative;
z-index: 1;
}
.earnings-header {
display: flex;
justify-content: space-between;
align-items: center;
justify-content: space-between;
margin-bottom: 32rpx;
}
.section-title {
.earnings-title {
display: flex;
align-items: center;
gap: 12rpx;
gap: 16rpx;
}
.title-icon {
font-size: 32rpx;
font-size: 36rpx;
}
.title-text {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
}
.referral-status {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 20rpx;
background: rgba(255, 77, 79, 0.2);
border-radius: 20rpx;
}
.status-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.7);
}
.status-value {
font-size: 28rpx;
font-weight: 700;
color: #FF4D4F;
font-weight: 500;
color: #ffffff;
}
/* 收益概览 */
.earnings-overview {
padding: 32rpx;
background: rgba(255, 77, 79, 0.1);
border-radius: 24rpx;
border: 2rpx solid rgba(255, 77, 79, 0.2);
margin-bottom: 24rpx;
}
.earnings-main {
text-align: center;
margin-bottom: 24rpx;
}
.earnings-label {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 12rpx;
}
.earnings-amount {
font-size: 64rpx;
font-weight: 700;
color: #FF4D4F;
letter-spacing: 2rpx;
}
.earnings-sub {
.earnings-link {
display: flex;
justify-content: space-around;
padding-top: 24rpx;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
}
.sub-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.sub-label {
.link-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
color: #00CED1;
}
.sub-value {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
.link-arrow {
font-size: 24rpx;
color: #00CED1;
}
/* 快速操作 */
.referral-actions {
.earnings-data {
display: flex;
gap: 16rpx;
align-items: flex-end;
gap: 48rpx;
margin-bottom: 32rpx;
}
.action-btn {
flex: 1;
font-size: 28rpx;
padding: 24rpx;
}
/* 推广数据 */
.referral-stats {
.data-item {
display: flex;
justify-content: space-around;
padding: 32rpx 0;
border-top: 2rpx solid rgba(255, 255, 255, 0.1);
border-bottom: 2rpx solid rgba(255, 255, 255, 0.1);
margin-bottom: 24rpx;
flex-direction: column;
}
.referral-stat-item {
text-align: center;
}
.stat-number {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
.data-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 8rpx;
}
.stat-name {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
/* 邀请码 */
.invite-code-section {
padding: 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
}
.invite-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 12rpx;
}
.invite-code-box {
display: flex;
align-items: center;
justify-content: space-between;
}
.invite-code {
.data-value {
font-size: 40rpx;
font-weight: 700;
color: #FF4D4F;
letter-spacing: 4rpx;
font-family: 'Courier New', monospace;
color: #ffffff;
}
.copy-btn {
padding: 12rpx 32rpx;
background: rgba(255, 77, 79, 0.2);
color: #FF7875;
border: none;
border-radius: 12rpx;
font-size: 26rpx;
.gold-gradient {
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 56rpx;
}
.earnings-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
padding: 24rpx;
background: linear-gradient(135deg, rgba(255, 215, 0, 0.8) 0%, rgba(255, 165, 0, 0.8) 100%);
border-radius: 24rpx;
font-weight: 600;
}
/* 功能菜单 */
.menu-section {
margin-bottom: 24rpx;
.btn-icon {
font-size: 28rpx;
}
.menu-group {
.btn-text {
font-size: 28rpx;
color: #000000;
}
/* ===== 推广入口 ===== */
.referral-card {
display: flex;
align-items: center;
justify-content: space-between;
margin: 32rpx;
padding: 32rpx;
background: linear-gradient(90deg, rgba(255, 215, 0, 0.1) 0%, #1c1c1e 100%);
border: 2rpx solid rgba(255, 215, 0, 0.2);
border-radius: 32rpx;
}
.referral-left {
display: flex;
align-items: center;
gap: 24rpx;
}
.referral-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
}
.gold-bg {
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
}
.referral-info {
display: flex;
flex-direction: column;
}
.referral-title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.referral-desc {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.4);
margin-top: 4rpx;
}
.referral-btn {
padding: 16rpx 32rpx;
background: rgba(255, 215, 0, 0.2);
color: #FFD700;
font-size: 26rpx;
font-weight: 500;
border-radius: 16rpx;
}
/* ===== Tab切换 ===== */
.tab-bar-custom {
display: flex;
gap: 16rpx;
margin: 32rpx;
}
.tab-item {
flex: 1;
padding: 20rpx;
text-align: center;
font-size: 28rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.6);
background: #1c1c1e;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
}
.tab-active {
background: rgba(0, 206, 209, 0.2);
color: #00CED1;
border: 2rpx solid rgba(0, 206, 209, 0.3);
}
.tab-icon {
font-size: 28rpx;
}
/* ===== Tab内容 ===== */
.tab-content {
padding: 0 32rpx;
width: 100%;
box-sizing: border-box;
}
/* ===== 菜单卡片 ===== */
.card {
background: #1c1c1e;
border-radius: 24rpx;
border: 2rpx solid rgba(255, 255, 255, 0.05);
overflow: hidden;
margin: 0 0 24rpx 0;
width: 100%;
box-sizing: border-box;
}
.menu-card {
padding: 0;
margin-bottom: 24rpx;
width: 100%;
box-sizing: border-box;
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
transition: background 0.3s;
justify-content: space-between;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:active {
@@ -288,15 +422,30 @@
.menu-left {
display: flex;
align-items: center;
gap: 20rpx;
gap: 24rpx;
}
.menu-icon {
font-size: 40rpx;
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
background: rgba(255, 255, 255, 0.1);
}
.menu-text {
font-size: 30rpx;
.icon-brand {
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
}
.icon-gray {
background: rgba(128, 128, 128, 0.2);
}
.menu-title {
font-size: 28rpx;
color: #ffffff;
}
@@ -306,17 +455,14 @@
gap: 16rpx;
}
.menu-count {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.4);
}
.menu-badge {
min-width: 32rpx;
height: 32rpx;
padding: 0 8rpx;
background: #FF4D4F;
border-radius: 16rpx;
font-size: 20rpx;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
font-weight: 500;
}
.menu-arrow {
@@ -324,99 +470,247 @@
color: rgba(255, 255, 255, 0.3);
}
.menu-divider {
height: 2rpx;
background: rgba(255, 255, 255, 0.05);
margin: 0 32rpx;
}
/* 退出登录 */
.logout-section {
padding: 0 32rpx;
margin-top: 48rpx;
}
.logout-btn {
width: 100%;
padding: 28rpx;
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.6);
border: 2rpx solid rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
font-size: 30rpx;
}
.logout-btn:active {
background: rgba(255, 255, 255, 0.08);
}
.bottom-space {
height: 40rpx;
}
/* 海报弹窗 */
.poster-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s;
}
.poster-content {
width: 90%;
max-width: 600rpx;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20rpx);
border-radius: 32rpx;
/* ===== 统计卡片 ===== */
.stats-card {
padding: 32rpx;
animation: slideUp 0.4s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(100rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.poster-header {
.card-title {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16rpx;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
margin-bottom: 24rpx;
}
.poster-title {
.stats-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24rpx;
}
.stat-box {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 24rpx;
}
.stat-icon {
font-size: 36rpx;
margin-bottom: 8rpx;
}
.stat-num {
font-size: 32rpx;
font-weight: 600;
font-weight: 700;
color: #ffffff;
}
.poster-close {
font-size: 56rpx;
color: rgba(255, 255, 255, 0.6);
line-height: 1;
.stat-text {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
margin-top: 4rpx;
}
.poster-canvas {
/* ===== 最近阅读 ===== */
.recent-card {
padding: 32rpx;
}
.recent-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.recent-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 24rpx;
}
.recent-left {
display: flex;
align-items: center;
gap: 24rpx;
}
.recent-index {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.3);
}
.recent-title {
font-size: 26rpx;
color: #ffffff;
}
.recent-btn {
font-size: 24rpx;
color: #00CED1;
}
/* ===== 空状态 ===== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding: 48rpx;
}
.empty-icon {
font-size: 64rpx;
opacity: 0.5;
margin-bottom: 16rpx;
}
.empty-text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.4);
}
.empty-btn {
margin-top: 16rpx;
font-size: 26rpx;
color: #00CED1;
}
/* ===== 匹配记录 ===== */
.match-card {
padding: 32rpx;
}
/* ===== 登录弹窗 ===== */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20rpx);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 48rpx;
}
.modal-content {
width: 100%;
height: 800rpx;
background: #ffffff;
border-radius: 16rpx;
max-width: 640rpx;
background: #1c1c1e;
border-radius: 32rpx;
padding: 48rpx;
position: relative;
}
.modal-close {
position: absolute;
top: 24rpx;
right: 24rpx;
width: 64rpx;
height: 64rpx;
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);
}
.login-icon {
font-size: 96rpx;
text-align: center;
display: block;
margin-bottom: 24rpx;
}
.save-poster-btn {
width: 100%;
.login-title {
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
text-align: center;
display: block;
margin-bottom: 16rpx;
}
.login-desc {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.6);
text-align: center;
display: block;
margin-bottom: 48rpx;
}
.btn-wechat {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
padding: 28rpx;
background: #07C160;
color: #ffffff;
font-size: 28rpx;
font-weight: 500;
border-radius: 24rpx;
margin-bottom: 24rpx;
border: none;
}
.btn-wechat::after {
border: none;
}
.btn-wechat-icon {
width: 40rpx;
height: 40rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
}
.btn-phone {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
padding: 28rpx;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-size: 28rpx;
border-radius: 24rpx;
border: 2rpx solid rgba(255, 255, 255, 0.1);
}
.btn-phone::after {
border: none;
}
.btn-phone-icon {
font-size: 32rpx;
}
.login-notice {
display: block;
margin-top: 32rpx;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.3);
text-align: center;
}
/* ===== 底部留白 ===== */
.bottom-space {
height: 40rpx;
}