Browse Source

账户余额,以及账户余额,和开通记录组件

master
DU 6 months ago
parent
commit
d3aae91fde
  1. 10
      src/app.config.ts
  2. 2
      src/pages/user/index.tsx
  3. 150
      src/pagesOrder/accountBalance/components/BalanceList.scss
  4. 94
      src/pagesOrder/accountBalance/components/BalanceList.tsx
  5. 162
      src/pagesOrder/accountBalance/components/OpenRecordList.scss
  6. 161
      src/pagesOrder/accountBalance/components/OpenRecordList.tsx
  7. 67
      src/pagesOrder/accountBalance/components/RechargeRecordList.scss
  8. 56
      src/pagesOrder/accountBalance/components/RechargeRecordList.tsx
  9. 5
      src/pagesOrder/accountBalance/index.config.ts
  10. 65
      src/pagesOrder/accountBalance/index.scss
  11. 147
      src/pagesOrder/accountBalance/index.tsx
  12. 5
      src/pagesUser/accountBalance/index.config.ts
  13. 5
      src/pagesUser/accountBalance/index.scss
  14. 20
      src/pagesUser/accountBalance/index.tsx

10
src/app.config.ts

@ -27,7 +27,6 @@ export default defineAppConfig({ @@ -27,7 +27,6 @@ export default defineAppConfig({
'helpCenter/index', // 帮助中心
'agreement/index', // 协议
'editProfile/index', // 编辑资料
'accountBalance/index', // 账户余额
'vipBenefits/index', // VIP权益
'benefitDetail/index', // 权益详情
]
@ -44,6 +43,13 @@ export default defineAppConfig({ @@ -44,6 +43,13 @@ export default defineAppConfig({
'renewFee/index', // 续费页面
'turnOffRestart/index', // 关灯重开页面
]
},
{
root: 'pagesOrder',
name: 'order-package',
pages: [
'accountBalance/index', // 账户余额
]
}
],
// 分包预下载规则
@ -54,7 +60,7 @@ export default defineAppConfig({ @@ -54,7 +60,7 @@ export default defineAppConfig({
},
'pages/user/index': {
network: 'all',
packages: ['user-package']
packages: ['user-package', 'order-package']
},
'pages/scan/index': {
network: 'all',

2
src/pages/user/index.tsx

@ -52,7 +52,7 @@ const UserIndex = observer(() => { @@ -52,7 +52,7 @@ const UserIndex = observer(() => {
// 点击账户余额
const handleBalanceClick = () => {
Taro.navigateTo({
url: '/pagesUser/accountBalance/index'
url: '/pagesOrder/accountBalance/index'
})
}

150
src/pagesOrder/accountBalance/components/BalanceList.scss

@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
.balance-list {
padding: 30px;
// 空数据展示
.empty-data {
display: flex;
justify-content: center;
align-items: center;
padding: 150px 0;
.empty-image {
width: 400px;
height: 400px;
}
}
.balance-card {
background-color: #202029;
border-radius: 12px;
padding: 33px 30px;
margin-bottom: 30px;
&:last-child {
margin-bottom: 0;
}
// 标题栏
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
.store-name {
font-size: 33px;
color: #FFFEFE;
flex: 1;
}
.points-info {
display: flex;
align-items: center;
gap: 8px;
.points-label {
font-size: 25px;
color: #999999;
}
.points-value {
font-size: 30px;
color: #FFFFFF;
}
}
}
// 余额和存柜信息卡片
.info-cards {
display: flex;
gap: 26px;
margin-bottom: 26px;
.info-card {
width: 302px;
height: 228px;
background-color: #292933;
border-radius: 13px;
padding: 30px;
display: flex;
flex-direction: column;
align-items: flex-start;
// 金额显示
.amount-row {
display: flex;
align-items: baseline;
color: #FFFFFF;
.currency {
font-size: 25px;
}
.integer {
font-size: 40px;
font-weight: 500;
}
.decimal {
font-size: 25px;
}
}
// 数字显示存柜柜数量
.count-value {
font-size: 40px;
font-weight: 500;
color: #FFFFFF;
}
// 卡片标签
.card-label {
font-size: 25px;
color: #B8B8B8;
margin: 26px 0 29px;
}
// 操作按钮
.action-button {
width: 100%;
height: 83px;
border-radius: 42px;
display: flex;
align-items: center;
justify-content: center;
margin-top: auto;
&.recharge {
background-color: #01CA68;
}
&.view {
background-color: #01CA68;
}
.button-text {
font-size: 31px;
color: #FFFFFF;
}
}
}
}
// VIP 到期日期
.vip-expire {
font-size: 25px;
color: #B8B8B8;
}
}
// 到底了提示
.list-end {
text-align: center;
padding: 40px 0;
.end-text {
font-size: 25px;
color: #999999;
}
}
}

94
src/pagesOrder/accountBalance/components/BalanceList.tsx

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
/**
*
*/
import { View, Text, Image } from '@tarojs/components'
import { FC } from 'react'
import './BalanceList.scss'
const noDataImg = require('../../../assets/big/noData.png')
interface StoreBalanceItem {
id: string
storeName: string
points: number
balance: number
cabinetCount: number
vipExpireDate: string
}
interface BalanceListProps {
balanceList: StoreBalanceItem[]
onRecharge: (storeId: string) => void
onViewCabinet: (storeId: string) => void
}
const BalanceList: FC<BalanceListProps> = ({ balanceList, onRecharge, onViewCabinet }) => {
// 格式化金额显示
const formatAmount = (amount: number) => {
const [integer, decimal] = amount.toFixed(2).split('.')
return { integer, decimal }
}
return (
<View className="balance-list">
{balanceList.length === 0 ? (
<View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" />
</View>
) : (
<>
{balanceList.map(item => {
const balanceAmount = formatAmount(item.balance)
return (
<View key={item.id} className="balance-card">
{/* 标题栏 */}
<View className="card-header">
<Text className="store-name">{item.storeName}</Text>
<View className="points-info">
<Text className="points-label"></Text>
<Text className="points-value">{item.points}</Text>
</View>
</View>
{/* 余额和存柜信息 */}
<View className="info-cards">
{/* 账户余额卡片 */}
<View className="info-card">
<View className="amount-row">
<Text className="currency"></Text>
<Text className="integer">{balanceAmount.integer}</Text>
<Text className="decimal">.{balanceAmount.decimal}</Text>
</View>
<Text className="card-label"></Text>
<View className="action-button recharge" onClick={() => onRecharge(item.id)}>
<Text className="button-text"></Text>
</View>
</View>
{/* 存柜柜卡片 */}
<View className="info-card">
<Text className="count-value">{item.cabinetCount}</Text>
<Text className="card-label"></Text>
<View className="action-button view" onClick={() => onViewCabinet(item.id)}>
<Text className="button-text"></Text>
</View>
</View>
</View>
{/* VIP 到期日期 */}
<Text className="vip-expire">VIP开通到 {item.vipExpireDate}</Text>
</View>
)
})}
{/* 到底了提示 */}
<View className="list-end">
<Text className="end-text">~</Text>
</View>
</>
)}
</View>
)
}
export default BalanceList

162
src/pagesOrder/accountBalance/components/OpenRecordList.scss

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
.open-record-list {
padding: 30px;
// 空数据展示
.empty-data {
display: flex;
justify-content: center;
align-items: center;
padding: 150px 0;
.empty-image {
width: 400px;
height: 400px;
}
}
// 月份卡片
.month-card {
background-color: #202029;
border-radius: 12px;
margin-bottom: 30px;
// 月份标题行
.month-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 36px 30px;
.month-left {
display: flex;
align-items: center;
gap: 10px;
.month-text {
font-size: 33px;
color: #FFFEFE;
}
.month-arrow {
flex-shrink: 0;
}
}
.month-right {
display: flex;
align-items: baseline;
gap: 4px;
.amount-label {
font-size: 25px;
color: #999999;
margin-right: 8px;
}
.amount-symbol {
font-size: 25px;
color: #FFFFFF;
}
.amount-value {
font-size: 30px;
color: #FFFFFF;
}
}
}
// 分割线
.divider {
height: 1px;
background-color: #45454C;
}
// 记录列表
.record-list {
padding: 20px;
.record-item {
display: flex;
flex-direction: column;
gap: 20px;
.item-row {
display: flex;
align-items: center;
justify-content: space-between;
.store-name {
font-size: 33px;
color: #FFFEFE;
flex: 1;
}
.status {
font-size: 25px;
color: #F5C8A6;
}
.vip-type {
font-size: 25px;
color: #B8B8B8;
}
.price {
font-size: 25px;
color: #FFFFFF;
}
.pay-time {
font-size: 25px;
color: #8B8B8B;
}
.actual-pay {
font-size: 25px;
color: #FFFFFF;
}
}
// 退款按钮行
.refund-row {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.refund-button {
width: 144px;
height: 56px;
border: 1px solid #FFFFFF;
border-radius: 28px;
display: flex;
align-items: center;
justify-content: center;
.refund-text {
font-size: 25px;
color: #FFFFFF;
}
}
}
}
// item 之间的分割线
.item-divider {
height: 1px;
background-color: #45454C;
margin: 20px 0;
}
}
}
// 到底了提示
.list-end {
text-align: center;
padding: 40px 0;
.end-text {
font-size: 25px;
color: #999999;
}
}
}

161
src/pagesOrder/accountBalance/components/OpenRecordList.tsx

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
/**
*
*/
import { View, Text, Image } from '@tarojs/components'
import { FC, useState } from 'react'
import { ArrowDown } from '@taroify/icons'
import Taro from '@tarojs/taro'
import './OpenRecordList.scss'
const noDataImg = require('../../../assets/big/noData.png')
interface OpenRecordItem {
id: string
storeName: string
payTime: string
vipType: string
amount: number
status: string
canRefund?: boolean // 是否可退款(7天内)
}
interface OpenRecordListProps {
recordList: OpenRecordItem[]
}
const OpenRecordList: FC<OpenRecordListProps> = ({ recordList }) => {
const [selectedMonth, setSelectedMonth] = useState('2025-02')
// 按月份分组记录
const groupByMonth = (records: OpenRecordItem[]) => {
const groups: Record<string, OpenRecordItem[]> = {}
records.forEach(record => {
const month = record.payTime.substring(0, 7) // 提取 YYYY-MM
if (!groups[month]) {
groups[month] = []
}
groups[month].push(record)
})
return groups
}
const groupedRecords = groupByMonth(recordList)
// 计算当月总金额
const calculateMonthTotal = (records: OpenRecordItem[]) => {
return records.reduce((sum, record) => sum + record.amount, 0)
}
// 选择月份
const handleSelectMonth = () => {
console.log('选择月份')
// TODO: 打开月份选择器
Taro.showToast({
title: '月份选择功能开发中',
icon: 'none'
})
}
// 退款
const handleRefund = (recordId: string) => {
console.log('退款', recordId)
Taro.showModal({
title: '确认退款',
content: '确定要申请退款吗?',
success: (res) => {
if (res.confirm) {
// TODO: 调用退款接口
Taro.showToast({
title: '退款申请已提交',
icon: 'success'
})
}
}
})
}
return (
<View className="open-record-list">
{recordList.length === 0 ? (
<View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" />
</View>
) : (
<>
{Object.keys(groupedRecords).map(month => {
const monthRecords = groupedRecords[month]
const monthTotal = calculateMonthTotal(monthRecords)
return (
<View key={month} className="month-card">
{/* 月份标题和金额统计 */}
<View className="month-header">
<View className="month-left" onClick={handleSelectMonth}>
<Text className="month-text">{month}</Text>
<ArrowDown className="month-arrow" size="20px" color="#FFFEFE" />
</View>
<View className="month-right">
<Text className="amount-label"></Text>
<Text className="amount-symbol"></Text>
<Text className="amount-value">{monthTotal.toFixed(2)}</Text>
</View>
</View>
{/* 分割线 */}
<View className="divider" />
{/* 记录列表 */}
<View className="record-list">
{monthRecords.map((item, index) => (
<View key={item.id}>
<View className="record-item">
{/* 第一行:店铺名称 + 状态 */}
<View className="item-row">
<Text className="store-name">{item.storeName}</Text>
<Text className="status">{item.status}</Text>
</View>
{/* 第二行:VIP类型 + 金额 */}
<View className="item-row">
<Text className="vip-type">{item.vipType}</Text>
<Text className="price">{item.amount.toFixed(2)}</Text>
</View>
{/* 第三行:支付时间 + 实付金额 */}
<View className="item-row">
<Text className="pay-time">{item.payTime}</Text>
<Text className="actual-pay"> {item.amount.toFixed(2)}</Text>
</View>
{/* 退款按钮(7天内可退款) */}
{item.canRefund && (
<View className="refund-row">
<View className="refund-button" onClick={() => handleRefund(item.id)}>
<Text className="refund-text">退</Text>
</View>
</View>
)}
</View>
{/* item 之间的分割线 */}
{index < monthRecords.length - 1 && (
<View className="item-divider" />
)}
</View>
))}
</View>
</View>
)
})}
{/* 到底了提示 */}
<View className="list-end">
<Text className="end-text">~</Text>
</View>
</>
)}
</View>
)
}
export default OpenRecordList

67
src/pagesOrder/accountBalance/components/RechargeRecordList.scss

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
.recharge-record-list {
padding: 30px;
// 空数据展示
.empty-data {
display: flex;
justify-content: center;
align-items: center;
padding: 150px 0;
.empty-image {
width: 400px;
height: 400px;
}
}
.record-card {
background-color: #202029;
border-radius: 12px;
padding: 33px 30px;
margin-bottom: 30px;
&:last-child {
margin-bottom: 0;
}
.record-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 25px;
.store-name {
font-size: 33px;
color: #FFFEFE;
flex: 1;
}
.amount {
font-size: 33px;
color: #01CA68;
}
}
.record-info {
display: flex;
flex-direction: column;
gap: 15px;
.info-text {
font-size: 25px;
color: #B8B8B8;
}
}
}
// 到底了提示
.list-end {
text-align: center;
padding: 40px 0;
.end-text {
font-size: 25px;
color: #999999;
}
}
}

56
src/pagesOrder/accountBalance/components/RechargeRecordList.tsx

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
/**
*
*/
import { View, Text, Image } from '@tarojs/components'
import { FC } from 'react'
import './RechargeRecordList.scss'
const noDataImg = require('../../../assets/big/noData.png')
interface RechargeRecordItem {
id: string
storeName: string
rechargeTime: string
rechargeType: string
amount: number
status: string
}
interface RechargeRecordListProps {
recordList: RechargeRecordItem[]
}
const RechargeRecordList: FC<RechargeRecordListProps> = ({ recordList }) => {
return (
<View className="recharge-record-list">
{recordList.length === 0 ? (
<View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" />
</View>
) : (
<>
{recordList.map(item => (
<View key={item.id} className="record-card">
<View className="record-header">
<Text className="store-name">{item.storeName}</Text>
<Text className="amount">+{item.amount.toFixed(2)}</Text>
</View>
<View className="record-info">
<Text className="info-text">{item.rechargeType}</Text>
<Text className="info-text">{item.rechargeTime}</Text>
<Text className="info-text">{item.status}</Text>
</View>
</View>
))}
{/* 到底了提示 */}
<View className="list-end">
<Text className="end-text">~</Text>
</View>
</>
)}
</View>
)
}
export default RechargeRecordList

5
src/pagesOrder/accountBalance/index.config.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default {
navigationBarTitleText: '账户余额',
navigationBarBackgroundColor: '#000000',
navigationBarTextStyle: 'white',
}

65
src/pagesOrder/accountBalance/index.scss

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
.account-balance-page {
min-height: 100vh;
background-color: #121212;
display: flex;
flex-direction: column;
// Tab 容器
.tab-container {
background-color: #0E0D0D;
.custom-tabs{
.taroify-tabs__nav {
background-color: #0E0D0D !important;
}
// 未选中 Tab - 文字颜色 #999
.taroify-tabs__tab {
font-size: 33px !important;
color: #999999 !important;
}
// 选中 Tab - 文字颜色 #fff
.taroify-tabs__tab--active {
// color: #FFFFFF !important;
// 选中指示线
.taroify-badge-wrapper {
color: #FFFFFF !important;
}
}
}
:global {
// Tab 导航容器 - 设置背景色
.taroify-tabs__nav {
background-color: #0E0D0D !important;
}
// 选中指示线
.taroify-tabs__line {
background-color: #FFFFFF !important;
}
// 未选中 Tab - 文字颜色 #999
.taroify-tabs__tab {
font-size: 33px !important;
color: #999999 !important;
}
// 选中 Tab - 文字颜色 #fff
.taroify-tabs__tab--active {
color: #FFFFFF !important;
}
}
}
// 内容滚动区域
.content-scroll {
flex: 1;
height: calc(100vh - 88px); // 减去 Tab 高度
}
}

147
src/pagesOrder/accountBalance/index.tsx

@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
/**
*
*/
import { View, ScrollView } from '@tarojs/components'
import { useLoad } from '@tarojs/taro'
import { useState } from 'react'
import { Tabs } from '@taroify/core'
import BalanceList from './components/BalanceList'
import OpenRecordList from './components/OpenRecordList'
import RechargeRecordList from './components/RechargeRecordList'
import './index.scss'
const AccountBalance = () => {
const [activeTab, setActiveTab] = useState(0)
// 账户余额模拟数据
const [balanceList] = useState([
{
id: '1',
storeName: '北京XXXXXX店',
points: 1600,
balance: 1000.00,
cabinetCount: 2,
vipExpireDate: '2026-01-01'
},
{
id: '2',
storeName: '北京XXXXXXXXXXXXX店',
points: 100,
balance: 10.00,
cabinetCount: 0,
vipExpireDate: '2028-11-01'
}
])
// 开通记录模拟数据
const [openRecordList] = useState([
{
id: '1',
storeName: '北京XXXXXX店',
payTime: '2025-02-15 12:00:00',
vipType: 'VIP',
amount: 19.90,
status: '已完成',
canRefund: false
},
{
id: '2',
storeName: '北京XXX店',
payTime: '2025-02-10 15:30:00',
vipType: 'VIP',
amount: 19.90,
status: '已完成',
canRefund: false
},
{
id: '3',
storeName: '北京XXXXXXXXXXXXX店',
payTime: '2025-03-09 10:20:00',
vipType: 'VIP',
amount: 19.90,
status: '已完成',
canRefund: true // 7天内可退款
},
{
id: '4',
storeName: '北京XXXXXX店',
payTime: '2025-03-06 14:15:00',
vipType: 'VIP',
amount: 19.90,
status: '已完成',
canRefund: true // 7天内可退款
}
])
// 充值记录模拟数据
const [rechargeRecordList] = useState([
{
id: '1',
storeName: '北京XXXXXX店',
rechargeTime: '2026-01-15 10:20:30',
rechargeType: '微信支付',
amount: 500.00,
status: '充值成功'
},
{
id: '2',
storeName: '北京XXXXXXXXXXXXX店',
rechargeTime: '2026-01-10 14:15:20',
rechargeType: '支付宝',
amount: 100.00,
status: '充值成功'
}
])
useLoad(() => {
console.log('账户余额页面加载')
})
const handleRecharge = (storeId: string) => {
console.log('充值', storeId)
// TODO: 跳转到充值页面
}
const handleViewCabinet = (storeId: string) => {
console.log('查看存柜', storeId)
// TODO: 跳转到存柜页面
}
return (
<View className="account-balance-page">
{/* Tab 切换 */}
<View className="tab-container">
<Tabs value={activeTab} onChange={setActiveTab} className="custom-tabs">
<Tabs.TabPane title="账户余额" />
<Tabs.TabPane title="开通记录" />
<Tabs.TabPane title="充值记录" />
</Tabs>
</View>
{/* 内容区域 */}
<ScrollView
className="content-scroll"
scrollY
scrollWithAnimation
>
{activeTab === 0 && (
<BalanceList
balanceList={balanceList}
onRecharge={handleRecharge}
onViewCabinet={handleViewCabinet}
/>
)}
{activeTab === 1 && (
<OpenRecordList recordList={openRecordList} />
)}
{activeTab === 2 && (
<RechargeRecordList recordList={rechargeRecordList} />
)}
</ScrollView>
</View>
)
}
export default AccountBalance

5
src/pagesUser/accountBalance/index.config.ts

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
export default {
navigationBarTitleText: '账户余额',
navigationBarBackgroundColor: '#FFFFFF',
navigationBarTextStyle: 'black',
}

5
src/pagesUser/accountBalance/index.scss

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
.account-balance-page {
min-height: 100vh;
background-color: #F2F3F5;
padding: 30px;
}

20
src/pagesUser/accountBalance/index.tsx

@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
/**
*
*/
import { View, Text } from '@tarojs/components'
import { useLoad } from '@tarojs/taro'
import './index.scss'
const AccountBalance = () => {
useLoad(() => {
console.log('账户余额详情页面加载')
})
return (
<View className="account-balance-page">
<Text></Text>
</View>
)
}
export default AccountBalance
Loading…
Cancel
Save