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';
|
||||
|
||||
export interface NdmKeyboardVO extends BaseModel {
|
||||
|
||||
deviceId: string;
|
||||
name: 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 { 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 { Layout, Menu, theme } from 'antd';
|
||||
import { Avatar, Dropdown, Layout, Menu, Space, theme } from 'antd';
|
||||
import { useState } from 'react';
|
||||
|
||||
const { Content, Footer, Header, Sider } = Layout;
|
||||
@@ -12,10 +12,27 @@ export const AppLayout: FC = () => {
|
||||
const { token: { colorBgContainer } } = theme.useToken();
|
||||
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 (
|
||||
<Layout style={{ height: '100%', minWidth: '960px' }}>
|
||||
<Header style={{ background: colorBgContainer }}>
|
||||
<Layout style={{ height: '100%', minWidth: '1600px' }}>
|
||||
<Header style={{ background: colorBgContainer, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<h2 style={{ fontWeight: 'bold' }}>网络设备管理平台</h2>
|
||||
<Dropdown menu={{ items }}>
|
||||
<Space>
|
||||
<span>Admin</span>
|
||||
<DownOutlined />
|
||||
</Space>
|
||||
</Dropdown>
|
||||
</Header>
|
||||
<Layout>
|
||||
<Sider collapsible defaultCollapsed collapsedWidth={64} trigger={null} style={{ background: colorBgContainer }}>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
import { AppLayout } from '../layouts/app-layout';
|
||||
import { AppLayout } from '@/layouts/app-layout';
|
||||
|
||||
export const Route = createFileRoute('/_app')({
|
||||
// TODO: 登录校验
|
||||
beforeLoad: async () => {},
|
||||
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')({
|
||||
component: 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 { Button, Card, Form, Input, Typography } from 'antd';
|
||||
|
||||
export const Route = createFileRoute('/login')({
|
||||
component: LoginPage,
|
||||
@@ -6,8 +8,39 @@ export const Route = createFileRoute('/login')({
|
||||
|
||||
function LoginPage() {
|
||||
return (
|
||||
<div style={{ padding: '50px', textAlign: 'center' }}>
|
||||
<h1>登录页面</h1>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', backgroundColor: '#f0f2f5' }}>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
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