consumer模块完成度85%,测试连接supabase

This commit is contained in:
2026-01-28 17:28:50 +08:00
parent 66aa909193
commit a4fa00c935
18 changed files with 2108 additions and 1573 deletions

View File

@@ -7,113 +7,121 @@
</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>
<view class="balance-actions">
<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>
</view>
<view class="stat-item">
<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>
</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>
<view class="dashboard-container">
<!-- 左侧/顶部区域:资产信息 -->
<view class="dashboard-main">
<!-- 余额概览 -->
<view class="balance-overview">
<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>
</view>
</view>
<view class="action-item" @click="goToRedPackets">
<text class="action-icon">🧧</text>
<text class="action-text">红包</text>
<!-- 资产统计 -->
<view class="assets-stats">
<view class="stat-item">
<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>
</view>
<view class="stat-item">
<text class="stat-label">累计提现</text>
<text class="stat-value">¥{{ stats.totalWithdraw.toFixed(2) }}</text>
</view>
</view>
<view class="action-item" @click="goToPoints">
<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>
</view>
</view>
</view>
<!-- 交易记录 -->
<view class="transactions-section">
<view class="section-header">
<text class="section-title">交易记录</text>
<view class="filter-tabs">
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
@click="changeFilter('all')">全部</text>
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
@click="changeFilter('income')">收入</text>
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
@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>
</view>
<!-- 交易列表 -->
<view class="transactions-list">
<view v-for="transaction in transactions"
:key="transaction.id"
class="transaction-item">
<view class="transaction-left">
<text class="transaction-icon">{{ getTransactionIcon(transaction.type) }}</text>
<view class="transaction-info">
<text class="transaction-title">{{ getTransactionTitle(transaction.type) }}</text>
<text class="transaction-time">{{ formatTime(transaction.created_at) }}</text>
<text v-if="transaction.remark" class="transaction-remark">{{ transaction.remark }}</text>
<!-- 快捷功能 -->
<view class="quick-actions">
<view class="action-grid">
<view class="action-item" @click="goToCoupons">
<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>
</view>
<view class="action-item" @click="goToPoints">
<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>
</view>
</view>
<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) }}
</text>
<text class="transaction-balance">余额: ¥{{ transaction.current_balance.toFixed(2) }}</text>
</view>
<!-- 安全提示 (移动端在底部PC端在左侧底部) -->
<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>
</view>
</view>
<!-- 右侧/底部区域:交易记录 -->
<view class="dashboard-side">
<!-- 交易记录 -->
<view class="transactions-section">
<view class="section-header">
<text class="section-title">交易记录</text>
<view class="filter-tabs">
<text :class="['filter-tab', { active: activeFilter === 'all' }]"
@click="changeFilter('all')">全部</text>
<text :class="['filter-tab', { active: activeFilter === 'income' }]"
@click="changeFilter('income')">收入</text>
<text :class="['filter-tab', { active: activeFilter === 'expense' }]"
@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>
</view>
<!-- 交易列表 -->
<view class="transactions-list">
<view v-for="transaction in transactions"
:key="transaction.id"
class="transaction-item">
<view class="transaction-left">
<text class="transaction-icon">{{ getTransactionIcon(transaction.type) }}</text>
<view class="transaction-info">
<text class="transaction-title">{{ getTransactionTitle(transaction.type) }}</text>
<text class="transaction-time">{{ formatTime(transaction.created_at) }}</text>
<text v-if="transaction.remark" class="transaction-remark">{{ transaction.remark }}</text>
</view>
</view>
<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) }}
</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>
</view>
<view v-if="!hasMore && transactions.length > 0" class="no-more">
<text class="no-more-text">没有更多记录了</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="isLoading" class="loading-more">
<text class="loading-text">加载中...</text>
</view>
<view v-if="!hasMore && transactions.length > 0" class="no-more">
<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>
</view>
</scroll-view>
@@ -251,23 +259,26 @@ const loadBalance = async () => {
if (!userId) return
try {
const { data, error } = await supa
.from('user_wallets')
.select('*')
.eq('user_id', userId)
.single()
if (error !== null) {
console.error('加载钱包失败:', error)
return
// 使用本地模拟数据
const mockBalance = {
balance: 12580.00,
total_recharge: 20000.00,
total_consume: 7420.00,
total_withdraw: 0.00
}
// 尝试从本地存储获取
const storedWallet = uni.getStorageSync(`wallet_${userId}`)
const data = storedWallet ? JSON.parse(storedWallet as string) : mockBalance
if (data) {
balance.value = data.balance || 0
// 类型断言,处理 any 类型
const walletData = data as any
balance.value = Number(walletData.balance || 0)
stats.value = {
totalRecharge: data.total_recharge || 0,
totalConsume: data.total_consume || 0,
totalWithdraw: data.total_withdraw || 0
totalRecharge: Number(walletData.total_recharge || 0),
totalConsume: Number(walletData.total_consume || 0),
totalWithdraw: Number(walletData.total_withdraw || 0)
}
}
} catch (err) {
@@ -289,30 +300,49 @@ const loadTransactions = async (loadMore: boolean = false) => {
const page = loadMore ? currentPage.value + 1 : 1
let query = supa
.from('balance_records')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false })
// 模拟交易记录数据
const mockTransactions: TransactionType[] = [
{
id: 't1',
user_id: userId,
change_amount: -128.00,
current_balance: 12580.00,
change_type: 'consume',
related_id: 'ord_001',
remark: '购买药品',
created_at: new Date().toISOString()
},
{
id: 't2',
user_id: userId,
change_amount: 500.00,
current_balance: 12708.00,
change_type: 'recharge',
related_id: 'rec_001',
remark: '账户充值',
created_at: new Date(Date.now() - 86400000).toISOString()
},
{
id: 't3',
user_id: userId,
change_amount: -58.50,
current_balance: 12208.00,
change_type: 'consume',
related_id: 'ord_002',
remark: '购买保健品',
created_at: new Date(Date.now() - 172800000).toISOString()
}
]
// 根据过滤器筛选
// 简单模拟分页和筛选
let filtered = mockTransactions
if (activeFilter.value === 'income') {
query = query.gt('change_amount', 0)
filtered = filtered.filter(t => t.change_amount > 0)
} else if (activeFilter.value === 'expense') {
query = query.lt('change_amount', 0)
filtered = filtered.filter(t => t.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)
return
}
const newTransactions = data || []
const newTransactions = filtered
if (loadMore) {
transactions.value.push(...newTransactions)
@@ -322,7 +352,8 @@ const loadTransactions = async (loadMore: boolean = false) => {
currentPage.value = 1
}
hasMore.value = newTransactions.length === pageSize.value
// 模拟没有更多数据
hasMore.value = false
} catch (err) {
console.error('加载交易记录异常:', err)
} finally {
@@ -484,6 +515,34 @@ const goBack = () => {
</script>
<style scoped>
/* 基础样式 */
.wallet-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.wallet-content {
flex: 1;
}
.dashboard-container {
display: flex;
flex-direction: column;
padding-bottom: 20px;
}
.dashboard-main {
display: flex;
flex-direction: column;
}
.dashboard-side {
display: flex;
flex-direction: column;
}
/* 响应式布局优化 */
@media screen and (min-width: 768px) {
.wallet-content {
@@ -491,22 +550,16 @@ const goBack = () => {
background-color: #f5f5f5;
}
.balance-overview {
.dashboard-container {
max-width: 800px;
margin: 0 auto;
width: 100%;
}
.balance-overview, .assets-stats, .quick-actions, .transactions-section, .security-tips {
border-radius: 12px;
margin-bottom: 20px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.assets-stats, .quick-actions, .transactions-section, .security-tips {
border-radius: 8px;
margin-bottom: 20px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.popup-content {
width: 400px;
left: 50%;
@@ -518,46 +571,48 @@ const goBack = () => {
@media screen and (min-width: 1024px) {
.wallet-page {
flex-direction: row; /* 大屏下改为横向布局 */
}
.wallet-header {
display: none; /* 大屏下隐藏顶部栏 */
flex-direction: column; /* 保持纵向,内容区内部处理横向 */
}
.wallet-content {
width: 100%;
max-width: 1000px;
max-width: 1200px;
margin: 0 auto;
}
.dashboard-container {
flex-direction: row; /* 横向排列 */
align-items: flex-start;
gap: 20px;
max-width: 100%;
}
.dashboard-main {
width: 400px; /* 左侧固定宽度 */
flex-shrink: 0;
}
.dashboard-side {
flex: 1; /* 右侧自适应 */
min-width: 0;
}
/* 调整各模块间距 */
.balance-overview,
.assets-stats,
.quick-actions,
.security-tips {
margin-bottom: 20px;
}
.transactions-section {
margin-top: 0; /* 移除顶部间距,与左侧对齐 */
height: 100%;
min-height: 600px; /* 保证右侧高度 */
}
}
.wallet-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.wallet-header {
background-color: #ffffff;
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #e5e5e5;
}
.back-btn {
font-size: 24px;
color: #333333;
padding: 5px;
}
.wallet-content {
flex: 1;
}
/* 模块样式 */
.balance-overview {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 30px 20px;