consumer模块完成度95%,准备部署消费者端测试

This commit is contained in:
cyh666666
2026-03-05 08:45:00 +08:00
parent cceb556c62
commit 7f7f723d93
1043 changed files with 53958 additions and 3445 deletions

View File

@@ -1,82 +1,82 @@
<!-- 钱包页面 -->
<!-- 閽卞寘椤甸潰 -->
<template>
<view class="wallet-page">
<!-- 顶部栏 -->
<!-- 椤堕儴鏍?-->
<!--<view class="wallet-header">
<text class="back-btn" @click="goBack"></text>
<text class="back-btn" @click="goBack">鈥?/text>
</view>-->
<scroll-view class="wallet-content" scroll-y>
<!-- 余额概览 -->
<!-- 浣欓姒傝 -->
<view class="balance-overview">
<text class="balance-label">账户余额</text>
<text class="balance-value">¥{{ balance.toFixed(2) }}</text>
<text class="balance-label">璐︽埛浣欓</text>
<text class="balance-value">{{ balance.toFixed(2) }}</text>
<view class="balance-actions">
<button class="action-btn recharge" @click="recharge">充值</button>
<button class="action-btn withdraw" @click="withdraw">提现</button>
<button class="action-btn recharge" @click="recharge">鍏呭€?/button>
<button class="action-btn withdraw" @click="withdraw">鎻愮幇</button>
</view>
</view>
<!-- 资产统计 -->
<!-- 璧勪骇缁熻 -->
<view class="assets-stats">
<view class="stat-item">
<text class="stat-label">累计充值</text>
<text class="stat-value">¥{{ stats.totalRecharge.toFixed(2) }}</text>
<text class="stat-label">绱鍏呭€?/text>
<text class="stat-value">{{ stats.totalRecharge.toFixed(2) }}</text>
</view>
<view class="stat-item">
<text class="stat-label">累计消费</text>
<text class="stat-value">¥{{ stats.totalConsume.toFixed(2) }}</text>
<text class="stat-label">绱娑堣垂</text>
<text class="stat-value">{{ stats.totalConsume.toFixed(2) }}</text>
</view>
<view class="stat-item">
<text class="stat-label">累计提现</text>
<text class="stat-value">¥{{ stats.totalWithdraw.toFixed(2) }}</text>
<text class="stat-label">绱鎻愮幇</text>
<text class="stat-value">{{ stats.totalWithdraw.toFixed(2) }}</text>
</view>
</view>
<!-- 快捷功能 -->
<!-- 蹇嵎鍔熻兘 -->
<view class="quick-actions">
<view class="action-grid">
<view class="action-item" @click="goToCoupons">
<text class="action-icon">🎫</text>
<text class="action-text">优惠券</text>
<text class="action-icon">馃帿</text>
<text class="action-text">浼樻儬鍒?/text>
</view>
<view class="action-item" @click="goToRedPackets">
<text class="action-icon">🧧</text>
<text class="action-text">红包</text>
<text class="action-icon">馃Ё</text>
<text class="action-text">绾㈠寘</text>
</view>
<view class="action-item" @click="goToPoints">
<text class="action-icon">⭐</text>
<text class="action-text">积分</text>
<text class="action-icon">猸?/text>
<text class="action-text">绉垎</text>
</view>
<view class="action-item" @click="goToBankCards">
<text class="action-icon">💳</text>
<text class="action-text">银行卡</text>
<text class="action-icon">馃挸</text>
<text class="action-text">閾惰鍗?/text>
</view>
</view>
</view>
<!-- 交易记录 -->
<!-- 浜ゆ槗璁板綍 -->
<view class="transactions-section">
<view class="section-header">
<text class="section-title">交易记录</text>
<text class="section-title">浜ゆ槗璁板綍</text>
<view class="filter-tabs">
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
@click="changeFilter('all')">全部</text>
@click="changeFilter('all')">鍏ㄩ儴</text>
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
@click="changeFilter('income')">收入</text>
@click="changeFilter('income')">鏀跺叆</text>
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
@click="changeFilter('expense')">支出</text>
@click="changeFilter('expense')">鏀嚭</text>
</view>
</view>
<!-- 空状态 -->
<!-- 绌虹姸鎬?-->
<view v-if="transactions.length === 0 && !isLoading" class="empty-transactions">
<text class="empty-icon">💰</text>
<text class="empty-text">暂无交易记录</text>
<text class="empty-subtext">快去使用钱包功能吧</text>
<text class="empty-icon">馃挵</text>
<text class="empty-text">鏆傛棤浜ゆ槗璁板綍</text>
<text class="empty-subtext">蹇幓浣跨敤閽卞寘鍔熻兘鍚?/text>
</view>
<!-- 交易列表 -->
<!-- 浜ゆ槗鍒楄〃 -->
<view class="transactions-list">
<view v-for="transaction in transactions"
:key="transaction.id"
@@ -92,47 +92,47 @@
<view class="transaction-right">
<text :class="['transaction-amount',
{ income: transaction.amount > 0, expense: transaction.amount < 0 }]">
{{ transaction.amount > 0 ? '+' : '' }}¥{{ Math.abs(transaction.amount).toFixed(2) }}
{{ transaction.amount > 0 ? '+' : '' }}{{ Math.abs(transaction.amount).toFixed(2) }}
</text>
<text class="transaction-balance">余额: ¥{{ transaction.current_balance.toFixed(2) }}</text>
<text class="transaction-balance">浣欓: {{ transaction.current_balance.toFixed(2) }}</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<!-- 鍔犺浇鏇村 -->
<view v-if="isLoading" class="loading-more">
<text class="loading-text">加载中...</text>
<text class="loading-text">鍔犺浇涓?..</text>
</view>
<view v-if="!hasMore && transactions.length > 0" class="no-more">
<text class="no-more-text">没有更多记录了</text>
<text class="no-more-text">娌℃湁鏇村璁板綍浜?/text>
</view>
</view>
<!-- 安全提示 -->
<!-- 瀹夊叏鎻愮ず -->
<view class="security-tips">
<text class="tip-title">安全提示</text>
<text class="tip-item">1. 请妥善保管您的支付密码</text>
<text class="tip-item">2. 不要向他人透露您的账户信息</text>
<text class="tip-item">3. 定期修改密码以确保账户安全</text>
<text class="tip-title">瀹夊叏鎻愮ず</text>
<text class="tip-item">1. 璇峰Ε鍠勪繚绠℃偍鐨勬敮浠樺瘑鐮?/text>
<text class="tip-item">2. 涓嶈鍚戜粬浜洪€忛湶鎮ㄧ殑璐︽埛淇℃伅</text>
<text class="tip-item">3. 瀹氭湡淇敼瀵嗙爜浠ョ‘淇濊处鎴峰畨鍏?/text>
</view>
</scroll-view>
<!-- 充值弹窗 -->
<!-- 鍏呭€煎脊绐?-->
<view v-if="showRechargePopup" class="recharge-popup">
<view class="popup-mask" @click="closeRechargePopup"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">充值</text>
<text class="popup-close" @click="closeRechargePopup">×</text>
<text class="popup-title">鍏呭€?/text>
<text class="popup-close" @click="closeRechargePopup"></text>
</view>
<view class="popup-body">
<text class="amount-label">充值金额</text>
<text class="amount-label">鍏呭€奸噾棰?/text>
<view class="amount-input">
<text class="currency-symbol">¥</text>
<text class="currency-symbol"></text>
<input class="amount-field"
v-model="rechargeAmount"
type="number"
placeholder="请输入充值金额"
placeholder="璇疯緭鍏ュ厖鍊奸噾棰?
focus />
</view>
<view class="quick-amounts">
@@ -140,17 +140,17 @@
:key="amount"
:class="['quick-amount', { active: rechargeAmount === amount.toString() }]"
@click="selectQuickAmount(amount)">
¥{{ amount }}
{{ amount }}
</text>
</view>
<text class="recharge-tip">单笔充值最低10元最高5000元</text>
<text class="recharge-tip">鍗曠瑪鍏呭€兼渶浣?0鍏冿紝鏈€楂?000鍏?/text>
</view>
<view class="popup-footer">
<button class="cancel-btn" @click="closeRechargePopup">取消</button>
<button class="cancel-btn" @click="closeRechargePopup">鍙栨秷</button>
<button class="confirm-btn"
:class="{ disabled: !canRecharge }"
@click="confirmRecharge">
确认充值
纭鍏呭€?
</button>
</view>
</view>
@@ -205,31 +205,31 @@ const showRechargePopup = ref<boolean>(false)
const rechargeAmount = ref<string>('')
const quickAmounts = [50, 100, 200, 500, 1000]
// 计算属性
// 璁$畻灞炴€?
const canRecharge = computed(() => {
const amount = parseFloat(rechargeAmount.value)
return !isNaN(amount) && amount >= 10 && amount <= 5000
})
// 监听过滤器变化
// 鐩戝惉杩囨护鍣ㄥ彉鍖?
watch(activeFilter, () => {
resetTransactions()
loadTransactions()
})
// 生命周期
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
loadWalletData()
})
// 重置交易记录
// 閲嶇疆浜ゆ槗璁板綍
const resetTransactions = () => {
transactions.value = []
currentPage.value = 1
hasMore.value = true
}
// 加载钱包数据
// 鍔犺浇閽卞寘鏁版嵁
const loadWalletData = async () => {
const userId = getCurrentUserId()
if (!userId) {
@@ -245,7 +245,7 @@ const loadWalletData = async () => {
])
}
// 加载余额信息
// 鍔犺浇浣欓淇℃伅
const loadBalance = async () => {
const userId = getCurrentUserId()
if (!userId) return
@@ -258,7 +258,7 @@ const loadBalance = async () => {
.single()
if (error !== null) {
console.error('加载钱包失败:', error)
console.error('鍔犺浇閽卞寘澶辫触:', error)
return
}
@@ -271,11 +271,11 @@ const loadBalance = async () => {
}
}
} catch (err) {
console.error('加载钱包异常:', err)
console.error('鍔犺浇閽卞寘寮傚父:', err)
}
}
// 加载交易记录
// 鍔犺浇浜ゆ槗璁板綍
const loadTransactions = async (loadMore: boolean = false) => {
if (isLoading.value || (!hasMore.value && loadMore)) {
return
@@ -295,20 +295,20 @@ const loadTransactions = async (loadMore: boolean = false) => {
.eq('user_id', userId)
.order('created_at', { ascending: false })
// 根据过滤器筛选
// 鏍规嵁杩囨护鍣ㄧ瓫閫?
if (activeFilter.value === 'income') {
query = query.gt('change_amount', 0)
} else if (activeFilter.value === 'expense') {
query = query.lt('change_amount', 0)
}
// 分页
// 鍒嗛〉
query = query.range((page - 1) * pageSize.value, page * pageSize.value - 1)
const { data, error } = await query
if (error !== null) {
console.error('加载交易记录失败:', error)
console.error('鍔犺浇浜ゆ槗璁板綍澶辫触:', error)
return
}
@@ -324,47 +324,47 @@ const loadTransactions = async (loadMore: boolean = false) => {
hasMore.value = newTransactions.length === pageSize.value
} catch (err) {
console.error('加载交易记录异常:', err)
console.error('鍔犺浇浜ゆ槗璁板綍寮傚父:', err)
} finally {
isLoading.value = false
}
}
// 获取当前用户ID
// 鑾峰彇褰撳墠鐢ㄦ埛ID
const getCurrentUserId = (): string => {
const userStore = uni.getStorageSync('userInfo')
return userStore?.id || ''
}
// 获取交易图标
// 鑾峰彇浜ゆ槗鍥炬爣
const getTransactionIcon = (type: string): string => {
const icons: Record<string, string> = {
recharge: '💳',
consume: '🛒',
withdraw: '🏦',
refund: '🔄',
reward: '🎁',
income: '💰',
expense: '📤'
recharge: '馃挸',
consume: '馃洅',
withdraw: '馃彟',
refund: '馃攧',
reward: '馃巵',
income: '馃挵',
expense: '馃摛'
}
return icons[type] || '💰'
return icons[type] || '馃挵'
}
// 获取交易标题
// 鑾峰彇浜ゆ槗鏍囬
const getTransactionTitle = (type: string): string => {
const titles: Record<string, string> = {
recharge: '账户充值',
consume: '商品消费',
withdraw: '余额提现',
refund: '订单退款',
reward: '活动奖励',
income: '收入',
expense: '支出'
recharge: '璐︽埛鍏呭€?,
consume: '鍟嗗搧娑堣垂',
withdraw: '浣欓鎻愮幇',
refund: '璁㈠崟閫€娆?,
reward: '娲诲姩濂栧姳',
income: '鏀跺叆',
expense: '鏀嚭'
}
return titles[type] || '交易'
return titles[type] || '浜ゆ槗'
}
// 格式化时间
// 鏍煎紡鍖栨椂闂?
const formatTime = (timeStr: string): string => {
const date = new Date(timeStr)
const month = (date.getMonth() + 1).toString().padStart(2, '0')
@@ -374,14 +374,14 @@ const formatTime = (timeStr: string): string => {
return `${month}-${day} ${hours}:${minutes}`
}
// 显示更多操作
// 鏄剧ず鏇村鎿嶄綔
const showMoreActions = () => {
uni.showActionSheet({
itemList: ['交易记录', '安全设置', '帮助中心'],
itemList: ['浜ゆ槗璁板綍', '瀹夊叏璁剧疆', '甯姪涓績'],
success: (res) => {
switch (res.tapIndex) {
case 0:
// 交易记录已经在当前页
// 浜ゆ槗璁板綍宸茬粡鍦ㄥ綋鍓嶉〉
break
case 1:
uni.navigateTo({
@@ -398,72 +398,72 @@ const showMoreActions = () => {
})
}
// 充值
// 鍏呭€?
const recharge = () => {
showRechargePopup.value = true
rechargeAmount.value = ''
}
// 提现
// 鎻愮幇
const withdraw = () => {
uni.navigateTo({
url: '/pages/mall/consumer/withdraw'
})
}
// 跳转到优惠券
// 璺宠浆鍒颁紭鎯犲埜
const goToCoupons = () => {
uni.navigateTo({
url: '/pages/mall/consumer/coupons'
})
}
// 跳转到红包
// 璺宠浆鍒扮孩鍖?
const goToRedPackets = () => {
uni.navigateTo({
url: '/pages/mall/consumer/red-packets'
})
}
// 跳转到积分
// 璺宠浆鍒扮Н鍒?
const goToPoints = () => {
uni.navigateTo({
url: '/pages/mall/consumer/points'
})
}
// 跳转到银行卡
// 璺宠浆鍒伴摱琛屽崱
const goToBankCards = () => {
uni.navigateTo({
url: '/pages/mall/consumer/bank-cards'
})
}
// 切换过滤器
// 鍒囨崲杩囨护鍣?
const changeFilter = (filter: string) => {
activeFilter.value = filter
}
// 加载更多
// 鍔犺浇鏇村
const loadMore = () => {
if (hasMore.value && !isLoading.value) {
loadTransactions(true)
}
}
// 选择快捷金额
// 閫夋嫨蹇嵎閲戦
const selectQuickAmount = (amount: number) => {
rechargeAmount.value = amount.toString()
}
// 确认充值
// 纭鍏呭€?
const confirmRecharge = async () => {
if (!canRecharge.value) return
const amount = parseFloat(rechargeAmount.value)
if (isNaN(amount)) return
// 这里应该跳转到支付页面进行充值
// 杩欓噷搴旇璺宠浆鍒版敮浠橀〉闈㈣繘琛屽厖鍊?
uni.navigateTo({
url: `/pages/mall/consumer/payment?type=recharge&amount=${amount}`
})
@@ -471,20 +471,20 @@ const confirmRecharge = async () => {
closeRechargePopup()
}
// 关闭充值弹窗
// 鍏抽棴鍏呭€煎脊绐?
const closeRechargePopup = () => {
showRechargePopup.value = false
rechargeAmount.value = ''
}
// 返回
// 杩斿洖
const goBack = () => {
uni.navigateBack()
}
</script>
<style scoped>
/* 响应式布局优化 */
/* 鍝嶅簲寮忓竷灞€浼樺寲 */
@media screen and (min-width: 768px) {
.wallet-content {
padding: 20px;
@@ -518,11 +518,11 @@ const goBack = () => {
@media screen and (min-width: 1024px) {
.wallet-page {
flex-direction: row; /* 大屏下改为横向布局 */
flex-direction: row; /* 澶у睆涓嬫敼涓烘í鍚戝竷灞€ */
}
.wallet-header {
display: none; /* 大屏下隐藏顶部栏 */
display: none; /* 澶у睆涓嬮殣钘忛《閮ㄦ爮 */
}
.wallet-content {
@@ -981,4 +981,5 @@ const goBack = () => {
background-color: #cccccc;
opacity: 0.6;
}
</style>
</style>