fix:修复了很多不规范用词,更改了手机绑定校验逻辑,调整最大限制购买次数为200次。

This commit is contained in:
tsui110 2026-01-02 19:44:22 +08:00
parent 5dfb2c3ecb
commit b959e634d2
8 changed files with 186 additions and 57 deletions

View File

@ -258,6 +258,18 @@ export function modifyUser(user_id, data) {
return authRequest({ url: `/api/app/users/${user_id}`, method: 'PUT', data })
}
/**
* 获取用户资料信息新接口
* @returns {Promise} 用户资料信息 { id, nickname, avatar, mobile, balance, invite_code, inviter_id }
*/
export function getUserProfile() {
return authRequest({ url: '/api/app/users/profile', method: 'GET' })
}
/**
* 获取用户信息兼容旧接口
* @deprecated 建议使用 getUserProfile
*/
export function getUserInfo() {
const user_info = uni.getStorageSync('user_info')
if (user_info) return Promise.resolve(user_info)

View File

@ -41,7 +41,13 @@
<view class="action-row">
<view class="stepper" @tap.stop>
<text class="step-btn minus" @tap="updateCount(pkg.id, -1)">-</text>
<text class="step-val">{{ counts[pkg.id] || 1 }}</text>
<input
class="step-input"
type="number"
:value="counts[pkg.id] || 1"
@input="onInputCount(pkg.id, $event)"
@blur="onBlurCount(pkg.id)"
/>
<text class="step-btn plus" @tap="updateCount(pkg.id, 1)">+</text>
</view>
<button class="btn-buy" :loading="purchasingId === pkg.id" @tap.stop="handlePurchase(pkg)">
@ -75,11 +81,33 @@ const counts = ref({})
function updateCount(pkgId, delta) {
const current = counts.value[pkgId] || 1
const newVal = current + delta
if (newVal >= 1 && newVal <= 99) {
if (newVal >= 1 && newVal <= 200) {
counts.value[pkgId] = newVal
}
}
function onInputCount(pkgId, e) {
const val = parseInt(e.detail.value) || 1
//
if (val >= 1 && val <= 200) {
counts.value[pkgId] = val
} else if (val < 1) {
counts.value[pkgId] = 1
} else if (val > 200) {
counts.value[pkgId] = 200
}
}
function onBlurCount(pkgId) {
//
const current = counts.value[pkgId] || 1
if (current < 1) {
counts.value[pkgId] = 1
} else if (current > 200) {
counts.value[pkgId] = 200
}
}
watch(() => props.visible, (val) => {
if (val) {
fetchPackages()
@ -340,7 +368,7 @@ function handleClose() {
background: #F3F4F6;
border-radius: 12rpx;
padding: 2rpx;
.step-btn {
width: 44rpx;
height: 44rpx;
@ -349,18 +377,29 @@ function handleClose() {
font-size: 32rpx;
color: #4B5563;
font-weight: 300;
flex-shrink: 0;
}
.minus {
color: #9CA3AF;
}
.step-val {
width: 40rpx;
.step-input {
width: 60rpx;
height: 44rpx;
line-height: 44rpx;
text-align: center;
font-size: 26rpx;
font-weight: bold;
color: #1F2937;
background: transparent;
border: none;
padding: 0;
margin: 0;
&::placeholder {
color: #9CA3AF;
}
}
}
</style>

View File

@ -34,9 +34,9 @@
<view class="popup-body">
<!-- 次数卡选项有数据时显示 -->
<view v-if="gamePasses" class="game-pass-section">
<view
class="game-pass-option"
:class="{ active: useGamePass, disabled: gamePassRemaining <= 0 }"
<view
class="game-pass-option"
:class="{ active: useGamePass, disabled: gamePassRemaining <= 0 }"
@tap="gamePassRemaining > 0 ? toggleGamePass() : null"
>
<view class="game-pass-radio">
@ -44,11 +44,10 @@
<view v-else-if="gamePassRemaining <= 0" class="radio-disabled" />
</view>
<view class="game-pass-info">
<text class="game-pass-label" :class="{ 'text-disabled': gamePassRemaining <= 0 }">🎮 使用次数卡</text>
<text class="game-pass-count" v-if="gamePassRemaining > 0">剩余 {{ gamePassRemaining }} </text>
<text class="game-pass-label" :class="{ 'text-disabled': gamePassRemaining <= 0 }">剩余次数</text>
<text class="game-pass-count" v-if="gamePassRemaining > 0">{{ gamePassRemaining }} </text>
<text class="game-pass-count text-disabled" v-else>暂无可用次数卡</text>
</view>
<text v-if="gamePassRemaining > 0" class="game-pass-free">免费畅玩</text>
</view>
<view v-if="!useGamePass" class="divider-line">
<text class="divider-text">或选择其他支付方式</text>
@ -841,16 +840,16 @@ function handleConfirm() {
&.active {
background: linear-gradient(135deg, #10B981, #059669);
border-color: #059669;
.game-pass-label, .game-pass-count, .game-pass-free {
.game-pass-label, .game-pass-count {
color: #FFFFFF;
}
.game-pass-radio {
background: #FFFFFF;
border-color: #FFFFFF;
}
.radio-checked {
color: #10B981;
}
@ -902,15 +901,6 @@ function handleConfirm() {
margin-top: 4rpx;
}
.game-pass-free {
font-size: $font-sm;
font-weight: 600;
color: #10B981;
padding: 6rpx 16rpx;
background: rgba(16, 185, 129, 0.1);
border-radius: $radius-md;
}
.divider-line {
display: flex;
align-items: center;

View File

@ -215,8 +215,8 @@ async function fetchProductMeta(productId) {
}
onShow(() => {
//
if (!checkPhoneBound()) return
//
if (!checkPhoneBoundSync()) return
// Check for external tab switch request
try {

View File

@ -180,8 +180,8 @@ export default {
}
},
onLoad() {
//
if (!checkPhoneBound()) return
//
if (!checkPhoneBoundSync()) return
// 200ms Token/Session
//

View File

@ -468,9 +468,9 @@
<script>
import {
getUserInfo, getUserStats, getPointsBalance, getUserPoints, getUserCoupons, getItemCards,
getUserTasks, getTaskProgress, getInviteRecords, modifyUser
getUserTasks, getTaskProgress, getInviteRecords, modifyUser, getUserProfile
} from '../../api/appUser.js'
import { checkPhoneBound } from '../../utils/checkPhone.js'
import { checkPhoneBoundSync } from '../../utils/checkPhone.js'
export default {
data() {
@ -537,8 +537,8 @@ export default {
}
},
onShow() {
//
if (!checkPhoneBound()) return
//
if (!checkPhoneBoundSync()) return
this.loadUserInfo()
},
@ -739,10 +739,49 @@ export default {
return
}
try {
// 使 profile API
const profile = await getUserProfile()
if (profile) {
console.log('[Mine] 从 profile API 获取用户信息:', profile)
this.userId = profile.id
this.nickname = profile.nickname
this.avatar = profile.avatar
this.title = profile.title || ''
this.inviteCode = profile.invite_code
this.pointsBalance = this.normalizePointsBalance(profile.balance)
this.mobile = profile.mobile || ''
//
const cachedUser = uni.getStorageSync('user_info') || {}
cachedUser.id = profile.id
cachedUser.nickname = profile.nickname
cachedUser.avatar = profile.avatar
cachedUser.title = profile.title
cachedUser.invite_code = profile.invite_code
cachedUser.mobile = profile.mobile
cachedUser.points_balance = this.pointsBalance
uni.setStorageSync('user_info', cachedUser)
uni.setStorageSync('user_id', profile.id)
//
if (profile.mobile) {
uni.setStorageSync('phone_number', profile.mobile)
}
// Load stats
const s = await getUserStats(profile.id)
if (s) this.stats = { ...this.stats, ...s }
return
}
//
console.log('[Mine] profile API 无数据,使用降级逻辑')
//
const cachedUser = uni.getStorageSync('user_info')
const cachedUserId = uni.getStorageSync('user_id')
if (cachedUser) {
this.userId = cachedUser.id || cachedUserId
this.nickname = cachedUser.nickname
@ -754,7 +793,7 @@ export default {
} else if (cachedUserId) {
this.userId = cachedUserId
}
if (this.userId) {
try {
const balanceRes = await getPointsBalance(this.userId)
@ -782,14 +821,14 @@ export default {
this.mobile = res.mobile || res.phone || res.phone_number || ''
uni.setStorageSync('user_info', res)
uni.setStorageSync('user_id', res.id)
// Load stats
const s = await getUserStats(res.id)
if(s) this.stats = { ...this.stats, ...s }
}
}
} catch (e) {
console.error(e)
console.error('[Mine] loadUserInfo 错误:', e)
// If 401, maybe clear token
}
},

View File

@ -414,8 +414,8 @@ async function onRedeemTap(item) {
}
onShow(() => {
//
if (!checkPhoneBound()) return
//
if (!checkPhoneBoundSync()) return
const token = uni.getStorageSync('token')
if (token) {

View File

@ -1,31 +1,80 @@
import { getUserProfile } from '../api/appUser'
/**
* 检查手机号绑定状态
* 如果未绑定手机号,则跳转到登录页面进行绑定
* @returns {Promise<boolean>} 是否已绑定手机号
*/
export async function checkPhoneBound() {
try {
// 调用新的用户资料接口
const profile = await getUserProfile()
console.log('[checkPhoneBound] 用户资料:', profile)
// 检查是否已绑定手机号
const mobile = profile?.mobile
if (mobile) {
console.log('[checkPhoneBound] 已检测到手机号,允许通过:', mobile)
// 缓存手机号
uni.setStorageSync('phone_number', mobile)
return true
}
// 未绑定手机号,显示提示并跳转
console.warn('[checkPhoneBound] 未检测到手机号,提示用户绑定')
uni.showModal({
title: '需要绑定手机号',
content: '为了账号安全,请先绑定手机号',
showCancel: false,
confirmText: '去绑定',
success: () => {
uni.navigateTo({ url: '/pages/login/index?mode=sms' })
}
})
return false
} catch (err) {
console.error('[checkPhoneBound] 获取用户信息失败:', err)
// 请求失败时,降级检查本地缓存
const phoneNumber = uni.getStorageSync('phone_number') || ''
console.log('[checkPhoneBound] 降级检查本地缓存:', phoneNumber ? phoneNumber : '未找到')
if (phoneNumber) {
return true
}
// 本地也没有,提示重新登录
uni.showModal({
title: '提示',
content: '获取用户信息失败,请重新登录',
showCancel: false,
confirmText: '去登录',
success: () => {
uni.navigateTo({ url: '/pages/login/index' })
}
})
return false
}
}
/**
* 同步检查手机号绑定状态仅检查本地缓存
* @returns {boolean} 是否已绑定手机号
*/
export function checkPhoneBound() {
// 直接检查 phone_number 缓存中是否有手机号
export function checkPhoneBoundSync() {
const phoneNumber = uni.getStorageSync('phone_number') || ''
console.log('[checkPhoneBound] 检查 phone_number 缓存:', phoneNumber ? phoneNumber : '未找到')
console.log('[checkPhoneBoundSync] 检查 phone_number 缓存:', phoneNumber ? phoneNumber : '未找到')
// 如果已绑定手机号,直接返回
if (phoneNumber) {
console.log('[checkPhoneBound] 已检测到手机号,允许通过:', phoneNumber)
console.log('[checkPhoneBoundSync] 已检测到手机号,允许通过:', phoneNumber)
return true
}
// 未绑定手机号,显示提示并跳转
console.warn('[checkPhoneBound] 未检测到手机号,提示用户绑定')
uni.showModal({
title: '需要绑定手机号',
content: '为了账号安全,请先绑定手机号',
showCancel: false,
confirmText: '去绑定',
success: () => {
uni.navigateTo({ url: '/pages/login/index?mode=sms' })
}
})
console.warn('[checkPhoneBoundSync] 未检测到手机号')
return false
}