feat: 优化通道运营商与金额展示

This commit is contained in:
hectorzhao
2026-08-12 13:16:25 +08:00
parent dc358798e9
commit f350bf5ef3
20 changed files with 301 additions and 95 deletions
+24
View File
@@ -0,0 +1,24 @@
import type { HTMLAttributes } from 'react';
const carrierMeta = {
mobile: { label: '移动', className: 'ui-carrier-tag--mobile' },
unicom: { label: '联通', className: 'ui-carrier-tag--unicom' },
telecom: { label: '电信', className: 'ui-carrier-tag--telecom' },
} as const;
export type CarrierTagValue = keyof typeof carrierMeta;
export function normalizeCarrierTag(value?: string | null): CarrierTagValue | null {
const normalized = String(value ?? '').trim().toLowerCase();
if (['mobile', 'cmcc', '移动', '中国移动'].includes(normalized)) return 'mobile';
if (['unicom', 'cucc', '联通', '中国联通'].includes(normalized)) return 'unicom';
if (['telecom', 'ctcc', '电信', '中国电信'].includes(normalized)) return 'telecom';
return null;
}
export function CarrierTag({ carrier, className = '', ...props }: HTMLAttributes<HTMLSpanElement> & { carrier: string }) {
const normalized = normalizeCarrierTag(carrier);
if (!normalized) return <span className={['ui-carrier-tag', 'ui-carrier-tag--neutral', className].filter(Boolean).join(' ')} {...props}>{carrier || '未知'}</span>;
const meta = carrierMeta[normalized];
return <span className={['ui-carrier-tag', meta.className, className].filter(Boolean).join(' ')} {...props}>{meta.label}</span>;
}
+3 -1
View File
@@ -19,6 +19,7 @@ type ModalProps = {
initialFocusRef?: RefObject<HTMLElement | null>;
closeGuardTitle?: string;
closeGuardDescription?: string;
className?: string;
};
const focusableSelector = [
@@ -96,6 +97,7 @@ export function Modal({
initialFocusRef,
closeGuardTitle = '放弃未保存的修改?',
closeGuardDescription = '当前内容尚未保存。放弃后无法恢复,请确认是否关闭。',
className,
}: ModalProps) {
const titleId = useId();
const guardTitleId = useId();
@@ -213,7 +215,7 @@ export function Modal({
<section
aria-labelledby={titleId}
aria-modal="true"
className={['ui-modal__panel', `ui-modal__panel--${size}`].join(' ')}
className={['ui-modal__panel', `ui-modal__panel--${size}`, className].filter(Boolean).join(' ')}
ref={panelRef}
role="dialog"
tabIndex={-1}
+2 -5
View File
@@ -1,8 +1,5 @@
import { Children, type ReactNode } from 'react';
import type { ReactNode } from 'react';
export function MoneyText({ children, className }: { children: ReactNode; className?: string }) {
const text = Children.toArray(children).map((value) => typeof value === 'string' || typeof value === 'number' ? String(value) : '').join('');
const match = text.match(/^(.*?)(\.\d+)(\s*[^\d]*)$/);
if (!match) return <span className={className}>{text}</span>;
return <span className={className}>{match[1]}<span className="money-text__fraction">{match[2]}</span>{match[3]}</span>;
return <span className={className}>{children}</span>;
}
+6 -6
View File
@@ -4,7 +4,6 @@ import { formatCents } from '@/utils/currency';
import { formatDateTime } from '@/utils/dateTime';
import { Button } from './Button';
import { Modal } from './Modal';
import { MoneyText } from './MoneyText';
type RechargeReceiptDialogProps = {
open: boolean;
@@ -14,7 +13,7 @@ type RechargeReceiptDialogProps = {
};
export function formatReceiptAmount(moneyUnits: number) {
return formatCents(Math.abs(moneyUnits)).replace(/\.?0+$/, '');
return formatCents(Math.abs(moneyUnits));
}
export function RechargeReceiptDialog({
@@ -35,12 +34,13 @@ export function RechargeReceiptDialog({
return (
<Modal
className="recharge-receipt-modal"
footer={<Button onClick={onClose}></Button>}
onClose={onClose}
open={open}
title="账户充值回执"
>
<article className="recharge-receipt">
<article className={['recharge-receipt', isCorrection ? 'recharge-receipt--correction' : ''].filter(Boolean).join(' ')}>
<header className="recharge-receipt__header">
<img
alt="聆界短信服务平台"
@@ -58,7 +58,7 @@ export function RechargeReceiptDialog({
<strong>
{isCorrection ? '' : '+'}
<small>¥</small>
<MoneyText>{formatReceiptAmount(record.amountCents)}</MoneyText>
{formatReceiptAmount(record.amountCents)}
</strong>
</section>
@@ -79,11 +79,11 @@ export function RechargeReceiptDialog({
</div>
<div>
<dt></dt>
<dd>{balanceBefore === null ? '-' : <MoneyText>¥{formatCents(balanceBefore)}</MoneyText>}</dd>
<dd>{balanceBefore === null ? '-' : <>¥{formatCents(balanceBefore)}</>}</dd>
</div>
<div>
<dt></dt>
<dd>{balanceAfter === null || balanceAfter === undefined ? '-' : <MoneyText>¥{formatCents(balanceAfter)}</MoneyText>}</dd>
<dd>{balanceAfter === null || balanceAfter === undefined ? '-' : <>¥{formatCents(balanceAfter)}</>}</dd>
</div>
<div>
<dt></dt>
+2
View File
@@ -1,6 +1,8 @@
export { Button } from './Button';
export { Breadcrumb } from './Breadcrumb';
export { Chart } from './Chart';
export { CarrierTag, normalizeCarrierTag } from './CarrierTag';
export type { CarrierTagValue } from './CarrierTag';
export { DateRangeInput } from './DateRangeInput';
export { DateTimeInput } from './DateTimeInput';
export { DetailInfoGrid, DetailProgressStats, DetailSection, DetailTitle, getRateTone, ProgressBar, RateCard, RateOverview } from './Detail';