Files
MYXY_Web/src/pages/ConfigHistoryModal.tsx
2025-12-12 20:01:39 +08:00

338 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 配置历史记录模态框组件
* @author MHXY Development Team
* @version 1.0.0
*/
import React, { useState, useEffect } from 'react';
import { Modal, Table, Tag, Space, Button, Typography, Tooltip, Empty } from 'antd';
import {
HistoryOutlined,
UserOutlined,
ClockCircleOutlined,
EyeOutlined,
InfoCircleOutlined
} from '@ant-design/icons';
// import { ConfigHistory } from '../types/systemConfig';
import systemConfigService from '../services/systemConfigService';
const { Text, Paragraph } = Typography;
const { confirm } = Modal;
interface ConfigHistoryModalProps {
visible: boolean;
configKey?: string;
onClose: () => void;
}
const ConfigHistoryModal: React.FC<ConfigHistoryModalProps> = ({
visible,
configKey,
onClose
}) => {
const [historyData, setHistoryData] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(false);
// 加载历史记录
const loadHistory = async () => {
try {
setLoading(true);
const history = await systemConfigService.getConfigHistory(configKey);
setHistoryData(history);
} catch (error) {
console.error('加载配置历史失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (visible) {
loadHistory();
}
}, [visible, configKey]);
// 查看配置变更详情
const handleViewDetails = (record: ConfigHistory) => {
confirm({
title: '配置变更详情',
width: 600,
content: (
<div style={{ marginTop: '16px' }}>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<Text code>{record.config_key}</Text>
</div>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<Text>{record.admin_user.real_name} ({record.admin_user.username})</Text>
</div>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<Text>{new Date(record.created_at).toLocaleString('zh-CN')}</Text>
</div>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<Paragraph>{record.changed_reason}</Paragraph>
</div>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<div style={{
marginTop: '8px',
padding: '8px',
background: '#fff2f0',
border: '1px solid #ffccc7',
borderRadius: '4px',
fontFamily: 'monospace',
fontSize: '12px'
}}>
<Paragraph copyable style={{ margin: 0, color: '#cf1322' }}>
{record.old_value}
</Paragraph>
</div>
</div>
<div style={{ marginBottom: '16px' }}>
<Text strong></Text>
<div style={{
marginTop: '8px',
padding: '8px',
background: '#f6ffed',
border: '1px solid #b7eb8f',
borderRadius: '4px',
fontFamily: 'monospace',
fontSize: '12px'
}}>
<Paragraph copyable style={{ margin: 0, color: '#389e0d' }}>
{record.new_value}
</Paragraph>
</div>
</div>
</div>
),
okText: '关闭',
cancelButtonProps: { style: { display: 'none' } }
});
};
// 表格列定义
const columns = [
{
title: '配置项',
dataIndex: 'config_key',
key: 'config_key',
width: 200,
render: (text: string) => (
<Text code style={{ fontSize: '12px' }}>{text}</Text>
)
},
{
title: '变更者',
dataIndex: ['admin_user', 'real_name'],
key: 'changed_by',
width: 120,
render: (text: string, record: ConfigHistory) => (
<div>
<div style={{ fontWeight: 500 }}>{text}</div>
<div style={{ fontSize: '11px', color: '#666' }}>
@{record.admin_user.username}
</div>
</div>
)
},
{
title: '变更内容',
key: 'change_content',
width: 300,
render: (record: ConfigHistory) => (
<div>
<div style={{ marginBottom: '4px' }}>
<Tag color="red" size="small"></Tag>
<Text code style={{ fontSize: '11px' }}>
{record.old_value.length > 20
? `${record.old_value.substring(0, 20)}...`
: record.old_value
}
</Text>
</div>
<div>
<Tag color="green" size="small"></Tag>
<Text code style={{ fontSize: '11px' }}>
{record.new_value.length > 20
? `${record.new_value.substring(0, 20)}...`
: record.new_value
}
</Text>
</div>
</div>
)
},
{
title: '变更原因',
dataIndex: 'changed_reason',
key: 'changed_reason',
width: 200,
render: (text: string) => (
<Paragraph
ellipsis={{ rows: 2, expandable: false }}
style={{ margin: 0, fontSize: '12px' }}
>
{text}
</Paragraph>
)
},
{
title: '变更时间',
dataIndex: 'created_at',
key: 'created_at',
width: 160,
render: (text: string) => (
<div>
<div style={{ fontSize: '12px' }}>
{new Date(text).toLocaleDateString('zh-CN')}
</div>
<div style={{ fontSize: '11px', color: '#666' }}>
{new Date(text).toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}
</div>
</div>
)
},
{
title: '操作',
key: 'actions',
width: 100,
render: (record: ConfigHistory) => (
<Space>
<Tooltip title="查看详情">
<Button
type="text"
size="small"
icon={<EyeOutlined />}
onClick={() => handleViewDetails(record)}
/>
</Tooltip>
</Space>
)
}
];
// 判断是否为敏感配置
const isSensitiveConfig = (configKey: string) => {
return configKey.includes('secret') || configKey.includes('key') || configKey.includes('psk');
};
return (
<Modal
title={
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<HistoryOutlined />
{configKey && (
<>
<span style={{ color: '#666' }}>-</span>
<Text code>{configKey}</Text>
</>
)}
</div>
}
open={visible}
onCancel={onClose}
width={900}
footer={[
<Button key="close" onClick={onClose}>
</Button>
]}
>
<div style={{ marginBottom: '16px' }}>
{configKey ? (
<div style={{
padding: '12px',
background: '#f0f2f5',
borderRadius: '6px',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
<InfoCircleOutlined style={{ color: '#1890ff' }} />
<span style={{ fontSize: '12px' }}>
<Text code>{configKey}</Text>
</span>
{isSensitiveConfig(configKey) && (
<Tag color="red" size="small"></Tag>
)}
</div>
) : (
<div style={{
padding: '12px',
background: '#f0f2f5',
borderRadius: '6px',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
<InfoCircleOutlined style={{ color: '#1890ff' }} />
<span style={{ fontSize: '12px' }}>
</span>
</div>
)}
</div>
<Table
columns={columns}
dataSource={historyData}
rowKey="id"
loading={loading}
pagination={{
pageSize: 10,
showSizeChanger: false,
showQuickJumper: false,
showTotal: (total) => `${total} 条记录`
}}
locale={{
emptyText: (
<Empty
description="暂无历史记录"
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
)
}}
size="small"
scroll={{ y: 400 }}
/>
{/* 说明信息 */}
<div style={{
marginTop: '16px',
padding: '12px',
background: '#fafafa',
border: '1px solid #d9d9d9',
borderRadius: '6px',
fontSize: '12px',
color: '#666'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
<InfoCircleOutlined style={{ color: '#1890ff' }} />
<strong></strong>
</div>
<ul style={{ margin: 0, paddingLeft: '20px' }}>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</Modal>
);
};
export default ConfigHistoryModal;