“单设备登录(同端互斥)”功能和后台全局用户配置
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
Button, Card, Checkbox, Form, Input, InputNumber, message, Modal, Popconfirm, Progress, Select, Space, Switch, Table, Tabs, Tag, Typography,
|
||||
Button, Card, Checkbox, Form, Input, InputNumber, message, Modal, Popconfirm, Progress, Radio, Select, Space, Switch, Table, Tabs, Tag, Typography,
|
||||
} from 'antd';
|
||||
import {
|
||||
UserOutlined, WalletOutlined, SearchOutlined, StopOutlined, CheckCircleOutlined, PlusOutlined, MinusOutlined, MenuOutlined, LockOutlined, SettingOutlined, SaveOutlined, DatabaseOutlined, TeamOutlined, PictureOutlined, SecurityScanOutlined,
|
||||
UserOutlined, WalletOutlined, SearchOutlined, StopOutlined, CheckCircleOutlined, PlusOutlined, MinusOutlined, MenuOutlined, LockOutlined, SettingOutlined, SaveOutlined, DatabaseOutlined, TeamOutlined, PictureOutlined, SecurityScanOutlined, SafetyOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
adminDeductCredits,
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
saveUserResourceCapacity,
|
||||
toggleUserStatus,
|
||||
updateFrontendUserKind,
|
||||
updateSingleDeviceLoginOverride,
|
||||
updateUserTeam,
|
||||
updateSystemConfig,
|
||||
updateUserMenus,
|
||||
@@ -84,6 +85,10 @@ const AdminUsers: React.FC = () => {
|
||||
const [capacityModal, setCapacityModal] = useState<{ open: boolean; user: AdminUser | null; detail: AdminUserResourceCapacityOut | null }>({ open: false, user: null, detail: null });
|
||||
const [teamModal, setTeamModal] = useState<{ open: boolean; user: AdminUser | null }>({ open: false, user: null });
|
||||
const [portraitModal, setPortraitModal] = useState<{ open: boolean; user: AdminUser | null; config: PrivatePortraitConfig | null }>({ open: false, user: null, config: null });
|
||||
const [singleDeviceModal, setSingleDeviceModal] = useState<{ open: boolean; user: AdminUser | null }>({ open: false, user: null });
|
||||
const [singleDeviceValue, setSingleDeviceValue] = useState<boolean | null>(null);
|
||||
const [singleDeviceSaving, setSingleDeviceSaving] = useState(false);
|
||||
const [globalSingleDeviceEnabled, setGlobalSingleDeviceEnabled] = useState(false);
|
||||
const [capacityLoading, setCapacityLoading] = useState(false);
|
||||
const [capacitySaving, setCapacitySaving] = useState(false);
|
||||
const [teamSaving, setTeamSaving] = useState(false);
|
||||
@@ -136,6 +141,9 @@ const AdminUsers: React.FC = () => {
|
||||
const formValues: Record<string, string> = {};
|
||||
credit.forEach(c => { formValues[c.key] = c.value; });
|
||||
configForm.setFieldsValue(formValues);
|
||||
// 获取全局单设备登录开关状态
|
||||
const singleDeviceConfig = configs.find(c => c.key === 'single_device_login_enabled');
|
||||
setGlobalSingleDeviceEnabled(singleDeviceConfig?.value === 'true');
|
||||
} catch { /* auth error handled by client */ }
|
||||
};
|
||||
loadCreditConfigs();
|
||||
@@ -431,6 +439,20 @@ const AdminUsers: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateSingleDeviceOverride = async (user: AdminUser, value: boolean | null) => {
|
||||
try {
|
||||
setSingleDeviceSaving(true);
|
||||
await updateSingleDeviceLoginOverride(user.id, value);
|
||||
message.success('已更新单设备登录设置');
|
||||
setSingleDeviceModal({ open: false, user: null });
|
||||
load();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '设置失败');
|
||||
} finally {
|
||||
setSingleDeviceSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const isAdminTab = activeTab === 'admin';
|
||||
|
||||
const columns = [
|
||||
@@ -591,6 +613,15 @@ const AdminUsers: React.FC = () => {
|
||||
onClick={() => { setResetPwdModal({ open: true, user: r }); resetPwdForm.resetFields(); }}>
|
||||
重置密码
|
||||
</Button>
|
||||
{!isAdminTab && (
|
||||
<Button type="link" size="small" icon={<SafetyOutlined />}
|
||||
onClick={() => {
|
||||
setSingleDeviceValue(r.singleDeviceLoginOverride ?? null);
|
||||
setSingleDeviceModal({ open: true, user: r });
|
||||
}}>
|
||||
单设备登录
|
||||
</Button>
|
||||
)}
|
||||
<Popconfirm
|
||||
title={r.isActive ? '确定禁用该用户?' : '确定启用该用户?'}
|
||||
onConfirm={() => handleToggleStatus(r)}
|
||||
@@ -1084,6 +1115,51 @@ const AdminUsers: React.FC = () => {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title={<Space><SafetyOutlined />单设备登录设置 - {singleDeviceModal.user?.username}</Space>}
|
||||
open={singleDeviceModal.open}
|
||||
onOk={() => {
|
||||
if (singleDeviceModal.user) {
|
||||
handleUpdateSingleDeviceOverride(singleDeviceModal.user, singleDeviceValue);
|
||||
}
|
||||
}}
|
||||
onCancel={() => setSingleDeviceModal({ open: false, user: null })}
|
||||
okText="保存" cancelText="取消" width={420}
|
||||
confirmLoading={singleDeviceSaving}
|
||||
>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
当前全局设置:<Tag color={globalSingleDeviceEnabled ? 'green' : 'default'}>{globalSingleDeviceEnabled ? '已开启' : '已关闭'}</Tag>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Radio.Group
|
||||
value={singleDeviceValue}
|
||||
onChange={e => setSingleDeviceValue(e.target.value)}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Radio value={null}>
|
||||
<Typography.Text>跟随全局</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, marginLeft: 8 }}>
|
||||
({globalSingleDeviceEnabled ? '当前受单设备登录限制' : '当前不受限制'})
|
||||
</Typography.Text>
|
||||
</Radio>
|
||||
<Radio value={true}>
|
||||
<Typography.Text>强制启用</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, marginLeft: 8 }}>
|
||||
(该用户始终启用单设备登录,不受全局影响)
|
||||
</Typography.Text>
|
||||
</Radio>
|
||||
<Radio value={false}>
|
||||
<Typography.Text>强制禁用</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, marginLeft: 8 }}>
|
||||
(该用户不受单设备登录限制,可多设备同时登录)
|
||||
</Typography.Text>
|
||||
</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user