fix: standardize currency and input selection styles

This commit is contained in:
hectorzhao
2026-07-11 10:49:00 +08:00
parent 20f89d14aa
commit a4d42cf702
15 changed files with 52 additions and 33 deletions
+1 -1
View File
@@ -627,7 +627,7 @@ export function AdminChannelsPage() {
</div>
<div className="sms-channel-carrier-price">
<Tag tone={carrierToneMap[channel.carrier]}>{carrierLabelMap[channel.carrier]}</Tag>
<strong>{channel.unitPrice.toFixed(1)} </strong>
<strong>{channel.unitPrice.toFixed(3)} </strong>
</div>
<div className="sms-channel-status-cell">
<Tag tone={statusToneMap[channel.status]}>{statusLabelMap[channel.status]}</Tag>
+2 -1
View File
@@ -3,6 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { ArrowLeft, FileText, MessageSquare, Server } from 'lucide-react';
import { adminApi, type ClientSmsSignature, type ClientSmsTemplate, type EnterpriseApplication, type TenantAccount, type TenantOption } from '@/api/adminApi';
import { Breadcrumb, Button, Table, Tag, type TableColumn } from '@/components/ui';
import { formatCents } from '@/utils/currency';
export function AdminCustomerDetailPage() {
const navigate = useNavigate();
@@ -67,7 +68,7 @@ export function AdminCustomerDetailPage() {
<div className="dashboard-grid enterprise-summary-grid">
<div className="surface mini-status-card"><Server size={22} /><div><span></span><strong>{tenant?.code ?? '-'}</strong><small>{tenant?.status ?? '-'}</small></div></div>
<div className="surface mini-status-card"><MessageSquare size={22} /><div><span></span><strong>{(account?.smsUnits ?? 0).toLocaleString('zh-CN')}</strong><small></small></div></div>
<div className="surface mini-status-card"><FileText size={22} /><div><span></span><strong>¥{((account?.balanceCents ?? 0) / 100).toLocaleString('zh-CN')}</strong><small></small></div></div>
<div className="surface mini-status-card"><FileText size={22} /><div><span></span><strong>¥{formatCents(account?.balanceCents)}</strong><small></small></div></div>
</div>
<div className="surface section-stack">
+6 -9
View File
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { Building2, DollarSign, Plus, Trash2, TrendingDown, TrendingUp } from 'lucide-react';
import { adminApi, type TenantManagementRow } from '@/api/adminApi';
import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, Textarea, type TableColumn } from '@/components/ui';
import { formatCents } from '@/utils/currency';
type AdminCustomersPageProps = {
basePath?: string;
@@ -16,10 +17,6 @@ type RechargeForm = {
remark: string;
};
function formatCurrency(cents: number) {
return (cents / 100).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function ConfirmModal({ message, onCancel, onConfirm }: { message: string; onCancel: () => void; onConfirm: () => void }) {
return (
<Modal footer={<><Button onClick={onCancel} variant="ghost"></Button><Button onClick={onConfirm}></Button></>} onClose={onCancel} open title="操作确认">
@@ -86,14 +83,14 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto
const balance = record.account?.balanceCents ?? 0;
return (
<span className={balance < 0 ? 'status-danger' : ''}>
¥{formatCurrency(balance)}
¥{formatCents(balance)}
{balance < 0 ? <Tag tone="danger" className="enterprise-inline-tag"></Tag> : null}
</span>
);
},
},
{ key: 'overdraftLimit', title: '透支限额', width: '150px', align: 'right', render: (record) => `¥${formatCurrency(record.account?.creditCents ?? 0)}` },
{ key: 'todaySpend', title: '今日消费', width: '150px', align: 'right', render: (record) => `¥${formatCurrency(record.todaySpendCents)}` },
{ key: 'overdraftLimit', title: '透支限额', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.account?.creditCents ?? 0)}` },
{ key: 'todaySpend', title: '今日消费', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.todaySpendCents)}` },
{ key: 'status', title: '企业状态', width: '130px', render: (record) => <Tag tone={record.status === 'active' ? 'success' : 'warning'}>{record.status === 'active' ? '正常' : '已禁用'}</Tag> },
{
key: 'actions',
@@ -172,7 +169,7 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto
<div className="surface mini-status-card"><Building2 size={22} /><div><span></span><strong>{records.length}</strong><small></small></div></div>
<div className="surface mini-status-card"><TrendingUp size={22} /><div><span></span><strong>{activeCount}</strong><small></small></div></div>
<div className="surface mini-status-card"><TrendingDown size={22} /><div><span></span><strong>{disabledCount}</strong><small></small></div></div>
<div className="surface mini-status-card"><DollarSign size={22} /><div><span></span><strong>¥{formatCurrency(totalBalance)}</strong><small></small></div></div>
<div className="surface mini-status-card"><DollarSign size={22} /><div><span></span><strong>¥{formatCents(totalBalance)}</strong><small></small></div></div>
</div>
<div className="surface ui-query-panel">
@@ -215,7 +212,7 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto
>
<div className="admin-system-modal-form">
<Input disabled label="企业名称" value={rechargeTarget.name} />
<Input disabled label="当前余额" prefix="¥" value={formatCurrency(rechargeTarget.account?.balanceCents ?? 0)} />
<Input disabled label="当前余额" prefix="¥" value={formatCents(rechargeTarget.account?.balanceCents ?? 0)} />
<Input label="充值金额" onChange={(event) => updateRechargeForm('amount', event.target.value)} prefix="¥" required type="number" value={rechargeForm.amount} />
<Input label="操作人" onChange={(event) => updateRechargeForm('operator', event.target.value)} value={rechargeForm.operator} />
<Textarea className="admin-system-modal-form__wide" label="充值备注" onChange={(event) => updateRechargeForm('remark', event.target.value)} rows={4} value={rechargeForm.remark} />
+2 -2
View File
@@ -36,8 +36,8 @@ const balanceTone = {
function formatCurrency(value: number) {
return value.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
minimumFractionDigits: 3,
maximumFractionDigits: 3,
});
}
+1 -11
View File
@@ -3,6 +3,7 @@ import { Plus, Search } from 'lucide-react';
import { Breadcrumb, Button, DateRangeInput, Input, Modal, Pagination, Select, Textarea, Tag, type DateRangeValue } from '@/components/ui';
import { adminApi, type RechargeOrder, type TenantOption } from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
import { formatAmount } from '@/utils/currency';
type ManualRechargeForm = {
tenantId: string;
@@ -16,17 +17,6 @@ function getDate(value: string) {
return value.slice(0, 10);
}
function formatAmount(value?: number) {
if (value === undefined) {
return '';
}
return value.toLocaleString('zh-CN', {
maximumFractionDigits: 2,
minimumFractionDigits: Number.isInteger(value) ? 0 : 2,
});
}
function RemarkCell({ value }: { value?: string }) {
return (
<div className={['admin-remark-cell', value ? '' : 'admin-remark-cell--empty'].filter(Boolean).join(' ')}>
@@ -80,7 +80,7 @@ export function AdminSmsApplicationFormPage() {
setAppName(application.name);
setScene(application.scene ?? '');
setDailyLimit(application.dailyLimit ? String(application.dailyLimit) : '');
setCustomerUnitPrice(((application.customerUnitPrice ?? 0) / 100).toFixed(4));
setCustomerUnitPrice(((application.customerUnitPrice ?? 0) / 100).toFixed(3));
setQueuePriority(application.queuePriority === 'priority' ? 'priority' : 'normal');
setCmppAccount(application.cmppAccount ?? '');
setCmppEnterpriseCode(application.cmppEnterpriseCode ?? application.tenant?.code ?? '');
@@ -185,7 +185,7 @@ export function AdminSmsApplicationFormPage() {
<Input label="应用名称" onChange={(event) => setAppName(event.target.value)} placeholder="请输入应用名称" required value={appName} />
<Input label="应用场景" onChange={(event) => setScene(event.target.value)} placeholder="行业通知/营销推广/验证码" value={scene} />
<Input label="日发送数量限制" onChange={(event) => setDailyLimit(event.target.value)} placeholder="100000" required value={dailyLimit} />
<Input label="客户单价(元/条)" onChange={(event) => setCustomerUnitPrice(event.target.value)} placeholder="0.0300" required value={customerUnitPrice} />
<Input label="客户单价(元/条)" onChange={(event) => setCustomerUnitPrice(event.target.value)} placeholder="0.030" required value={customerUnitPrice} />
<div className="admin-app-form-row admin-app-form-row--wide">
<span></span>
<div className="radio-row">
+1 -1
View File
@@ -146,7 +146,7 @@ function downloadCsv(records: SmsMessageRecord[]) {
record.channel?.sendRegion ?? '',
getCarrierLabel(record.channel?.carrier),
record.billingUnits,
(record.amountCents / 100).toFixed(2),
(record.amountCents / 100).toFixed(3),
record.channel?.name ?? record.channelId ?? '',
getStatusLabel(record.status),
getTime(record.deliveredAt),
+2 -1
View File
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { ClipboardCopy, FileText } from 'lucide-react';
import { clientApi, type ApplicationCmppParams, type ClientSmsApplication } from '@/api/adminApi';
import { formatCents } from '@/utils/currency';
import { Button, Modal, Pagination, Tag } from '@/components/ui';
type LinkStatus = 'connected' | 'degraded' | 'disconnected' | 'inactive';
@@ -33,7 +34,7 @@ function normalizeStatus(application: ClientSmsApplication): LinkStatus {
}
function formatPrice(cents?: number | null) {
return `${((cents ?? 0) / 100).toFixed(4)}`;
return `${formatCents(cents)}`;
}
function mapParams(params: ApplicationCmppParams): ParamRow[] {
+2 -1
View File
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { CreditCard } from 'lucide-react';
import { Button, Pagination, Tag } from '@/components/ui';
import { clientApi, type BillingPlan } from '@/api/adminApi';
import { formatCents } from '@/utils/currency';
export function ClientBillingPage() {
const [plans, setPlans] = useState<BillingPlan[]>([]);
@@ -54,7 +55,7 @@ export function ClientBillingPage() {
<strong>{plan.smsUnits.toLocaleString('zh-CN')} </strong>
<p className="muted">{plan.description ?? '适合阶段性短信发送和活动通知。'}</p>
<Button icon={<CreditCard size={16} />} onClick={() => createOrder(plan)} variant={plan.smsUnits >= 100000 ? 'primary' : 'ghost'}>
¥{(plan.amountCents / 100).toLocaleString('zh-CN')}
¥{formatCents(plan.amountCents)}
</Button>
</article>
))}
+5 -4
View File
@@ -14,6 +14,7 @@ import { Button, Chart, Table, Tag, type TableColumn } from '@/components/ui';
import { clientApi, type DashboardResponse } from '@/api/adminApi';
import { createLineOption, createPieOption } from '@/theme/chartOptions';
import { formatDateTime } from '@/utils/dateTime';
import { formatAmount, formatCents } from '@/utils/currency';
type RecentTaskRow = {
id: string;
@@ -102,8 +103,8 @@ export function ClientHome() {
<div className="dashboard-grid">
<div className="surface metric-card metric-card--featured">
<span></span>
<strong>¥{availableBalance.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</strong>
<small> ¥{todaySpend.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</small>
<strong>¥{formatAmount(availableBalance)}</strong>
<small> ¥{formatAmount(todaySpend)}</small>
</div>
<div className="surface metric-card">
<span></span>
@@ -112,7 +113,7 @@ export function ClientHome() {
</div>
<div className="surface metric-card">
<span></span>
<strong>¥{todayRefund.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</strong>
<strong>¥{formatAmount(todayRefund)}</strong>
<small>退</small>
</div>
</div>
@@ -169,7 +170,7 @@ export function ClientHome() {
</div>
<div>
<span></span>
<strong>{latestRecharge ? `¥${(latestRecharge.amountCents / 100).toFixed(2)}` : '暂无充值'}</strong>
<strong>{latestRecharge ? `¥${formatCents(latestRecharge.amountCents)}` : '暂无充值'}</strong>
</div>
</div>
<div>