diff --git a/apps/admin-web/src/components/AdminUserDetailDialog.tsx b/apps/admin-web/src/components/AdminUserDetailDialog.tsx
index 66e31cf27..f3a02184e 100644
--- a/apps/admin-web/src/components/AdminUserDetailDialog.tsx
+++ b/apps/admin-web/src/components/AdminUserDetailDialog.tsx
@@ -9,6 +9,7 @@ import {
AdminStatusPill,
AdminTag,
AdminTagList,
+ formatAdminCurrencyCents,
} from '@genarrative/shared/components';
import { RefreshCcw, ShieldAlert, UserRound, X } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
@@ -382,12 +383,14 @@ export function AdminUserDetailDialog({
{order.productTitle || order.productId} |
{order.paidAmountCents > 0
- ? formatMoney(order.paidAmountCents)
+ ? formatAdminCurrencyCents(order.paidAmountCents)
: '未支付'}
|
{order.pointsDelta} 泥点 |
- {formatMoney(order.cumulativeSuccessRefundCents)}
+ {formatAdminCurrencyCents(
+ order.cumulativeSuccessRefundCents,
+ )}
欠账 {order.unrecoveredPoints} 泥点
|
{formatOrderStatus(order.status)} |
@@ -442,7 +445,7 @@ function UserIdentityHeader({ detail }: { detail: AdminUserDetailResponse }) {
value: (
<>
{typeof detail.cumulativeRechargedCents === 'number'
- ? formatMoney(detail.cumulativeRechargedCents)
+ ? formatAdminCurrencyCents(detail.cumulativeRechargedCents)
: '未知'}
>
),
@@ -580,10 +583,6 @@ function MembershipSection({
);
}
-function formatMoney(cents: number) {
- return `¥${(cents / 100).toFixed(2)}`;
-}
-
function formatMicros(value: number) {
if (!Number.isFinite(value) || value <= 0) {
return '-';
diff --git a/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx b/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx
index 75a9c3365..c9198234f 100644
--- a/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx
+++ b/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx
@@ -8,6 +8,7 @@ import {
AdminPage,
AdminPageHeading,
AdminPanel,
+ formatAdminCurrencyCents,
} from '@genarrative/shared/components';
import { RefreshCcw, Save } from 'lucide-react';
import { FormEvent, useEffect, useState } from 'react';
@@ -410,8 +411,8 @@ export function AdminMembershipPlanPage({
- {formatPrice(entry.monthPriceCents)} /{' '}
- {formatPrice(entry.yearPriceCents)}
+ {formatAdminCurrencyCents(entry.monthPriceCents)} /{' '}
+ {formatAdminCurrencyCents(entry.yearPriceCents)}
|
{entry.periodPoints} |
{formatPlanLimits(entry)} |
@@ -460,7 +461,3 @@ function formatPlanLimits(entry: ProfileMembershipPlanAdminResponse) {
: `${entry.concurrentJobLimit} 个`;
return `${modelLabel} · 并发 ${concurrency}`;
}
-
-function formatPrice(priceCents: number) {
- return `¥${(priceCents / 100).toFixed(2)}`;
-}
diff --git a/apps/admin-web/src/pages/AdminRechargeOrderPage.tsx b/apps/admin-web/src/pages/AdminRechargeOrderPage.tsx
index efca3ed1d..1d5bf6c42 100644
--- a/apps/admin-web/src/pages/AdminRechargeOrderPage.tsx
+++ b/apps/admin-web/src/pages/AdminRechargeOrderPage.tsx
@@ -13,6 +13,7 @@ import {
AdminPanelHeading,
AdminTag,
AdminTagList,
+ formatAdminCurrencyCents,
} from '@genarrative/shared/components';
import {
AlertTriangle,
@@ -312,7 +313,7 @@ export function AdminRechargeOrderPage({
setRefundRequestId(requestId);
const confirmed = await confirmWrite({
action: '发起微信退款并追回泥点',
- target: `${refundOrder.orderId} / ${formatMoney(amountCents)}`,
+ target: `${refundOrder.orderId} / ${formatAdminCurrencyCents(amountCents)}`,
});
if (!confirmed || submitLockRef.current) {
return;
@@ -721,15 +722,19 @@ export function AdminRechargeOrderPage({
预检后剩余可退>,
- value: <>{formatMoney(preview.remainingRefundableCents)}>,
+ value: (
+ <>
+ {formatAdminCurrencyCents(
+ preview.remainingRefundableCents,
+ )}
+ >
+ ),
},
]}
/>
@@ -984,16 +995,32 @@ export function AdminRechargeOrderPage({
},
{
label: <>本地订单总额>,
- value: <>{formatMoney(manualReviewTarget.order.amountCents)}>,
+ value: (
+ <>
+ {formatAdminCurrencyCents(
+ manualReviewTarget.order.amountCents,
+ )}
+ >
+ ),
},
{
label: <>退款订单总额>,
- value: <>{formatMoney(manualReviewTarget.refund.totalCents)}>,
+ value: (
+ <>
+ {formatAdminCurrencyCents(
+ manualReviewTarget.refund.totalCents,
+ )}
+ >
+ ),
},
{
label: <>本次退款金额>,
value: (
- <>{formatMoney(manualReviewTarget.refund.refundCents)}>
+ <>
+ {formatAdminCurrencyCents(
+ manualReviewTarget.refund.refundCents,
+ )}
+ >
),
},
]}
@@ -1107,7 +1134,7 @@ function RechargeOrderRow({
|
{formatOrderPaidAmount(order)}
{order.paidAmountCents > 0 ? null : (
- 订单 {formatMoney(order.amountCents)}
+ 订单 {formatAdminCurrencyCents(order.amountCents)}
)}
|
@@ -1117,7 +1144,9 @@ function RechargeOrderRow({
) : null}
|
- 累计 {formatMoney(order.cumulativeSuccessRefundCents)}
+
+ 累计 {formatAdminCurrencyCents(order.cumulativeSuccessRefundCents)}
+
应追回 {order.targetRecoveryPoints} / 已追回 {order.recoveredPoints}
@@ -1313,14 +1342,10 @@ function formatMembershipChangeSummary(
return `${action} ${planPath}${cycle}`;
}
-function formatMoney(cents: number) {
- return `¥${(cents / 100).toFixed(2)}`;
-}
-
/** 实付只属于真正支付过的订单:未支付 / 已关闭 / 已过期订单显示“未支付”。 */
function formatOrderPaidAmount(order: AdminRechargeOrderEntryPayload) {
return order.paidAmountCents > 0
- ? formatMoney(order.paidAmountCents)
+ ? formatAdminCurrencyCents(order.paidAmountCents)
: '未支付';
}
diff --git a/apps/admin-web/src/pages/AdminRechargeProductPage.tsx b/apps/admin-web/src/pages/AdminRechargeProductPage.tsx
index 4b6c3c0ea..a53ab5285 100644
--- a/apps/admin-web/src/pages/AdminRechargeProductPage.tsx
+++ b/apps/admin-web/src/pages/AdminRechargeProductPage.tsx
@@ -8,6 +8,7 @@ import {
AdminPage,
AdminPageHeading,
AdminPanel,
+ formatAdminCurrencyCents,
formatAdminDateTime,
} from '@genarrative/shared/components';
import { RefreshCcw, Save } from 'lucide-react';
@@ -333,7 +334,7 @@ export function AdminRechargeProductPage({
{entry.productId}
|
- {formatPrice(entry.priceCents)} |
+ {formatAdminCurrencyCents(entry.priceCents)} |
{formatProductContent(entry)} |
{entry.enabled ? '启用' : '停用'} |
@@ -384,10 +385,6 @@ function formatProductContent(
return `${entry.pointsAmount} 泥点`;
}
-function formatPrice(priceCents: number) {
- return `¥${(priceCents / 100).toFixed(2)}`;
-}
-
function parseInteger(value: string) {
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed)) {
diff --git a/packages/shared/src/components/admin/AdminFormat.test.ts b/packages/shared/src/components/admin/AdminFormat.test.ts
index 13b169cab..f3a8c7466 100644
--- a/packages/shared/src/components/admin/AdminFormat.test.ts
+++ b/packages/shared/src/components/admin/AdminFormat.test.ts
@@ -1,6 +1,10 @@
import { describe, expect, test } from 'vitest';
-import { formatAdminDate, formatAdminDateTime } from './AdminFormat';
+import {
+ formatAdminCurrencyCents,
+ formatAdminDate,
+ formatAdminDateTime,
+} from './AdminFormat';
describe('formatAdminDateTime', () => {
test('把后端的 seconds.microsZ 口径当成同一时刻渲染', () => {
@@ -44,3 +48,11 @@ describe('formatAdminDate', () => {
expect(formatAdminDate('1791008678.817257Z')).not.toBe('-');
});
});
+
+describe('formatAdminCurrencyCents', () => {
+ test('分转成带两位小数的元', () => {
+ expect(formatAdminCurrencyCents(0)).toBe('¥0.00');
+ expect(formatAdminCurrencyCents(600)).toBe('¥6.00');
+ expect(formatAdminCurrencyCents(1234)).toBe('¥12.34');
+ });
+});
diff --git a/packages/shared/src/components/admin/AdminFormat.ts b/packages/shared/src/components/admin/AdminFormat.ts
index 21e10f666..d69a0edfc 100644
--- a/packages/shared/src/components/admin/AdminFormat.ts
+++ b/packages/shared/src/components/admin/AdminFormat.ts
@@ -54,6 +54,11 @@ export function formatAdminDateTime(
}
}
+/** 后台统一的金额展示:分 → `¥12.34`;避免各后台页面各自实现导致格式漂移。 */
+export function formatAdminCurrencyCents(cents: number): string {
+ return `¥${(cents / 100).toFixed(2)}`;
+}
+
/** 后台统一的日期展示(不含时分秒):同样接受 `seconds.microsZ` 口径。 */
export function formatAdminDate(
value: string | number | Date | null | undefined,
diff --git a/packages/shared/src/components/admin/index.ts b/packages/shared/src/components/admin/index.ts
index 8483c98ba..081fcb5ec 100644
--- a/packages/shared/src/components/admin/index.ts
+++ b/packages/shared/src/components/admin/index.ts
@@ -19,7 +19,11 @@ export type {
AdminFormRowProps,
} from './AdminField';
export { AdminField, AdminFieldGroup, AdminFormRow } from './AdminField';
-export { formatAdminDate, formatAdminDateTime } from './AdminFormat';
+export {
+ formatAdminCurrencyCents,
+ formatAdminDate,
+ formatAdminDateTime,
+} from './AdminFormat';
export type {
AdminInfoItemProps,
AdminInfoListEntry,