first commit

This commit is contained in:
2025-03-13 16:10:41 +08:00
commit da4637265d
44 changed files with 9587 additions and 0 deletions

186
src/App.css Normal file
View File

@@ -0,0 +1,186 @@
.logo.vite:hover {
filter: drop-shadow(0 0 2em #747bff);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafb);
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
/* 防止iOS上的橡皮筋效果 */
overscroll-behavior: none;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
position: fixed;
touch-action: manipulation; /* 优化触摸事件 */
}
#root {
height: 100%;
width: 100%;
overflow: hidden;
}
/* 移动端触摸优化 */
a, button, [role="button"] {
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
/* 自定义滚动条样式 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.container {
margin: 0;
padding-top: 10vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: 0.75s;
}
.logo.tauri:hover {
filter: drop-shadow(0 0 2em #24c8db);
}
.row {
display: flex;
justify-content: center;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
h1 {
text-align: center;
}
input,
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
color: #0f0f0f;
background-color: #ffffff;
transition: border-color 0.25s;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}
button {
cursor: pointer;
}
button:hover {
border-color: #396cd8;
}
button:active {
border-color: #396cd8;
background-color: #e8e8e8;
}
input,
button {
outline: none;
}
#greet-input {
margin-right: 5px;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
a:hover {
color: #24c8db;
}
input,
button {
color: #ffffff;
background-color: #0f0f0f98;
}
button:active {
background-color: #0f0f0f69;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
}
}
/* 移动设备样式优化 */
@media (max-width: 768px) {
:root {
font-size: 14px;
}
.MuiPaper-root {
padding: 12px !important;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0.5em !important;
margin-bottom: 0.5em !important;
}
}
/* 触屏反馈优化 */
@media (hover: none) {
a:active,
button:active {
opacity: 0.7;
}
}

117
src/App.jsx Normal file
View File

@@ -0,0 +1,117 @@
import { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { createTheme, ThemeProvider, CssBaseline, useMediaQuery } from '@mui/material';
import MainLayout from './layouts/MainLayout';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import SettingsPage from './pages/SettingsPage';
import SplashScreen from './components/SplashScreen';
import './App.css';
function App() {
const [loading, setLoading] = useState(true);
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const isMobile = useMediaQuery('(max-width:600px)');
// 创建自定义主题
const theme = createTheme({
palette: {
mode: prefersDarkMode ? 'dark' : 'light',
primary: {
main: '#24c8db',
},
secondary: {
main: '#646cff',
},
background: {
default: prefersDarkMode ? '#1e1e1e' : '#f5f5f5',
paper: prefersDarkMode ? '#2d2d2d' : '#ffffff',
},
},
typography: {
fontFamily: 'Inter, Avenir, Helvetica, Arial, sans-serif',
// 移动端字体大小调整
h4: {
fontSize: isMobile ? '1.75rem' : '2.125rem',
},
h6: {
fontSize: isMobile ? '1.1rem' : '1.25rem',
},
body1: {
fontSize: isMobile ? '0.95rem' : '1rem',
},
body2: {
fontSize: isMobile ? '0.85rem' : '0.875rem',
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
textTransform: 'none',
fontWeight: 500,
},
},
},
MuiPaper: {
styleOverrides: {
root: {
borderRadius: 12,
},
},
},
MuiCssBaseline: {
styleOverrides: {
body: {
WebkitTapHighlightColor: 'transparent', // 禁用移动端点击高亮
WebkitOverflowScrolling: 'touch', // 使滚动更流畅
overscrollBehavior: 'none', // 防止滚动穿透
},
'*::-webkit-scrollbar': {
width: '8px',
height: '8px',
},
'*::-webkit-scrollbar-thumb': {
backgroundColor: prefersDarkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)',
borderRadius: '4px',
},
'*::-webkit-scrollbar-track': {
backgroundColor: 'transparent',
},
}
},
},
});
// 模拟应用启动加载
useEffect(() => {
// 模拟启动过程,实际项目中可根据资源加载情况决定
const timer = setTimeout(() => {
setLoading(false);
}, 2000);
return () => clearTimeout(timer);
}, []);
if (loading) {
return <SplashScreen onFinish={() => setLoading(false)} />;
}
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Router>
<MainLayout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</MainLayout>
</Router>
</ThemeProvider>
);
}
export default App;

1
src/assets/react.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,98 @@
import { Paper, BottomNavigation, BottomNavigationAction } from '@mui/material';
import {
Home as HomeIcon,
Info as InfoIcon,
Settings as SettingsIcon
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
export default function MobileBottomNav() {
const navigate = useNavigate();
const location = useLocation();
const getPathValue = () => {
switch (location.pathname) {
case '/':
return 0;
case '/about':
return 1;
case '/settings':
return 2;
default:
return 0;
}
};
const handleChange = (event, newValue) => {
switch (newValue) {
case 0:
navigate('/');
break;
case 1:
navigate('/about');
break;
case 2:
navigate('/settings');
break;
default:
navigate('/');
}
};
return (
<Paper
sx={{
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
display: { xs: 'block', sm: 'none' },
zIndex: 1000,
borderRadius: '12px 12px 0 0',
boxShadow: 3
}}
elevation={3}
>
<BottomNavigation
showLabels
value={getPathValue()}
onChange={handleChange}
sx={{
height: 65,
'& .MuiBottomNavigationAction-root': {
minWidth: 'auto',
py: 1
}
}}
>
<BottomNavigationAction
label="首页"
icon={<HomeIcon />}
sx={{
'&.Mui-selected': {
color: '#24c8db'
}
}}
/>
<BottomNavigationAction
label="关于"
icon={<InfoIcon />}
sx={{
'&.Mui-selected': {
color: '#24c8db'
}
}}
/>
<BottomNavigationAction
label="设置"
icon={<SettingsIcon />}
sx={{
'&.Mui-selected': {
color: '#24c8db'
}
}}
/>
</BottomNavigation>
</Paper>
);
}

View File

@@ -0,0 +1,98 @@
import { useState, useEffect } from 'react';
import { Box, Typography, CircularProgress } from '@mui/material';
export default function SplashScreen({ onFinish }) {
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) => {
if (prevProgress >= 100) {
clearInterval(timer);
setTimeout(() => {
onFinish();
}, 300);
return 100;
}
return prevProgress + 10;
});
}, 100);
return () => {
clearInterval(timer);
};
}, [onFinish]);
return (
<Box
sx={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#2f2f2f',
zIndex: 9999,
}}
>
<Box
component="img"
src="/tauri.svg"
alt="Tauri Logo"
sx={{
width: '120px',
height: '120px',
marginBottom: 4,
animation: 'pulse 1.5s infinite',
'@keyframes pulse': {
'0%': {
transform: 'scale(1)',
opacity: 1,
},
'50%': {
transform: 'scale(1.1)',
opacity: 0.8,
},
'100%': {
transform: 'scale(1)',
opacity: 1,
},
},
}}
/>
<Typography
variant="h5"
sx={{
color: '#fff',
marginBottom: 2,
fontWeight: 'bold',
}}
>
Tauri 移动应用
</Typography>
<Box sx={{ width: '200px', marginTop: 4 }}>
<CircularProgress
variant="determinate"
value={progress}
size={60}
thickness={4}
sx={{ color: '#24c8db' }}
/>
<Typography
variant="body2"
sx={{
color: '#fff',
marginTop: 1,
textAlign: 'center',
}}
>
加载中... {progress}%
</Typography>
</Box>
</Box>
);
}

145
src/layouts/MainLayout.jsx Normal file
View File

@@ -0,0 +1,145 @@
import { useState } from 'react';
import {
AppBar,
Box,
CssBaseline,
Drawer,
IconButton,
Toolbar,
Typography,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Divider,
useMediaQuery
} from '@mui/material';
import {
Menu as MenuIcon,
Home as HomeIcon,
Info as InfoIcon,
Settings as SettingsIcon
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
const drawerWidth = 240;
export default function MainLayout({ children }) {
const [mobileOpen, setMobileOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const isMobile = useMediaQuery('(max-width:600px)');
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const menuItems = [
{ text: '首页', icon: <HomeIcon />, path: '/' },
{ text: '关于', icon: <InfoIcon />, path: '/about' },
{ text: '设置', icon: <SettingsIcon />, path: '/settings' },
];
const drawer = (
<div>
<Toolbar>
<Typography variant="h6" noWrap component="div">
Tauri应用
</Typography>
</Toolbar>
<Divider />
<List>
{menuItems.map((item) => (
<ListItem key={item.text} disablePadding>
<ListItemButton
selected={location.pathname === item.path}
onClick={() => {
navigate(item.path);
setMobileOpen(false);
}}
>
<ListItemIcon>
{item.icon}
</ListItemIcon>
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
))}
</List>
</div>
);
return (
<Box sx={{ display: 'flex', height: '100vh' }}>
<CssBaseline />
<AppBar
position="fixed"
sx={{
width: { sm: `calc(100% - ${drawerWidth}px)` },
ml: { sm: `${drawerWidth}px` },
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: 'none' } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Tauri移动应用
</Typography>
</Toolbar>
</AppBar>
<Box
component="nav"
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
>
{/* 移动端侧边栏 */}
<Drawer
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // 提高移动端性能
}}
sx={{
display: { xs: 'block', sm: 'none' },
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
>
{drawer}
</Drawer>
{/* 桌面端永久侧边栏 */}
<Drawer
variant="permanent"
sx={{
display: { xs: 'none', sm: 'block' },
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
open
>
{drawer}
</Drawer>
</Box>
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - ${drawerWidth}px)` },
marginTop: '64px',
height: 'calc(100vh - 64px)',
overflow: 'auto',
paddingBottom: '16px'
}}
>
{children}
</Box>
</Box>
);
}

26
src/main.jsx Normal file
View File

@@ -0,0 +1,26 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
// 防止默认触摸行为
document.addEventListener('touchstart', function(e) {
if (e.touches.length > 1) {
e.preventDefault();
}
}, { passive: false });
// 防止双击缩放
document.addEventListener('dblclick', function(e) {
e.preventDefault();
}, { passive: false });
// 禁用移动设备上的缩放手势
document.addEventListener('gesturestart', function(e) {
e.preventDefault();
}, { passive: false });
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);

59
src/pages/AboutPage.jsx Normal file
View File

@@ -0,0 +1,59 @@
import { Box, Typography, Paper, Card, CardContent, Divider, Link } from '@mui/material';
export default function AboutPage() {
return (
<Box sx={{ width: '100%' }}>
<Paper
elevation={3}
sx={{ p: 3, mb: 4, borderRadius: 2, bgcolor: 'background.paper' }}
>
<Typography variant="h4" gutterBottom sx={{ textAlign: 'center', fontWeight: 'bold', color: '#2a384d' }}>
关于我们
</Typography>
<Typography variant="body1" paragraph sx={{ mt: 2 }}>
这是一个使用Tauri和React构建的跨平台应用程序Tauri允许你使用Web技术构建轻量级安全且高性能的桌面和移动应用程序
</Typography>
<Divider sx={{ my: 3 }} />
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="h6" gutterBottom color="primary">
技术栈
</Typography>
<Typography variant="body2" paragraph>
Tauri - 安全轻量级的跨平台应用开发框架
</Typography>
<Typography variant="body2" paragraph>
React - 用户界面库
</Typography>
<Typography variant="body2" paragraph>
Material UI - 组件库
</Typography>
<Typography variant="body2" paragraph>
Rust - 后端代码
</Typography>
</CardContent>
</Card>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom color="primary">
联系方式
</Typography>
<Typography variant="body2" paragraph>
如果您有任何问题或建议请通过以下方式联系我们:
</Typography>
<Typography variant="body2" paragraph>
Email: <Link href="mailto:contact@example.com">contact@example.com</Link>
</Typography>
<Typography variant="body2" paragraph>
Website: <Link href="https://example.com" target="_blank" rel="noopener">example.com</Link>
</Typography>
</CardContent>
</Card>
</Paper>
</Box>
);
}

183
src/pages/HomePage.jsx Normal file
View File

@@ -0,0 +1,183 @@
import { useState } from "react";
import {
Box,
Typography,
Paper,
TextField,
Button,
Card,
CardContent,
CardMedia,
Grid
} from '@mui/material';
import { invoke } from "@tauri-apps/api/core";
import reactLogo from "../assets/react.svg";
export default function HomePage() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
setGreetMsg(await invoke("greet", { name }));
}
return (
<Box sx={{ width: '100%' }}>
<Paper
elevation={3}
sx={{ p: 3, mb: 4, borderRadius: 2, bgcolor: 'background.paper' }}
>
<Typography variant="h4" gutterBottom sx={{ textAlign: 'center', fontWeight: 'bold', color: '#2a384d' }}>
欢迎使用 Tauri + React
</Typography>
<Grid container spacing={3} justifyContent="center" sx={{ mt: 2 }}>
<Grid item xs={4} sm={4} sx={{ textAlign: 'center' }}>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.3s',
'&:hover': { transform: 'scale(1.05)' }
}}
>
<CardMedia
component="img"
sx={{
height: 140,
p: 2,
objectFit: 'contain'
}}
image="/vite.svg"
alt="Vite logo"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
Vite
</Typography>
</CardContent>
</Card>
</Grid>
<Grid item xs={4} sm={4} sx={{ textAlign: 'center' }}>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.3s',
'&:hover': { transform: 'scale(1.05)' }
}}
>
<CardMedia
component="img"
sx={{
height: 140,
p: 2,
objectFit: 'contain'
}}
image="/tauri.svg"
alt="Tauri logo"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
Tauri
</Typography>
</CardContent>
</Card>
</Grid>
<Grid item xs={4} sm={4} sx={{ textAlign: 'center' }}>
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.3s',
'&:hover': { transform: 'scale(1.05)' }
}}
>
<CardMedia
component="img"
sx={{
height: 140,
p: 2,
objectFit: 'contain'
}}
image={reactLogo}
alt="React logo"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
React
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</Paper>
<Paper
elevation={3}
sx={{
p: 3,
borderRadius: 2,
bgcolor: 'background.paper'
}}
>
<Box
component="form"
onSubmit={(e) => {
e.preventDefault();
greet();
}}
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2
}}
>
<TextField
fullWidth
label="输入你的名字"
variant="outlined"
value={name}
onChange={(e) => setName(e.target.value)}
sx={{ mb: 2 }}
/>
<Button
type="submit"
variant="contained"
color="primary"
size="large"
sx={{
borderRadius: 2,
py: 1.5
}}
>
问候
</Button>
</Box>
{greetMsg && (
<Typography
variant="h6"
sx={{
mt: 3,
p: 2,
borderRadius: 2,
bgcolor: 'primary.light',
color: 'primary.contrastText',
textAlign: 'center'
}}
>
{greetMsg}
</Typography>
)}
</Paper>
</Box>
);
}

156
src/pages/SettingsPage.jsx Normal file
View File

@@ -0,0 +1,156 @@
import { useState } from 'react';
import {
Box,
Typography,
Paper,
Switch,
FormGroup,
FormControlLabel,
Slider,
TextField,
InputAdornment,
Select,
MenuItem,
FormControl,
InputLabel,
Button,
Divider
} from '@mui/material';
import {
DarkMode as DarkModeIcon,
Notifications as NotificationsIcon,
Language as LanguageIcon,
Save as SaveIcon
} from '@mui/icons-material';
export default function SettingsPage() {
const [darkMode, setDarkMode] = useState(false);
const [notifications, setNotifications] = useState(true);
const [fontSize, setFontSize] = useState(16);
const [language, setLanguage] = useState('zh');
const [username, setUsername] = useState('');
const handleDarkModeChange = () => {
setDarkMode(!darkMode);
};
const handleNotificationsChange = () => {
setNotifications(!notifications);
};
const handleFontSizeChange = (event, newValue) => {
setFontSize(newValue);
};
const handleLanguageChange = (event) => {
setLanguage(event.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Settings saved:', { darkMode, notifications, fontSize, language, username });
// 这里可以调用Tauri API保存设置
};
return (
<Box sx={{ width: '100%' }}>
<Paper
elevation={3}
sx={{ p: 3, mb: 4, borderRadius: 2, bgcolor: 'background.paper' }}
>
<Typography variant="h4" gutterBottom sx={{ textAlign: 'center', fontWeight: 'bold', color: '#2a384d' }}>
应用设置
</Typography>
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 4 }}>
<Typography variant="h6" gutterBottom color="primary" sx={{ display: 'flex', alignItems: 'center' }}>
<DarkModeIcon sx={{ mr: 1 }} /> 外观
</Typography>
<FormGroup sx={{ mb: 4, ml: 2 }}>
<FormControlLabel
control={<Switch checked={darkMode} onChange={handleDarkModeChange} />}
label="深色模式"
/>
<Box sx={{ mt: 2 }}>
<Typography id="font-size-slider" gutterBottom>
字体大小: {fontSize}px
</Typography>
<Slider
value={fontSize}
onChange={handleFontSizeChange}
aria-labelledby="font-size-slider"
valueLabelDisplay="auto"
min={12}
max={24}
sx={{ maxWidth: 400 }}
/>
</Box>
</FormGroup>
<Divider sx={{ my: 3 }} />
<Typography variant="h6" gutterBottom color="primary" sx={{ display: 'flex', alignItems: 'center', mt: 3 }}>
<NotificationsIcon sx={{ mr: 1 }} /> 通知
</Typography>
<FormGroup sx={{ mb: 4, ml: 2 }}>
<FormControlLabel
control={<Switch checked={notifications} onChange={handleNotificationsChange} />}
label="启用通知"
/>
</FormGroup>
<Divider sx={{ my: 3 }} />
<Typography variant="h6" gutterBottom color="primary" sx={{ display: 'flex', alignItems: 'center', mt: 3 }}>
<LanguageIcon sx={{ mr: 1 }} /> 语言和个人资料
</Typography>
<Box sx={{ mb: 4, ml: 2 }}>
<FormControl sx={{ m: 1, minWidth: 200 }}>
<InputLabel id="language-select-label">语言</InputLabel>
<Select
labelId="language-select-label"
id="language-select"
value={language}
label="语言"
onChange={handleLanguageChange}
>
<MenuItem value="zh">中文</MenuItem>
<MenuItem value="en">English</MenuItem>
<MenuItem value="ja">日本語</MenuItem>
<MenuItem value="ko">한국어</MenuItem>
</Select>
</FormControl>
<TextField
margin="normal"
fullWidth
id="username"
label="用户名"
name="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
sx={{ maxWidth: 400 }}
/>
</Box>
<Box sx={{ mt: 4, textAlign: 'center' }}>
<Button
type="submit"
variant="contained"
color="primary"
size="large"
startIcon={<SaveIcon />}
sx={{ borderRadius: 2, py: 1.5, px: 4 }}
>
保存设置
</Button>
</Box>
</Box>
</Paper>
</Box>
);
}