From 951b44b582cacc74427c0f3a5f58a58bcd082595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sun, 4 Oct 2026 10:32:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3(=E4=BC=9A=E5=91=98=E5=BC=B9?= =?UTF-8?q?=E5=B1=82)=EF=BC=9A=E5=8D=87=E7=BA=A7=E6=8A=A5=E4=BB=B7?= =?UTF-8?q?=E6=9C=AA=E5=88=B0=E6=97=B6=E4=B8=8D=E5=86=8D=E6=8A=8A=E6=A0=87?= =?UTF-8?q?=E5=87=86=E4=BB=B7=E6=98=BE=E7=A4=BA=E4=B8=BA=E6=9C=AC=E6=AC=A1?= =?UTF-8?q?=E8=A1=A5=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 amountPending:升级报价缺失时金额区显示占位符,CTA 只显示「补差升级」 - 补一条回归测试覆盖报价加载中的确认页 --- .../index.test.tsx | 43 +++++++++++++++++++ .../PlatformProfileRechargeModal/index.tsx | 23 +++++++--- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx b/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx index 18db8512a..fbea85958 100644 --- a/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx +++ b/packages/shared/src/components/PlatformProfileRechargeModal/index.test.tsx @@ -261,6 +261,49 @@ describe('PlatformProfileRechargeModal', () => { expect(onSubmitMembershipCheckout).toHaveBeenCalledWith('max', 'yearly'); }); + test('shows a placeholder instead of the list price while the upgrade quote is loading', async () => { + const user = userEvent.setup(); + + render( + , + ); + + await user.click(screen.getByRole('button', { name: 'Max 年付 补差升级' })); + + const checkout = screen.getByRole('dialog', { name: '确认套餐变更' }); + // 报价未到:金额占位,不能把 Max 标准年价(¥6990)当成「本次补付」。 + expect(within(checkout).getByText('—')).toBeTruthy(); + expect( + within(checkout).queryByRole('button', { name: /^补差升级 ¥/ }), + ).toBeNull(); + expect( + within(checkout).getByRole('button', { name: '补差升级' }), + ).toHaveProperty('disabled', true); + }); + test('recharge tab picks a point product and forwards the buy action', async () => { const user = userEvent.setup(); const onBuy = vi.fn(); diff --git a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx index 64c24649c..aaec13d74 100644 --- a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx +++ b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx @@ -1013,9 +1013,20 @@ function PlatformRechargeCheckoutDialog({ membershipQuote.cycleKind === target.cycleKind ? membershipQuote : null; + // 升级金额只能来自服务端报价:报价未到 / 失败时不能拿新套餐标准价冒充「本次补付」。 + const amountPending = isUpgrade && !quoteForTarget; const amountCents = quoteForTarget ? quoteForTarget.amountCents : listPriceCents; + const amountText = amountPending ? '—' : formatRechargePrice(amountCents); + let ctaLabel: string; + if (!isUpgrade) { + ctaLabel = `支付 ${amountText}`; + } else if (amountPending) { + ctaLabel = '补差升级'; + } else { + ctaLabel = `补差升级 ${amountText}`; + } const canSubmit = Boolean(plan) && !submitting && @@ -1072,12 +1083,9 @@ function PlatformRechargeCheckoutDialog({ title={isUpgrade ? '确认套餐变更' : '确认订单'} submitting={submitting} amountCents={amountCents} + amountPending={amountPending} amountLabel={isUpgrade ? '本次补付' : '本次支付'} - ctaLabel={ - isUpgrade - ? `补差升级 ${formatRechargePrice(amountCents)}` - : `支付 ${formatRechargePrice(amountCents)}` - } + ctaLabel={ctaLabel} ctaDisabled={!canSubmit} onClose={onClose} onSubmit={() => { @@ -1103,6 +1111,7 @@ function PlatformRechargeCheckoutDialog({ function PlatformRechargeCheckoutShell({ title, amountCents, + amountPending = false, amountLabel = '本次支付', ctaLabel, ctaDisabled = false, @@ -1115,6 +1124,8 @@ function PlatformRechargeCheckoutShell({ }: { title: string; amountCents: number; + /** 金额未知(升级报价未到 / 失败)时只显示占位符,避免拿标准价冒充补差金额。 */ + amountPending?: boolean; amountLabel?: string; ctaLabel: string; ctaDisabled?: boolean; @@ -1142,7 +1153,7 @@ function PlatformRechargeCheckoutShell({ {amountLabel}
- {formatRechargePrice(amountCents)} + {amountPending ? '—' : formatRechargePrice(amountCents)}
{errorState ?
{errorState}
: null}