260 lines
8.7 KiB
TypeScript
260 lines
8.7 KiB
TypeScript
import { Button, Image, Input, Text, View } from '@tarojs/components'
|
||
import Taro, { useDidShow, useLoad } from '@tarojs/taro'
|
||
import { useMemo, useRef, useState } from 'react'
|
||
import { getOrdersByPhone } from '@/api/pay'
|
||
import { LOCAL_IMAGES } from '@/lib/assets'
|
||
import { bootstrapSession } from '@/lib/bootstrap'
|
||
import { COPY_ICON, PAGE_TITLE } from '@/lib/cdn'
|
||
import { getNavMetrics } from '@/lib/nav'
|
||
import { pickServiceContact, type ServiceContact } from '@/lib/contact'
|
||
import { setOrderDraft } from '@/lib/orderDraft'
|
||
import { filterOrdersForProduct, getOrderDisplayName, type OrderLike } from '@/lib/order'
|
||
import { isValidPhone } from '@/lib/phone'
|
||
import { getSession } from '@/lib/session'
|
||
import {
|
||
filterOrdersByTab,
|
||
getOrderCardActions,
|
||
getStatusMeta,
|
||
ORDER_TABS,
|
||
type OrderTabKey,
|
||
} from '@/lib/status'
|
||
import ContactModal, { ContactFab } from './contact'
|
||
import DetailDrawer from './detail'
|
||
import './index.scss'
|
||
|
||
export default function Index () {
|
||
const nav = useMemo(() => getNavMetrics(), [])
|
||
const [ready, setReady] = useState(false)
|
||
const [inputPhone, setInputPhone] = useState('')
|
||
const [checking, setChecking] = useState(false)
|
||
const [searched, setSearched] = useState(false)
|
||
const [activeTab, setActiveTab] = useState<OrderTabKey>('all')
|
||
const [orders, setOrders] = useState<OrderLike[]>([])
|
||
const [detailOrder, setDetailOrder] = useState<OrderLike | null>(null)
|
||
const [showContact, setShowContact] = useState(false)
|
||
const [contact, setContact] = useState<ServiceContact>({ online: '', phone: '', weixin: '' })
|
||
const inputPhoneRef = useRef(inputPhone)
|
||
const searchedRef = useRef(searched)
|
||
inputPhoneRef.current = inputPhone
|
||
searchedRef.current = searched
|
||
|
||
useLoad(async (options) => {
|
||
try {
|
||
const session = await bootstrapSession(options || {})
|
||
setInputPhone(session.phone)
|
||
setContact(pickServiceContact(session.config))
|
||
setReady(true)
|
||
if (session.phone) {
|
||
await queryOrders(session.phone)
|
||
}
|
||
} catch (error) {
|
||
Taro.showToast({
|
||
title: error instanceof Error ? error.message : '初始化失败',
|
||
icon: 'none',
|
||
})
|
||
}
|
||
})
|
||
|
||
async function queryOrders (phone: string) {
|
||
if (!phone) {
|
||
Taro.showToast({ title: '请输入联系电话', icon: 'none' })
|
||
return
|
||
}
|
||
if (!isValidPhone(phone)) {
|
||
Taro.showToast({ title: '请填写正确联系电话', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
setChecking(true)
|
||
try {
|
||
const res = await getOrdersByPhone(phone)
|
||
const nextOrders = filterOrdersForProduct(getSession().goodsType, res.data || [])
|
||
setOrders(nextOrders)
|
||
setSearched(true)
|
||
} catch {
|
||
Taro.showToast({ title: '当前无法查询,请稍后再试', icon: 'none' })
|
||
} finally {
|
||
setChecking(false)
|
||
}
|
||
}
|
||
|
||
useDidShow(() => {
|
||
if (searchedRef.current && inputPhoneRef.current) {
|
||
void queryOrders(inputPhoneRef.current)
|
||
}
|
||
})
|
||
|
||
const visibleOrders = filterOrdersByTab(orders, activeTab)
|
||
const emptyText = searched && orders.length === 0
|
||
? '该手机号未查询到订单,请联系客服处理'
|
||
: '暂无订单列表'
|
||
|
||
const openOrderPage = (path: string, order: OrderLike) => {
|
||
setOrderDraft(order)
|
||
Taro.navigateTo({ url: path })
|
||
}
|
||
|
||
return (
|
||
<View className='home' style={{ paddingTop: nav.statusBarHeight }}>
|
||
<View className='home-nav' style={{ height: nav.navBarHeight, paddingRight: nav.paddingRight }}>
|
||
<Image className='home-nav__logo' src={LOCAL_IMAGES.logo} />
|
||
<Text className='home-nav__title'>{PAGE_TITLE}</Text>
|
||
</View>
|
||
|
||
<View className='home-search'>
|
||
<View className='home-search__field'>
|
||
<Image className='home-search__icon' src={LOCAL_IMAGES.search} />
|
||
<Input
|
||
className='home-search__input'
|
||
type='number'
|
||
maxlength={12}
|
||
placeholder='请输入手机号查询订单'
|
||
placeholderClass='home-search__placeholder'
|
||
value={inputPhone}
|
||
onInput={(event) => setInputPhone(event.detail.value.replace(/\D/g, ''))}
|
||
/>
|
||
{inputPhone ? (
|
||
<Image
|
||
className='home-search__clear'
|
||
src={LOCAL_IMAGES.clear}
|
||
onClick={() => setInputPhone('')}
|
||
/>
|
||
) : null}
|
||
</View>
|
||
<Button
|
||
className='home-search__button'
|
||
loading={checking}
|
||
disabled={!ready || checking}
|
||
onClick={() => void queryOrders(inputPhone)}
|
||
>
|
||
查询
|
||
</Button>
|
||
</View>
|
||
|
||
<View className='home-tabs'>
|
||
{ORDER_TABS.map((tab) => (
|
||
<View
|
||
key={tab.key}
|
||
className={`home-tabs__item ${activeTab === tab.key ? 'home-tabs__item--active' : ''}`}
|
||
onClick={() => setActiveTab(tab.key)}
|
||
>
|
||
<Text className='home-tabs__label'>{tab.label}</Text>
|
||
{activeTab === tab.key ? <View className='home-tabs__line' /> : null}
|
||
</View>
|
||
))}
|
||
</View>
|
||
|
||
{visibleOrders.length ? (
|
||
<View className='home-list'>
|
||
{visibleOrders.map((order, index) => (
|
||
<OrderCard
|
||
key={order.order_id || order.transaction_id || index}
|
||
order={order}
|
||
onCopy={() => {
|
||
if (!order.transaction_id) return
|
||
Taro.setClipboardData({ data: String(order.transaction_id) })
|
||
}}
|
||
onViewDetail={() => setDetailOrder(order)}
|
||
onResubmit={() => openOrderPage('/pages/resubmit/index', order)}
|
||
onComplaint={() => openOrderPage('/pages/complaint/index', order)}
|
||
/>
|
||
))}
|
||
</View>
|
||
) : (
|
||
<View className='home-empty'>
|
||
<Image className='home-empty__image' src={LOCAL_IMAGES.empty} mode='aspectFit' />
|
||
<Text className='home-empty__text'>{emptyText}</Text>
|
||
</View>
|
||
)}
|
||
|
||
<ContactFab onClick={() => setShowContact(true)} />
|
||
|
||
{detailOrder ? (
|
||
<DetailDrawer order={detailOrder} onCancel={() => setDetailOrder(null)} />
|
||
) : null}
|
||
|
||
<ContactModal
|
||
open={showContact}
|
||
contact={contact}
|
||
onCancel={() => setShowContact(false)}
|
||
/>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function OrderCard ({
|
||
order,
|
||
onCopy,
|
||
onViewDetail,
|
||
onResubmit,
|
||
onComplaint,
|
||
}: {
|
||
order: OrderLike
|
||
onCopy: () => void
|
||
onViewDetail: () => void
|
||
onResubmit: () => void
|
||
onComplaint: () => void
|
||
}) {
|
||
const status = getStatusMeta(order.process_status, order.process_status_name)
|
||
const actions = getOrderCardActions(order)
|
||
|
||
return (
|
||
<View className='order-card'>
|
||
<View className='order-card__head'>
|
||
<Text className='order-card__goods-name'>{getOrderDisplayName(order)}</Text>
|
||
<View className='order-card__status' style={{ backgroundColor: status.bg }}>
|
||
<Text className='order-card__status-text' style={{ color: status.color }}>{status.result}</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<InfoLine label='门店电话:' value={order.entity_phone || '-'} />
|
||
<InfoLine label='订单时间:' value={order.create_time || order.pay_time || '-'} />
|
||
|
||
<View className='order-card__row'>
|
||
<Text className='order-card__label'>交易单号:</Text>
|
||
<Text className='order-card__value order-card__value--id'>{order.transaction_id || '-'}</Text>
|
||
{order.transaction_id ? (
|
||
<Image className='order-card__copy' src={COPY_ICON} onClick={onCopy} />
|
||
) : null}
|
||
</View>
|
||
|
||
<View className='order-card__row'>
|
||
<Text className='order-card__label'>订单金额:</Text>
|
||
<Text className='order-card__fee'>¥{order.total_fee || '-'}</Text>
|
||
</View>
|
||
|
||
{order.reject_reason ? (
|
||
<View className='order-card__reject'>驳回理由:{order.reject_reason}</View>
|
||
) : null}
|
||
|
||
{actions.complaint || actions.submit || actions.detail ? (
|
||
<>
|
||
<View className='order-card__divider' />
|
||
<View className='order-card__actions'>
|
||
{actions.complaint ? (
|
||
<View className='order-card__action' onClick={onComplaint}>投诉</View>
|
||
) : null}
|
||
{actions.detail ? (
|
||
<View className='order-card__action' onClick={onViewDetail}>查看详情</View>
|
||
) : null}
|
||
{actions.submit ? (
|
||
<View className='order-card__action order-card__action--primary' onClick={onResubmit}>
|
||
补充资料
|
||
</View>
|
||
) : null}
|
||
</View>
|
||
</>
|
||
) : null}
|
||
</View>
|
||
)
|
||
}
|
||
|
||
function InfoLine ({ label, value }: { label: string, value: string }) {
|
||
return (
|
||
<View className='order-card__row'>
|
||
<Text className='order-card__label'>{label}</Text>
|
||
<Text className='order-card__value'>{value}</Text>
|
||
</View>
|
||
)
|
||
}
|