体现UI修改.

This commit is contained in:
18631081161 2025-12-24 20:40:27 +08:00
parent 1b0cc39255
commit ec0eb17fb3
11 changed files with 283 additions and 41 deletions

View File

@ -143,7 +143,7 @@
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="收款信息" :span="2">
{{ formatPaymentDetailsLong(withdrawalDetails.paymentMethod, withdrawalDetails.paymentDetails) }}
<span class="payment-details-text">{{ formatPaymentDetailsLong(withdrawalDetails.paymentMethod, withdrawalDetails.paymentDetails) }}</span>
</el-descriptions-item>
<el-descriptions-item label="收款码" :span="2" v-if="getQrcodeUrl(withdrawalDetails.paymentDetails)">
<el-image
@ -180,20 +180,14 @@
<el-descriptions-item label="用户昵称">
{{ withdrawalDetails.user.nickname || '-' }}
</el-descriptions-item>
<el-descriptions-item label="真实姓名">
{{ withdrawalDetails.user.realName || '-' }}
<el-descriptions-item label="UID">
{{ withdrawalDetails.user.uid || '-' }}
</el-descriptions-item>
<el-descriptions-item label="手机号">
{{ withdrawalDetails.user.phone || '-' }}
<el-descriptions-item label="当前人民币余额">
<span class="balance">¥{{ withdrawalDetails.user.balance || '0.00' }}</span>
</el-descriptions-item>
<el-descriptions-item label="WhatsApp">
{{ withdrawalDetails.user.whatsapp || '-' }}
</el-descriptions-item>
<el-descriptions-item label="微信号">
{{ withdrawalDetails.user.wechatId || '-' }}
</el-descriptions-item>
<el-descriptions-item label="当前余额">
<span class="balance">¥{{ withdrawalDetails.user.balance }}</span>
<el-descriptions-item label="当前比索余额">
<span class="balance">{{ withdrawalDetails.user.balancePeso || '0.00' }}</span>
</el-descriptions-item>
</el-descriptions>
</template>
@ -617,15 +611,12 @@ function formatPaymentDetailsLong(method, details) {
return `支付宝账号: ${parsed.alipayAccount || parsed.account || '-'}`
case 'bank':
// : 使 bankCardNumber/cardholderName, 使 bankAccount/accountName
const bankName = parsed.bankName || '银行'
const bankName = parsed.bankName || '-'
const account = parsed.bankCardNumber || parsed.bankAccount || '-'
const name = parsed.cardholderName || parsed.accountName || '-'
const swiftCode = parsed.swiftCode
let result = `${bankName} | 账号: ${account} | 户名: ${name}`
if (swiftCode) {
result += ` | SWIFT: ${swiftCode}`
}
return result
const swiftCode = parsed.swiftCode || '-'
// 使
return `银行卡号:${account}\n持卡人${name}\n开户行${bankName}\nSwift号${swiftCode}`
default:
return JSON.stringify(parsed)
}
@ -723,6 +714,11 @@ onMounted(() => {
color: #f56c6c;
}
.payment-details-text {
white-space: pre-line;
line-height: 1.8;
}
.image-error {
display: flex;
flex-direction: column;

View File

@ -0,0 +1,65 @@
/**
* 为指定用户添加双币种可提现余额
* 用法: node src/scripts/addUserBalanceDual.js <uid> <amountRmb> <amountPeso>
* 示例: node src/scripts/addUserBalanceDual.js 704963 20 20
*/
require('dotenv').config();
const { sequelize } = require('../config/database');
const User = require('../models/User');
async function addUserBalanceDual(uid, amountRmb, amountPeso) {
try {
console.log(`正在查找用户 UID: ${uid}...`);
const user = await User.findOne({ where: { uid } });
if (!user) {
console.error(`错误: 未找到 UID 为 ${uid} 的用户`);
process.exit(1);
}
console.log(`找到用户: ${user.nickname || user.realName || '未设置昵称'}`);
console.log(`当前人民币余额: ¥${user.balance}`);
console.log(`当前比索余额: ₱${user.balancePeso || 0}`);
const oldBalanceRmb = parseFloat(user.balance) || 0;
const oldBalancePeso = parseFloat(user.balancePeso) || 0;
const newBalanceRmb = oldBalanceRmb + parseFloat(amountRmb);
const newBalancePeso = oldBalancePeso + parseFloat(amountPeso);
await user.update({
balance: newBalanceRmb,
balancePeso: newBalancePeso
});
console.log(`\n✅ 余额更新成功!`);
console.log(`人民币余额:`);
console.log(` 原余额: ¥${oldBalanceRmb}`);
console.log(` 增加: +¥${amountRmb}`);
console.log(` 新余额: ¥${newBalanceRmb}`);
console.log(`比索余额:`);
console.log(` 原余额: ₱${oldBalancePeso}`);
console.log(` 增加: +₱${amountPeso}`);
console.log(` 新余额: ₱${newBalancePeso}`);
} catch (error) {
console.error('操作失败:', error.message);
process.exit(1);
} finally {
await sequelize.close();
}
}
// 从命令行参数获取
const uid = process.argv[2];
const amountRmb = parseFloat(process.argv[3]) || 0;
const amountPeso = parseFloat(process.argv[4]) || 0;
if (!uid) {
console.log('用法: node src/scripts/addUserBalanceDual.js <uid> <amountRmb> <amountPeso>');
console.log('示例: node src/scripts/addUserBalanceDual.js 704963 20 20');
process.exit(1);
}
addUserBalanceDual(uid, amountRmb, amountPeso);

View File

@ -268,7 +268,7 @@ const getWithdrawalDetails = async (withdrawalId) => {
{
model: User,
as: 'user',
attributes: ['id', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId', 'balance'],
attributes: ['id', 'uid', 'nickname', 'balance', 'balancePeso'],
},
],
});
@ -292,12 +292,10 @@ const getWithdrawalDetails = async (withdrawalId) => {
updatedAt: withdrawal.updatedAt,
user: withdrawal.user ? {
id: withdrawal.user.id,
uid: withdrawal.user.uid,
nickname: withdrawal.user.nickname,
realName: withdrawal.user.realName,
phone: withdrawal.user.phone,
whatsapp: withdrawal.user.whatsapp,
wechatId: withdrawal.user.wechatId,
balance: parseFloat(withdrawal.user.balance).toFixed(2),
balance: parseFloat(withdrawal.user.balance || 0).toFixed(2),
balancePeso: parseFloat(withdrawal.user.balancePeso || 0).toFixed(2),
} : null,
};
};

View File

@ -24,6 +24,19 @@ const generateInvitationCode = () => {
return code;
};
/**
* Generate random suffix for default nickname
* @returns {string} 4-character random suffix (lowercase letters and digits)
*/
const generateNicknameSuffix = () => {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let suffix = '';
for (let i = 0; i < 4; i++) {
suffix += chars.charAt(Math.floor(Math.random() * chars.length));
}
return suffix;
};
/**
* Authenticate user with WeChat
* @param {string} code - WeChat authorization code
@ -79,7 +92,7 @@ const wechatLogin = async (code, userInfo = {}, invitationCode = null, deviceInf
// Create new user
const userData = {
wechatOpenId: openId,
nickname: userInfo.nickname || 'User',
nickname: userInfo.nickname || `User_${generateNicknameSuffix()}`,
avatar: userInfo.avatar || defaultAvatar,
invitationCode: await generateUniqueInvitationCode(),
};

View File

@ -98,6 +98,23 @@ export default {
general: 'Common Features',
other: 'Other Features'
},
profile: {
title: 'Profile',
nickname: 'Nickname',
nicknamePlaceholder: 'Tap to get WeChat nickname',
realName: 'Real Name',
realNamePlaceholder: 'Enter real name',
phone: 'Phone',
phonePlaceholder: 'Enter phone number',
whatsapp: 'WhatsApp',
whatsappPlaceholder: 'Enter WhatsApp number',
wechatId: 'WeChat ID',
wechatIdPlaceholder: 'Enter WeChat ID',
avatar: 'Avatar',
avatarTip: 'Tap to get WeChat avatar',
saveSuccess: 'Saved successfully',
saveFailed: 'Save failed'
},
language: {
chinese: '中文',
english: 'English',
@ -244,6 +261,7 @@ If you have privacy questions, please contact us through the application.`
statusWaitingPeso: 'PHP Pending',
statusProcessing: 'Processing',
statusCompleted: 'Completed',
statusRejected: 'Rejected',
ruleTitle: 'Rules',
ruleContent: '1. Invite new users to register and complete first purchase\n2. Get cash reward for each successful invitation\n3. Rewards will be credited within 24 hours after payment\n4. Minimum withdrawal amount is 100 yuan',
qrcodeGenerated: 'QR Code Generated',

View File

@ -98,6 +98,23 @@ export default {
general: 'Funciones Comunes',
other: 'Otras Funciones'
},
profile: {
title: 'Perfil',
nickname: 'Apodo',
nicknamePlaceholder: 'Toca para obtener apodo de WeChat',
realName: 'Nombre Real',
realNamePlaceholder: 'Ingresa nombre real',
phone: 'Teléfono',
phonePlaceholder: 'Ingresa número de teléfono',
whatsapp: 'WhatsApp',
whatsappPlaceholder: 'Ingresa número de WhatsApp',
wechatId: 'ID de WeChat',
wechatIdPlaceholder: 'Ingresa ID de WeChat',
avatar: 'Avatar',
avatarTip: 'Toca para obtener avatar de WeChat',
saveSuccess: 'Guardado exitosamente',
saveFailed: 'Error al guardar'
},
language: {
chinese: '中文',
english: 'English',
@ -244,6 +261,7 @@ Si tiene preguntas sobre privacidad, contáctenos a través de la aplicación.`
statusWaitingPeso: 'Peso Pendiente',
statusProcessing: 'Procesando',
statusCompleted: 'Completado',
statusRejected: 'Rechazado',
ruleTitle: 'Reglas',
ruleContent: '1. Invita nuevos usuarios a registrarse y completar la primera compra\n2. Recibe recompensa en efectivo por cada invitación exitosa\n3. Las recompensas se acreditarán dentro de 24 horas después del pago\n4. El monto mínimo de retiro es de 100 yuan',
qrcodeGenerated: 'Código QR Generado',

View File

@ -101,7 +101,7 @@ export default {
profile: {
title: '个人资料',
nickname: '昵称',
nicknamePlaceholder: '请输入昵称',
nicknamePlaceholder: '点击获取微信昵称',
realName: '真实姓名',
realNamePlaceholder: '请输入真实姓名',
phone: '手机号',
@ -111,6 +111,7 @@ export default {
wechatId: '微信号',
wechatIdPlaceholder: '请输入微信号',
avatar: '头像',
avatarTip: '点击获取微信头像',
saveSuccess: '保存成功',
saveFailed: '保存失败'
},
@ -260,6 +261,7 @@ export default {
statusWaitingPeso: '比索待提现',
statusProcessing: '提现中',
statusCompleted: '已提现',
statusRejected: '已拒绝',
ruleTitle: '活动规则',
ruleContent: '1. 邀请新用户注册并完成首次消费\n2. 每成功邀请一位用户可获得现金奖励\n3. 奖励将在用户完成支付后24小时内到账\n4. 提现金额满100元即可申请提现',
qrcodeGenerated: '二维码已生成',

View File

@ -644,6 +644,11 @@ AppServer.prototype.UploadImage = async function(filePath) {
authToken = uni.getStorageSync("token") || "";
}
// 确保token带有Bearer前缀
if (authToken && !authToken.startsWith('Bearer ')) {
authToken = 'Bearer ' + authToken;
}
return new Promise((resolve, reject) => {
uni.uploadFile({
url: url,

View File

@ -350,6 +350,7 @@
invitationCode: ''
},
loading: false,
submitting: false, //
inviteRules: '' //
}
},
@ -804,6 +805,11 @@
})
},
async submitWithdraw() {
//
if (this.submitting) {
return
}
// /
if (this.paymentMethod !== 'bank' && !this.qrcodeImage) {
uni.showToast({
@ -824,6 +830,13 @@
}
}
//
this.submitting = true
uni.showLoading({
title: '提交中...',
mask: true
})
//
let paymentDetails = {}
if (this.paymentMethod === 'bank') {
@ -843,6 +856,8 @@
qrcodeUrl: uploadRes.data.url
}
} else {
uni.hideLoading()
this.submitting = false
uni.showToast({
title: '上传收款码失败',
icon: 'none'
@ -851,6 +866,8 @@
}
} catch (error) {
console.error('上传收款码失败:', error)
uni.hideLoading()
this.submitting = false
uni.showToast({
title: '上传收款码失败',
icon: 'none'
@ -868,6 +885,9 @@
paymentDetails: paymentDetails
})
uni.hideLoading()
this.submitting = false
// : { code: 0, message: "", data: {...} }
if (res && res.code === 0) {
uni.showToast({
@ -885,6 +905,8 @@
}
} catch (error) {
console.error('提现申请失败:', error)
uni.hideLoading()
this.submitting = false
uni.showToast({
title: '提现申请失败',
icon: 'none'

View File

@ -10,7 +10,7 @@
<!-- 已登录状态 -->
<view v-if="isLogin && user" class="row" style="align-items: center; margin-top: 35rpx;" @click="goToProfileEdit">
<image :src="user.avatar || '/static/default_avatar.png'"
<image :src="getAvatarUrl(user.avatar)"
style="width: 129.65rpx; height: 129.65rpx; border-radius: 50%; background-color: #E0E0E0; margin-left: 33rpx;"
mode="aspectFill"></image>
@ -25,7 +25,7 @@
<!-- 未登录状态 -->
<view v-else class="row" style="align-items: center; margin-top: 35rpx;" @click="handleLogout">
<image src="/static/default_avatar.png"
<image :src="defaultAvatar"
style="width: 129.65rpx; height: 129.65rpx; border-radius: 50%; background-color: #E0E0E0; margin-left: 33rpx;"
mode="aspectFill"></image>
@ -235,6 +235,7 @@
import { updateTabBarI18n } from '@/utils/tabbar-i18n.js'
import { requireAuth, getCurrentUser, isLoggedIn, logout, saveUserInfo } from '@/utils/auth.js'
import { AppServer } from '@/modules/api/AppServer.js'
import Config from '@/modules/Config.js'
export default {
data() {
@ -258,6 +259,17 @@
computed: {
currentLanguage() {
return this.$i18n.locale
},
/**
* 获取默认头像从后台配置
*/
defaultAvatar() {
const app = getApp()
if (app && app.globalData && app.globalData.config && app.globalData.config.default_avatar) {
return Config.getImageUrl(app.globalData.config.default_avatar)
}
//
return ''
}
},
onLoad() {
@ -274,6 +286,16 @@
}
},
methods: {
/**
* 获取头像URL如果没有头像则使用默认头像
*/
getAvatarUrl(avatar) {
if (avatar) {
return Config.getImageUrl(avatar)
}
return this.defaultAvatar
},
/**
* 检查登录状态
*/

View File

@ -21,15 +21,28 @@
<!-- 头像区域 -->
<view class="avatar-section">
<view class="avatar-wrapper">
<!-- 用户头像点击可更换 -->
<image :src="form.avatar || '/static/default-avatar.png'" class="avatar" mode="aspectFill"
@click="chooseAvatar">
</image>
<!-- 编辑图标 -->
<view class="avatar-edit-icon">
<text class="edit-icon"></text>
<!-- #ifdef MP-WEIXIN -->
<!-- 微信小程序使用 button 获取微信头像 -->
<button class="avatar-button" open-type="chooseAvatar" @chooseavatar="onChooseWechatAvatar">
<image :src="form.avatar || defaultAvatar" class="avatar" mode="aspectFill"></image>
<!-- 编辑图标 -->
<view class="avatar-edit-icon">
<text class="edit-icon"></text>
</view>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<!-- 非微信小程序点击选择图片 -->
<view @click="chooseAvatar">
<image :src="form.avatar || defaultAvatar" class="avatar" mode="aspectFill"></image>
<!-- 编辑图标 -->
<view class="avatar-edit-icon">
<text class="edit-icon"></text>
</view>
</view>
<!-- #endif -->
</view>
<text class="avatar-tip">{{ $t('profile.avatarTip') || '点击获取微信头像' }}</text>
</view>
<!-- 表单区域 -->
@ -37,8 +50,16 @@
<!-- 昵称输入 -->
<view class="form-item">
<text class="form-label">{{ $t('profile.nickname') || '昵称' }}</text>
<!-- #ifdef MP-WEIXIN -->
<!-- 微信小程序使用 type="nickname" 获取微信昵称 -->
<input v-model="form.nickname" class="form-input" type="nickname"
:placeholder="$t('profile.nicknamePlaceholder') || '点击获取微信昵称'" maxlength="20"
@blur="onNicknameBlur" />
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<input v-model="form.nickname" class="form-input"
:placeholder="$t('profile.nicknamePlaceholder') || '请输入昵称'" maxlength="20" />
<!-- #endif -->
</view>
<!-- 用户UID只读 -->
@ -81,6 +102,19 @@
}
},
computed: {
/**
* 获取默认头像从后台配置
*/
defaultAvatar() {
const app = getApp()
if (app && app.globalData && app.globalData.config && app.globalData.config.default_avatar) {
return Config.getImageUrl(app.globalData.config.default_avatar)
}
return ''
}
},
/**
* 页面加载时执行
*/
@ -151,6 +185,30 @@
})
},
/**
* 微信小程序获取微信头像回调
* @param {Object} e - 事件对象包含微信头像临时路径
*/
onChooseWechatAvatar(e) {
const { avatarUrl } = e.detail
if (avatarUrl) {
//
this.uploadAvatar(avatarUrl)
}
},
/**
* 微信昵称输入框失焦回调
* 用于获取微信昵称
* @param {Object} e - 事件对象
*/
onNicknameBlur(e) {
const nickname = e.detail.value
if (nickname) {
this.form.nickname = nickname
}
},
/**
* 上传头像到服务器
* @param {String} filePath - 本地图片路径
@ -316,8 +374,9 @@
/* 头像区域 */
.avatar-section {
display: flex;
justify-content: center;
padding: 60rpx 0;
flex-direction: column;
align-items: center;
padding: 60rpx 0 40rpx;
background-color: #fff;
margin-bottom: 20rpx;
@ -328,10 +387,27 @@
height: 160rpx;
}
/* 微信头像按钮 */
.avatar-button {
position: relative;
width: 160rpx;
height: 160rpx;
padding: 0;
margin: 0;
background: transparent;
border: none;
border-radius: 50%;
overflow: visible;
&::after {
border: none;
}
}
/* 头像图片 */
.avatar {
width: 100%;
height: 100%;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background-color: #e0e0e0;
}
@ -354,6 +430,13 @@
font-size: 20rpx;
}
}
/* 头像提示文字 */
.avatar-tip {
margin-top: 16rpx;
font-size: 24rpx;
color: #999;
}
}
/* 表单区域 */