some updates
This commit is contained in:
4
src/apis/client.ts
Normal file
4
src/apis/client.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { Request } from '@/utils/request';
|
||||||
|
|
||||||
|
export const ndmClient = new Request();
|
||||||
|
export const userClient = new Request();
|
||||||
1
src/apis/domains/index.ts
Normal file
1
src/apis/domains/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './station';
|
||||||
7
src/apis/domains/station.ts
Normal file
7
src/apis/domains/station.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface Station {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StationStatusRecord = Record<string, boolean>;
|
||||||
@@ -2,7 +2,6 @@ import type { BaseModel } from '../../base/model';
|
|||||||
import type { ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO } from '../../base/reduce';
|
import type { ReduceForPageQuery, ReduceForSaveVO, ReduceForUpdateVO } from '../../base/reduce';
|
||||||
|
|
||||||
export interface NdmKeyboardVO extends BaseModel {
|
export interface NdmKeyboardVO extends BaseModel {
|
||||||
|
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
name: string;
|
name: string;
|
||||||
manufacturer: string;
|
manufacturer: string;
|
||||||
|
|||||||
43
src/components/dashboard/station/index.tsx
Normal file
43
src/components/dashboard/station/index.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { AlertOutlined, ApiOutlined } from '@ant-design/icons';
|
||||||
|
import { Card, Col, Row, Statistic, Tag } from 'antd';
|
||||||
|
|
||||||
|
interface StationProps {
|
||||||
|
name: string;
|
||||||
|
online: boolean;
|
||||||
|
offlineDeviceCount: number;
|
||||||
|
alarmCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Station: FC<StationProps> = ({ name, online, offlineDeviceCount, alarmCount }) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
size='small'
|
||||||
|
title={name}
|
||||||
|
styles={{ header: name.length > 7 ? { fontSize: '12px' } : {} }}
|
||||||
|
extra={<Tag color={online ? 'success' : 'error'}>{online ? '在线' : '离线'}</Tag>}
|
||||||
|
>
|
||||||
|
{online && (
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={<span style={{ cursor: 'pointer', textDecoration: 'underline dotted' }}>离线设备</span>}
|
||||||
|
value={offlineDeviceCount}
|
||||||
|
prefix={<ApiOutlined />}
|
||||||
|
valueStyle={{ color: offlineDeviceCount > 0 ? '#cf1322' : undefined }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={<span style={{ cursor: 'pointer', textDecoration: 'underline dotted' }}>告警记录</span>}
|
||||||
|
value={alarmCount}
|
||||||
|
prefix={<AlertOutlined />}
|
||||||
|
valueStyle={{ color: alarmCount > 0 ? '#faad14' : undefined }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
4
src/contants/index.ts
Normal file
4
src/contants/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const JAVA_INTEGER_MAX_VALUE = 2147483647;
|
||||||
|
export const JAVA_UNSIGNED_INTEGER_MAX_VALUE = 4294967295;
|
||||||
|
|
||||||
|
export const NDM_SWITCH_PROBE_INTERVAL = 5;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { MenuTheme } from 'antd';
|
import type { MenuProps, MenuTheme } from 'antd';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { AlertFilled, AreaChartOutlined, FileTextFilled, HomeFilled, VideoCameraFilled } from '@ant-design/icons';
|
import { AlertFilled, AreaChartOutlined, DownOutlined, FileTextFilled, HomeFilled, UserOutlined, VideoCameraFilled } from '@ant-design/icons';
|
||||||
import { Link, Outlet } from '@tanstack/react-router';
|
import { Link, Outlet } from '@tanstack/react-router';
|
||||||
import { Layout, Menu, theme } from 'antd';
|
import { Avatar, Dropdown, Layout, Menu, Space, theme } from 'antd';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const { Content, Footer, Header, Sider } = Layout;
|
const { Content, Footer, Header, Sider } = Layout;
|
||||||
@@ -12,10 +12,27 @@ export const AppLayout: FC = () => {
|
|||||||
const { token: { colorBgContainer } } = theme.useToken();
|
const { token: { colorBgContainer } } = theme.useToken();
|
||||||
const [menuTheme] = useState<MenuTheme>('light');
|
const [menuTheme] = useState<MenuTheme>('light');
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: 'logout',
|
||||||
|
label: (
|
||||||
|
<a target='_blank' rel='noopener noreferrer' href='https://www.aliyun.com'>
|
||||||
|
退出登录
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout style={{ height: '100%', minWidth: '960px' }}>
|
<Layout style={{ height: '100%', minWidth: '1600px' }}>
|
||||||
<Header style={{ background: colorBgContainer }}>
|
<Header style={{ background: colorBgContainer, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<h2 style={{ fontWeight: 'bold' }}>网络设备管理平台</h2>
|
<h2 style={{ fontWeight: 'bold' }}>网络设备管理平台</h2>
|
||||||
|
<Dropdown menu={{ items }}>
|
||||||
|
<Space>
|
||||||
|
<span>Admin</span>
|
||||||
|
<DownOutlined />
|
||||||
|
</Space>
|
||||||
|
</Dropdown>
|
||||||
</Header>
|
</Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Sider collapsible defaultCollapsed collapsedWidth={64} trigger={null} style={{ background: colorBgContainer }}>
|
<Sider collapsible defaultCollapsed collapsedWidth={64} trigger={null} style={{ background: colorBgContainer }}>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
|
|
||||||
import { AppLayout } from '../layouts/app-layout';
|
import { AppLayout } from '@/layouts/app-layout';
|
||||||
|
|
||||||
export const Route = createFileRoute('/_app')({
|
export const Route = createFileRoute('/_app')({
|
||||||
|
// TODO: 登录校验
|
||||||
|
beforeLoad: async () => {},
|
||||||
component: AppLayout,
|
component: AppLayout,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,59 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { createFileRoute, useLocation } from '@tanstack/react-router';
|
||||||
|
import { Col, Row } from 'antd';
|
||||||
|
|
||||||
|
import { userClient } from '@/apis/client';
|
||||||
|
import { Station } from '@/components/dashboard/station';
|
||||||
|
|
||||||
export const Route = createFileRoute('/_app/dashboard')({
|
export const Route = createFileRoute('/_app/dashboard')({
|
||||||
component: DashboardPage,
|
component: DashboardPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
function DashboardPage() {
|
function DashboardPage() {
|
||||||
return <div>Hello from "/_app/dashboard"!</div>;
|
// const location = useLocation();
|
||||||
|
// const { data, isLoading, isFetching, isPending } = useQuery({
|
||||||
|
// queryKey: ['station'],
|
||||||
|
// queryFn: async () => {
|
||||||
|
// const [err, , resp] = await userClient.get<any[]>('/api/users');
|
||||||
|
// if (err || !resp) {
|
||||||
|
// throw err;
|
||||||
|
// }
|
||||||
|
// console.log(resp);
|
||||||
|
// return resp;
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const stations = Array.from({ length: 40 }).map((_, i) => ({
|
||||||
|
id: i,
|
||||||
|
name: `测试站点${i + 1}`,
|
||||||
|
isOnline: true,
|
||||||
|
offlineDeviceCount: 12,
|
||||||
|
alarmCount: 3,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
// <div>
|
||||||
|
// <Station name='测试站点' isOnline={true} offlineDeviceCount={12} alarmCount={3} />
|
||||||
|
// <div>
|
||||||
|
// <h2>当前路由信息</h2>
|
||||||
|
// <pre>{JSON.stringify(location, null, 2)}</pre>
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// <pre>{JSON.stringify(data, null, 2)}</pre>
|
||||||
|
// </div>
|
||||||
|
<div style={{ padding: '6px' }}>
|
||||||
|
<Row gutter={[6, 6]}>
|
||||||
|
{stations.map(station => (
|
||||||
|
<Col key={station.id} span={3}>
|
||||||
|
<Station
|
||||||
|
name={station.name}
|
||||||
|
online={station.isOnline}
|
||||||
|
offlineDeviceCount={station.offlineDeviceCount}
|
||||||
|
alarmCount={station.alarmCount}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
|
import { Button, Card, Form, Input, Typography } from 'antd';
|
||||||
|
|
||||||
export const Route = createFileRoute('/login')({
|
export const Route = createFileRoute('/login')({
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
@@ -6,8 +8,39 @@ export const Route = createFileRoute('/login')({
|
|||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '50px', textAlign: 'center' }}>
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', backgroundColor: '#f0f2f5' }}>
|
||||||
<h1>登录页面</h1>
|
<Card style={{ width: 400 }}>
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: 24 }}>
|
||||||
|
<Typography.Title level={3}>网络设备管理平台</Typography.Title>
|
||||||
|
</div>
|
||||||
|
<Form
|
||||||
|
name='normal_login'
|
||||||
|
initialValues={{ remember: true }}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name='username'
|
||||||
|
rules={[{ required: true, message: '请输入用户名!' }]}
|
||||||
|
>
|
||||||
|
<Input prefix={<UserOutlined />} placeholder='用户名' />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name='password'
|
||||||
|
rules={[{ required: true, message: '请输入密码!' }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
prefix={<LockOutlined />}
|
||||||
|
type='password'
|
||||||
|
placeholder='密码'
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Button type='primary' htmlType='submit' style={{ width: '100%' }}>
|
||||||
|
登录
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/store/station.ts
Normal file
17
src/store/station.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
import type { Station, StationStatusRecord } from '@/apis/domains';
|
||||||
|
|
||||||
|
export interface StationState {
|
||||||
|
stationList: Station[];
|
||||||
|
stationStatusRecord: StationStatusRecord;
|
||||||
|
setStationList: (stationList: Station[]) => void;
|
||||||
|
setStationStatusRecord: (stationStatusRecord: StationStatusRecord) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStationStore = create<StationState>(set => ({
|
||||||
|
stationList: [],
|
||||||
|
stationStatusRecord: {},
|
||||||
|
setStationList: (stationList: Station[]) => set({ stationList }),
|
||||||
|
setStationStatusRecord: (stationStatusRecord: StationStatusRecord) => set({ stationStatusRecord }),
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user