PDF需求全面修复 - v1.15

## 后端
1. 数据概览改为从API获取真实用户/订单数
2. 提现API增加容错和withdrawals表自动创建

## 小程序
1. 设置页:去掉支付宝,微信号直接输入
2. 我的页面:优先显示微信号
3. 找伙伴-资源对接:新增三项填写(能帮到什么/需要什么/擅长什么)

## 部署配置
- 更新为小型宝塔 42.194.232.22
This commit is contained in:
卡若
2026-01-29 15:50:45 +08:00
parent 174253584f
commit 132743ce34
10 changed files with 135 additions and 49 deletions

View File

@@ -3,22 +3,39 @@
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { useStore } from "@/lib/store"
import { Users, BookOpen, ShoppingBag, TrendingUp, RefreshCw, ChevronRight } from "lucide-react" import { Users, BookOpen, ShoppingBag, TrendingUp, RefreshCw, ChevronRight } from "lucide-react"
export default function AdminDashboard() { export default function AdminDashboard() {
const router = useRouter() const router = useRouter()
const { getAllUsers, getAllPurchases } = useStore()
const [mounted, setMounted] = useState(false) const [mounted, setMounted] = useState(false)
const [users, setUsers] = useState<any[]>([]) const [users, setUsers] = useState<any[]>([])
const [purchases, setPurchases] = useState<any[]>([]) const [purchases, setPurchases] = useState<any[]>([])
// 从API获取数据
async function loadData() {
try {
// 获取用户数据
const usersRes = await fetch('/api/db/users')
const usersData = await usersRes.json()
if (usersData.success && usersData.users) {
setUsers(usersData.users)
}
// 获取订单数据
const ordersRes = await fetch('/api/orders')
const ordersData = await ordersRes.json()
if (ordersData.success && ordersData.orders) {
setPurchases(ordersData.orders)
}
} catch (e) {
console.log('加载数据失败', e)
}
}
useEffect(() => { useEffect(() => {
setMounted(true) setMounted(true)
// 客户端加载数据 loadData()
setUsers(getAllUsers()) }, [])
setPurchases(getAllPurchases())
}, [getAllUsers, getAllPurchases])
// 防止Hydration错误服务端渲染时显示加载状态 // 防止Hydration错误服务端渲染时显示加载状态
if (!mounted) { if (!mounted) {

View File

@@ -54,6 +54,11 @@ Page({
joinError: '', joinError: '',
needBindFirst: false, needBindFirst: false,
// 资源对接表单
canHelp: '',
needHelp: '',
goodAt: '',
// 解锁弹窗 // 解锁弹窗
showUnlockModal: false, showUnlockModal: false,
@@ -417,6 +422,17 @@ Page({
joinError: '' 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) { onWechatInput(e) {

View File

@@ -207,7 +207,25 @@
</view> </view>
</view> </view>
<!-- 输入区域 --> <!-- 资源对接专用输入 -->
<block wx:if="{{joinType === 'investor'}}">
<view class="resource-form">
<view class="form-item">
<text class="form-label">我能帮到什么</text>
<input class="form-input-new" placeholder="例如:私域运营、品牌策划..." value="{{canHelp}}" bindinput="onCanHelpInput"/>
</view>
<view class="form-item">
<text class="form-label">我需要什么帮助</text>
<input class="form-input-new" placeholder="例如:流量、技术支持..." value="{{needHelp}}" bindinput="onNeedHelpInput"/>
</view>
<view class="form-item">
<text class="form-label">我擅长什么</text>
<input class="form-input-new" placeholder="例如:电商运营、内容创作..." value="{{goodAt}}" bindinput="onGoodAtInput"/>
</view>
</view>
</block>
<!-- 联系方式输入区域 -->
<view class="input-area"> <view class="input-area">
<view class="input-wrapper"> <view class="input-wrapper">
<text class="input-prefix">{{contactType === 'phone' ? '+86' : '@'}}</text> <text class="input-prefix">{{contactType === 'phone' ? '+86' : '@'}}</text>

View File

@@ -1175,3 +1175,28 @@
border-radius: 40rpx; border-radius: 40rpx;
border: 1rpx solid rgba(255, 255, 255, 0.2); 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 userId = userInfo.id || ''
const userIdShort = userId.length > 20 ? userId.slice(0, 10) + '...' + userId.slice(-6) : userId const userIdShort = userId.length > 20 ? userId.slice(0, 10) + '...' + userId.slice(-6) : userId
// 获取微信号(优先显示)
const userWechat = wx.getStorageSync('user_wechat') || userInfo.wechat || ''
this.setData({ this.setData({
isLoggedIn: true, isLoggedIn: true,
userInfo, userInfo,
userIdShort, userIdShort,
userWechat,
purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0), purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0),
referralCount: userInfo.referralCount || 0, referralCount: userInfo.referralCount || 0,
earnings: userInfo.earnings || 0, earnings: userInfo.earnings || 0,

View File

@@ -42,7 +42,7 @@
<text class="edit-icon-small">✎</text> <text class="edit-icon-small">✎</text>
</view> </view>
<view class="user-id-row" bindtap="copyUserId"> <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> <text class="copy-icon">📋</text>
</view> </view>
</view> </view>

View File

@@ -156,22 +156,37 @@ Page({
}) })
}, },
// 绑定微信号 // 微信号输入
bindWechat() { onWechatInput(e) {
this.setData({ this.setData({ wechatId: e.detail.value })
showBindModal: true,
bindType: 'wechat',
bindValue: ''
})
}, },
// 绑定支付宝 // 保存微信号
bindAlipay() { async saveWechat() {
this.setData({ const { wechatId } = this.data
showBindModal: true, if (!wechatId || wechatId.length < 6) return
bindType: 'alipay',
bindValue: '' 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)
}
}, },
// 输入绑定值 // 输入绑定值

View File

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

View File

@@ -25,9 +25,10 @@
.bind-icon.phone-icon { background: rgba(0,206,209,0.2); } .bind-icon.phone-icon { background: rgba(0,206,209,0.2); }
.bind-icon.wechat-icon { background: rgba(158,158,158,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-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-label { font-size: 28rpx; color: #fff; font-weight: 500; }
.bind-value { font-size: 24rpx; color: rgba(255,255,255,0.5); } .bind-value { font-size: 24rpx; color: rgba(255,255,255,0.5); }
.bind-input { font-size: 24rpx; color: #00CED1; background: transparent; padding: 8rpx 0; }
.bind-right { display: flex; align-items: center; } .bind-right { display: flex; align-items: center; }
.bind-check { color: #00CED1; font-size: 32rpx; } .bind-check { color: #00CED1; font-size: 32rpx; }
.bind-btn { color: #00CED1; font-size: 26rpx; } .bind-btn { color: #00CED1; font-size: 26rpx; }

Binary file not shown.