158 lines
5.2 KiB
TypeScript
158 lines
5.2 KiB
TypeScript
import { Button, Empty, ErrorBlock, PullToRefresh, Skeleton, Tag } from 'antd-mobile'
|
|
import { RightOutline } from 'antd-mobile-icons'
|
|
import { Link, useNavigate } from 'react-router-dom'
|
|
|
|
import { BrandLogo } from '@/components/BrandLogo'
|
|
import { useMyOrders } from '@/features/orders/api'
|
|
import { orderStatusMeta, roleLabel } from '@/features/orders/labels'
|
|
import type { UserOrderListItem } from '@/features/orders/types'
|
|
import { useSessionStore } from '@/stores/session'
|
|
import { formatDateTime, formatMoney, formatText } from '@/shared/utils/format'
|
|
|
|
function orderSummary(item: UserOrderListItem) {
|
|
return item.rental_days ? `${item.rental_days} 天` : formatText(item.login_platform)
|
|
}
|
|
|
|
function MobileOrderCard({ item }: { item: UserOrderListItem }) {
|
|
const navigate = useNavigate()
|
|
const status = orderStatusMeta(item.status)
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="mobile-order-card"
|
|
onClick={() => navigate(`/m/orders/${item.id}`)}
|
|
>
|
|
<div className="mobile-order-card__top">
|
|
<span className="mobile-order-card__no">{item.order_no}</span>
|
|
<Tag color={status.tone} fill="outline">
|
|
{status.label}
|
|
</Tag>
|
|
</div>
|
|
|
|
<div className="mobile-order-card__title">{item.title}</div>
|
|
|
|
<div className="mobile-order-card__meta">
|
|
<span>{roleLabel(item.role)}</span>
|
|
<span>{formatText(item.server_region)}</span>
|
|
<span>{orderSummary(item)}</span>
|
|
</div>
|
|
|
|
<div className="mobile-order-card__bottom">
|
|
<div>
|
|
<strong>{formatMoney(item.display_amount_cent)}</strong>
|
|
<small>押金 {formatMoney(item.deposit_amount_cent)}</small>
|
|
</div>
|
|
<span className="mobile-order-card__date">
|
|
{formatDateTime(item.created_at)} <RightOutline />
|
|
</span>
|
|
</div>
|
|
</button>
|
|
)
|
|
}
|
|
|
|
export function MobileOrdersPage() {
|
|
const navigate = useNavigate()
|
|
const token = useSessionStore(state => state.token)
|
|
const hydrated = useSessionStore(state => state.hydrated)
|
|
const query = useMyOrders(Boolean(token))
|
|
const items = query.data?.pages.flatMap(page => page.items) ?? []
|
|
const total = query.data?.pages[0]?.total ?? 0
|
|
|
|
if (!hydrated) {
|
|
return (
|
|
<div className="mobile-orders">
|
|
<Skeleton.Title animated />
|
|
<Skeleton.Paragraph lineCount={6} animated />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!token) {
|
|
return (
|
|
<div className="mobile-orders">
|
|
<section className="mobile-orders__head">
|
|
<span className="mobile-orders__eyebrow">MY ORDERS</span>
|
|
<h1>我的订单</h1>
|
|
<p>登录后查看租用进度、交接与结账记录。</p>
|
|
</section>
|
|
<div className="mobile-orders__login">
|
|
<BrandLogo height={40} variant="seal" />
|
|
<h2>先登录,再看订单</h2>
|
|
<p>订单、押金与结账明细只对交易双方可见。</p>
|
|
<Button
|
|
block
|
|
color="primary"
|
|
onClick={() => navigate('/m/login?redirect=/m/orders')}
|
|
>
|
|
去登录
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="mobile-orders">
|
|
<section className="mobile-orders__head">
|
|
<span className="mobile-orders__eyebrow">MY ORDERS</span>
|
|
<h1>我的订单</h1>
|
|
<p>{query.data ? `共 ${total} 单 · 下拉可刷新` : '正在读取你的订单…'}</p>
|
|
</section>
|
|
|
|
{query.isPending ? (
|
|
<div className="mobile-orders__skeleton">
|
|
{Array.from({ length: 3 }).map((_, index) => (
|
|
<div className="mobile-order-card mobile-order-card--skeleton" key={index}>
|
|
<Skeleton.Title animated />
|
|
<Skeleton.Paragraph lineCount={3} animated />
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : query.isError ? (
|
|
<ErrorBlock
|
|
status="disconnected"
|
|
title="订单没有加载出来"
|
|
description="检查网络后重试,或重新登录。"
|
|
>
|
|
<Button color="primary" size="small" onClick={() => void query.refetch()}>
|
|
重新加载
|
|
</Button>
|
|
</ErrorBlock>
|
|
) : (
|
|
<PullToRefresh onRefresh={() => query.refetch().then(() => undefined)}>
|
|
<div className="mobile-orders__list">
|
|
{items.length === 0 ? (
|
|
<div className="mobile-orders__empty">
|
|
<Empty description="还没有订单" />
|
|
<Link to="/m" className="mobile-orders__empty-link">
|
|
去逛逛商品 <RightOutline />
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
items.map(item => <MobileOrderCard item={item} key={item.id} />)
|
|
)}
|
|
|
|
{items.length > 0 ? (
|
|
<div className="mobile-orders__more">
|
|
{query.hasNextPage ? (
|
|
<Button
|
|
block
|
|
fill="outline"
|
|
loading={query.isFetchingNextPage}
|
|
onClick={() => void query.fetchNextPage()}
|
|
>
|
|
加载更多
|
|
</Button>
|
|
) : (
|
|
<span>已经看到全部订单</span>
|
|
)}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</PullToRefresh>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|