Browse Source

余额充值,重置明细,订单页面, 订单详情

master
DU 6 months ago
parent
commit
e0f5f7d9e2
  1. 3
      src/app.config.ts
  2. 2
      src/pagesOrder/accountBalance/components/BalanceList.scss
  3. 6
      src/pagesOrder/accountBalance/index.tsx
  4. 5
      src/pagesOrder/recharge/index.config.ts
  5. 187
      src/pagesOrder/recharge/index.scss
  6. 143
      src/pagesOrder/recharge/index.tsx
  7. 5
      src/pagesOrder/rechargeDetail/index.config.ts
  8. 147
      src/pagesOrder/rechargeDetail/index.scss
  9. 5
      src/pagesUser/orderDetail/index.config.ts
  10. 353
      src/pagesUser/orderDetail/index.scss
  11. 230
      src/pagesUser/orderDetail/index.tsx
  12. 179
      src/pagesUser/orderList/components/FilterPopup.scss
  13. 283
      src/pagesUser/orderList/components/FilterPopup.tsx
  14. 181
      src/pagesUser/orderList/index.scss
  15. 189
      src/pagesUser/orderList/index.tsx

3
src/app.config.ts

@ -22,6 +22,7 @@ export default defineAppConfig({ @@ -22,6 +22,7 @@ export default defineAppConfig({
pages: [
'poleStorage/index', // 存杆柜记录
'orderList/index', // 订单记录
'orderDetail/index', // 订单详情
'unsettledOrders/index', // 未结算账单
'goodsCons/index', // 商品消费
'helpCenter/index', // 帮助中心
@ -49,6 +50,8 @@ export default defineAppConfig({ @@ -49,6 +50,8 @@ export default defineAppConfig({
name: 'order-package',
pages: [
'accountBalance/index', // 账户余额
'recharge/index', // 余额充值
'rechargeDetail/index', // 充值明细
]
}
],

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

@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
padding: 30px;
display: flex;
flex-direction: column;
align-items: flex-start;
align-items: center;
// 金额显示
.amount-row {

6
src/pagesOrder/accountBalance/index.tsx

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
*
*/
import { View, ScrollView } from '@tarojs/components'
import { useLoad } from '@tarojs/taro'
import Taro, { useLoad } from '@tarojs/taro'
import { useState } from 'react'
import { Tabs } from '@taroify/core'
import BalanceList from './components/BalanceList'
@ -99,7 +99,9 @@ const AccountBalance = () => { @@ -99,7 +99,9 @@ const AccountBalance = () => {
const handleRecharge = (storeId: string) => {
console.log('充值', storeId)
// TODO: 跳转到充值页面
Taro.navigateTo({
url: '/pagesOrder/recharge/index'
})
}
const handleViewCabinet = (storeId: string) => {

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

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default definePageConfig({
navigationBarTitleText: '余额充值',
navigationBarBackgroundColor: '#121212',
navigationBarTextStyle: 'white'
})

187
src/pagesOrder/recharge/index.scss

@ -0,0 +1,187 @@ @@ -0,0 +1,187 @@
.recharge-page {
min-height: 100vh;
background-color: #FFFFFF;
padding-bottom: 150px; // 为底部按钮留出空间
// 上部分余额卡片区域
.balance-card-area {
background-color: #121212;
padding: 40px 30px 0;
.store-name {
display: block;
font-size: 40px;
color: #F5C8A6;
margin-bottom: 33px;
}
.balance-card {
background: linear-gradient(-30deg, #F0BE86 0%, #FADEB9 100%);
border-top-left-radius: 21px;
border-top-right-radius: 21px;
padding: 33px;
.card-row {
display: flex;
align-items: center;
justify-content: space-between;
.label {
font-size: 30px;
color: #844614;
}
.detail-wrapper {
display: flex;
align-items: center;
gap: 5px;
.detail-link {
font-size: 25px;
color: #844614;
}
}
}
.amount-row {
display: flex;
align-items: baseline;
margin-top: 28px;
margin-bottom: 40px;
.currency {
font-size: 25px;
color: #844614;
margin-right: 5px;
}
.amount {
font-size: 40px;
font-weight: bold;
color: #844614;
}
}
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
.info-item {
display: flex;
align-items: baseline;
.info-text {
font-size: 25px;
color: #844614;
}
.info-value {
font-size: 25px;
color: #844614;
}
}
}
}
}
// 下部分套餐和提示区域
.packages-area {
padding: 40px 30px;
.section-title {
display: block;
font-weight: bold;
font-size: 33px;
color: #333333;
margin-bottom: 30px;
}
// 充值套餐网格
.packages-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
margin-bottom: 40px;
.package-item {
width: 211px;
height: 150px;
background: #F2F3F5;
border-radius: 13px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
box-sizing: border-box;
.package-amount {
font-weight: bold;
font-size: 38px;
color: #333333;
}
.package-bonus {
font-size: 29px;
color: #FF6B00;
}
// 选中状态
&.selected {
border: 2px solid #C77734;
.package-amount {
color: #844614;
}
.package-bonus {
color: #844614;
}
}
}
}
// 温馨提示
.tips-section {
.tips-title {
font-weight: bold;
font-size: 25px;
color: #666666;
}
.tips-content {
font-size: 25px;
color: #666666;
line-height: 1.8;
}
}
}
// 底部固定按钮
.footer-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 30px;
background-color: #FFFFFF;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
.recharge-btn {
width: 100%;
height: 88px;
background: linear-gradient(-90deg, #F0BE86 0%, #FADEB9 100%);
border-radius: 44px;
display: flex;
align-items: center;
justify-content: center;
.recharge-btn-text {
font-weight: bold;
font-size: 33px;
color: #844614;
}
}
}
}

143
src/pagesOrder/recharge/index.tsx

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
/**
*
*/
import { View, Text } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import { useState } from 'react'
import './index.scss'
import { Arrow } from '@taroify/icons'
// 充值套餐数据
interface RechargePackage {
id: number
amount: number // 充值金额
bonus: number // 赠送金额
}
const RechargePackages: RechargePackage[] = [
{ id: 1, amount: 100, bonus: 50 },
{ id: 2, amount: 200, bonus: 100 },
{ id: 3, amount: 300, bonus: 200 },
{ id: 4, amount: 500, bonus: 300 },
{ id: 5, amount: 800, bonus: 500 },
{ id: 6, amount: 1000, bonus: 700 },
]
const RechargePage = () => {
const [selectedPackageId, setSelectedPackageId] = useState<number | null>(null)
const [currentBalance] = useState(28.8) // 当前余额
useLoad(() => {
console.log('余额充值页面加载')
})
// 选择套餐
const handleSelectPackage = (id: number) => {
setSelectedPackageId(id)
}
// 查看明细
const handleViewDetail = () => {
Taro.navigateTo({
url: '/pagesOrder/rechargeDetail/index'
})
}
// 立即充值
const handleRecharge = () => {
if (!selectedPackageId) {
Taro.showToast({
title: '请选择充值套餐',
icon: 'none',
duration: 2000
})
return
}
const selectedPackage = RechargePackages.find(pkg => pkg.id === selectedPackageId)
if (selectedPackage) {
Taro.showToast({
title: `充值 ¥${selectedPackage.amount}`,
icon: 'none',
duration: 2000
})
// TODO: 调用充值接口
}
}
return (
<View className="recharge-page">
{/* 上部分:余额卡片区域 */}
<View className="balance-card-area">
<Text className="store-name"></Text>
<View className="balance-card">
<View className="card-row">
<Text className="label"></Text>
<View className="detail-wrapper" onClick={handleViewDetail}>
<Text className="detail-link"></Text>
<Arrow color='#844614' size="16px" />
</View>
</View>
<View className="amount-row">
<Text className="currency"></Text>
<Text className="amount">{currentBalance.toFixed(2)}</Text>
</View>
<View className="info-row">
<View className="info-item">
<Text className="info-text"></Text>
<Text className="info-value"> 0.00</Text>
</View>
<View className="info-item">
<Text className="info-text"></Text>
<Text className="info-value"> 0.00</Text>
</View>
</View>
</View>
</View>
{/* 下部分:套餐和提示区域 */}
<View className="packages-area">
{/* 余额充值标题 */}
<Text className="section-title"></Text>
{/* 充值套餐列表 */}
<View className="packages-grid">
{RechargePackages.map(pkg => (
<View
key={pkg.id}
className={`package-item ${selectedPackageId === pkg.id ? 'selected' : ''}`}
onClick={() => handleSelectPackage(pkg.id)}
>
<Text className="package-amount"> {pkg.amount}</Text>
<Text className="package-bonus"> {pkg.bonus}</Text>
</View>
))}
</View>
{/* 温馨提示 */}
<View className="tips-section">
<Text className="tips-title">{'\n'}</Text>
<Text className="tips-content">
1. 使使{'\n'}
退{'\n'}
2. {'\n'}
3. {'\n'}
使退
</Text>
</View>
</View>
{/* 底部固定按钮 */}
<View className="footer-bar">
<View className="recharge-btn" onClick={handleRecharge}>
<Text className="recharge-btn-text"></Text>
</View>
</View>
</View>
)
}
export default RechargePage

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

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default definePageConfig({
navigationBarTitleText: '充值明细',
navigationBarBackgroundColor: '#FFFFFF',
navigationBarTextStyle: 'black'
})

147
src/pagesOrder/rechargeDetail/index.scss

@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
.recharge-detail-page {
padding: 30px;
background-color: #F2F3F5;
min-height: 100vh;
// 空数据展示
.empty-data {
display: flex;
justify-content: center;
align-items: center;
padding: 150px 0;
.empty-image {
width: 400px;
height: 400px;
}
}
// 月份卡片
.month-card {
background-color: #FFFFFF;
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: #000000;
}
.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: #000000;
}
.amount-value {
font-size: 30px;
color: #000000;
}
}
}
// 分割线
.divider {
height: 1px;
background-color: #F2F3F5;
}
// 记录列表
.record-list {
padding: 20px;
.record-item {
display: flex;
flex-direction: column;
gap: 20px;
.item-row {
display: flex;
align-items: baseline;
justify-content: space-between;
.store-name {
font-size: 33px;
color: #000000;
flex: 1;
}
.status {
font-size: 32px;
color: #D0955E;
}
.recharge-type {
font-size: 25px;
color: #333333;
}
.recharge-time {
font-size: 22px;
color: #999999;
}
.actual-pay {
display: flex;
align-items: baseline;
.pay-label {
font-size: 22px;
color: #595959;
}
.pay-amount {
font-size: 33px;
color: #000000;
}
}
}
}
// item 之间的分割线
.item-divider {
height: 1px;
background-color: #F2F3F5;
margin: 20px 0;
}
}
}
// 到底了提示
.list-end {
text-align: center;
padding: 40px 0;
.end-text {
font-size: 25px;
color: #999999;
}
}
}

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

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default {
navigationBarTitleText: '订单详情',
navigationBarBackgroundColor: '#FFFFFF',
navigationBarTextStyle: 'black',
}

353
src/pagesUser/orderDetail/index.scss

@ -0,0 +1,353 @@ @@ -0,0 +1,353 @@
.order-detail-page {
min-height: 100vh;
background-color: #F2F3F5;
padding-bottom: 30px; // 底部留白无按钮区域
// 内容区域
.content-area {
padding: 30px;
// 台球桌信息卡片
.table-card {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 190px;
border-radius: 12px;
padding: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-bottom: 40px;
// 顶部类型和电话
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
.table-type {
font-size: 25px;
color: #FFFFFF;
}
.phone-box {
display: flex;
align-items: center;
gap: 8px;
.phone-icon {
width: 28px;
height: 28px;
}
.phone-text {
font-size: 25px;
color: #FFFFFF;
}
}
}
// 台球桌标题和使用状态
.table-title-row {
display: flex;
align-items: center;
gap: 20px;
.table-title {
font-weight: bold;
font-size: 42px;
color: #FFFFFF;
line-height: 1.3;
}
.table-status {
font-size: 42px;
color: #8B8B8B;
font-weight: bold;
line-height: 1.3;
}
}
// 位置信息
.location-info {
display: flex;
align-items: center;
gap: 8px;
.location-icon {
width: 28px;
height: 28px;
}
.location-text {
font-size: 25px;
color: #FFFFFF;
}
}
}
// 开台订单信息卡片
.order-info-card {
background-color: #FFFFFF;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
// 顶部开台方式和详情按钮
.order-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
.order-tag {
background-color: transparent;
border: 1px solid #FF6B00;
border-radius: 4px;
padding: 6px 12px;
.tag-text {
font-size: 25px;
color: #FF6B00;
}
}
.detail-btn {
display: flex;
align-items: center;
gap: 6px;
.detail-text {
font-size: 25px;
color: #595959;
}
.arrow-icon {
transition: transform 0.3s ease;
&.expanded {
transform: rotate(180deg);
}
}
}
}
// 订单统计
.order-stats {
display: flex;
justify-content: space-around;
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
.stat-value {
font-size: 37px;
color: #000000;
font-weight: 500;
}
.stat-label {
font-size: 25px;
color: #8D8D8D;
}
}
}
// 收费规则
.pricing-rules {
padding-top: 30px;
border-top: 1px solid #F2F3F5;
.rules-content {
background-color: #F2F3F5;
border-radius: 13px;
padding: 30px;
display: flex;
flex-direction: column;
gap: 25px;
.rules-title {
font-weight: 400;
font-size: 29px;
color: #000000;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
.price-item {
display: flex;
align-items: center;
gap: 4px;
.price-label {
font-size: 25px;
color: #8B8B8B;
}
.price-value {
font-size: 25px;
color: #333333;
}
}
}
}
}
}
// 展开的详细信息卡片支付信息开台信息
.info-card {
background-color: #FFFFFF;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
.card-title {
font-size: 33px;
color: #000000;
font-weight: 500;
margin-bottom: 30px;
display: block;
}
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 0;
border-bottom: 1px solid #F2F3F5;
&:last-child {
border-bottom: none;
}
&.total {
padding-top: 25px;
.info-label {
color: #8B8B8B;
}
.info-value {
font-size: 35px;
font-weight: 500;
}
}
&.expandable {
cursor: pointer;
}
.info-label {
font-size: 30px;
color: #595959;
flex-shrink: 0;
}
.info-value {
font-size: 30px;
color: #000000;
text-align: right;
flex: 1;
margin-left: 20px;
word-break: break-all;
}
.info-value-box {
display: flex;
align-items: center;
gap: 10px;
}
.info-desc {
font-size: 30px;
color: #8B8B8B;
}
.info-right {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
.info-desc {
font-size: 30px;
color: #8B8B8B;
flex: none;
}
.info-value {
font-size: 30px;
color: #FF6B00;
margin-left: 16px;
margin-right: 10px;
flex: none;
text-align: left;
}
.arrow-icon {
transition: transform 0.3s ease;
flex-shrink: 0;
&.expanded {
transform: rotate(180deg);
}
}
}
}
// 展开内容
.expand-content {
background-color: #F2F3F5;
border-radius: 13px;
padding: 25px;
margin-top: 15px;
margin-bottom: 15px;
.expand-text {
display: block;
font-size: 28px;
color: #333333;
line-height: 1.8;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
.expand-value {
display: block;
font-size: 28px;
color: #FF6B00;
line-height: 1.8;
margin-top: 10px;
}
// 优惠券展开内容横向布局
&.coupon {
display: flex;
justify-content: space-between;
align-items: center;
.expand-text {
margin-bottom: 0;
flex: 1;
}
.expand-value {
margin-top: 0;
flex-shrink: 0;
margin-left: 20px;
}
}
}
}
}
}

230
src/pagesUser/orderDetail/index.tsx

@ -0,0 +1,230 @@ @@ -0,0 +1,230 @@
/**
*
*/
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { ArrowDown } from '@taroify/icons'
import './index.scss'
// 使用本地图片资源
const phoneIcon = require('../../pageScan/startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png')
const tableBgImg = require('../../pageScan/startTable/images/startBiliardCardBg.png')
const OrderDetail = () => {
const router = useRouter()
const { orderId } = router.params as { orderId?: string }
const [expanded, setExpanded] = useState(false) // 订单详情是否展开
const [couponExpanded, setCouponExpanded] = useState(false) // 优惠券详情是否展开
const [priceExpanded, setPriceExpanded] = useState(false) // 收费标准详情是否展开
useLoad(() => {
console.log('订单详情页面加载,订单ID:', orderId)
})
// 拨打电话
const handlePhoneCall = () => {
Taro.makePhoneCall({
phoneNumber: '100-666-XXXX',
}).catch((err) => {
console.error('拨打电话失败:', err)
})
}
// 切换展开/收起
const handleToggleExpand = () => {
setExpanded(!expanded)
}
return (
<View className="order-detail-page">
{/* 内容区域 */}
<View className="content-area">
{/* 台球桌信息卡片 */}
<View className="table-card" style={{ backgroundImage: `url(${tableBgImg})` }}>
{/* 顶部:类型和店名 */}
<View className="card-header">
<Text className="table-type"></Text>
<View className="phone-box" onClick={handlePhoneCall}>
<Image className="phone-icon" src={phoneIcon} mode="aspectFit" />
<Text className="phone-text"></Text>
</View>
</View>
{/* 台球桌标题和使用状态 */}
<View className="table-title-row">
<Text className="table-title">.-A03/3</Text>
<Text className="table-status"></Text>
</View>
{/* 位置信息 */}
<View className="location-info">
<Image className="location-icon" src={locationIcon} mode="aspectFit" />
<Text className="location-text"></Text>
</View>
</View>
{/* 开台订单信息卡片 */}
<View className="order-info-card">
{/* 顶部:开台方式和详情按钮 */}
<View className="order-header" onClick={handleToggleExpand}>
<View className="order-tag">
<Text className="tag-text"></Text>
</View>
<View className="detail-btn">
<Text className="detail-text"></Text>
<ArrowDown
className={`arrow-icon ${expanded ? 'expanded' : ''}`}
size="16px"
color="#646566"
/>
</View>
</View>
{/* 订单统计 */}
<View className="order-stats">
<View className="stat-item">
<Text className="stat-value">156</Text>
<Text className="stat-label"></Text>
</View>
<View className="stat-item">
<Text className="stat-value">100.00</Text>
<Text className="stat-label"></Text>
</View>
</View>
{/* 收费规则 */}
<View className="pricing-rules">
<View className="rules-content">
<Text className="rules-title"></Text>
<View className="price-row">
<View className="price-item">
<Text className="price-label"></Text>
<Text className="price-value">90.00/</Text>
</View>
<View className="price-item">
<Text className="price-label"></Text>
<Text className="price-value">80.00/</Text>
</View>
</View>
</View>
</View>
</View>
{/* 展开的详细信息 */}
{expanded && (
<>
{/* 支付信息卡片 */}
<View className="info-card">
<Text className="card-title"></Text>
<View className="info-row">
<Text className="info-label">线</Text>
<View className="info-value-box">
<Text className="info-desc">0</Text>
</View>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">0</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">92.80</Text>
</View>
{/* 优惠券抵扣 - 可展开 */}
<View className="info-row expandable" onClick={() => setCouponExpanded(!couponExpanded)}>
<Text className="info-label"></Text>
<View className="info-right">
<Text className="info-desc"></Text>
<Text className="info-value">-96.00</Text>
<ArrowDown
className={`arrow-icon ${couponExpanded ? 'expanded' : ''}`}
size="16px"
color="#646566"
/>
</View>
</View>
{/* 优惠券展开内容 */}
{couponExpanded && (
<View className="expand-content coupon">
<Text className="expand-text">2</Text>
<Text className="expand-value">-96.00</Text>
</View>
)}
<View className="info-row total">
<Text className="info-label"></Text>
<Text className="info-value">92.8</Text>
</View>
</View>
{/* 开台信息卡片 */}
<View className="info-card">
<Text className="card-title"></Text>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">XXXXXX店</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value"> .-A03/3</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">2025-01-01 00:00:00</Text>
</View>
<View className="info-row">
<Text className="info-label">使</Text>
<Text className="info-value">156</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">987654321012345678</Text>
</View>
{/* 收费标准 - 可展开 */}
<View className="info-row expandable" onClick={() => setPriceExpanded(!priceExpanded)}>
<Text className="info-label"></Text>
<View className="info-right">
<Text className="info-value">80/</Text>
<ArrowDown
className={`arrow-icon ${priceExpanded ? 'expanded' : ''}`}
size="16px"
color="#646566"
/>
</View>
</View>
{/* 收费标准展开内容 */}
{priceExpanded && (
<View className="expand-content">
<Text className="expand-text">116.67/5</Text>
<Text className="expand-text"> 00:00-11:00 90/80/</Text>
<Text className="expand-text"> 11:00-24:00 100/90/</Text>
</View>
)}
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value"></Text>
</View>
</View>
</>
)}
</View>
</View>
)
}
export default OrderDetail

179
src/pagesUser/orderList/components/FilterPopup.scss

@ -0,0 +1,179 @@ @@ -0,0 +1,179 @@
.filter-popup {
.filter-container {
background-color: #FFFFFF;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
max-height: 80vh;
display: flex;
flex-direction: column;
// 标题栏
.filter-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 33px;
border-bottom: 1px solid #F2F3F5;
.header-title {
font-size: 32px;
color: #333333;
font-weight: bold;
}
.close-icon {
cursor: pointer;
}
}
// 筛选内容
.filter-content {
flex: 1;
overflow-y: auto;
padding: 0 29px;
.filter-section {
.section-title {
display: block;
font-size: 29px;
color: #333333;
margin: 33px 0;
}
// 预制时间段选项
.period-options {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
// 支付方式选项
.payment-options {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 30px;
}
// 选项按钮样式
.option-item {
width: 217px;
height: 71px;
background: #F2F3F5;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.option-text {
font-size: 29px;
color: #333333;
}
// 选中状态
&.selected {
background: #3A3D42;
.option-text {
color: #FFFFFF;
}
}
}
// 日期范围输入
.date-range {
display: flex;
align-items: center;
gap: 25px;
margin-bottom: 30px;
.date-input {
width: 333px;
height: 71px;
background: #FFFFFF;
border-radius: 8px;
border: 1px solid #CCCCCC;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.date-text {
font-size: 29px;
color: #333333;
}
.date-placeholder {
font-size: 29px;
color: #999999;
}
// 有值时的样式
&.has-value {
border-color: #CCCCCC;
}
}
.date-separator {
font-size: 29px;
color: #333333;
}
}
}
}
// 底部按钮
.filter-footer {
display: flex;
gap: 20px;
padding: 30px;
border-top: 1px solid #F2F3F5;
.reset-btn {
flex: 1;
height: 88px;
background: #FFFFFF;
border: 2px solid #03C175;
border-radius: 44px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.reset-text {
font-size: 33px;
color: #03C175;
}
}
.confirm-btn {
flex: 2;
height: 88px;
background: #03C175;
border-radius: 44px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.confirm-text {
font-size: 33px;
color: #FFFFFF;
font-weight: bold;
}
}
}
// 日期选择器 Popup需要浮在筛选 Popup
.date-picker-popup {
z-index: 10001 !important;
:global {
.taroify-popup {
z-index: 10000 !important;
}
}
}
}
}

283
src/pagesUser/orderList/components/FilterPopup.tsx

@ -0,0 +1,283 @@ @@ -0,0 +1,283 @@
/**
*
*/
import { View, Text } from '@tarojs/components'
import { FC, useState } from 'react'
import { Popup, DatetimePicker } from '@taroify/core'
import { Cross } from '@taroify/icons'
import Taro from '@tarojs/taro'
import './FilterPopup.scss'
interface FilterData {
startDate: string
endDate: string
paymentMethods: string[] // 支付方式(可多选)
}
interface FilterPopupProps {
open: boolean
onClose: () => void
onConfirm: (data: FilterData) => void
}
const FilterPopup: FC<FilterPopupProps> = ({ open, onClose, onConfirm }) => {
const [selectedPeriod, setSelectedPeriod] = useState<string>('') // 选中的时间段
const [startDate, setStartDate] = useState('')
const [endDate, setEndDate] = useState('')
const [paymentMethods, setPaymentMethods] = useState<string[]>([])
// 日期选择器相关状态
const [datePickerOpen, setDatePickerOpen] = useState(false)
const [datePickerType, setDatePickerType] = useState<'start' | 'end'>('start') // 选择开始还是结束时间
const [pickerValue, setPickerValue] = useState(new Date())
// 计算日期
const calculateDates = (type: '1week' | '1month' | '3months') => {
const today = new Date()
const yesterday = new Date(today)
yesterday.setDate(yesterday.getDate() - 1)
const startDay = new Date(yesterday)
if (type === '1week') {
startDay.setDate(startDay.getDate() - 7)
} else if (type === '1month') {
startDay.setMonth(startDay.getMonth() - 1)
} else if (type === '3months') {
startDay.setMonth(startDay.getMonth() - 3)
}
const formatDate = (date: Date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
return {
start: formatDate(startDay),
end: formatDate(yesterday)
}
}
// 选择时间段
const handlePeriodSelect = (type: '1week' | '1month' | '3months') => {
setSelectedPeriod(type)
const dates = calculateDates(type)
setStartDate(dates.start)
setEndDate(dates.end)
}
// 切换支付方式选择
const togglePaymentMethod = (method: string) => {
if (paymentMethods.includes(method)) {
setPaymentMethods(paymentMethods.filter(m => m !== method))
} else {
setPaymentMethods([...paymentMethods, method])
}
}
// 打开日期选择器
const openDatePicker = (type: 'start' | 'end') => {
setDatePickerType(type)
// 如果已有日期,使用已有日期作为初始值
if (type === 'start' && startDate) {
setPickerValue(new Date(startDate))
} else if (type === 'end' && endDate) {
setPickerValue(new Date(endDate))
} else {
setPickerValue(new Date())
}
setDatePickerOpen(true)
}
// 格式化日期
const formatDate = (date: Date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// 日期选择确认
const handleDateConfirm = (value: Date) => {
const selectedDate = formatDate(value)
if (datePickerType === 'start') {
// 选择开始时间
if (endDate && selectedDate > endDate) {
Taro.showToast({
title: '开始时间不能晚于结束时间',
icon: 'none',
duration: 2000
})
setDatePickerOpen(false)
return
}
setStartDate(selectedDate)
} else {
// 选择结束时间
if (startDate && selectedDate < startDate) {
Taro.showToast({
title: '结束时间不能早于开始时间',
icon: 'none',
duration: 2000
})
setDatePickerOpen(false)
return
}
setEndDate(selectedDate)
}
// 取消预制时间段的选中状态
setSelectedPeriod('')
setDatePickerOpen(false)
}
// 取消日期选择
const handleDateCancel = () => {
setDatePickerOpen(false)
}
// 重置
const handleReset = () => {
setSelectedPeriod('')
setStartDate('')
setEndDate('')
setPaymentMethods([])
}
// 确定
const handleConfirm = () => {
onConfirm({
startDate,
endDate,
paymentMethods
})
onClose()
}
return (
<Popup
open={open}
placement="bottom"
onClose={onClose}
className="filter-popup"
>
<View className="filter-container">
{/* 标题栏 */}
<View className="filter-header">
<Text className="header-title"></Text>
<Cross className="close-icon" size="20px" color="#333333" onClick={onClose} />
</View>
{/* 筛选内容 */}
<View className="filter-content">
{/* 下单时间 */}
<View className="filter-section">
<Text className="section-title"></Text>
{/* 预制时间段选择 */}
<View className="period-options">
<View
className={`option-item ${selectedPeriod === '1week' ? 'selected' : ''}`}
onClick={() => handlePeriodSelect('1week')}
>
<Text className="option-text">1</Text>
</View>
<View
className={`option-item ${selectedPeriod === '1month' ? 'selected' : ''}`}
onClick={() => handlePeriodSelect('1month')}
>
<Text className="option-text">1</Text>
</View>
<View
className={`option-item ${selectedPeriod === '3months' ? 'selected' : ''}`}
onClick={() => handlePeriodSelect('3months')}
>
<Text className="option-text">3</Text>
</View>
</View>
{/* 日期范围输入 */}
<View className="date-range">
<View
className={`date-input ${startDate ? 'has-value' : ''}`}
onClick={() => openDatePicker('start')}
>
<Text className={startDate ? 'date-text' : 'date-placeholder'}>
{startDate || '开始时间'}
</Text>
</View>
<Text className="date-separator">-</Text>
<View
className={`date-input ${endDate ? 'has-value' : ''}`}
onClick={() => openDatePicker('end')}
>
<Text className={endDate ? 'date-text' : 'date-placeholder'}>
{endDate || '结束时间'}
</Text>
</View>
</View>
</View>
{/* 支付方式 */}
<View className="filter-section">
<Text className="section-title"></Text>
<View className="payment-options">
<View
className={`option-item ${paymentMethods.includes('balance') ? 'selected' : ''}`}
onClick={() => togglePaymentMethod('balance')}
>
<Text className="option-text"></Text>
</View>
<View
className={`option-item ${paymentMethods.includes('cash') ? 'selected' : ''}`}
onClick={() => togglePaymentMethod('cash')}
>
<Text className="option-text"></Text>
</View>
<View
className={`option-item ${paymentMethods.includes('coupon') ? 'selected' : ''}`}
onClick={() => togglePaymentMethod('coupon')}
>
<Text className="option-text"></Text>
</View>
</View>
</View>
</View>
{/* 底部按钮 */}
<View className="filter-footer">
<View className="reset-btn" onClick={handleReset}>
<Text className="reset-text"></Text>
</View>
<View className="confirm-btn" onClick={handleConfirm}>
<Text className="confirm-text"></Text>
</View>
</View>
{/* 日期选择器 Popup */}
<Popup
open={datePickerOpen}
placement="bottom"
onClose={handleDateCancel}
className="date-picker-popup"
>
<DatetimePicker
type="date"
value={pickerValue}
onChange={setPickerValue}
onCancel={handleDateCancel}
onConfirm={handleDateConfirm}
/>
</Popup>
</View>
</Popup>
)
}
export default FilterPopup

181
src/pagesUser/orderList/index.scss

@ -1,5 +1,184 @@ @@ -1,5 +1,184 @@
.order-list-page {
min-height: 100vh;
background-color: #F2F3F5;
padding: 30px;
// 第一层 Tab台球/棋牌
.category-tab-container {
background-color: #FFFFFF;
:global {
// Tab 导航容器
.taroify-tabs__nav {
background-color: #FFFFFF !important;
}
// 未选中 Tab
.taroify-tabs__tab {
font-size: 33px !important;
color: #8B8B8B !important;
}
// 选中 Tab
.taroify-tabs__tab--active {
color: #333333 !important;
}
// 选中指示线
.taroify-tabs__line {
background-color: #03C175 !important;
}
}
}
// 第二层 Tab全部/进行中/已完成
.status-tab-container {
display: flex;
align-items: center;
background-color: none;
padding: 0 30px;
position: relative;
.status-tabs {
flex: 1;
.taroify-tabs__wrap{
.taroify-tabs__wrap__scroll{
background: none !important;
.wx-scroll-view{
.wx-scroll-view{
.taroify-tabs__nav{
background: none !important;
}
}
}
}
}
:global {
// 未选中 Tab
.taroify-tabs__tab {
font-size: 30px !important;
color: #8B8B8B !important;
}
// 选中 Tab
.taroify-tabs__tab--active {
color: #03C175 !important;
}
// 选中指示线
.taroify-tabs__line {
background-color: #03C175 !important;
}
}
}
// 筛选按钮
.filter-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 10px 0;
.filter-text {
font-size: 30px;
color: #8B8B8B;
}
}
}
// 订单列表
.order-list {
padding: 30px;
// 空数据
.empty-data {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150px 0;
.empty-image {
width: 400px;
height: 400px;
margin-bottom: 30px;
}
.empty-text {
font-size: 30px;
color: #999999;
}
}
// 订单卡片
.order-card {
background-color: #FFFFFF;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
// 标题行
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
.table-name {
font-size: 33px;
color: #333333;
font-weight: bold;
}
.status {
font-size: 33px;
&.ongoing {
color: #03C175;
}
&.completed {
color: #8B8B8B;
}
}
}
// 订单信息
.card-info {
display: flex;
flex-direction: column;
gap: 20px;
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
.info-label {
font-size: 30px;
color: #333333;
}
.info-value {
font-size: 30px;
color: #595959;
}
}
}
}
// 到底了提示
.list-end {
text-align: center;
padding: 40px 0;
.end-text {
font-size: 25px;
color: #999999;
}
}
}
}

189
src/pagesUser/orderList/index.tsx

@ -1,18 +1,201 @@ @@ -1,18 +1,201 @@
/**
*
*/
import { View, Text } from '@tarojs/components'
import { useLoad } from '@tarojs/taro'
import { View, Text, Image } from '@tarojs/components'
import { useState } from 'react'
import Taro, { useLoad } from '@tarojs/taro'
import { Tabs, Divider } from '@taroify/core'
import { ArrowDown } from '@taroify/icons'
import FilterPopup from './components/FilterPopup'
import './index.scss'
const noDataImg = require('../../assets/big/noData.png')
// 订单数据接口
interface OrderItem {
id: string
tableName: string // 台球 A02
storeName: string // 消费门店
startTime: string // 开台时间
duration: string // 开台时长
amount: number // 实付金额
status: 'ongoing' | 'completed' // 进行中 | 已完成
}
const OrderList = () => {
const [categoryTab, setCategoryTab] = useState(0) // 0: 台球, 1: 棋牌
const [statusTab, setStatusTab] = useState(0) // 0: 全部, 1: 进行中, 2: 已完成
const [filterPopupOpen, setFilterPopupOpen] = useState(false) // 筛选弹窗
const [filterData, setFilterData] = useState({
startDate: '',
endDate: '',
paymentMethods: [] as string[]
})
// 模拟订单数据
const [orderList] = useState<OrderItem[]>([
{
id: '1',
tableName: '台球 A02',
storeName: 'XXXXXXXXXXXX店',
startTime: '2025-01-02 16:30:01',
duration: '1小时30分钟',
amount: 28.80,
status: 'ongoing'
},
{
id: '2',
tableName: '台球 A01',
storeName: 'XXXXXXXXXXXX店',
startTime: '2025-01-01 16:30:01',
duration: '1小时30分钟',
amount: 28.80,
status: 'completed'
}
])
useLoad(() => {
console.log('订单记录页面加载')
})
// 筛选按钮点击
const handleFilter = () => {
setFilterPopupOpen(true)
}
// 筛选确认
const handleFilterConfirm = (data: any) => {
setFilterData(data)
// 计算筛选数量
let count = 0
if (data.startDate || data.endDate) count++
if (data.paymentMethods.length > 0) count += data.paymentMethods.length
console.log('筛选数据', data, '筛选数量', count)
Taro.showToast({
title: '筛选成功',
icon: 'success',
duration: 2000
})
}
// 计算筛选数量
const getFilterCount = () => {
let count = 0
if (filterData.startDate || filterData.endDate) count++
if (filterData.paymentMethods.length > 0) count += filterData.paymentMethods.length
return count
}
// 根据选中的 Tab 筛选订单
const getFilteredOrders = () => {
let filtered = orderList
// 根据状态筛选
if (statusTab === 1) {
filtered = filtered.filter(order => order.status === 'ongoing')
} else if (statusTab === 2) {
filtered = filtered.filter(order => order.status === 'completed')
}
return filtered
}
const filteredOrders = getFilteredOrders()
// 订单卡片点击
const handleOrderClick = (orderId: string) => {
console.log('订单详情', orderId)
Taro.navigateTo({
url: `/pagesUser/orderDetail/index?orderId=${orderId}`
})
}
return (
<View className="order-list-page">
<Text></Text>
{/* 第一层 Tab:台球/棋牌 */}
<View className="category-tab-container">
<Tabs value={categoryTab} onChange={setCategoryTab} className="category-tabs">
<Tabs.TabPane title="台球" />
<Tabs.TabPane title="棋牌" />
</Tabs>
</View>
{/* 第二层 Tab:全部/进行中/已完成 + 筛选 */}
<View className="status-tab-container">
<Tabs value={statusTab} onChange={setStatusTab} className="status-tabs">
<Tabs.TabPane title="全部" />
<Tabs.TabPane title="进行中" />
<Tabs.TabPane title="已完成" />
</Tabs>
<View className="filter-btn" onClick={handleFilter}>
<Text className="filter-text">{getFilterCount() > 0 ? ` ${getFilterCount()}` : ''}</Text>
<ArrowDown size="16px" color="#8B8B8B" />
</View>
</View>
{/* 订单列表 */}
<View className="order-list">
{filteredOrders.length === 0 ? (
<View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" />
<Text className="empty-text">~</Text>
</View>
) : (
<>
{filteredOrders.map(order => (
<View
key={order.id}
className="order-card"
onClick={() => handleOrderClick(order.id)}
>
{/* 标题行 */}
<View className="card-header">
<Text className="table-name">{order.tableName}</Text>
<Text className={`status ${order.status}`}>
{order.status === 'ongoing' ? '进行中' : '已完成'}
</Text>
</View>
<Divider dashed={true} />
{/* 订单信息 */}
<View className="card-info">
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.storeName}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.startTime}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.duration}</Text>
</View>
<View className="info-row">
<Text className="info-label"></Text>
<Text className="info-value">{order.amount.toFixed(2)}</Text>
</View>
</View>
</View>
))}
{/* 到底了提示 */}
<View className="list-end">
<Text className="end-text">~</Text>
</View>
</>
)}
</View>
{/* 筛选弹窗 */}
<FilterPopup
open={filterPopupOpen}
onClose={() => setFilterPopupOpen(false)}
onConfirm={handleFilterConfirm}
/>
</View>
)
}

Loading…
Cancel
Save