blob: 951ee4480418cd502cb33e2f7348c29efa18c61a [file] [log] [blame] [raw]
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Container from '@mui/material/Container';
import { useState, useEffect } from 'react';
import { AppBar, Toolbar, Typography, Button, Box, useTheme, useMediaQuery } from '@mui/material';
import DnsTable from './components/DnsTable';
import config from './config';
import HistoryDialog from './components/HistoryDialog';
import { theme } from './theme';
import HistoryIcon from '@mui/icons-material/History';
import Footer from './components/Footer';
function App() {
const [hosts, setHosts] = useState(null);
const [title, setTitle] = useState('DNS 配置管理');
const [hints, setHints] = useState({});
const [versions, setVersions] = useState({ cn: 0, oversea: 0 });
const [historyOpen, setHistoryOpen] = useState(false);
const [history, setHistory] = useState({ cn: [], oversea: [] });
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
useEffect(() => {
fetchConfig();
}, []);
const fetchConfig = async () => {
try {
const response = await fetch(`${config.api.baseUrl}/api/config`);
const data = await response.json();
if (data.error) {
alert('加载配置失败: ' + data.error);
return;
}
console.log('Fetched config:', data);
setHosts(data.hosts);
setTitle(data.title);
setHints(data.hints);
setVersions({
cn: data.cnVersion,
oversea: data.overseaVersion
});
} catch (error) {
alert('加载配置失败: ' + error.message);
}
};
const fetchHistory = async () => {
try {
const response = await fetch(`${config.api.baseUrl}/api/history`);
const data = await response.json();
if (data.error) {
alert('加载历史记录失败: ' + data.error);
return;
}
setHistory(data);
} catch (error) {
alert('加载历史记录失败: ' + error.message);
}
};
const handleSave = async (newHosts) => {
try {
const response = await fetch(`${config.api.baseUrl}/api/config`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ hosts: newHosts }),
});
const data = await response.json();
if (data.error) {
alert('保存失败: ' + data.error);
return false;
}
// 重新加载配置
await fetchConfig();
return true;
} catch (error) {
alert('保存失败: ' + error.message);
return false;
}
};
const handleRestoreVersion = async (type, version) => {
try {
const response = await fetch(`${config.api.baseUrl}/api/history/restore`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ type, version }),
});
const data = await response.json();
if (data.error) {
alert('恢复配置失败: ' + data.error);
return;
}
alert('配置已恢复');
setHistoryOpen(false);
fetchConfig(); // 重新加载配置
} catch (error) {
alert('恢复配置失败: ' + error.message);
}
};
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Box
sx={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh'
}}
>
<AppBar
position="static"
elevation={0}
sx={{
backgroundColor: 'background.paper',
borderBottom: '1px solid',
borderColor: 'divider'
}}
>
<Toolbar sx={{ flexDirection: isMobile ? 'column' : 'row', py: isMobile ? 2 : 0 }}>
<Typography
variant={isMobile ? "h6" : "h6"}
component="div"
sx={{
flexGrow: isMobile ? 0 : 1,
color: 'text.primary',
fontWeight: 600,
mb: isMobile ? 2 : 0
}}
>
{title}
</Typography>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 2,
flexDirection: isMobile ? 'row' : 'row',
flexWrap: 'wrap',
justifyContent: 'center'
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 2,
flexWrap: 'wrap',
justifyContent: 'center'
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 1,
minWidth: isMobile ? '120px' : 'auto'
}}>
<Typography
variant="body2"
color="text.secondary"
noWrap
>
国内版本
</Typography>
<Typography
variant="body2"
sx={{
backgroundColor: 'primary.main',
color: 'white',
px: 1.5,
py: 0.5,
borderRadius: 2,
fontWeight: 500,
minWidth: '40px',
textAlign: 'center'
}}
>
v{versions.cn}
</Typography>
</Box>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 1,
minWidth: isMobile ? '120px' : 'auto'
}}>
<Typography
variant="body2"
color="text.secondary"
noWrap
>
海外版本
</Typography>
<Typography
variant="body2"
sx={{
backgroundColor: 'secondary.main',
color: 'white',
px: 1.5,
py: 0.5,
borderRadius: 2,
fontWeight: 500,
minWidth: '40px',
textAlign: 'center'
}}
>
v{versions.oversea}
</Typography>
</Box>
</Box>
<Button
variant="outlined"
color="primary"
onClick={() => {
fetchHistory();
setHistoryOpen(true);
}}
startIcon={<HistoryIcon />}
sx={{
minWidth: isMobile ? '120px' : 'auto',
height: '36px'
}}
>
历史记录
</Button>
</Box>
</Toolbar>
</AppBar>
<Container
maxWidth="lg"
sx={{
mt: 4,
mb: 4,
flex: 1 // 让内容区域占据剩余空间
}}
>
<DnsTable
hosts={hosts}
hints={hints}
onSave={handleSave}
/>
</Container>
<Footer />
<HistoryDialog
open={historyOpen}
onClose={() => setHistoryOpen(false)}
history={history}
onRestore={handleRestoreVersion}
/>
</Box>
</ThemeProvider>
);
}
export default App;