first commit
This commit is contained in:
186
src/App.css
Normal file
186
src/App.css
Normal 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
117
src/App.jsx
Normal 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
1
src/assets/react.svg
Normal 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 |
98
src/components/MobileBottomNav.jsx
Normal file
98
src/components/MobileBottomNav.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
98
src/components/SplashScreen.jsx
Normal file
98
src/components/SplashScreen.jsx
Normal 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
145
src/layouts/MainLayout.jsx
Normal 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
26
src/main.jsx
Normal 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
59
src/pages/AboutPage.jsx
Normal 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
183
src/pages/HomePage.jsx
Normal 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
156
src/pages/SettingsPage.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user