From a7c23b9baf7b2bdf6a13bb82e1c8900779fc1935 Mon Sep 17 00:00:00 2001 From: zhangjianjun Date: Thu, 20 Aug 2026 16:47:44 +0800 Subject: [PATCH] ud --- src/assets/images/icon-close.png | Bin 1075 -> 2630 bytes src/lib/status.test.ts | 7 +++ src/lib/status.ts | 16 +++--- src/pages/index/detail.tsx | 30 +++++++++- src/pages/index/index.scss | 96 +++++++++++++++++++++++++------ src/pages/index/index.tsx | 29 ++++++---- src/pages/index/weixin-qr.tsx | 51 +++++++++++----- 7 files changed, 173 insertions(+), 56 deletions(-) diff --git a/src/assets/images/icon-close.png b/src/assets/images/icon-close.png index 470ab28aef6b524befec388abfa0b8bfb68b5f97..afc05b013c478fd79c2c07f6da54f897c5426923 100644 GIT binary patch literal 2630 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 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 () => {