-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
50ff1d7
commit b64d462
Showing
90 changed files
with
5,936 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
import React, { useEffect, useState } from 'react' | ||
import { Layout, Menu, Breadcrumb, Button, Message, Grid, ConfigProvider } from '@arco-design/web-react'; | ||
import "@arco-themes/react-vhbs/css/arco.css"; | ||
//import "@arco-design/web-react/dist/css/arco.css"; | ||
import { Routes, Route, Link, useNavigate, useLocation } from "react-router-dom"; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
import { Test } from './controller/test'; | ||
import { Routers } from './type/routers'; | ||
import { Home_page } from './page/home/home'; | ||
import { Storage_page } from './page/storage/storage'; | ||
import { AddStorage_page } from './page/storage/add'; | ||
import { Explorer_page } from './page/storage/explorer'; | ||
import { Mount_page } from './page/mount/mount'; | ||
import { Transmit_page } from './page/transmit/transmit'; | ||
import { Task_page } from './page/task/task'; | ||
import Setting_page from './page/setting/setting'; | ||
import AddMount_page from './page/mount/add'; | ||
import { IconAttachment, IconClose, IconCloud, IconHome, IconLink, IconList, IconMinus, IconSettings, IconStorage, IconSwap } from '@arco-design/web-react/icon'; | ||
import { windowsHide, windowsMini } from './controller/window'; | ||
import { rcloneInfo } from './services/rclone'; | ||
import { AddTask_page } from './page/task/add'; | ||
import { hooks } from './services/hook'; | ||
import { getLocale } from './controller/language/language'; | ||
import { nmConfig } from './services/config'; | ||
import { getLangCode } from './controller/language/localized'; | ||
import { Locale } from '@arco-design/web-react/es/locale/interface'; | ||
|
||
const { Item: MenuItem, SubMenu } = Menu; | ||
const { Sider, Header, Content, Footer } = Layout; | ||
const Row = Grid.Row; | ||
const Col = Grid.Col; | ||
|
||
|
||
//递归查询对应的路由 | ||
function searchRoute( | ||
path: string, | ||
routes: Routers[] | ||
): Routers | null { | ||
for (let item of routes) { | ||
if (item.path === path) { | ||
return item; | ||
} | ||
if (item.children) { | ||
const found = searchRoute(path, item.children); | ||
if (found) { | ||
return found; // 当在子路由中找到匹配项时,直接返回 | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
//生成菜单 | ||
function mapMenuItem(routes: Routers[]): JSX.Element { | ||
return <>{ | ||
routes.map((item) => { | ||
if (item.hide) { | ||
return <></> | ||
} else if (item.children && item.children.length > 0 && !item.hideChildren) { | ||
return (<SubMenu key={item.path} title={item.title} >{mapMenuItem(item.children)}</SubMenu>) | ||
} else { | ||
return (<MenuItem key={item.path} > {item.title}</MenuItem>) | ||
} | ||
}) | ||
}</> | ||
|
||
} | ||
|
||
//生成页面 | ||
function mapRouters(routes: Routers[]): JSX.Element { | ||
return <>{ | ||
routes.map((item) => { // 添加index作为map方法的第二个参数,用于生成唯一键 | ||
if (item.children && item.children.length > 0) { | ||
return <React.Fragment key={`${item.path}-group`}> {/* 给包含子路由的Fragment添加一个唯一的key */} | ||
{mapRouters(item.children)} | ||
{item.component ? <Route key={item.path} path={item.path} element={item.component}></Route> : <></>} | ||
</React.Fragment>; | ||
} else { | ||
return <Route key={item.path} path={item.path} element={item.component}></Route>; | ||
} | ||
}) | ||
}</> | ||
} | ||
|
||
//生成面包屑 | ||
function generateBreadcrumb(pathname: string, routes: Routers[]): JSX.Element[] { | ||
const pathSnippets = pathname.split('/').filter(i => i); | ||
const breadcrumbItems: JSX.Element[] = []; | ||
|
||
if (pathSnippets.length == 1) { | ||
return []; | ||
} | ||
// 创建面包屑项(根据是否有子菜单和是否隐藏子菜单决定是否为链接) | ||
function createBreadcrumbItem(route: Routers): JSX.Element { | ||
if (route.children && route.children.length > 0 && !route.hideChildren) { | ||
return <>{route.title}</>; | ||
} else { | ||
return <Link to={route.path}>{route.title}</Link>; | ||
} | ||
} | ||
|
||
pathSnippets.reduce((prevPath, pathSnippet) => { | ||
const currentPath = `${prevPath}/${pathSnippet}`; | ||
const route = searchRoute(currentPath, routes); | ||
|
||
let breadcrumbItem: JSX.Element; | ||
|
||
if (route) { | ||
breadcrumbItem = ( | ||
<Breadcrumb.Item key={currentPath}> | ||
{createBreadcrumbItem(route)} | ||
</Breadcrumb.Item> | ||
); | ||
} else { | ||
breadcrumbItem = ( | ||
<Breadcrumb.Item key={currentPath}> | ||
{pathSnippet} | ||
</Breadcrumb.Item> | ||
); | ||
} | ||
breadcrumbItems.push(breadcrumbItem); | ||
return currentPath; | ||
}, ''); | ||
|
||
return breadcrumbItems; | ||
} | ||
|
||
|
||
function App() { | ||
//const [router, setRouter] = useState<Routers | null>(); | ||
const navigate = useNavigate(); | ||
const location = useLocation(); | ||
const { t } = useTranslation() | ||
const [localeStr, setLocaleStr] = useState<string>(getLangCode(nmConfig.settings.language!)) | ||
const [selectedKeys, setSelectedKeys] = useState<string[]>(['/']); | ||
|
||
|
||
const routers: Array<Routers> = [ | ||
{ | ||
title: <><IconHome />{t('home')}</>, | ||
path: '/', | ||
component: <Home_page />, | ||
}, | ||
{ | ||
title: <><IconCloud />{t('storage')}</>, | ||
path: '/storage', | ||
children: [ | ||
{ | ||
title: t('manage'), | ||
path: '/storage/manage', | ||
component: <Storage_page />, | ||
hideChildren: true, | ||
children: [ | ||
{ | ||
title: t('add'), | ||
path: '/storage/manage/add', | ||
key: '/storage/manage',//因为父菜单隐藏了子菜单项,在此页面时设置父菜单key以选择父菜单项 | ||
component: <AddStorage_page />, | ||
} | ||
] | ||
}, | ||
{ | ||
title: t('explorer'), | ||
path: '/storage/explorer', | ||
component: <Explorer_page /> | ||
} | ||
] | ||
}, { | ||
title: <><IconStorage />{t('mount')}</>, | ||
path: '/mount', | ||
component: <Mount_page />, | ||
hideChildren: true, | ||
children: [ | ||
{ | ||
title: t('add'), | ||
path: '/mount/add', | ||
key: '/mount',//因为父菜单隐藏了子菜单项,在此页面时设置父菜单key以选择父菜单项 | ||
component: <AddMount_page />, | ||
} | ||
] | ||
}, | ||
{ | ||
title: <><IconSwap style={{ transform: 'rotate(90deg)' }} />{t('transmit')}</> /* +(rcloneInfo.stats.transferring? '(' + rcloneInfo.stats.transferring.length + ')': '') */, | ||
path: '/transmit', | ||
component: <Transmit_page />, | ||
}, | ||
{ | ||
title: <><IconList />{t('task')}</>, | ||
path: '/task', | ||
component: <Task_page />, | ||
hideChildren: true, | ||
children: [ | ||
{ | ||
title: t('add'), | ||
path: '/task/add', | ||
key: '/task',//因为父菜单隐藏了子菜单项,在此页面时设置父菜单key以选择父菜单项 | ||
component: <AddTask_page />, | ||
} | ||
] | ||
}, | ||
{ | ||
title: <><IconSettings />{t('setting')}</>, | ||
path: '/setting', | ||
component: <Setting_page />, | ||
} | ||
] | ||
|
||
useEffect(() => { | ||
hooks.setLocaleStr = setLocaleStr | ||
}, []) | ||
|
||
useEffect(() => { | ||
|
||
hooks.navigate = (path: string) => { | ||
if (path != location.pathname) { | ||
location.pathname.includes('add') && Message.warning(t('prompt_for_leaving_the_add_or_edit_page')) | ||
navigate(path) | ||
} | ||
} | ||
|
||
//setRouter(searchRoute(location.pathname, routers)); | ||
const route = searchRoute(location.pathname, routers); | ||
if (route) { | ||
if (route.key) { | ||
setSelectedKeys([route.key]); | ||
} else { | ||
setSelectedKeys([route.path]); | ||
} | ||
} | ||
}, [location]); | ||
|
||
|
||
/* | ||
<Layout style={{ height: '400px' }}> | ||
<Header>Header</Header> | ||
<Layout> | ||
<Sider>Sider</Sider> | ||
<Content>Content</Content> | ||
</Layout> | ||
<Footer>Footer</Footer> | ||
</Layout> */ | ||
return ( | ||
<ConfigProvider locale={getLocale(localeStr)}> | ||
<Layout style={{ | ||
width: '100%', | ||
height: '100%', | ||
backgroundColor: 'var(--color-bg-1)' | ||
}}> | ||
<Header style={{ width: '100%', height: '2.4rem', backgroundColor: 'var(--color-bg-2)', borderBlockEnd: '1px solid var(--color-border-2)' }}> | ||
<Row > | ||
<Col flex={'auto'} data-tauri-drag-region style={{ height: '2.4rem', display: 'flex' }}> | ||
<img src='/img/color.svg' style={{ width: '1.8rem', height: '1.8rem', marginTop: '0.3rem', marginLeft: '0.6rem' }} data-tauri-drag-region /> | ||
<span style={{ marginLeft: '0.3rem', fontSize: '1.2rem', marginTop: '0.3rem', color: 'var(--color-text-1)' }} data-tauri-drag-region>NetMount</span> | ||
</Col> | ||
<Col flex={'5rem'} style={{ textAlign: 'right' }}> | ||
<Button onClick={windowsMini} icon={<IconMinus style={{ fontSize: '1.1rem', color: 'var(--color-text-2)' }} />} type='text' style={{ width: '2.5rem', paddingTop: '0.5rem' }} /> | ||
<Button onClick={windowsHide} icon={<IconClose style={{ fontSize: '1.1rem' }} />} type='text' status='danger' style={{ width: '2.5rem', paddingTop: '0.5rem' }} /> | ||
</Col> | ||
</Row> | ||
</Header> | ||
|
||
<Layout style={{ maxHeight: 'calc(100% - 2.4rem)' }}> | ||
<Sider style={{ width: '10rem' }} > | ||
<Menu | ||
defaultOpenKeys={['/storage']} | ||
selectedKeys={selectedKeys} | ||
style={{ height: '100%' }} | ||
onClickMenuItem={(path) => { | ||
hooks.navigate(path) | ||
}} | ||
>{mapMenuItem(routers)}</Menu> | ||
</Sider> | ||
<Content style={{ maxHeight: '100%', padding: '1.1rem' }}> | ||
{/* <Breadcrumb style={{ margin: '16px 0' }}>{generateBreadcrumb(location.pathname, routers)}</Breadcrumb> */} | ||
<Routes>{mapRouters(routers)}</Routes> | ||
</Content> | ||
</Layout> | ||
</Layout> | ||
</ConfigProvider> | ||
) | ||
} | ||
|
||
export { App } |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
//日志处理(错误处理) | ||
import { Modal } from "@arco-design/web-react"; | ||
import { t } from "i18next"; | ||
import { ReactNode } from "react"; | ||
window.onerror = async function (msg, url, lineNo, columnNo, error) { | ||
let message = [ | ||
'Message: ' + msg, | ||
'URL: ' + url, | ||
'Line: ' + lineNo, | ||
'Column: ' + columnNo, | ||
'Error object: ' + JSON.stringify(error) | ||
].join(' - '); | ||
|
||
await errorThrowToUser(message) | ||
return false; | ||
}; | ||
|
||
window.addEventListener('unhandledrejection', async function (event) { | ||
await errorThrowToUser(event.reason) | ||
}); | ||
|
||
window.addEventListener('error', async (event) => { | ||
await errorThrowToUser(event.message) | ||
}, true); | ||
|
||
async function errorThrowToUser(message: string) { | ||
//排除这个错误 | ||
if (message.toString().includes('ResizeObserver')) { return } | ||
|
||
let content = t('error_tips') + ',Error:' + message | ||
|
||
//提示错误 | ||
await errorDialog(t('error'), content) | ||
} | ||
//错误对话框 | ||
function errorDialog(title: string, content: ReactNode) { | ||
return new Promise((resolve) => { | ||
Modal.error( | ||
{ | ||
title: title, | ||
content: content, | ||
onOk: () => { resolve(true) }, | ||
onCancel: () => { resolve(false) }, | ||
maskClosable: false, | ||
closable: false | ||
} | ||
) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import zhCN from '@arco-design/web-react/es/locale/zh-CN'; | ||
import enUS from '@arco-design/web-react/es/locale/en-US'; | ||
import jaJP from '@arco-design/web-react/es/locale/ja-JP'; | ||
import koKR from '@arco-design/web-react/es/locale/ko-KR'; | ||
import idID from '@arco-design/web-react/es/locale/id-ID'; | ||
import thTH from '@arco-design/web-react/es/locale/th-TH'; | ||
import zhHK from '@arco-design/web-react/es/locale/zh-HK'; | ||
import frFR from '@arco-design/web-react/es/locale/fr-FR'; | ||
import esES from '@arco-design/web-react/es/locale/es-ES'; | ||
import deDE from '@arco-design/web-react/es/locale/de-DE'; | ||
import itIT from '@arco-design/web-react/es/locale/it-IT'; | ||
import viVN from '@arco-design/web-react/es/locale/vi-VN'; | ||
import { Locale } from '@arco-design/web-react/es/locale/interface'; | ||
|
||
function getLocale(locale:string):Locale { | ||
switch (locale) { | ||
case 'zh-cn': | ||
return zhCN; | ||
case 'en-us': | ||
return enUS; | ||
case 'ja-jp': | ||
return jaJP; | ||
case 'ko-kr': | ||
return koKR as unknown as Locale; | ||
case 'id-id': | ||
return idID as unknown as Locale; | ||
case 'th-th': | ||
return thTH as unknown as Locale; | ||
case 'zh-hk': | ||
return zhHK; | ||
case 'fr-fr': | ||
return frFR as unknown as Locale; | ||
case 'es-es': | ||
return esES as unknown as Locale; | ||
case 'de-de': | ||
return deDE as unknown as Locale; | ||
case 'it-it': | ||
return itIT as unknown as Locale; | ||
case 'vi-vn': | ||
return viVN as unknown as Locale; | ||
default: | ||
return zhCN; | ||
} | ||
} | ||
export{getLocale} |
Oops, something went wrong.