diff --git a/src/assets/images/icon-close.png b/src/assets/images/icon-close.png index 470ab28..afc05b0 100644 Binary files a/src/assets/images/icon-close.png and b/src/assets/images/icon-close.png differ diff --git a/src/lib/status.test.ts b/src/lib/status.test.ts index 9d65688..714420f 100644 --- a/src/lib/status.test.ts +++ b/src/lib/status.test.ts @@ -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', () => { diff --git a/src/lib/status.ts b/src/lib/status.ts index 0120d47..d2a37fe 100644 --- a/src/lib/status.ts +++ b/src/lib/status.ts @@ -1,14 +1,14 @@ import { isCorpGoodsType } from './detail' export const STATUS_CONFIG: Record = { - '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 = { diff --git a/src/pages/index/detail.tsx b/src/pages/index/detail.tsx index 09c57c2..39cd41d 100644 --- a/src/pages/index/detail.tsx +++ b/src/pages/index/detail.tsx @@ -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 ( - - event.stopPropagation()}> + + event.stopPropagation()} + > 详情 - + diff --git a/src/pages/index/index.scss b/src/pages/index/index.scss index 30abd71..83d2d45 100644 --- a/src/pages/index/index.scss +++ b/src/pages/index/index.scss @@ -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 { diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index ff568e5..171e1a4 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -201,7 +201,9 @@ function OrderCard ({ {order.goods_name || '-'} - {status.result} + + {status.result} + @@ -226,17 +228,20 @@ function OrderCard ({ ) : null} {actions.complaint || actions.submit || actions.detail ? ( - - {actions.complaint ? ( - 投诉 - ) : null} - {actions.detail ? ( - 查看详情 - ) : null} - {actions.submit ? ( - 立即提交 - ) : null} - + <> + + + {actions.complaint ? ( + 投诉 + ) : null} + {actions.detail ? ( + 查看详情 + ) : null} + {actions.submit ? ( + 补充资料 + ) : null} + + ) : null} ) diff --git a/src/pages/index/weixin-qr.tsx b/src/pages/index/weixin-qr.tsx index 1053bdc..2ada4ea 100644 --- a/src/pages/index/weixin-qr.tsx +++ b/src/pages/index/weixin-qr.tsx @@ -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((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 () => {