feat(dashboard): 首页三数共享保存+智能校验+保底;轮播种子批量操作 (#7)
- HomeStatsConfig: 三指标共享保存/立即更新;智能关系校验弹窗;真实值保底;初始基数护栏提示;saveAll 半失败合并 - HomeMarqueeSeeds: 勾选 + 批量启用/停用/删除 - 附带并行会话 WIP:users 页 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: OuYingJun1024 <1034284404@qq.com> Reviewed-on: #7 Co-authored-by: ouzhou <ouzhou@wonderable.ai> Co-committed-by: ouzhou <ouzhou@wonderable.ai>
This commit was merged in pull request #7.
This commit is contained in:
@@ -18,9 +18,10 @@ import {
|
||||
import { api, errMsg } from '@/lib/api';
|
||||
import { canDo } from '@/lib/auth';
|
||||
import { useCursorList } from '@/lib/useCursorList';
|
||||
import type { UserListItem } from '@/lib/types';
|
||||
import type { UserListItem, UserOverview } from '@/lib/types';
|
||||
|
||||
const STATUS_COLOR: Record<string, string> = { active: 'green', disabled: 'red', deleted: 'default' };
|
||||
const yuan = (c: number) => `¥${(c / 100).toFixed(2)}`;
|
||||
|
||||
export default function UsersPage() {
|
||||
const router = useRouter();
|
||||
@@ -37,6 +38,20 @@ export default function UsersPage() {
|
||||
|
||||
const [coinUser, setCoinUser] = useState<UserListItem | null>(null);
|
||||
const [coinForm] = Form.useForm();
|
||||
const [cashUser, setCashUser] = useState<UserListItem | null>(null);
|
||||
const [cashForm] = Form.useForm();
|
||||
// 弹窗里展示该用户当前余额:列表行本身不带余额,开弹窗时拉一次 360 概览
|
||||
const [balances, setBalances] = useState<{ coin: number; cashCents: number } | null>(null);
|
||||
|
||||
const openAdjust = (u: UserListItem, kind: 'coin' | 'cash') => {
|
||||
setBalances(null);
|
||||
if (kind === 'coin') setCoinUser(u);
|
||||
else setCashUser(u);
|
||||
api
|
||||
.get<UserOverview>(`/admin/api/users/${u.id}`)
|
||||
.then((r) => setBalances({ coin: r.data.coin_balance, cashCents: r.data.cash_balance_cents }))
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const search = () => setFilters({ phone: phone || undefined, status });
|
||||
|
||||
@@ -52,6 +67,27 @@ export default function UsersPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 发/扣现金:输入元,×100 转分后调后端(主要给无现金用户发钱直接测试提现)
|
||||
const submitCash = async () => {
|
||||
const v = await cashForm.validateFields();
|
||||
const amountCents = Math.round(Number(v.amount_yuan) * 100);
|
||||
if (amountCents === 0) {
|
||||
message.warning('金额不能为 0(且不小于 1 分)');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api.post(`/admin/api/users/${cashUser!.id}/cash`, {
|
||||
amount_cents: amountCents,
|
||||
reason: v.reason,
|
||||
});
|
||||
message.success(`已${amountCents > 0 ? '发放' : '扣减'} ¥${(Math.abs(amountCents) / 100).toFixed(2)} 现金`);
|
||||
setCashUser(null);
|
||||
cashForm.resetFields();
|
||||
} catch (e) {
|
||||
message.error(errMsg(e));
|
||||
}
|
||||
};
|
||||
|
||||
const toggleStatus = (u: UserListItem) => {
|
||||
const next = u.status === 'active' ? 'disabled' : 'active';
|
||||
Modal.confirm({
|
||||
@@ -109,7 +145,8 @@ export default function UsersPage() {
|
||||
render: (_: unknown, u: UserListItem) => (
|
||||
<Space>
|
||||
<a onClick={() => router.push(`/users/${u.id}`)}>详情</a>
|
||||
{canCoins && <a onClick={() => setCoinUser(u)}>调金币</a>}
|
||||
{canCoins && <a onClick={() => openAdjust(u, 'coin')}>调金币</a>}
|
||||
{canCoins && <a onClick={() => openAdjust(u, 'cash')}>调现金</a>}
|
||||
{canStatus && u.status !== 'deleted' && (
|
||||
<a onClick={() => toggleStatus(u)}>{u.status === 'active' ? '封禁' : '解封'}</a>
|
||||
)}
|
||||
@@ -163,8 +200,11 @@ export default function UsersPage() {
|
||||
open={!!coinUser}
|
||||
onOk={submitCoins}
|
||||
onCancel={() => setCoinUser(null)}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
<p style={{ color: '#888', marginBottom: 12 }}>
|
||||
当前余额:金币 {balances ? balances.coin : '…'} | 现金 {balances ? yuan(balances.cashCents) : '…'}
|
||||
</p>
|
||||
<Form form={coinForm} layout="vertical">
|
||||
<Form.Item name="amount" label="金币变动(正=增加,负=扣减)" rules={[{ required: true }]}>
|
||||
<InputNumber style={{ width: '100%' }} />
|
||||
@@ -174,6 +214,30 @@ export default function UsersPage() {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title={`调现金 - ${cashUser?.phone ?? ''}`}
|
||||
open={!!cashUser}
|
||||
onOk={submitCash}
|
||||
onCancel={() => setCashUser(null)}
|
||||
destroyOnHidden
|
||||
>
|
||||
<p style={{ color: '#888', marginBottom: 12 }}>
|
||||
当前余额:金币 {balances ? balances.coin : '…'} | 现金 {balances ? yuan(balances.cashCents) : '…'}
|
||||
</p>
|
||||
<Form form={cashForm} layout="vertical">
|
||||
<Form.Item
|
||||
name="amount_yuan"
|
||||
label="现金变动(元,正=发放,负=扣减;不可扣成负)"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<InputNumber style={{ width: '100%' }} step={0.01} precision={2} addonAfter="元" />
|
||||
</Form.Item>
|
||||
<Form.Item name="reason" label="原因(入审计)" rules={[{ required: true }]}>
|
||||
<Input.TextArea rows={2} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user