feat: harden platform workflows and UI governance

This commit is contained in:
hectorzhao
2026-07-22 14:14:55 +08:00
parent ef957f7daa
commit 0f223f7f91
80 changed files with 4958 additions and 764 deletions
+19 -70
View File
@@ -1,15 +1,9 @@
import { useEffect, useMemo, useState } from 'react';
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 { Breadcrumb, Button, DateRangeInput, Input, ManualRechargeDialog, Pagination, Tag, type DateRangeValue } from '@/components/ui';
import { adminApi, type RechargeOrder, type TenantAccount, type TenantOption } from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
import { formatCents, isValidMoneyInput, yuanToMoneyUnits } from '@/utils/currency';
type ManualRechargeForm = {
tenantId: string;
amount: string;
remark: string;
};
import { formatCents } from '@/utils/currency';
function getDate(value: string) {
return value.slice(0, 10);
@@ -26,27 +20,26 @@ function RemarkCell({ value }: { value?: string }) {
export function AdminRechargeRecordsPage() {
const [records, setRecords] = useState<RechargeOrder[]>([]);
const [tenants, setTenants] = useState<TenantOption[]>([]);
const [accounts, setAccounts] = useState<TenantAccount[]>([]);
const [enterpriseKeyword, setEnterpriseKeyword] = useState('');
const [dateRange, setDateRange] = useState<DateRangeValue>({});
const [manualOpen, setManualOpen] = useState(false);
const [form, setForm] = useState<ManualRechargeForm>({ tenantId: '', amount: '', remark: '' });
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [manualError, setManualError] = useState('');
const [submitting, setSubmitting] = useState(false);
async function loadData() {
setLoading(true);
setError('');
try {
const [nextTenants, nextRecords] = await Promise.all([
const [nextTenants, nextAccounts, nextRecords] = await Promise.all([
adminApi.listTenants(),
adminApi.listAccounts(),
adminApi.listManualRecharges(),
]);
setTenants(nextTenants);
setAccounts(nextAccounts);
setRecords(nextRecords);
setForm((current) => ({ ...current, tenantId: current.tenantId || nextTenants[0]?.id || '' }));
} catch (err) {
setError(err instanceof Error ? err.message : '充值记录加载失败');
setRecords([]);
@@ -84,35 +77,6 @@ export function AdminRechargeRecordsPage() {
setDateRange({});
}
function updateForm<K extends keyof ManualRechargeForm>(key: K, value: ManualRechargeForm[K]) {
setForm((current) => ({ ...current, [key]: value }));
setManualError('');
}
async function submitManualRecharge() {
const amount = Number(form.amount);
if (!form.tenantId || !Number.isFinite(amount) || !isValidMoneyInput(form.amount, { allowNegative: true, allowZero: false })) {
setManualError('请填写非 0 的充值金额;金额支持负数冲正。');
return;
}
setSubmitting(true);
setManualError('');
try {
await adminApi.createManualRecharge({
tenantId: form.tenantId,
amountCents: yuanToMoneyUnits(form.amount),
remark: form.remark,
});
await loadData();
setManualOpen(false);
setForm({ tenantId: tenants[0]?.id ?? '', amount: '', remark: '' });
} catch (failure) {
setManualError(failure instanceof Error ? failure.message : '人工充值失败');
} finally {
setSubmitting(false);
}
}
return (
<section className="page-stack admin-recharge-page">
<div className="page-heading">
@@ -181,33 +145,18 @@ export function AdminRechargeRecordsPage() {
/>
</div>
{manualOpen ? (
<Modal
footer={(
<>
<Button disabled={submitting} onClick={() => setManualOpen(false)} variant="ghost"></Button>
<Button disabled={submitting} onClick={() => { void submitManualRecharge(); }}>{submitting ? '充值中...' : '确认充值'}</Button>
</>
)}
onClose={() => setManualOpen(false)}
open
size="md"
title="企业人工充值"
>
<div className="admin-system-modal-form">
<Select
label="企业名称"
onChange={(event) => updateForm('tenantId', event.target.value)}
options={tenants.map((tenant) => ({ label: tenant.name, value: tenant.id }))}
required
value={form.tenantId}
/>
<Input label="充值金额" onChange={(event) => updateForm('amount', event.target.value)} prefix="¥" required step="0.0001" type="number" value={form.amount} />
<Textarea className="admin-system-modal-form__wide" label="充值备注" onChange={(event) => updateForm('remark', event.target.value)} rows={4} value={form.remark} />
</div>
{manualError ? <p className="form-error">{manualError}</p> : null}
</Modal>
) : null}
<ManualRechargeDialog
initialTargetId={tenants.find((tenant) => tenant.status !== 'deleted')?.id}
onClose={() => setManualOpen(false)}
onCompleted={loadData}
open={manualOpen}
targets={tenants.filter((tenant) => tenant.status !== 'deleted').map((tenant) => ({
id: tenant.id,
name: tenant.name,
code: tenant.code,
balanceCents: accounts.find((account) => account.tenantId === tenant.id)?.balanceCents ?? 0,
}))}
/>
</section>
);
}