From f2303f7fb86a23399ea0c56b4d0265ac492d89f2 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 22 Jul 2026 23:11:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B8=E9=94=80=E9=87=91=E9=A2=9D=EF=BC=9A?= =?UTF-8?q?=E6=9C=80=E4=BD=8E=200.01=20=E5=85=83=EF=BC=9B=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E6=9C=80=E5=A4=9A=E4=B8=A4=E4=BD=8D=E5=B0=8F=E6=95=B0?= =?UTF-8?q?=EF=BC=9B=E4=BD=8E=E4=BA=8E=E8=AF=A5=E5=80=BC=E4=BC=9A=20toast?= =?UTF-8?q?=20=E6=8F=90=E7=A4=BA=EF=BC=8C=E6=97=A0=E6=B3=95=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=A0=B8=E9=94=80=E7=A0=81=E3=80=82=20=E5=BA=95?= =?UTF-8?q?=E9=83=A8=E6=8F=90=E7=A4=BA=EF=BC=9A=E5=B7=A6=E5=8F=B3=E7=95=99?= =?UTF-8?q?=E7=99=BD=20+=20=E5=86=85=E8=BE=B9=E8=B7=9D=EF=BC=8C=E6=B5=85?= =?UTF-8?q?=E5=BA=95=E5=9C=86=E8=A7=92=E5=9D=97=EF=BC=8C=E6=96=87=E6=A1=88?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=9C=80=E4=BD=8E=200.01=E3=80=81=E4=BD=99?= =?UTF-8?q?=E9=A2=9D=E4=B8=8A=E9=99=90=E3=80=813=20=E5=88=86=E9=92=9F?= =?UTF-8?q?=E5=88=B0=E5=BA=97=E5=87=BA=E7=A4=BA=E3=80=82=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=9C=B0=E5=9D=80=EF=BC=9A=E8=BF=9B=E5=85=A5=E9=A1=B5?= =?UTF-8?q?=E4=BC=9A=E5=B0=9D=E8=AF=95=E5=AE=9A=E4=BD=8D=EF=BC=8C=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=80=89=E4=B8=AD=E5=BD=93=E5=89=8D=E7=9C=81=E5=B8=82?= =?UTF-8?q?=E5=8C=BA=EF=BC=88=E5=A4=B1=E8=B4=A5=E5=88=99=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=83=91=E5=B7=9E=EF=BC=89=EF=BC=9B=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=9C=B0=E5=9D=80=E4=B8=8D=E9=87=8D=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/address-edit/index.tsx | 44 ++++++++++++++++++- apps/mini-user/src/pages/redeem/index.tsx | 36 +++++++++++---- apps/mini-user/src/styles/redeem.css | 14 ++++-- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/apps/mini-user/src/pages/address-edit/index.tsx b/apps/mini-user/src/pages/address-edit/index.tsx index ef9a06a..c512ddd 100644 --- a/apps/mini-user/src/pages/address-edit/index.tsx +++ b/apps/mini-user/src/pages/address-edit/index.tsx @@ -5,9 +5,15 @@ import PageShell from '../../components/PageShell'; import SubPageHeader from '../../components/SubPageHeader'; import RegionPicker from '../../components/RegionPicker'; import { buildAddressListUrl, readCheckoutContext } from '../../lib/checkout-nav'; -import { DEFAULT_REGION, formatRegion, type RegionSelection } from '../../lib/region-data'; +import { + DEFAULT_REGION, + REGION_ALL, + formatRegion, + type RegionSelection, +} from '../../lib/region-data'; import { normalizePhoneInput, validateMobilePhone } from '../../lib/phone'; import { getStoredUserPhone, resolveDefaultUserPhone } from '../../lib/user-phone'; +import { resolveUserCity } from '../../lib/user-location'; import { request, toast, type UserProfile } from '../../lib/api'; type AddressForm = { @@ -27,6 +33,7 @@ export default function AddressEditPage() { const checkoutCtx = readCheckoutContext(router.params); const [pickerOpen, setPickerOpen] = useState(false); const [saving, setSaving] = useState(false); + const [locating, setLocating] = useState(false); const [error, setError] = useState(''); const [form, setForm] = useState(() => ({ receiverName: '', @@ -49,6 +56,35 @@ export default function AddressEditPage() { .catch(() => {}); }, [id]); + useEffect(() => { + if (id) return; + let cancelled = false; + setLocating(true); + void resolveUserCity(true) + .then((resolved) => { + if (cancelled) return; + const district = + resolved.region.district && resolved.region.district !== REGION_ALL + ? resolved.region.district + : resolved.district && resolved.district !== REGION_ALL + ? resolved.district + : DEFAULT_REGION.district; + setForm((prev) => ({ + ...prev, + province: resolved.region.province || prev.province, + city: resolved.region.city || prev.city, + district: district || prev.district, + })); + }) + .catch(() => {}) + .finally(() => { + if (!cancelled) setLocating(false); + }); + return () => { + cancelled = true; + }; + }, [id]); + useEffect(() => { if (!id) return; request>>('/user/addresses').then((list) => { @@ -161,7 +197,11 @@ export default function AddressEditPage() { style={{ display: 'flex', alignItems: 'center' }} onClick={() => setPickerOpen(true)} > - {regionText || '请选择省市区'} + + {locating && !isEdit + ? '定位中…' + : regionText || '请选择省市区'} + diff --git a/apps/mini-user/src/pages/redeem/index.tsx b/apps/mini-user/src/pages/redeem/index.tsx index f9e1030..30cb143 100644 --- a/apps/mini-user/src/pages/redeem/index.tsx +++ b/apps/mini-user/src/pages/redeem/index.tsx @@ -11,10 +11,26 @@ type BenefitSummary = { maxRedeemAmount: number; }; +const MIN_REDEEM_AMOUNT = 0.01; + function formatMoney(amount: number) { return amount.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } +/** 核销金额输入:最多两位小数,禁止非法字符 */ +function sanitizeRedeemAmountInput(raw: string): string { + let next = raw.replace(/[^\d.]/g, ''); + const firstDot = next.indexOf('.'); + if (firstDot >= 0) { + next = + next.slice(0, firstDot + 1) + next.slice(firstDot + 1).replace(/\./g, ''); + const [intPart, decPart = ''] = next.split('.'); + next = `${intPart}.${decPart.slice(0, 2)}`; + } + if (next.startsWith('.')) next = `0${next}`; + return next; +} + export default function RedeemPage() { const router = useRouter(); const couponId = router.params.couponId; @@ -50,14 +66,14 @@ export default function RedeemPage() { }, [couponId]); function fillMaxAmount() { - if (redeemableMax <= 0) return; + if (redeemableMax < MIN_REDEEM_AMOUNT) return; setAmount(String(redeemableMax)); } async function submit() { const value = Math.round(Number(amount) * 100) / 100; - if (!(value > 0)) { - toast('请输入核销金额'); + if (!Number.isFinite(value) || value < MIN_REDEEM_AMOUNT) { + toast(`核销金额不能低于 ${MIN_REDEEM_AMOUNT.toFixed(2)} 元`); return; } if (value > redeemableMax) { @@ -103,7 +119,7 @@ export default function RedeemPage() { placeholder="输入核销金额" placeholderClass="redeem-input-placeholder" value={amount} - onInput={(e) => setAmount(e.detail.value)} + onInput={(e) => setAmount(sanitizeRedeemAmountInput(e.detail.value))} style={{ textAlign: 'center' }} /> @@ -115,12 +131,14 @@ export default function RedeemPage() { 全部核销 - - 直接核销:金额须大于 0 且不超过全部可用权益余额。核销码有效期 3 分钟,请到店出示。 - + + + 核销金额最低 0.01 元,且不超过可用权益余额。核销码有效期 3 分钟,请到店出示给收银员扫码。 + + {loading ? '生成中...' : '生成核销码'} diff --git a/apps/mini-user/src/styles/redeem.css b/apps/mini-user/src/styles/redeem.css index 953cae0..3995378 100644 --- a/apps/mini-user/src/styles/redeem.css +++ b/apps/mini-user/src/styles/redeem.css @@ -77,12 +77,18 @@ .redeem-tips { display: block; box-sizing: border-box; - width: 100%; - margin: 0; - padding: 0 var(--space-page); + margin: 8px var(--space-page) 0; + padding: 12px 16px; + border-radius: var(--radius-md); + background: rgba(0, 0, 0, 0.03); +} + +.redeem-tips-text { + display: block; font-size: 12px; color: var(--color-subtle-gray); - line-height: 1.6; + line-height: 1.7; + word-break: break-word; } .redeem-amount-hint {