|
|
@@ -4,9 +4,12 @@ import '@rongmz/react-stock-heatmap/example/src/index.css';
|
|
|
import toast, { Toaster } from 'react-hot-toast';
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
import axios from 'axios';
|
|
|
+import { Select, Button, Input, Space } from 'tdesign-react';
|
|
|
+import { DeleteIcon } from 'tdesign-icons-react';
|
|
|
+import 'tdesign-react/es/style/index.css';
|
|
|
+import value from "d3-interpolate/src/value";
|
|
|
|
|
|
-const path = require('path');
|
|
|
-const fs = require('fs');
|
|
|
+const {Option} = Select;
|
|
|
|
|
|
function formatTimestamp(timestamp) {
|
|
|
// 创建一个新的 Date 对象
|
|
|
@@ -83,6 +86,7 @@ function parseStockData(data) {
|
|
|
export default () => {
|
|
|
const [isActivation, setIsActivation] = React.useState(false);
|
|
|
const [activationCode, setActivationCode] = React.useState();
|
|
|
+ const [loginSymbol,setLoginSymbol]= React.useState();
|
|
|
const [loading, setLoading] = React.useState(true);
|
|
|
const progressRef = React.useRef(null);
|
|
|
/** @type {React.MutableRefObject<StockHeatmap>} */
|
|
|
@@ -94,9 +98,61 @@ export default () => {
|
|
|
setAutoScroll(value);
|
|
|
};
|
|
|
|
|
|
+ // 下拉框部分
|
|
|
+ const [symbolOptions, setSymbolOptions] = React.useState([]);
|
|
|
+ const [editOrCreate, toggleEditOrCreate] = React.useState('edit');
|
|
|
+
|
|
|
+ const [inputSymbol, changeInputSymbol] = React.useState({ });
|
|
|
+
|
|
|
+ const handleClickConfirm = async () => {
|
|
|
+ toast.remove()
|
|
|
+ if (!inputSymbol.symbol) return toast.error("品种不能为空!")
|
|
|
+ if (!inputSymbol.port) return toast.error("端口号不能为空!")
|
|
|
+
|
|
|
+ const options = {...inputSymbol, id: uuidv4()};
|
|
|
+ const newOptions = [...symbolOptions, options];
|
|
|
+ setSymbolOptions(newOptions);
|
|
|
+ changeInputSymbol({symbol:"",port:""});
|
|
|
+ toggleEditOrCreate('edit');
|
|
|
+ await window.electronAPI.setSymbolData(newOptions)
|
|
|
+ };
|
|
|
+ const handleDeleteSymbolOption= async (option)=>{
|
|
|
+ const newOptions = symbolOptions.filter(item=>item.id !== option.id)
|
|
|
+ setSymbolOptions(newOptions)
|
|
|
+ if(loginSymbol === option.id) setLoginSymbol(undefined)
|
|
|
+ await window.electronAPI.setSymbolData(newOptions)
|
|
|
+ }
|
|
|
+
|
|
|
// ------------ Load data -------------
|
|
|
- React.useEffect(() => {
|
|
|
- const ws = new WebSocket('ws://localhost:6789');
|
|
|
+ // React.useEffect(() => {
|
|
|
+ // const symbolInfo = symbolOptions.find((item)=> item.id == loginSymbol)
|
|
|
+ // const ws = new WebSocket(`ws://localhost:${symbolInfo.port}`);
|
|
|
+ // let ref = heatmapRef.current
|
|
|
+ //
|
|
|
+ // console.log('ws创建完成')
|
|
|
+ // ws.onmessage = function(event) {
|
|
|
+ // const message = JSON.parse(event.data);
|
|
|
+ //
|
|
|
+ // let stock = parseStockData(message)
|
|
|
+ //
|
|
|
+ // ref.addData(stock)
|
|
|
+ //
|
|
|
+ // if (progressRef.current !== null) {
|
|
|
+ // progressRef.current.innerHTML = ` 等待数据推送 ${(100 * ref.data.length / ref.windowLength + 1).toFixed(0)}% ...`
|
|
|
+ //
|
|
|
+ // if (ref.data.length >= ref.windowLength) {
|
|
|
+ // setLoading(false)
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ //
|
|
|
+ // ws.onerror = function(event) {
|
|
|
+ // console.error("WebSocket error observed:", event);
|
|
|
+ // };
|
|
|
+ // }, []);
|
|
|
+ const connectWebSocket = ()=>{
|
|
|
+ const symbolInfo = symbolOptions.find((item)=> item.id === loginSymbol)
|
|
|
+ const ws = new WebSocket(`ws://localhost:${symbolInfo.port}`);
|
|
|
let ref = heatmapRef.current
|
|
|
|
|
|
console.log('ws创建完成')
|
|
|
@@ -119,7 +175,7 @@ export default () => {
|
|
|
ws.onerror = function(event) {
|
|
|
console.error("WebSocket error observed:", event);
|
|
|
};
|
|
|
- }, []);
|
|
|
+ }
|
|
|
|
|
|
// ------------ Load data -------------
|
|
|
const checkStatus = async ()=>{
|
|
|
@@ -155,13 +211,14 @@ export default () => {
|
|
|
checkStatus()
|
|
|
},5000)
|
|
|
setIsActivation(true)
|
|
|
+ connectWebSocket()
|
|
|
}else{
|
|
|
toast.error(response.data.msg);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ---------- window update ------------
|
|
|
- React.useEffect(async () => {
|
|
|
+ React.useEffect( async () => {
|
|
|
// 处理是否有登录信息
|
|
|
let loginInfo = await window.electronAPI.getLoginInfoData();
|
|
|
if(loginInfo.loginInfo) {
|
|
|
@@ -176,6 +233,12 @@ export default () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 处理品种选择
|
|
|
+ let symbolInfo = await window.electronAPI.getSymbolData();
|
|
|
+ if(symbolInfo.length > 0) {
|
|
|
+ setSymbolOptions(symbolInfo)
|
|
|
+ }
|
|
|
+
|
|
|
const updateFn = () => {
|
|
|
setWindowDim([
|
|
|
window.innerWidth,
|
|
|
@@ -251,10 +314,69 @@ export default () => {
|
|
|
<div className="title">
|
|
|
软件登录
|
|
|
</div>
|
|
|
- <div className="iptWp">
|
|
|
- <input placeholder="请输入登录码" value={activationCode || ''} onChange={(e)=>{
|
|
|
- setActivationCode(e.target.value)
|
|
|
- }} />
|
|
|
+ <div className="iptWp customIpt">
|
|
|
+ <Input className="password ipt" placeholder="请输入登录码" type="password" value={activationCode || ''} onChange={(value)=>{setActivationCode(value)}} />
|
|
|
+ <Select
|
|
|
+ placeholder="请选择品种"
|
|
|
+ className="symbol ipt"
|
|
|
+ clearable
|
|
|
+ value={loginSymbol}
|
|
|
+ onChange={(value)=>{setLoginSymbol(value)}}
|
|
|
+ panelBottomContent={
|
|
|
+ <div
|
|
|
+ className="select-panel-footer"
|
|
|
+ style={{
|
|
|
+ position: 'sticky',
|
|
|
+ bottom: 0,
|
|
|
+ backgroundColor: 'var(--td-bg-color-container)',
|
|
|
+ zIndex: 2,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {editOrCreate === 'edit' ? (
|
|
|
+ <div
|
|
|
+ style={{
|
|
|
+ padding: '8px 6px',
|
|
|
+ borderTop: '1px solid var(--td-border-level-2-color)',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Button theme="primary" size="small" variant="text" onClick={() => toggleEditOrCreate('create')}>
|
|
|
+ 新增选项
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div style={{ padding: 8, borderTop: '1px solid var(--td-border-level-2-color)' }}>
|
|
|
+ <Space>
|
|
|
+ <Input placeholder="请输入品种" size="small" autofocus value={inputSymbol.symbol} onChange={(value) => changeInputSymbol({...inputSymbol,symbol:value})}></Input>
|
|
|
+ <Input placeholder="请输入端口号" size="small" autofocus value={inputSymbol.port} onChange={(value) => changeInputSymbol({...inputSymbol,port:value})}></Input>
|
|
|
+ </Space>
|
|
|
+ <Button size="small" style={{ marginTop: '12px' }} onClick={handleClickConfirm}>
|
|
|
+ 确认
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ theme="default"
|
|
|
+ size="small"
|
|
|
+ style={{ marginTop: '12px', marginLeft: '8px' }}
|
|
|
+ onClick={() => toggleEditOrCreate('edit')}
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ >
|
|
|
+ {symbolOptions.map((option) => (
|
|
|
+ <Option key={option.id} value={option.id} label={`${option.symbol}:${option.port}`}>
|
|
|
+ <div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
+ <div style={{ flex:"1",paddingRight:"10px",boxSizing:"border-box" }}>{`${option.symbol}:${option.port}`}</div>
|
|
|
+ <DeleteIcon style={{ width:"16px"}} onClick={(e)=>{
|
|
|
+ e.stopPropagation();
|
|
|
+ handleDeleteSymbolOption(option)
|
|
|
+ }}/>
|
|
|
+ </div>
|
|
|
+ </Option>
|
|
|
+ ))}
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
<div className="btnWp">
|
|
|
<div className="btn" onClick={handleActivation}>登 录</div>
|