Initial commit
This commit is contained in:
20
.eslintrc.cjs
Normal file
20
.eslintrc.cjs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react/jsx-runtime',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||||
|
settings: { react: { version: '18.2' } },
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# React + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
17
index.html
Normal file
17
index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
|
||||||
|
<title>Vite + React</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
4573
package-lock.json
generated
Normal file
4573
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "spectrometer-dash",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"preinstall": "npx npm-force-resolutions"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"antd": "^5.7.3",
|
||||||
|
"paho-mqtt": "^1.1.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.15",
|
||||||
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
|
"eslint": "^8.45.0",
|
||||||
|
"eslint-plugin-react": "^7.32.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
|
"react-error-overlay": "^6.0.9",
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"react-error-overlay": "6.0.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
3
src/App.css
Normal file
3
src/App.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.demo-logo h2 {
|
||||||
|
color: antiquewhite;
|
||||||
|
}
|
||||||
74
src/App.jsx
Normal file
74
src/App.jsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import ConnectForm from "./pages/Login";
|
||||||
|
import { Client } from "paho-mqtt";
|
||||||
|
import UnauthPage from "./layout/UnauthPage";
|
||||||
|
import { Spin } from "antd";
|
||||||
|
import Page from "./layout/Page";
|
||||||
|
import Spectrum from "./pages/Spectrum";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [client, setClient] = useState(null);
|
||||||
|
const [connected, setConnected] = useState(false);
|
||||||
|
const [connecting, setConnecting] = useState(false);
|
||||||
|
const [alert, setAlert] = useState(null);
|
||||||
|
|
||||||
|
const onConnect = (url, options) => {
|
||||||
|
console.log(`Trying to connect to ${url}...`);
|
||||||
|
|
||||||
|
const _client = new Client(url, options.clientId);
|
||||||
|
|
||||||
|
_client.onConnectionLost = onDisconnect;
|
||||||
|
|
||||||
|
_client.connect({
|
||||||
|
onSuccess: onConnected,
|
||||||
|
onFailure: onError,
|
||||||
|
userName: options.username,
|
||||||
|
password: options.password,
|
||||||
|
cleanSession: options.clean,
|
||||||
|
timeout: options.connectTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Options:', options);
|
||||||
|
|
||||||
|
setClient(_client);
|
||||||
|
setConnecting(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConnected = () => {
|
||||||
|
console.log("Connected!");
|
||||||
|
setConnecting(false);
|
||||||
|
setConnected(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onError = (err) => {
|
||||||
|
console.error("Failed to connect", err);
|
||||||
|
setConnecting(false);
|
||||||
|
setAlert({ type: 'error', msg: `Could not connect, error: ${err.errorMessage}` });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDisconnect = () => {
|
||||||
|
client.disconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (connecting)
|
||||||
|
setAlert(null);
|
||||||
|
}, [connecting]);
|
||||||
|
|
||||||
|
if (!connected)
|
||||||
|
return (
|
||||||
|
<UnauthPage content={
|
||||||
|
<ConnectForm
|
||||||
|
connect={onConnect}
|
||||||
|
btnContent={connecting ? <Spin /> : 'Connect'}
|
||||||
|
/>
|
||||||
|
} alert={alert} />
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page
|
||||||
|
content={<Spectrum />} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
65
src/ConnectedPage.jsx
Normal file
65
src/ConnectedPage.jsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Client } from 'paho-mqtt';
|
||||||
|
|
||||||
|
|
||||||
|
const ConnectedPage = () => {
|
||||||
|
const StateMessages = [
|
||||||
|
"Clique no botão para conectar",
|
||||||
|
"Conectando...",
|
||||||
|
"Conectado",
|
||||||
|
"Erro na conexão",
|
||||||
|
];
|
||||||
|
|
||||||
|
const [connected, setConnected] = useState(false);
|
||||||
|
const [client, setClient] = useState(null);
|
||||||
|
const [state, setState] = useState(0)
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
const options = {
|
||||||
|
clientId: 'aaaaa',
|
||||||
|
username: 'test',
|
||||||
|
password: '31415926',
|
||||||
|
clean: true,
|
||||||
|
reconnectPeriod: 1000,
|
||||||
|
connectTimeout: 30 * 1000,
|
||||||
|
host: 'firmwarejun.net',
|
||||||
|
port: 8883,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _client = new Client(`ws://${options.host}:${options.port}/mqtt`, options.clientId);
|
||||||
|
|
||||||
|
_client.connect({
|
||||||
|
onSuccess: () => {
|
||||||
|
console.log("Conectado");
|
||||||
|
setConnected(true);
|
||||||
|
setState(2);
|
||||||
|
},
|
||||||
|
onFailure: (err) => {
|
||||||
|
console.log(`Erro ao conectar ${err}`);
|
||||||
|
setState(3);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
setClient(_client);
|
||||||
|
setState(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={handleClick}>
|
||||||
|
Conectar ao servidor MQTT
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p>Status: {StateMessages[state]}</p>
|
||||||
|
|
||||||
|
{connected && (
|
||||||
|
<div>
|
||||||
|
{/* O resto da sua página vai aqui */}
|
||||||
|
<p>Conectado ao servidor MQTT! Agora você pode ver o resto da página.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConnectedPage;
|
||||||
16
src/layout/Footer.jsx
Normal file
16
src/layout/Footer.jsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Layout } from "antd";
|
||||||
|
const { Footer } = Layout;
|
||||||
|
|
||||||
|
const SFooter = () => {
|
||||||
|
return (
|
||||||
|
<Footer
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Jun © 2023
|
||||||
|
</Footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SFooter;
|
||||||
70
src/layout/Page.jsx
Normal file
70
src/layout/Page.jsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Alert, Layout, Menu, theme } from 'antd';
|
||||||
|
import SFooter from './Footer';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
const { Header, Content } = Layout;
|
||||||
|
|
||||||
|
import '../App.css'
|
||||||
|
|
||||||
|
const menus = [
|
||||||
|
{
|
||||||
|
key: 'spectrum',
|
||||||
|
label: 'Espectro',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'config',
|
||||||
|
label: 'Configuração',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'about',
|
||||||
|
label: 'sobre',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const Page = ({ content, alert }) => {
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer },
|
||||||
|
} = theme.useToken();
|
||||||
|
return (
|
||||||
|
<Layout className="layout">
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="demo-logo"
|
||||||
|
style={{ margin: '0 30px' }}
|
||||||
|
>
|
||||||
|
<h2>Spectrometer</h2>
|
||||||
|
</div>
|
||||||
|
<Menu
|
||||||
|
theme="dark"
|
||||||
|
mode="horizontal"
|
||||||
|
defaultSelectedKeys={['spectrum']}
|
||||||
|
items={menus}
|
||||||
|
/>
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
{alert && <Alert type={alert.type} message={alert.msg} banner />}
|
||||||
|
<div
|
||||||
|
className="site-layout-content"
|
||||||
|
style={{
|
||||||
|
padding: '16px 50px',
|
||||||
|
background: colorBgContainer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
<SFooter />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Page.propTypes = {
|
||||||
|
content: PropTypes.element.isRequired,
|
||||||
|
alert: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
49
src/layout/UnauthPage.jsx
Normal file
49
src/layout/UnauthPage.jsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { Alert, Layout, theme } from "antd";
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
const { Header, Content } = Layout;
|
||||||
|
|
||||||
|
import '../App.css'
|
||||||
|
import SFooter from "./Footer";
|
||||||
|
|
||||||
|
const UnauthPage = ({ content, alert }) => {
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer },
|
||||||
|
} = theme.useToken();
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="demo-logo"
|
||||||
|
style={{ margin: '0 30px' }}
|
||||||
|
>
|
||||||
|
<h2>Spectrometer</h2>
|
||||||
|
</div>
|
||||||
|
</Header>
|
||||||
|
<Content>
|
||||||
|
{alert && <Alert type={alert.type} message={alert.msg} banner />}
|
||||||
|
<div
|
||||||
|
className="site-layout-content"
|
||||||
|
style={{
|
||||||
|
padding: '16px 50px',
|
||||||
|
background: colorBgContainer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
<SFooter />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
UnauthPage.propTypes = {
|
||||||
|
content: PropTypes.element.isRequired,
|
||||||
|
alert: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnauthPage;
|
||||||
9
src/main.jsx
Normal file
9
src/main.jsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.jsx'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
0
src/pages/Calib.jsx
Normal file
0
src/pages/Calib.jsx
Normal file
103
src/pages/Login.jsx
Normal file
103
src/pages/Login.jsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Card, Button, Form, Input, Row, Col, Space } from 'antd'
|
||||||
|
|
||||||
|
const ConnectForm = ({ connect, btnContent }) => {
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const initialConnectionOptions = {
|
||||||
|
// ws or wss
|
||||||
|
protocol: 'ws',
|
||||||
|
host: 'firmwarejun.net',
|
||||||
|
clientId: `sp_${Math.random().toString(16).substring(2, 8)}`,
|
||||||
|
// ws -> 8083; wss -> 8084
|
||||||
|
port: 8883,
|
||||||
|
/**
|
||||||
|
* By default, EMQX allows clients to connect without authentication.
|
||||||
|
* https://docs.emqx.com/en/enterprise/v4.4/advanced/auth.html#anonymous-login
|
||||||
|
*/
|
||||||
|
username: 'test',
|
||||||
|
password: '31415926',
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFinish = (values) => {
|
||||||
|
const { host, clientId, port, username, password } = values
|
||||||
|
const url = `ws://${host}:${port}/mqtt`
|
||||||
|
const options = {
|
||||||
|
clientId,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
clean: true,
|
||||||
|
reconnectPeriod: 1000, // ms
|
||||||
|
connectTimeout: 30 * 1000, // ms
|
||||||
|
}
|
||||||
|
connect(url, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConnect = () => {
|
||||||
|
form.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConnectionForm = (
|
||||||
|
<Form
|
||||||
|
layout="vertical"
|
||||||
|
name="basic"
|
||||||
|
form={form}
|
||||||
|
initialValues={initialConnectionOptions}
|
||||||
|
onFinish={onFinish}
|
||||||
|
>
|
||||||
|
<Row gutter={20}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Host" name="host">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Port" name="port">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Client ID" name="clientId">
|
||||||
|
<Input disabled={true} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Username" name="username">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Password" name="password">
|
||||||
|
<Input type='password' />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space
|
||||||
|
align='center'
|
||||||
|
style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
title={<h2>Please, connect before using the system</h2>}
|
||||||
|
actions={[
|
||||||
|
<Button type="primary" onClick={handleConnect} key={'Connect'} ghost>
|
||||||
|
{btnContent}
|
||||||
|
</Button>
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{ConnectionForm}
|
||||||
|
</Card>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectForm.propTypes = {
|
||||||
|
connect: PropTypes.func.isRequired,
|
||||||
|
btnContent: PropTypes.oneOfType([
|
||||||
|
PropTypes.element, PropTypes.string,
|
||||||
|
]).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConnectForm
|
||||||
66
src/pages/Spectrum.jsx
Normal file
66
src/pages/Spectrum.jsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Button, Card, Form, Input, Layout, Radio, theme } from "antd";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const { Content, Sider } = Layout;
|
||||||
|
|
||||||
|
const Spectrum = () => {
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer },
|
||||||
|
} = theme.useToken();
|
||||||
|
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [mode, setMode] = useState('Angle');
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
mode: "Angle",
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFormChange = ({ mode }) => {
|
||||||
|
setMode(mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const angleForm = (
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
onValuesChange={onFormChange}
|
||||||
|
initialValues={initialValues}
|
||||||
|
layout="horizontal"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<Form.Item label="Mode" name="mode">
|
||||||
|
<Radio.Group value={mode}>
|
||||||
|
<Radio.Button value="Angle">Angle</Radio.Button>
|
||||||
|
<Radio.Button value="Wavelength">Wavelength</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label={`Target ${mode} (${mode == "Angle" ? '°' : 'Å'})`}>
|
||||||
|
<Input placeholder="30.127" />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ height: 500 }}>
|
||||||
|
<Content>
|
||||||
|
<h2>kk eae men</h2>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Sider width={300} style={{ background: colorBgContainer }}>
|
||||||
|
<Card
|
||||||
|
title="Motor control"
|
||||||
|
size="small"
|
||||||
|
actions={[
|
||||||
|
<Button type="primary" key='sendAngle' size="small" ghost>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{angleForm}
|
||||||
|
</Card>
|
||||||
|
</Sider>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Spectrum;
|
||||||
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user