交易流水对账明细导出完成

This commit is contained in:
2026-06-25 16:53:04 +08:00
parent 8aafd7de32
commit 30db7d3bf5
34 changed files with 2491 additions and 327 deletions
+47 -6
View File
@@ -5,7 +5,7 @@ import {
import {
UserOutlined, WalletOutlined, SearchOutlined, StopOutlined, CheckCircleOutlined, PlusOutlined, MenuOutlined, LockOutlined, SettingOutlined, SaveOutlined,
} from '@ant-design/icons';
import { getAdminUsers, adjustCredits, toggleUserStatus, createUser, updateUserMenus, getMenuConfigs, resetUserPassword, getSystemConfigs, updateSystemConfig } from '../api';
import { getAdminUsers, adjustCredits, toggleUserStatus, createUser, updateUserMenus, getMenuConfigs, resetUserPassword, getSystemConfigs, updateSystemConfig, updateFrontendUserKind } from '../api';
import type { AdminUser, SystemConfig } from '../types';
import { formatDate } from '../utils/formatDate';
@@ -14,6 +14,7 @@ const AdminUsers: React.FC = () => {
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState('');
const [activeTab, setActiveTab] = useState<string>('frontend');
const [frontendKindFilter, setFrontendKindFilter] = useState<string>('');
const [creditModal, setCreditModal] = useState<{ open: boolean; user: AdminUser | null }>({ open: false, user: null });
const [createModal, setCreateModal] = useState(false);
const [createType, setCreateType] = useState<string>('frontend');
@@ -36,14 +37,14 @@ const AdminUsers: React.FC = () => {
const load = async () => {
setLoading(true);
try {
const data = await getAdminUsers(page, pageSize, search || undefined);
const data = await getAdminUsers(page, pageSize, search || undefined, activeTab, activeTab === 'frontend' ? (frontendKindFilter || undefined) : undefined);
setUsers(data.items || []);
setTotal(data.total || 0);
} catch { /* auth error handled by client */ }
setLoading(false);
};
useEffect(() => { load(); }, [page, pageSize, search]);
useEffect(() => { load(); }, [page, pageSize, search, activeTab, frontendKindFilter]);
useEffect(() => {
const loadCreditConfigs = async () => {
@@ -116,6 +117,7 @@ const AdminUsers: React.FC = () => {
phone: userType === 'frontend' ? values.phone : (values.phone || undefined),
credits: values.credits || 0,
user_type: userType,
frontend_user_kind: values.frontend_user_kind || 'external',
});
message.success('用户创建成功');
setCreateModal(false);
@@ -176,7 +178,16 @@ const AdminUsers: React.FC = () => {
} catch { /* validation */ }
};
const filteredUsers = users.filter(u => u.userType === activeTab);
const handleUpdateFrontendKind = async (user: AdminUser, kind: 'internal' | 'external') => {
try {
await updateFrontendUserKind(user.id, kind);
message.success(kind === 'internal' ? '已设为前台内部用户' : '已设为前台外部用户');
load();
} catch (e: any) {
message.error(e?.message || '设置失败');
}
};
const isAdminTab = activeTab === 'admin';
const columns = [
@@ -214,6 +225,10 @@ const AdminUsers: React.FC = () => {
title: '手机号', dataIndex: 'phone', width: 130,
render: (v: string) => <Typography.Text type="secondary">{v || '-'}</Typography.Text>,
},
...(!isAdminTab ? [{
title: '前台归类', dataIndex: 'frontendUserKind', width: 110,
render: (v: string) => <Tag color={v === 'internal' ? 'geekblue' : 'default'}>{v === 'internal' ? '内部用户' : '外部用户'}</Tag>,
}] : []),
{
title: '状态', dataIndex: 'isActive', width: 80,
render: (v: boolean) => (
@@ -238,6 +253,12 @@ const AdminUsers: React.FC = () => {
</Button>
)}
{!isAdminTab && r.frontendUserKind !== 'internal' && (
<Button type="link" size="small" onClick={() => handleUpdateFrontendKind(r, 'internal')}></Button>
)}
{!isAdminTab && r.frontendUserKind === 'internal' && (
<Button type="link" size="small" onClick={() => handleUpdateFrontendKind(r, 'external')}></Button>
)}
<Button type="link" size="small" icon={<MenuOutlined />}
onClick={() => openMenuModal(r)}>
@@ -329,6 +350,18 @@ const AdminUsers: React.FC = () => {
style={{ width: 280, borderRadius: 8 }}
allowClear
/>
{!isAdminTab && (
<Select
value={frontendKindFilter}
onChange={(v) => { setPage(1); setFrontendKindFilter(v); }}
style={{ width: 140 }}
options={[
{ value: '', label: '全部前台用户' },
{ value: 'internal', label: '内部用户' },
{ value: 'external', label: '外部用户' },
]}
/>
)}
<Button type="primary" onClick={handleSearch} style={{ borderRadius: 8 }}></Button>
</div>
<Button type="primary" icon={<PlusOutlined />}
@@ -340,7 +373,7 @@ const AdminUsers: React.FC = () => {
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
onChange={(key) => { setActiveTab(key); setFrontendKindFilter(''); setPage(1); }}
items={[
{ key: 'frontend', label: '前台用户' },
{ key: 'admin', label: '后台用户' },
@@ -349,7 +382,7 @@ const AdminUsers: React.FC = () => {
<Table
columns={columns}
dataSource={filteredUsers}
dataSource={users}
rowKey="id"
loading={loading}
pagination={{
@@ -412,6 +445,14 @@ const AdminUsers: React.FC = () => {
{ value: 'admin', label: '后台管理员' },
]} />
</Form.Item>
{createType === 'frontend' && (
<Form.Item name="frontend_user_kind" label="前台归类" initialValue="external">
<Select size="large" options={[
{ value: 'external', label: '外部用户' },
{ value: 'internal', label: '内部用户' },
]} />
</Form.Item>
)}
{createType === 'frontend' ? (
<Form.Item name="phone" label="手机号" rules={[{ required: true, message: '请输入手机号' }, { pattern: /^1\d{10}$/, message: '请输入正确的手机号' }]}>
<Input placeholder="请输入手机号" maxLength={11} size="large" />