This commit is contained in:
parent
8e1f3fbcc8
commit
a7c23b9baf
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 2.6 KiB |
|
|
@ -15,6 +15,13 @@ describe('order status labels', () => {
|
|||
expect(getStatusMeta('2').result).toBe('已完成')
|
||||
expect(ORDER_TABS.map((tab) => tab.label)).toEqual(['全部', '办理中', '待处理', '待办理', '已完成'])
|
||||
})
|
||||
|
||||
it('uses filled badge colors from the order card design', () => {
|
||||
expect(getStatusMeta('4')).toMatchObject({ color: '#0261FC', bg: '#E5EFFE' })
|
||||
expect(getStatusMeta('6')).toMatchObject({ color: '#FC6202', bg: '#FEEFE5' })
|
||||
expect(getStatusMeta('1')).toMatchObject({ color: '#FF2E2E', bg: '#FFEAEA' })
|
||||
expect(getStatusMeta('2')).toMatchObject({ color: '#07C160', bg: '#E6F8EF' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('filterOrdersByTab', () => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { isCorpGoodsType } from './detail'
|
||||
|
||||
export const STATUS_CONFIG: Record<string, { color: string, bg: string, result: string }> = {
|
||||
'4': { color: '#2F7BFF', bg: '#EAF2FF', result: '办理中' },
|
||||
'1': { color: '#FF5A5A', bg: '#FFF1F0', result: '待办理' },
|
||||
'3': { color: '#FF5A5A', bg: '#FFF1F0', result: '待办理' },
|
||||
'2': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' },
|
||||
'5': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' },
|
||||
'7': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' },
|
||||
'8': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' },
|
||||
'6': { color: '#FF8E13', bg: '#FFF2E4', result: '待处理' },
|
||||
'4': { color: '#0261FC', bg: '#E5EFFE', result: '办理中' },
|
||||
'1': { color: '#FF2E2E', bg: '#FFEAEA', result: '待办理' },
|
||||
'3': { color: '#FF2E2E', bg: '#FFEAEA', result: '待办理' },
|
||||
'2': { color: '#07C160', bg: '#E6F8EF', result: '已完成' },
|
||||
'5': { color: '#07C160', bg: '#E6F8EF', result: '已完成' },
|
||||
'7': { color: '#07C160', bg: '#E6F8EF', result: '已完成' },
|
||||
'8': { color: '#07C160', bg: '#E6F8EF', result: '已完成' },
|
||||
'6': { color: '#FC6202', bg: '#FEEFE5', result: '待处理' },
|
||||
}
|
||||
|
||||
export const PAY_ICON: Record<string, string> = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Image, Text, View } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { LOCAL_IMAGES } from '@/lib/assets'
|
||||
import { RECEIPT_STAMP_DONE, RECEIPT_STAMP_PENDING } from '@/lib/cdn'
|
||||
import {
|
||||
|
|
@ -11,25 +12,48 @@ import {
|
|||
} from '@/lib/detail'
|
||||
import type { OrderLike } from '@/lib/order'
|
||||
|
||||
const DRAWER_ANIMATION_MS = 300
|
||||
|
||||
type Props = {
|
||||
order: OrderLike
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export default function DetailDrawer ({ order, onCancel }: Props) {
|
||||
const [leaving, setLeaving] = useState(false)
|
||||
const onCancelRef = useRef(onCancel)
|
||||
onCancelRef.current = onCancel
|
||||
|
||||
const showReceipt = shouldShowReceipt(order.goods_type)
|
||||
const receipt = getReceiptMeta(order.goods_type, order.goods_name)
|
||||
const files = order.process_status === '2' ? splitOrderFiles(order.files) : { images: [], pdfs: [] }
|
||||
const empty = !showReceipt && files.images.length === 0 && files.pdfs.length === 0
|
||||
const imageUrls = files.images.map((file) => String(file.url))
|
||||
|
||||
const handleClose = () => {
|
||||
if (leaving) return
|
||||
setLeaving(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!leaving) return
|
||||
const timer = setTimeout(() => onCancelRef.current(), DRAWER_ANIMATION_MS)
|
||||
return () => clearTimeout(timer)
|
||||
}, [leaving])
|
||||
|
||||
return (
|
||||
<View className='detail-mask' onClick={onCancel}>
|
||||
<View className='detail-drawer' onClick={(event) => event.stopPropagation()}>
|
||||
<View
|
||||
className={`detail-mask${leaving ? ' detail-mask--leave' : ''}`}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<View
|
||||
className={`detail-drawer${leaving ? ' detail-drawer--leave' : ''}`}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<View className='detail-drawer__handle' />
|
||||
<View className='detail-dialog__head'>
|
||||
<Text className='detail-dialog__title'>详情</Text>
|
||||
<View className='detail-dialog__close' onClick={onCancel}>
|
||||
<View className='detail-dialog__close' onClick={handleClose}>
|
||||
<Image className='detail-dialog__close-img' src={LOCAL_IMAGES.close} />
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@
|
|||
}
|
||||
|
||||
.home-list {
|
||||
padding: 16px 24px 160px;
|
||||
padding: 24px 24px 160px;
|
||||
}
|
||||
|
||||
.home-empty {
|
||||
|
|
@ -149,52 +149,69 @@
|
|||
}
|
||||
|
||||
.order-card {
|
||||
margin-bottom: 24px;
|
||||
padding: 32px 28px 28px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
margin-bottom: 32px;
|
||||
padding: 32px 24px 24px;
|
||||
border-radius: 24px;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 20px rgba(0, 78, 206, 0.1);
|
||||
}
|
||||
|
||||
.order-card__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.order-card__goods-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: 16px;
|
||||
color: #1d2129;
|
||||
color: #222;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.order-card__status {
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 106px;
|
||||
height: 48px;
|
||||
padding: 0 14px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.order-card__status-text {
|
||||
font-size: 26px;
|
||||
line-height: 44px;
|
||||
font-weight: 400;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.order-card__row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 12px;
|
||||
font-size: 26px;
|
||||
line-height: 36px;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
font-size: 28px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.order-card__label {
|
||||
flex-shrink: 0;
|
||||
color: #86909c;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.order-card__value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #4e5969;
|
||||
color: #222;
|
||||
font-weight: 400;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
|
@ -232,6 +249,14 @@
|
|||
line-height: 36px;
|
||||
}
|
||||
|
||||
.order-card__divider {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
margin-top: 32px;
|
||||
border: 0;
|
||||
border-top: 2px solid #eee;
|
||||
}
|
||||
|
||||
.order-card__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
|
@ -263,7 +288,13 @@
|
|||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
overflow: hidden;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
animation: detail-mask-in 0.3s ease both;
|
||||
}
|
||||
|
||||
.detail-mask--leave {
|
||||
animation: detail-mask-out 0.3s ease both;
|
||||
}
|
||||
|
||||
.detail-drawer {
|
||||
|
|
@ -273,6 +304,31 @@
|
|||
overflow: hidden;
|
||||
border-radius: 24px 24px 0 0;
|
||||
background: #fff;
|
||||
animation: detail-drawer-in 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
|
||||
}
|
||||
|
||||
.detail-drawer--leave {
|
||||
animation: detail-drawer-out 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
|
||||
}
|
||||
|
||||
@keyframes detail-mask-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes detail-mask-out {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes detail-drawer-in {
|
||||
from { transform: translate3d(0, 100%, 0); }
|
||||
to { transform: translate3d(0, 0, 0); }
|
||||
}
|
||||
|
||||
@keyframes detail-drawer-out {
|
||||
from { transform: translate3d(0, 0, 0); }
|
||||
to { transform: translate3d(0, 100%, 0); }
|
||||
}
|
||||
|
||||
.detail-drawer__handle {
|
||||
|
|
@ -300,18 +356,20 @@
|
|||
|
||||
.detail-dialog__close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.detail-dialog__close-img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.detail-dialog__body {
|
||||
|
|
|
|||
|
|
@ -201,7 +201,9 @@ function OrderCard ({
|
|||
<View className='order-card'>
|
||||
<View className='order-card__head'>
|
||||
<Text className='order-card__goods-name'>{order.goods_name || '-'}</Text>
|
||||
<Text className='order-card__status' style={{ color: status.color }}>{status.result}</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 || '-'} />
|
||||
|
|
@ -226,17 +228,20 @@ function OrderCard ({
|
|||
) : null}
|
||||
|
||||
{actions.complaint || actions.submit || actions.detail ? (
|
||||
<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>
|
||||
<>
|
||||
<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>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,26 @@ import Taro from '@tarojs/taro'
|
|||
import { useEffect, useState } from 'react'
|
||||
import { buildPublicQrImageUrl } from '@/lib/qrImage'
|
||||
|
||||
function toastQrFail (err?: { errMsg?: string }) {
|
||||
const message = String(err?.errMsg || '')
|
||||
if (/合法域名/.test(message) || /not in domain list/i.test(message)) {
|
||||
Taro.showToast({ title: '二维码域名未生效,请完全退出微信后重试', icon: 'none', duration: 3000 })
|
||||
return
|
||||
}
|
||||
Taro.showToast({ title: '二维码加载失败', icon: 'none' })
|
||||
}
|
||||
|
||||
function writeQrFile (filePath: string, data: ArrayBuffer | string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Taro.getFileSystemManager().writeFile({
|
||||
filePath,
|
||||
data,
|
||||
success: () => resolve(),
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default function WeixinQrImage ({ url }: { url: string }) {
|
||||
const [src, setSrc] = useState('')
|
||||
|
||||
|
|
@ -17,21 +37,24 @@ export default function WeixinQrImage ({ url }: { url: string }) {
|
|||
return
|
||||
}
|
||||
|
||||
Taro.downloadFile({
|
||||
const filePath = `${Taro.env.USER_DATA_PATH}/weixin-qr.png`
|
||||
|
||||
Taro.request({
|
||||
url: remote,
|
||||
success (res) {
|
||||
if (cancelled) return
|
||||
if (res.statusCode === 200 && res.tempFilePath) {
|
||||
setSrc(res.tempFilePath)
|
||||
return
|
||||
}
|
||||
Taro.showToast({ title: '二维码加载失败', icon: 'none' })
|
||||
},
|
||||
fail () {
|
||||
if (!cancelled) {
|
||||
Taro.showToast({ title: '二维码加载失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
dataType: '其他',
|
||||
timeout: 15000,
|
||||
}).then(async (res) => {
|
||||
if (cancelled) return
|
||||
if (res.statusCode !== 200 || !res.data) {
|
||||
toastQrFail()
|
||||
return
|
||||
}
|
||||
await writeQrFile(filePath, res.data as ArrayBuffer)
|
||||
if (!cancelled) setSrc(filePath)
|
||||
}).catch((err) => {
|
||||
if (!cancelled) toastQrFail(err)
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue