账单
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '账单详情',
|
||||
})
|
||||
@@ -0,0 +1,264 @@
|
||||
.bill-detail {
|
||||
min-height: 100vh;
|
||||
background: #f7f8fa;
|
||||
padding: 20rpx 24rpx 60rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.bill-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
&__no {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
&__status {
|
||||
font-size: 24rpx;
|
||||
|
||||
// 0 待支付 / 1 审核中 / 2 已支付
|
||||
&--0 { color: #ee0a24; }
|
||||
&--1 { color: #ff976a; }
|
||||
&--2 { color: #07c160; }
|
||||
}
|
||||
|
||||
&__tip {
|
||||
display: block;
|
||||
margin: -4rpx 0 16rpx;
|
||||
padding: 12rpx 16rpx;
|
||||
background: #fffbe8;
|
||||
border-radius: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #ed6a0c;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10rpx 0;
|
||||
|
||||
&--total {
|
||||
margin-top: 12rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #ebedf0;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 26rpx;
|
||||
color: #969799;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 26rpx;
|
||||
color: #323233;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__total {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #ee0a24;
|
||||
}
|
||||
}
|
||||
|
||||
.bill-section {
|
||||
&__title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #323233;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
font-size: 22rpx;
|
||||
color: #c8c9cc;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bill-goods {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__spec {
|
||||
font-size: 22rpx;
|
||||
color: #c8c9cc;
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
&__side {
|
||||
text-align: right;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__qty {
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__amount {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bill-order {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__no {
|
||||
font-size: 26rpx;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
&__date {
|
||||
font-size: 22rpx;
|
||||
color: #969799;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
&__status {
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
margin: 0 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__amount {
|
||||
font-size: 26rpx;
|
||||
color: #323233;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 订单明细弹层 =====
|
||||
.order-popup {
|
||||
padding: 32rpx 32rpx 24rpx;
|
||||
|
||||
&__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
margin-top: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
&__status {
|
||||
color: #ee0a24;
|
||||
}
|
||||
|
||||
&__list {
|
||||
max-height: 480rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
|
||||
&-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-name {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-spec {
|
||||
margin-top: 6rpx;
|
||||
font-size: 22rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
&-amount {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__total {
|
||||
font-size: 30rpx;
|
||||
color: #ee0a24;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useRouter } from '@tarojs/taro'
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { Empty, Popup } from '@antmjs/vantui'
|
||||
import { getBillDetailApi } from '@/services/bill'
|
||||
import { getOrderDetailApi } from '@/services/order'
|
||||
import { ORDER_STATUS_TEXT } from '@/types/order'
|
||||
import type { OrderDetail } from '@/types/order'
|
||||
import type { BillDetail } from '@/services/bill'
|
||||
import './index.less'
|
||||
|
||||
/**
|
||||
* 账单详情
|
||||
* 账单信息 + 金额构成(商品/配送费/筐托盘附加)+ 合并商品明细 + 关联订单(可下钻订单明细)
|
||||
*/
|
||||
export default function BillDetailPage() {
|
||||
const router = useRouter()
|
||||
const id = Number(router.params.id ?? 0)
|
||||
|
||||
const [detail, setDetail] = useState<BillDetail | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
/** 关联订单明细弹层 */
|
||||
const [showOrder, setShowOrder] = useState(false)
|
||||
const [orderDetail, setOrderDetail] = useState<OrderDetail | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
setLoading(true)
|
||||
getBillDetailApi(id)
|
||||
.then(res => setDetail(res.data))
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false))
|
||||
}, [id])
|
||||
|
||||
/** 下钻关联订单明细 */
|
||||
const handleOrderTap = useCallback(async (orderId: number) => {
|
||||
try {
|
||||
const res = await getOrderDetailApi(orderId)
|
||||
setOrderDetail(res.data)
|
||||
setShowOrder(true)
|
||||
} catch {
|
||||
// 错误已由 request 层 toast
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (loading && !detail) {
|
||||
return <View className='bill-detail'><Empty description='加载中...' /></View>
|
||||
}
|
||||
if (!detail) {
|
||||
return <View className='bill-detail'><Empty description='账单不存在' /></View>
|
||||
}
|
||||
|
||||
const { bill, items, orders } = detail
|
||||
|
||||
return (
|
||||
<View className='bill-detail'>
|
||||
{/* ===== 账单信息 ===== */}
|
||||
<View className='bill-card'>
|
||||
<View className='bill-card__header'>
|
||||
<Text className='bill-card__no'>{bill.bill_no}</Text>
|
||||
<Text className={`bill-card__status bill-card__status--${bill.pay_state}`}>
|
||||
{bill.pay_state_name}
|
||||
</Text>
|
||||
</View>
|
||||
{bill.pay_state === 1 && (
|
||||
<Text className='bill-card__tip'>已提交付款凭证,待后台审核;审核拒绝后将回到待支付</Text>
|
||||
)}
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>账单日期</Text>
|
||||
<Text className='bill-card__value'>{bill.bill_date}</Text>
|
||||
</View>
|
||||
{bill.purchase && (
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>关联采购单</Text>
|
||||
<Text className='bill-card__value'>
|
||||
{bill.purchase.purchase_no}({bill.purchase.purchase_date})
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>应结算日期</Text>
|
||||
<Text className='bill-card__value'>{bill.settlement_date}</Text>
|
||||
</View>
|
||||
{bill.pay_state === 2 && bill.paid_at && (
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>付款时间</Text>
|
||||
<Text className='bill-card__value'>{bill.paid_at}</Text>
|
||||
</View>
|
||||
)}
|
||||
{bill.pay_remark && (
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>付款备注</Text>
|
||||
<Text className='bill-card__value'>{bill.pay_remark}</Text>
|
||||
</View>
|
||||
)}
|
||||
{bill.remark && (
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>账单备注</Text>
|
||||
<Text className='bill-card__value'>{bill.remark}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* ===== 金额构成 ===== */}
|
||||
<View className='bill-card'>
|
||||
<Text className='bill-section__title'>金额构成</Text>
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>商品金额</Text>
|
||||
<Text className='bill-card__value'>¥{bill.product_amount}</Text>
|
||||
</View>
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>配送费</Text>
|
||||
<Text className='bill-card__value'>¥{bill.delivery_fee}</Text>
|
||||
</View>
|
||||
<View className='bill-card__row'>
|
||||
<Text className='bill-card__label'>
|
||||
附加金额(筐 {bill.box_num}×¥{bill.box_price},托盘 {bill.tray_num}×¥{bill.tray_price})
|
||||
</Text>
|
||||
<Text className='bill-card__value'>¥{bill.added_amount}</Text>
|
||||
</View>
|
||||
<View className='bill-card__row bill-card__row--total'>
|
||||
<Text className='bill-card__label'>账单总额</Text>
|
||||
<Text className='bill-card__total'>¥{bill.total_amount}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* ===== 商品明细(跨订单按商品合并) ===== */}
|
||||
<View className='bill-card'>
|
||||
<Text className='bill-section__title'>商品明细({items?.length ?? 0})</Text>
|
||||
<Text className='bill-section__desc'>同一商品跨订单合并,单价为加权平均价</Text>
|
||||
{(items ?? []).map(item => (
|
||||
<View key={item.product_id} className='bill-goods'>
|
||||
<View className='bill-goods__main'>
|
||||
<Text className='bill-goods__name'>{item.product_name}</Text>
|
||||
<Text className='bill-goods__spec'>
|
||||
{item.product_spec ? `${item.product_spec} ` : ''}¥{item.price}/{item.unit}
|
||||
</Text>
|
||||
</View>
|
||||
<View className='bill-goods__side'>
|
||||
<Text className='bill-goods__qty'>×{item.quantity}</Text>
|
||||
<Text className='bill-goods__amount'>¥{item.amount}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
{(items ?? []).length === 0 && <Empty description='暂无商品记录' />}
|
||||
</View>
|
||||
|
||||
{/* ===== 关联订单 ===== */}
|
||||
<View className='bill-card'>
|
||||
<Text className='bill-section__title'>关联订单({orders?.length ?? 0})</Text>
|
||||
{(orders ?? []).map(order => (
|
||||
<View key={order.id} className='bill-order' onClick={() => handleOrderTap(order.id)}>
|
||||
<View className='bill-order__main'>
|
||||
<Text className='bill-order__no'>{order.order_no}</Text>
|
||||
<Text className='bill-order__date'>{order.order_date}</Text>
|
||||
</View>
|
||||
<Text className='bill-order__status'>{ORDER_STATUS_TEXT[order.status] || ''}</Text>
|
||||
<Text className='bill-order__amount'>¥{order.total_amount}</Text>
|
||||
</View>
|
||||
))}
|
||||
{(orders ?? []).length === 0 && <Empty description='暂无订单记录' />}
|
||||
</View>
|
||||
|
||||
{/* ===== 订单明细弹层 ===== */}
|
||||
<Popup
|
||||
show={showOrder}
|
||||
position='bottom'
|
||||
round
|
||||
closeable
|
||||
closeOnClickOverlay
|
||||
safeAreaInsetBottom
|
||||
onClose={() => setShowOrder(false)}
|
||||
>
|
||||
{orderDetail && (
|
||||
<View className='order-popup'>
|
||||
<Text className='order-popup__title'>{orderDetail.order_no}</Text>
|
||||
<View className='order-popup__meta'>
|
||||
<Text>{orderDetail.order_date}</Text>
|
||||
<Text className='order-popup__status'>
|
||||
{ORDER_STATUS_TEXT[orderDetail.status] || ''}
|
||||
</Text>
|
||||
</View>
|
||||
<ScrollView scrollY className='order-popup__list'>
|
||||
{(orderDetail.items ?? []).map(item => (
|
||||
<View key={item.id} className='order-popup__item'>
|
||||
<View className='order-popup__item-info'>
|
||||
<Text className='order-popup__item-name'>{item.product_name}</Text>
|
||||
<Text className='order-popup__item-spec'>
|
||||
{item.product_spec ? `${item.product_spec} ` : ''}¥{item.price}/{item.unit} × {item.quantity}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='order-popup__item-amount'>¥{item.amount}</Text>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
<View className='order-popup__footer'>
|
||||
<Text className='order-popup__total'>合计 ¥{orderDetail.total_amount}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</Popup>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user