some updates

This commit is contained in:
2025-08-11 20:12:16 +08:00
parent 1746334a8f
commit 0899d4bb79
11 changed files with 188 additions and 11 deletions

4
src/apis/client.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Request } from '@/utils/request';
export const ndmClient = new Request();
export const userClient = new Request();

View File

@@ -0,0 +1 @@
export * from './station';

View File

@@ -0,0 +1,7 @@
export interface Station {
id: string;
code: string;
name: string;
}
export type StationStatusRecord = Record<string, boolean>;

View File

@@ -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;

View 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
View 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;

View File

@@ -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 }}>

View File

@@ -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,
});

View File

@@ -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>
);
}

View File

@@ -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
View 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 }),
}));