From a1e99e53036c206fe7660ee4ced637d4923f80ef Mon Sep 17 00:00:00 2001 From: GyDi Date: Wed, 8 Dec 2021 23:36:34 +0800 Subject: [PATCH] feat: finish main layout --- src/assets/styles/index.scss | 53 +--------------------- src/assets/styles/layout.scss | 28 ++++++++++++ src/components/list-item-link.tsx | 31 +++++++++++++ src/components/traffic.tsx | 69 ++++++++++++++++++++++++++++ src/main.tsx | 49 +++++++++----------- src/pages/_layout.tsx | 74 +++++++++++++++++++++++++++++++ src/pages/connections.tsx | 5 +++ src/pages/home.tsx | 16 ++----- src/pages/log.tsx | 5 +++ src/pages/profiles.tsx | 2 +- src/pages/proxy.tsx | 5 +++ src/pages/setting.tsx | 5 +++ src/utils/parse-traffic.ts | 23 ++++++++++ 13 files changed, 273 insertions(+), 92 deletions(-) create mode 100644 src/assets/styles/layout.scss create mode 100644 src/components/list-item-link.tsx create mode 100644 src/components/traffic.tsx create mode 100644 src/pages/_layout.tsx create mode 100644 src/pages/connections.tsx create mode 100644 src/pages/log.tsx create mode 100644 src/pages/proxy.tsx create mode 100644 src/pages/setting.tsx create mode 100644 src/utils/parse-traffic.ts diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index c751c11..3d5ba11 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -1,5 +1,5 @@ html { - background-color: #fff; + background-color: #fefefe; } body { @@ -15,53 +15,4 @@ code { monospace; } -.layout { - width: 100%; - height: 100%; - display: flex; - - &__sidebar { - height: 100vh; - flex: 1 1 25%; - border-right: 1px solid #ccc; - - > h1 { - text-align: center; - color: #303133; - } - - > h3 { - text-align: center; - color: #909399; - } - } - - &__links { - $link-height: 60px; - - border-top: 1px solid #ccc; - - > a { - display: block; - width: 100%; - height: $link-height; - line-height: $link-height; - text-align: center; - user-select: none; - font-size: 24px; - color: #606266; - border-bottom: 1px solid #ccc; - text-decoration: none; - - &.active { - background-color: #eee; - } - } - } - - &__content { - flex: 1 1 75%; - padding: 20px 30px; - box-sizing: border-box; - } -} +@import "./layout.scss"; diff --git a/src/assets/styles/layout.scss b/src/assets/styles/layout.scss new file mode 100644 index 0000000..5f52266 --- /dev/null +++ b/src/assets/styles/layout.scss @@ -0,0 +1,28 @@ +.layout { + width: 100%; + height: 100%; + display: flex; + + &__sidebar { + position: relative; + height: 100vh; + flex: 1 1 25%; + } + + &__traffic { + position: absolute; + left: 0; + right: 0; + bottom: 18px; + + > div { + margin: 0 auto; + } + } + + &__content { + flex: 1 1 75%; + padding: 20px 30px; + box-sizing: border-box; + } +} diff --git a/src/components/list-item-link.tsx b/src/components/list-item-link.tsx new file mode 100644 index 0000000..95fc527 --- /dev/null +++ b/src/components/list-item-link.tsx @@ -0,0 +1,31 @@ +import { ListItem, ListItemButton, ListItemText } from "@mui/material"; +import { useMatch, useResolvedPath, useNavigate } from "react-router-dom"; +import type { LinkProps } from "react-router-dom"; + +const ListItemLink = (props: LinkProps) => { + const { to, children } = props; + + const resolved = useResolvedPath(to); + const match = useMatch({ path: resolved.pathname, end: true }); + const navigate = useNavigate(); + + return ( + + navigate(to)} + > + + + + ); +}; + +export default ListItemLink; diff --git a/src/components/traffic.tsx b/src/components/traffic.tsx new file mode 100644 index 0000000..26b8f55 --- /dev/null +++ b/src/components/traffic.tsx @@ -0,0 +1,69 @@ +import axios from "axios"; +import { useEffect, useState } from "react"; +import { ArrowDownward, ArrowUpward } from "@mui/icons-material"; +import parseTraffic from "../utils/parse-traffic"; +import { Typography } from "@mui/material"; +import { Box } from "@mui/system"; + +const Traffic = () => { + const [traffic, setTraffic] = useState({ up: 0, down: 0 }); + + useEffect(() => { + const onTraffic = () => { + axios({ + url: `http://127.0.0.1:9090/traffic`, + method: "GET", + onDownloadProgress: (progressEvent) => { + const data = progressEvent.currentTarget.response || ""; + const lastData = data.slice(data.trim().lastIndexOf("\n") + 1); + try { + if (lastData) setTraffic(JSON.parse(lastData)); + } catch {} + }, + }).catch(() => setTimeout(onTraffic, 500)); + }; + + onTraffic(); + }, []); + + const [up, upUnit] = parseTraffic(traffic.up); + const [down, downUnit] = parseTraffic(traffic.down); + + const valStyle: any = { + component: "span", + color: "primary", + textAlign: "center", + sx: { flex: "1 1 54px" }, + }; + const unitStyle: any = { + component: "span", + color: "grey.500", + fontSize: "12px", + textAlign: "right", + sx: { flex: "0 1 28px", userSelect: "none" }, + }; + + return ( + + + 0 ? "primary" : "disabled"} + /> + {up} + {upUnit} + + + + 0 ? "primary" : "disabled"} + /> + {down} + {downUnit} + + + ); +}; + +export default Traffic; diff --git a/src/main.tsx b/src/main.tsx index 40dca8c..a9fa329 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,39 +2,32 @@ import "./assets/styles/index.scss"; import React from "react"; import ReactDOM from "react-dom"; -import { BrowserRouter, NavLink, Route, Routes } from "react-router-dom"; -import HomePage from "./pages/home"; -import ProfilesPage from "./pages/profiles"; -import { version } from "../package.json"; +import { BrowserRouter } from "react-router-dom"; +import { createTheme, ThemeProvider } from "@mui/material"; +import Layout from "./pages/_layout"; -function Layout() { - return ( -
-
-

Clash Verge

-

{version}

+const theme = createTheme({ + palette: { + mode: "light", + primary: { + main: "#5b5c9d", + }, + text: { + primary: "#637381", + secondary: "#909399", + }, + }, +}); -
- Home - Profiles -
-
- -
- - } /> - } /> - -
-
- ); -} +// console.log(theme); ReactDOM.render( - - - + + + + + , document.getElementById("root") ); diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx new file mode 100644 index 0000000..ee09077 --- /dev/null +++ b/src/pages/_layout.tsx @@ -0,0 +1,74 @@ +import { Route, Routes } from "react-router-dom"; +import { List, Paper, Typography } from "@mui/material"; +import LogPage from "../pages/log"; +import HomePage from "../pages/home"; +import ProxyPage from "../pages/proxy"; +import SettingPage from "../pages/setting"; +import ProfilesPage from "../pages/profiles"; +import ConnectionsPage from "../pages/connections"; +import ListItemLink from "../components/list-item-link"; +import Traffic from "../components/traffic"; + +const Layout = () => { + const routers = [ + { + label: "代理", + link: "/proxy", + }, + { + label: "规则", + link: "/profiles", + }, + { + label: "连接", + link: "/connections", + }, + { + label: "日志", + link: "/log", + }, + { + label: "设置", + link: "/setting", + }, + ]; + + return ( + +
+ + Clash Verge + + + + {routers.map((router) => ( + + {router.label} + + ))} + + +
+ +
+
+ +
+ + } /> + } /> + } /> + } /> + } /> + } /> + +
+
+ ); +}; + +export default Layout; diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx new file mode 100644 index 0000000..cb267e0 --- /dev/null +++ b/src/pages/connections.tsx @@ -0,0 +1,5 @@ +const ConnectionsPage = () => { + return

Connection

; +}; + +export default ConnectionsPage; diff --git a/src/pages/home.tsx b/src/pages/home.tsx index f7ff78f..5bda849 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -1,18 +1,10 @@ -import { useState } from "react"; -import { TextField } from "@material-ui/core"; +import { Typography } from "@mui/material"; const HomePage = () => { - const [port, setPort] = useState("7890"); - return ( -
- setPort(e.target.value)} - /> -
+ + Hello Clash! + ); }; diff --git a/src/pages/log.tsx b/src/pages/log.tsx new file mode 100644 index 0000000..6b5c9e6 --- /dev/null +++ b/src/pages/log.tsx @@ -0,0 +1,5 @@ +const LogPage = () => { + return

Log

; +}; + +export default LogPage; diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 4ed657b..067833e 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { invoke } from "@tauri-apps/api"; -import { Button, Grid, TextField } from "@material-ui/core"; +import { Button, Grid, TextField } from "@mui/material"; const ProfilesPage = () => { const [url, setUrl] = useState(""); diff --git a/src/pages/proxy.tsx b/src/pages/proxy.tsx new file mode 100644 index 0000000..2808614 --- /dev/null +++ b/src/pages/proxy.tsx @@ -0,0 +1,5 @@ +const ProxyPage = () => { + return

Proxy

; +}; + +export default ProxyPage; diff --git a/src/pages/setting.tsx b/src/pages/setting.tsx new file mode 100644 index 0000000..be8d857 --- /dev/null +++ b/src/pages/setting.tsx @@ -0,0 +1,5 @@ +const SettingPage = () => { + return

Setting

; +}; + +export default SettingPage; diff --git a/src/utils/parse-traffic.ts b/src/utils/parse-traffic.ts new file mode 100644 index 0000000..529cc3d --- /dev/null +++ b/src/utils/parse-traffic.ts @@ -0,0 +1,23 @@ +const parseTraffic = (num: number) => { + const gb = 1024 ** 3; + const mb = 1024 ** 2; + const kb = 1024; + let t = num; + let u = "B"; + + if (num < 1000) return [`${Math.round(t)}`, "B/s"]; + if (num <= mb) { + t = num / kb; + u = "KB"; + } else if (num <= gb) { + t = num / mb; + u = "MB"; + } else { + t = num / gb; + u = "GB"; + } + if (t >= 100) return [`${Math.round(t)}`, `${u}/s`]; + return [`${Math.round(t * 10) / 10}`, `${u}/s`]; +}; + +export default parseTraffic;