feat: add manual recharge and operation logs
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ChevronLeft, ChevronRight, Search } from 'lucide-react';
|
||||
import { Breadcrumb, Button, DateRangeInput, Input, Select, type DateRangeValue } from '@/components/ui';
|
||||
import { ChevronLeft, ChevronRight, Plus, Search } from 'lucide-react';
|
||||
import { Breadcrumb, Button, DateRangeInput, Input, Modal, Select, Textarea, Tag, type DateRangeValue } from '@/components/ui';
|
||||
|
||||
type RechargeRecord = {
|
||||
id: string;
|
||||
@@ -9,17 +9,23 @@ type RechargeRecord = {
|
||||
amount?: number;
|
||||
balance?: number;
|
||||
operator?: string;
|
||||
type: 'manual' | 'package';
|
||||
remark?: string;
|
||||
};
|
||||
|
||||
const rechargeRecords: RechargeRecord[] = [
|
||||
{ id: 'RCG202601120001', enterprise: 'XXXX科技有限公司', rechargedAt: '2026-01-12 19:27:19', amount: 1000, balance: 1000, operator: '李XXX' },
|
||||
{ id: 'RCG202601120002', enterprise: 'XXX公司名字', rechargedAt: '2026-01-12 19:27:19', amount: 500, balance: 5896.25, operator: '张三' },
|
||||
{ id: 'RCG202601120003', enterprise: 'XXX公司名字XXX公司名字', rechargedAt: '2026-01-12 19:27:19', amount: 192.29, balance: 0, operator: '张三' },
|
||||
{ id: 'RCG202601120004', enterprise: '', rechargedAt: '2026-01-12 19:27:19', amount: 2617.09, balance: 0, operator: '李四' },
|
||||
{ id: 'RCG202601120005', enterprise: '', rechargedAt: '2026-01-12 19:27:19', amount: 122, balance: 0, operator: '' },
|
||||
{ id: 'RCG202601120006', enterprise: '', rechargedAt: '2026-01-12 19:27:19' },
|
||||
{ id: 'RCG202601120007', enterprise: '', rechargedAt: '2026-01-12 19:27:19' },
|
||||
{ id: 'RCG202601120008', enterprise: '', rechargedAt: '2026-01-12 19:27:19' },
|
||||
type ManualRechargeForm = {
|
||||
enterprise: string;
|
||||
amount: string;
|
||||
operator: string;
|
||||
remark: string;
|
||||
};
|
||||
|
||||
const rechargeRecordsSeed: RechargeRecord[] = [
|
||||
{ id: 'RCG202601120001', enterprise: 'XXXX科技有限公司', rechargedAt: '2026-01-12 19:27:19', amount: 1000, balance: 1000, operator: '李XXX', type: 'manual', remark: '线下转账到账' },
|
||||
{ id: 'RCG202601120002', enterprise: 'XXX公司名字', rechargedAt: '2026-01-12 19:27:19', amount: 500, balance: 5896.25, operator: '张三', type: 'package' },
|
||||
{ id: 'RCG202601120003', enterprise: 'XXX公司名字XXX公司名字', rechargedAt: '2026-01-12 19:27:19', amount: 192.29, balance: 0, operator: '张三', type: 'manual', remark: '运营补差额' },
|
||||
{ id: 'RCG202601120004', enterprise: '北京鸣川科技', rechargedAt: '2026-01-12 19:27:19', amount: 2617.09, balance: 0, operator: '李四', type: 'package' },
|
||||
{ id: 'RCG202601120005', enterprise: '广州麦芒科技', rechargedAt: '2026-01-12 19:27:19', amount: 122, balance: 0, operator: '王五', type: 'manual', remark: '客服人工充值' },
|
||||
];
|
||||
|
||||
function getDate(value: string) {
|
||||
@@ -38,18 +44,21 @@ function formatAmount(value?: number) {
|
||||
}
|
||||
|
||||
export function AdminRechargeRecordsPage() {
|
||||
const [records, setRecords] = useState(rechargeRecordsSeed);
|
||||
const [enterpriseKeyword, setEnterpriseKeyword] = useState('');
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue>({});
|
||||
const [manualOpen, setManualOpen] = useState(false);
|
||||
const [form, setForm] = useState<ManualRechargeForm>({ enterprise: '', amount: '', operator: '运营', remark: '' });
|
||||
|
||||
const filteredRows = useMemo(
|
||||
() => rechargeRecords.filter((item) => {
|
||||
() => records.filter((item) => {
|
||||
const rechargeDate = getDate(item.rechargedAt);
|
||||
const matchesEnterprise = !enterpriseKeyword || item.enterprise.includes(enterpriseKeyword);
|
||||
const matchesStartDate = !dateRange.start || rechargeDate >= dateRange.start;
|
||||
const matchesEndDate = !dateRange.end || rechargeDate <= dateRange.end;
|
||||
return matchesEnterprise && matchesStartDate && matchesEndDate;
|
||||
}),
|
||||
[dateRange.end, dateRange.start, enterpriseKeyword],
|
||||
[dateRange.end, dateRange.start, enterpriseKeyword, records],
|
||||
);
|
||||
|
||||
function resetFilters() {
|
||||
@@ -57,6 +66,32 @@ export function AdminRechargeRecordsPage() {
|
||||
setDateRange({});
|
||||
}
|
||||
|
||||
function updateForm<K extends keyof ManualRechargeForm>(key: K, value: ManualRechargeForm[K]) {
|
||||
setForm((current) => ({ ...current, [key]: value }));
|
||||
}
|
||||
|
||||
function submitManualRecharge() {
|
||||
const amount = Number(form.amount);
|
||||
if (!form.enterprise.trim() || !Number.isFinite(amount) || amount <= 0) {
|
||||
return;
|
||||
}
|
||||
setRecords((current) => [
|
||||
{
|
||||
id: `RCG${Date.now()}`,
|
||||
enterprise: form.enterprise,
|
||||
rechargedAt: '2026-07-01 13:58:00',
|
||||
amount,
|
||||
balance: amount + 1200,
|
||||
operator: form.operator,
|
||||
type: 'manual',
|
||||
remark: form.remark,
|
||||
},
|
||||
...current,
|
||||
]);
|
||||
setManualOpen(false);
|
||||
setForm({ enterprise: '', amount: '', operator: '运营', remark: '' });
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page-stack admin-recharge-page">
|
||||
<div className="page-heading">
|
||||
@@ -64,6 +99,7 @@ export function AdminRechargeRecordsPage() {
|
||||
<Breadcrumb items={['数据详单', '充值记录']} />
|
||||
<h1>充值记录</h1>
|
||||
</div>
|
||||
<Button icon={<Plus size={16} />} onClick={() => setManualOpen(true)}>人工充值</Button>
|
||||
</div>
|
||||
|
||||
<div className="surface admin-recharge-filter">
|
||||
@@ -84,7 +120,9 @@ export function AdminRechargeRecordsPage() {
|
||||
<th>充值时间</th>
|
||||
<th>充值金额</th>
|
||||
<th>充值后余额</th>
|
||||
<th>充值类型</th>
|
||||
<th>操作人</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -94,7 +132,9 @@ export function AdminRechargeRecordsPage() {
|
||||
<td>{record.rechargedAt}</td>
|
||||
<td>{formatAmount(record.amount)}</td>
|
||||
<td>{formatAmount(record.balance)}</td>
|
||||
<td><Tag tone={record.type === 'manual' ? 'warning' : 'info'}>{record.type === 'manual' ? '人工充值' : '套餐充值'}</Tag></td>
|
||||
<td>{record.operator}</td>
|
||||
<td>{record.remark ?? '-'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -114,6 +154,28 @@ export function AdminRechargeRecordsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{manualOpen ? (
|
||||
<Modal
|
||||
footer={(
|
||||
<>
|
||||
<Button onClick={() => setManualOpen(false)} variant="ghost">取消</Button>
|
||||
<Button onClick={submitManualRecharge}>确认充值</Button>
|
||||
</>
|
||||
)}
|
||||
onClose={() => setManualOpen(false)}
|
||||
open
|
||||
size="md"
|
||||
title="企业人工充值"
|
||||
>
|
||||
<div className="admin-system-modal-form">
|
||||
<Input label="企业名称" onChange={(event) => updateForm('enterprise', event.target.value)} value={form.enterprise} />
|
||||
<Input label="充值金额" onChange={(event) => updateForm('amount', event.target.value)} prefix="¥" type="number" value={form.amount} />
|
||||
<Input label="操作人" onChange={(event) => updateForm('operator', event.target.value)} value={form.operator} />
|
||||
<Textarea className="admin-system-modal-form__wide" label="充值备注" onChange={(event) => updateForm('remark', event.target.value)} rows={4} value={form.remark} />
|
||||
</div>
|
||||
</Modal>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user