corp-mp/src/pages/index/index.tsx

260 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
)
}