Files
web-Iot/src/components/SystemSetting/BasicSettings.tsx
2026-08-21 11:08:18 +08:00

613 lines
25 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Form,
Input,
Select,
Switch,
Button,
Row,
Col,
Card as AntdCard,
Tag,
Table,
message,
Typography,
Space,
TimePicker,
Divider,
} from 'antd';
import {
CloudOutlined,
TeamOutlined,
BellOutlined,
DatabaseOutlined,
CalendarOutlined,
CheckCircleOutlined,
DownloadOutlined,
FileTextOutlined,
DeleteOutlined,
RestOutlined,
SettingOutlined,
UserOutlined,
NotificationOutlined,
ApiOutlined,
SafetyCertificateOutlined,
ToolOutlined,
RightOutlined,
EyeOutlined,
} from '@ant-design/icons';
import {
Smartphone,
Mail,
Send,
MessageSquare,
ShieldCheck,
Users,
LineChart,
Wrench,
Cpu,
Camera,
Wind,
Box,
Activity,
Zap,
RefreshCw,
Shield,
Key,
Lock,
} from 'lucide-react';
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import { getRoleList } from '@/api/role';
const { Text, Title } = Typography;
const BasicSettings = () => {
const { t } = useTranslation();
const [form] = Form.useForm();
const [roles, setRoles] = useState<any[]>([]);
// 响应式字体大小
const fontNormal = 'clamp(12px, 0.75vw, 13px)';
const fontSmall = 'clamp(11px, 0.7vw, 12px)';
const fontTitle = 'clamp(14px, 0.85vw, 16px)';
const fontLarge = 'clamp(18px, 1.1vw, 22px)';
const handleSave = () => {
message.success(t('systemSetting.fixed2SettingsSaveFixed2'));
};
// 顶部统计卡片数据
const topCards = [
{
title: t('systemSetting.fixed2Current'),
value: 'v2.8.1',
tag: '最新版',
tagColor: 'blue',
icon: <Cpu size={24} color="#1677ff" />,
bg: 'linear-gradient(135deg, #e6f4ff 0%, #bae0ff 100%)'
},
{
title: t('systemSetting.onlineUsers'),
value: '12',
trend: '↑ 2',
trendColor: '#52c41a',
icon: <Users size={24} color="#52c41a" />,
bg: 'linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%)'
},
{
title: t('systemSetting.fixed2AlertFixed2Fixed22'),
value: '3 / 4',
trend: t('roles.normal'),
trendColor: '#52c41a',
icon: <BellOutlined style={{ fontSize: 24, color: '#ff4d4f' }} />,
bg: 'linear-gradient(135deg, #fff2f0 0%, #ffccc7 100%)'
},
{
title: t('systemSetting.statusFixed2'),
value: t('roles.normal'),
trend: '●',
trendColor: '#52c41a',
icon: <DatabaseOutlined style={{ fontSize: 24, color: '#13c2c2' }} />,
bg: 'linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%)'
},
{
title: t('systemSetting.s282'),
value: '2025-05-20',
icon: <CalendarOutlined style={{ fontSize: 24, color: '#722ed1' }} />,
bg: 'linear-gradient(135deg, #f9f0ff 0%, #efdbff 100%)'
},
];
// 角色权限概览
const roleOverview = [
//平台管理员
{
title: t('roles.superAdmin'),
users: 2,
perms: 28,
tags: ['全部权限', '系统管理'],
color: '#1677ff',
bg: '#e6f4ff',
icon: <ShieldCheck size={20} color="#1677ff" />
},
//普通用户
{
title: t('systemSetting.fixed2OM'),
users: 5,
perms: 18,
tags: ["处理工单","处理任务"],
color: '#52c41a',
bg: '#f6ffed',
icon: <Wrench size={20} color="#52c41a" />
},
//机构管理员
{
title: t('systemSetting.fixed2Inspection2'),
users: 8,
perms: 12,
tags: ['管理机构下所有场站', '告警处理'],
color: '#fa8c16',
bg: '#fff7e6',
icon: <Users size={20} color="#fa8c16" />
},
//场站管理员
{
title: t('systemSetting.s322'),
users: 3,
perms: 15,
tags: ['管理自己场站', '报表查看','告警处理'],
color: '#722ed1',
bg: '#f9f0ff',
icon: <LineChart size={20} color="#722ed1" />
},
];
const matchRoleStyle = (roleName: string) => {
const found = roleOverview.find(item => item.title === roleName);
if (found) return found;
// 兜底默认样式
return {
color: '#8c8c8c',
bg: '#f5f5f5',
icon: null,
tags: []
};
};
// 设备状态
const deviceStatuses = [
{ name: t('home.inverter'), count: '5/5', status: t('roles.normal') },
{ name: t('home.combinerBox'), count: '20/20', status: t('roles.normal') },
{ name: t('home.WeatherStation'), count: '1/1', status: t('roles.normal') },
{ name: t('devices.camera'), count: '12/12', status: t('roles.normal') },
{ name: t('constants.uav'), count: '2/2', status: t('roles.normal') },
{ name: t('devices.robot'), count: '5/5', status: t('roles.normal') },
{ name: t('systemSetting.api'), count: t('roles.normal'), status: t('roles.normal') },
{ name: 'Modbus', count: t('roles.normal'), status: t('roles.normal') },
];
const getRoleData = () => {
const params = { pageNum: 1, pageSize: 9999 };
getRoleList(params).then(res => {
if (res.code === 200) {
let sortedRoles = (res.data || []).sort((a, b) => (a.roleSort || 999) - (b.roleSort || 999));
setRoles(sortedRoles);
}
});
};
useEffect(() => {
getRoleData();
}, [])
return (
<div className="normal-spacing" style={{ padding: '8px 0px', background: '#f5f7fa', minHeight: '100vh' }}>
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>{t('systemSetting.basicSettings')}</Typography.Title>
</div>
{/* 顶部统计卡片 */}
<Row gutter={[8, 8]} style={{ marginBottom: 8 }}>
{topCards.map((item, idx) => (
<Col key={idx} style={{ minWidth: '168px', flex: '1 0 auto', maxWidth: "250px" }}>
<AntdCard bordered={false} bodyStyle={{ padding: '12px 16px' }} style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
{/*<div style={{ width: 40, height: 40, borderRadius: 10, background: item.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{React.cloneElement(item.icon as React.ReactElement, { size: 20, style: item.icon.props.style ? { ...item.icon.props.style, fontSize: 20 } : { fontSize: 20 } })}
</div>*/}
<div style={{ flex: 1 }}>
<div style={{ color: '#8c8c8c', fontSize: fontSmall, marginBottom: 2 }}>{item.title}</div>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
<span style={{ fontSize: 'clamp(16px, 1vw, 20px)', fontWeight: 700, color: '#262626' }}>{item.value}</span>
{item.tag && <Tag color={item.tagColor} style={{ fontSize: 9, margin: 0, borderRadius: 4, padding: '0 4px' }}>{item.tag}</Tag>}
{item.trend && <span style={{ color: item.trendColor, fontSize: fontSmall, fontWeight: 600 }}>{item.trend}</span>}
</div>
</div>
</div>
</AntdCard>
</Col>
))}
</Row>
<Row gutter={[8, 8]}>
{/* 左侧 */}
<Col xs={24} xl={17}>
{/* 基础配置 */}
<AntdCard
title={<Space><SettingOutlined style={{ color: '#1677ff' }} /><b>{t('systemSetting.basicSettings')}</b></Space>}
bordered={false}
style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '16px' }}
>
<Form form={form} layout="vertical">
<Row gutter={16}>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.systemName')} name="systemName" style={{ marginBottom: 12 }}>
<Input size="small" defaultValue={t('login.systemName')} />
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.fixed2Default2')} name="defaultSite" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue={t('layout.defaultStationName')}>
<Select.Option value="1MW">{t('layout.defaultStationName')}</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.defaultLanguage')} name="defaultLang" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue="zh">
<Select.Option value="zh">{t('users.chinese')}</Select.Option>
<Select.Option value="en">English</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.timezone')} name="timezone" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue="UTC+08:00">
<Select.Option value="8">UTC+08:00</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.default')} name="defaultPage" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue="dashboard">
<Select.Option value="dashboard">{t('router.overview')}</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.refreshFixed2FrequencyFixed2')} name="refreshRate" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue="5min">
<Select.Option value="5min">{t('systemSetting.5')}</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} sm={12} md={8}>
<Form.Item label={t('systemSetting.s232')} name="mapService" style={{ marginBottom: 12 }}>
<Select size="small" defaultValue="gaode">
<Select.Option value="gaode">{t('systemSetting.s24')}</Select.Option>
</Select>
</Form.Item>
</Col>
<Col xs={24} md={16}>
<div style={{ display: 'flex', gap: 'clamp(8px, 1vw, 20px)', marginTop: 4, flexWrap: 'wrap' }}>
<Space>
<span style={{ fontSize: fontSmall }}>{t('systemSetting.s34')}</span>
<Switch size="small" defaultChecked />
</Space>
<Space>
<span style={{ fontSize: fontSmall }}>{t('systemSetting.fixed2Ai')}</span>
<Switch size="small" defaultChecked />
</Space>
<Space>
<span style={{ fontSize: fontSmall }}>{t('systemSetting.s52')}</span>
<Switch size="small" defaultChecked />
</Space>
</div>
</Col>
</Row>
<div style={{ marginTop: 12 }}>
<Button type="primary" size="small" onClick={handleSave} style={{ width: 100, borderRadius: 6 }}>{t('systemSetting.fixed2SaveFixed2SettingsFixed2')}</Button>
<Button size="small" style={{ marginLeft: 8, borderRadius: 6 }}>{t('systemSetting.fixed2ResumeFixed2DefaultFixed2')}</Button>
</div>
</Form>
</AntdCard>
{/* 通知与告警策略 */}
<AntdCard
title={<Space><BellOutlined style={{ color: '#1677ff' }} /><b>{t('systemSetting.alert2')}</b></Space>}
bordered={false}
style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '16px' }}
>
<Row gutter={[12, 12]} style={{ marginBottom: 8 }}>
{[
{ label: t('alerts.sms'), icon: <Smartphone size={16} />, color: '#52c41a', status: `${t('alerts.enabled')}` },
{ label: t('alerts.email'), icon: <Mail size={16} />, color: '#1677ff', status: `${t('alerts.enabled')}` },
{ label: 'Telegram', icon: <Send size={16} />, color: '#13c2c2', status: `${t('alerts.enabled')}` },
{ label: t('report.systemNotification'), icon: <BellOutlined style={{ fontSize: 16 }} />, color: '#fa8c16', status: `${t('alerts.enabled')}` },
].map((item, idx) => (
<Col xs={24} sm={12} lg={6} key={idx}>
<div style={{ display: 'flex', alignItems: 'center', padding: '10px 12px', background: '#f8fafc', borderRadius: 8, border: '1px solid #f1f5f9' }}>
<div style={{ width: 32, height: 32, borderRadius: '50%', background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 1px 4px rgba(0,0,0,0.04)', marginRight: 10 }}>
{/*{React.cloneElement(item.icon as React.ReactElement, { style: { color: item.color } })}*/}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 10, color: '#8c8c8c' }}>{item.label}</div>
<div style={{ fontSize: 10, color: '#52c41a' }}>{item.status}</div>
</div>
<Switch size="small" defaultChecked />
</div>
</Col>
))}
</Row>
<Row gutter={[24, 12]}>
<Col xs={24} sm={12} md={8}><Space size={8}><span style={{ fontSize: fontSmall }}>{t('systemSetting.fixed2CriticalAlert')}</span><Switch size="small" defaultChecked /></Space></Col>
<Col xs={24} sm={12} md={8}><Space size={8}><span style={{ fontSize: fontSmall }}>{t('systemSetting.fixed2GenerationFixed2Descend15')}</span><Switch size="small" defaultChecked /></Space></Col>
<Col xs={24} sm={12} md={8}><Space size={8}><span style={{ fontSize: fontSmall }}>{t('systemSetting.fixed2Communicating')}</span><Switch size="small" defaultChecked /></Space></Col>
<Col xs={24} sm={12} md={8}><Space size={8}><span style={{ fontSize: fontSmall }}>{t('systemSetting.everydayPush')}</span><Switch size="small" defaultChecked /></Space></Col>
<Col xs={24} md={16}>
<Space size="middle" style={{ flexWrap: 'wrap' }}>
<span style={{ fontSize: fontSmall }}>{t('systemSetting.Nighttime')}</span>
<TimePicker.RangePicker size="small" defaultValue={[dayjs('23:00', 'HH:mm'), dayjs('07:00', 'HH:mm')]} format="HH:mm" style={{ width: 140 }} />
<Switch size="small" />
</Space>
</Col>
</Row>
</AntdCard>
{/* 用户与权限预览 */}
<AntdCard
title={<Space><Users size={16} style={{ color: '#1677ff' }} /><b>{t('systemSetting.fixed2RolePermissionFixed2OverviewFixed2')}</b></Space>}
bordered={false}
style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)', marginBottom: 0 }}
bodyStyle={{ padding: '12px' }}
>
<Table
size="small"
scroll={{ x: 'max-content' }}
rowStyle={{ height: '32px', padding: 0 }}
empty={false}
pagination={{ size: 'small', total: 4, pageSize: 4, showTotal: (total) => `共 ${total} 条` }}
columns={[
{ title: t('systemSetting.s36'), dataIndex: 'name', key: 'name', fixed: 'left', width: 80 },
{
title: t('users.role'),
dataIndex: 'role',
key: 'role',
width: 100,
render: (role) => {
const config: any = {
'超级管理员': { color: '#1677ff', bg: '#e6f4ff' },
'运维工程师': { color: '#52c41a', bg: '#f6ffed' },
'巡检主管': { color: '#fa8c16', bg: '#fff7e6' },
'数据分析师': { color: '#722ed1', bg: '#f9f0ff' },
};
const s = config[role] || { color: '#8c8c8c', bg: '#f5f5f5' };
return <Tag bordered={false} style={{ color: s.color, background: s.bg, fontSize: 10, margin: 0 }}>{role}</Tag>;
}
},
{ title: t('users.phone'), dataIndex: 'phone', key: 'phone', width: 110 },
{ title: t('users.email'), dataIndex: 'email', key: 'email', ellipsis: true },
{
title: t('roles.status'),
dataIndex: 'status',
key: 'status',
width: 70,
render: (status) => (
<Space size={4}>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: status === t('devices.online') ? '#52c41a' : '#bfbfbf' }} />
<span style={{ fontSize: 10 }}>{status}</span>
</Space>
)
},
{
title: t('roles.actions'), key: 'action', width: 60, render: () => <Button
type="link"
size="small"
icon={<EyeOutlined />}
style={{ padding: 0, fontSize: 11 }}
>{t('common.view')}</Button>, fixed: 'right'
},
]}
dataSource={[
{ name: '张运维', role: '超级管理员', phone: '138****0001', email: 'zhangyw@pvsmart.com', status: t('devices.online') },
{ name: '李工程师', role: '运维工程师', phone: '139****0002', email: 'li.gongcheng@pvsmart.com', status: t('devices.online') },
{ name: '王班长', role: '巡检主管', phone: '137****0003', email: 'wang.banzhang@pvsmart.com', status: t('devices.online') },
{ name: '赵分析师', role: '数据分析师', phone: '136****0004', email: 'zhao.fx@pvsmart.com', status: t('devices.online') },
]}
/>
</AntdCard>
</Col>
{/* 右侧 */}
<Col xs={24} xl={7}>
{/* 角色权限概览 */}
<AntdCard
title={<Space><Users size={16} style={{ color: '#1677ff' }} /><b>{t('systemSetting.fixed2RolePermissionFixed2OverviewFixed2')}</b></Space>}
bordered={false}
style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '10px' }}
>
<Row gutter={[8, 8]}>
{roles.map((role, idx) => {
const styleConf = matchRoleStyle(role.roleName);
return (
<Col xs={24} sm={12} key={idx}>
<div style={{ background: styleConf.bg, borderRadius: 8, padding: 8, height: '100%' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4, alignItems: 'center' }}>
{styleConf.icon}
</div>
<div style={{ fontWeight: 600, fontSize: 11, color: styleConf.color, marginBottom: 2 }}>
{role.roleName}
</div>
<div style={{ fontSize: 9, color: '#8c8c8c', marginBottom: 6 }}>
用户 {role.users} | 菜单权限 {role.menuIds?.length ?? 0}
</div>
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
{styleConf.tags.map((tag, tidx) => (
<span
key={tidx}
style={{
fontSize: 8,
padding: '0 4px',
background: '#fff',
color: styleConf.color,
borderRadius: 2,
border: `1px solid ${styleConf.color}20`
}}
>
{tag}
</span>
))}
</div>
</div>
</Col>
);
})}
</Row>
</AntdCard>
{/* 设备接入状态 */}
<AntdCard
title={<Space><ApiOutlined style={{ color: '#1677ff' }} /><b>{t('systemSetting.fixed2DeviceAccessFixed2StatusFixed2')}</b></Space>}
extra={<Button type="link" size="small" style={{ fontSize: 11, padding: 0 }}>{t('common.detail')}<RightOutlined style={{ fontSize: 9 }} /></Button>}
bordered={false}
style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '12px' }}
>
<div style={{ display: 'flex', gap: 12 }}>
<div style={{ flex: '0 0 90px' }}>
{deviceStatuses.map((item, idx) => (
<div key={idx} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
<Space size={4}>
<div style={{ width: 5, height: 5, borderRadius: '50%', background: '#52c41a' }} />
<span style={{ fontSize: 10, color: '#595959' }}>{item.name}</span>
</Space>
<span style={{ fontSize: 10, fontWeight: 500 }}>{item.count}</span>
</div>
))}
</div>
<div style={{ flex: 1, position: 'relative', height: 130, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(22, 119, 255, 0.02)', borderRadius: 10 }}>
<svg width="100%" height="100%" viewBox="0 0 200 160" style={{ maxWidth: 180 }}>
<defs>
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
</defs>
<g stroke="#1677ff30" strokeWidth="0.8" strokeDasharray="3 2">
<line x1="100" y1="80" x2="60" y2="40" />
<line x1="100" y1="80" x2="140" y2="40" />
<line x1="100" y1="80" x2="160" y2="80" />
<line x1="100" y1="80" x2="140" y2="120" />
<line x1="100" y1="80" x2="60" y2="120" />
<line x1="100" y1="80" x2="40" y2="80" />
</g>
<circle cx="100" cy="80" r="14" fill="#1677ff" filter="url(#glow)" />
<rect x="93" y="73" width="14" height="14" rx="2" fill="none" stroke="#fff" strokeWidth="1.2" />
<path d="M96 76h8M96 80h8M96 84h8" stroke="#fff" strokeWidth="0.8" />
{[
{ x: 60, y: 40, icon: <Cpu size={10} />, label: t('home.inverter') },
{ x: 140, y: 40, icon: <Box size={10} />, label: t('home.combinerBox') },
{ x: 160, y: 80, icon: <Wind size={10} />, label: t('video.WeatherStation') },
{ x: 140, y: 120, icon: <Camera size={10} />, label: t('devices.camera') },
{ x: 60, y: 120, icon: <Activity size={10} />, label: t('devices.robot') },
{ x: 40, y: 80, icon: <Zap size={10} />, label: t('systemSetting.s38') },
].map((node, i) => (
<g key={i}>
<circle cx={node.x} cy={node.y} r="10" fill="#fff" stroke="#e8e8e8" strokeWidth="0.8" />
<foreignObject x={node.x - 5} y={node.y - 5} width="10" height="10">
<div style={{ color: '#1677ff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{node.icon}</div>
</foreignObject>
<text x={node.x} y={node.y + 16} textAnchor="middle" style={{ fontSize: 7, fill: '#8c8c8c' }}>{node.label}</text>
</g>
))}
</svg>
</div>
</div>
</AntdCard>
{/* 数据与安全 */}
<AntdCard
title={<Space><Shield size={16} style={{ color: '#1677ff' }} /><b>{t('systemSetting.dataSafety')}</b></Space>}
bordered={false}
style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '12px' }}
>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '10px 16px' }}>
{[
{ label: t('systemSetting.s39'), value: '3 年', icon: <DatabaseOutlined style={{ color: '#1677ff' }} /> },
{ label: t('systemSetting.fixed2Login2'), value: '已开启', icon: <CheckCircleOutlined style={{ color: '#52c41a' }} />, color: '#52c41a' },
{ label: t('systemSetting.s412'), value: '02:00', icon: <RefreshCw size={12} color="#1677ff" /> },
{ label: t('systemSetting.api'), value: t('roles.normal'), icon: <Key size={12} color="#52c41a" />, color: '#52c41a' },
{ label: t('systemSetting.s43'), value: '2025-05-24 02:00', icon: <CalendarOutlined style={{ color: '#1677ff' }} /> },
{ label: t('systemSetting.ssl'), value: '有效期至 2026-12-31', icon: <SafetyCertificateOutlined style={{ color: '#fa8c16' }} /> },
{ label: t('systemSetting.s45'), value: '已开启', icon: <Lock size={12} color="#52c41a" />, color: '#52c41a' },
].map((item, idx) => (
<div key={idx} style={{ gridColumn: item.isFull ? '1 / -1' : 'auto', display: 'flex', alignItems: 'center', gap: 6 }}>
<div style={{ width: 20, height: 20, borderRadius: 5, background: '#f8fafc', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{React.isValidElement(item.icon) ? React.cloneElement(item.icon as React.ReactElement, { size: 12, style: { ...item.icon.props.style, fontSize: 12 } }) : item.icon}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 9, color: '#8c8c8c' }}>{item.label}</div>
<div style={{ fontSize: 10, fontWeight: 500, color: item.color || '#262626' }}>{item.value}</div>
</div>
</div>
))}
</div>
</AntdCard>
{/* 系统维护 */}
<AntdCard
title={<Space><Wrench size={16} style={{ color: '#1677ff' }} /><b>{t('systemSetting.systemMaintenance')}</b></Space>}
extra={<Button type="link" size="small" danger style={{ fontSize: 10, padding: 0 }}>{t('systemSetting.s192')}</Button>}
bordered={false}
style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}
bodyStyle={{ padding: '12px' }}
>
<Row gutter={[8, 8]}>
{[
{ label: t('systemSetting.s46'), icon: <DeleteOutlined />, color: '#1677ff' },
{ label: t('systemSetting.s47'), icon: <RefreshCw size={16} />, color: '#1677ff' },
{ label: t('systemSetting.downloadFixed2'), icon: <DownloadOutlined />, color: '#1677ff' },
{ label: t('systemSetting.fixed2LogDesc6Fixed2ExportFixed2'), icon: <FileTextOutlined />, color: '#1677ff' },
].map((btn, idx) => (
<Col xs={12} sm={6} key={idx}>
<Button
size="small"
style={{
width: '100%',
height: 52,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
gap: 2,
border: '1px solid #f0f0f0',
background: '#fff',
padding: '2px'
}}
>
<div style={{ color: btn.color, fontSize: 16 }}>
{React.isValidElement(btn.icon) ? React.cloneElement(btn.icon as React.ReactElement, { size: 16, style: { fontSize: 16 } }) : btn.icon}
</div>
<div style={{ fontSize: 10, color: '#595959' }}>{btn.label}</div>
</Button>
</Col>
))}
</Row>
</AntdCard>
</Col>
</Row>
</div>
);
};
export default BasicSettings;