Skip to content

Commit

Permalink
feat: 离线数据回放
Browse files Browse the repository at this point in the history
  • Loading branch information
Sherlocksuper committed Sep 18, 2024
1 parent 0bdcde5 commit c763292
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 194 deletions.
5 changes: 4 additions & 1 deletion front_end/src/apis/standard/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export interface IHistory {
endTime: number
template: ITemplate
historyData: {
[key: string]: number
time: number
data: {
[key: number]: number
}
}[]
}

Expand Down
4 changes: 2 additions & 2 deletions front_end/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ export const my_router = createBrowserRouter([
//测试模板配置
path: '/test-template-for-config',
element: <DndProvider backend={HTML5Backend}>
<TestTemplateForConfig/>,
<TestTemplateForConfig dataMode={"ONLINE"}/>,
</DndProvider>
},
{
path: '/offline-show',
element: <DndProvider backend={HTML5Backend}>
<TestTemplateForConfig/>,
<TestTemplateForConfig dataMode={"OFFLINE"}/>,
</DndProvider>
},
{
Expand Down
306 changes: 163 additions & 143 deletions front_end/src/views/demo/OffLine/offline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Button, Card, Input, message, Modal, Row, Slider, Typography, Upload, UploadProps} from "antd";
import React, {useRef, useState} from "react";
import {Button, Card, Input, message, Modal, Row, Slider, Space, Typography, Upload, UploadProps} from "antd";
import React, {useState} from "react";
import {IHistory} from "@/apis/standard/history.ts";
import {InboxOutlined} from "@ant-design/icons";
import {debounce, formatFileSize, formatTime} from "@/utils";
Expand All @@ -9,164 +9,184 @@ const {Title, Text} = Typography;


const OfflineDate = () => {
const [history, setHistory] = useState<IHistory>()
const [file, setFile] = useState<File>()
const [name, setName] = useState<string>('')
const [period, setPeriod] = useState<number[]>([0, 0])
const debounceSetPeriod = debounce((value) => {
setPeriod(value as number[])
const [history, setHistory] = useState<IHistory>()
const [file, setFile] = useState<File>()
const [name, setName] = useState<string>('')
const [period, setPeriod] = useState<number[]>([0, 0])
const debounceSetPeriod = debounce((value) => {
setPeriod(value as number[])
})

const exportFile = () => {
const worker = getExportFileWorker()
worker.onmessage = (e) => {
const file = e.data
const a = document.createElement('a')
a.href = URL.createObjectURL(file)
a.download = file.name
a.click()
}
worker.postMessage({
startTime: period[0],
endTime: period[1],
fileName: name,
file: file
})


const exportFile = () => {
const worker = getExportFileWorker()
worker.postMessage({
startTime: period[0],
endTime: period[1],
fileName: name,
file: file
})
worker.onmessage = (e) => {
const file = e.data
const a = document.createElement('a')
a.href = URL.createObjectURL(file)
a.download = file.name
a.click()
}
}

const showData = () => {
const worker = getExportFileWorker()
worker.onmessage = (e) => {
const file = e.data
const win = window.open('/offline-show')
setTimeout(() => {
win!.postMessage(file, '*')
}, 1000)
}
worker.postMessage({
startTime: period[0],
endTime: period[1],
fileName: name,
file: file
})
}

const showData = () => {
const win = window.open('/offline-show')

setTimeout(() => {
win!.postMessage(file, '*')
}, 1000)
const buttonFunction = (type: 'EXPORT' | 'SHOW') => {
if (type === 'EXPORT') {
exportFile()
} else {
showData()
}

const buttonFunction = (type: 'EXPORT' | 'SHOW') => {
if (type === 'EXPORT') {
exportFile()
}

return <Card style={{
margin: 'auto',
padding: '20px',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
overflow: 'scroll'
}}>
{
!file && <Upload.Dragger
name='file'
multiple={false}
customRequest={(options) => {
const file = options.file as File
if (file.size > 1024 * 1024 * 100) {
message.error('文件大小不能超过100M')
return
}
showData()
}
// 加载
message.loading('文件加载中', 0)
const worker = getFileToHistoryWorker()
worker.onmessage = (e) => {
const history = e.data as IHistory
setHistory(history)
setFile(file)
setPeriod([history.startTime, history.endTime])
message.destroy()
}
worker.postMessage(file)
}}
showUploadList={false}
>
<p className="ant-upload-drag-icon">
<InboxOutlined/>
</p>
<p className="ant-upload-text">点击或者拖拽文件到这里</p>
<p className="ant-upload-hint">支持单个文件上传</p>
</Upload.Dragger>
}

return <Card style={{
margin: 'auto',
padding: '20px',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}>
<Upload.Dragger
name={'file'}
multiple={false}
showUploadList={false}
accept={'application/json'}
action={(file) => {
message.loading('文件上传中', 0)
setFile(file)
const worker = getFileToHistoryWorker()

worker.postMessage(file)
worker.onmessage = (e) => {
const data = e.data
setHistory(data)
message.destroy()
}

return Promise.resolve('File processed')
}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined/>
</p>
<p className="ant-upload-text">点击或者拖拽以输入或替换文件</p>
</Upload.Dragger>
{
(file && history) && <>
<FileInfo file={file}/>
总时长:{formatTime(history.endTime - history.startTime)}
操作时间段:
<Slider range
defaultValue={[history.startTime, history.endTime]}
min={history.startTime}
max={history.endTime}
marks={{
[history.startTime]: formatTime(history.startTime),
[history.endTime]: formatTime(history.endTime)
}}
tooltip={{
formatter: (value: number | number[] | undefined) => {
if (typeof value === 'number') {
return <div>{formatTime(value)}</div>;
} else if (Array.isArray(value)) {
return <div>{formatTime(value[0])} - {formatTime(value[1])}</div>
}
return null;
}
}}
onChange={(value) => {
debounceSetPeriod(value)
}}
/>
<br/>
导出文件名: <Input placeholder={'请输入导出文件名'} onChange={(e) => {
setName(e.target.value)
}}/>
<br/>
<Row>
<FunctionButton type={'EXPORT'} selectTime={period} buttonFunction={buttonFunction}
fileName={name}/>
<FunctionButton type={'SHOW'} selectTime={period} buttonFunction={buttonFunction} fileName={name}/>
</Row>
</>
}
</Card>
{
(file) && <>
<FileInfo file={file}/>
总时长:{formatTime(history.endTime - history.startTime)}
操作时间段:
<Slider range
defaultValue={[history.startTime, history.endTime]}
min={history.startTime}
max={history.endTime}
marks={{
[history.startTime]: formatTime(history.startTime),
[history.endTime]: formatTime(history.endTime)
}}
tooltip={{
formatter: (value: number | number[] | undefined) => {
if (typeof value === 'number') {
return <div>{formatTime(value)}</div>;
} else if (Array.isArray(value)) {
return <div>{formatTime(value[0])} - {formatTime(value[1])}</div>
}
return null;
}
}}
onChange={(value) => {
debounceSetPeriod(value)
}}
/>
<br/>
导出文件名: <Input placeholder={'请输入导出文件名'} onChange={(e) => {
setName(e.target.value)
}}/>
<br/>
<Space style={{marginTop: '20px'}}>
<FunctionButton type={'EXPORT'} selectTime={period} buttonFunction={buttonFunction}
fileName={name}/>
<FunctionButton type={'SHOW'} selectTime={period} buttonFunction={buttonFunction} fileName={name}/>
</Space>
</>
}
</Card>

}

const FileInfo = (props: { file: File }) => {
return (
<Card title="文件信息" style={{width: 300, margin: 20}}>
<Title level={5}>文件名:</Title>
<Text>{props.file.name}</Text>
<Title level={5} style={{marginTop: '20px'}}>数据信息:</Title>
<Text>最近更改时间:{formatTime(props.file.lastModified)}</Text>
<br/>
<Text style={{marginTop: '10px'}}>数据量:{formatFileSize(props.file.size)}</Text>
</Card>
);
return (
<Card title="文件信息" style={{width: 300, margin: 20}}>
<Title level={5}>文件名:</Title>
<Text>{props.file.name}</Text>
<Title level={5} style={{marginTop: '20px'}}>数据信息:</Title>
<Text>最近更改时间:{formatTime(props.file.lastModified)}</Text>
<br/>
<Text style={{marginTop: '10px'}}>数据量:{formatFileSize(props.file.size)}</Text>
</Card>
);
}

const FunctionButton = (props: {
type: 'EXPORT' | 'SHOW',
selectTime: number[],
buttonFunction: (type: 'EXPORT' | 'SHOW') => void,
fileName: string
type: 'EXPORT' | 'SHOW',
selectTime: number[],
buttonFunction: (type: 'EXPORT' | 'SHOW') => void,
fileName: string
}) => {

const [open, setOpen] = useState(false)
const [open, setOpen] = useState(false)

return <>
<Button onClick={() => {
setOpen(true)
}}>{props.type === 'EXPORT' ? '导出文件' : '展示离线数据'}</Button>
return <>
<Button onClick={() => {
setOpen(true)
}}>{props.type === 'EXPORT' ? '导出文件' : '开始展示'}</Button>

<Modal title={props.type === 'EXPORT' ? '导出文件' : '展示离线数据'} open={open} onOk={() => {
props.buttonFunction(props.type)
setOpen(false)
}} onCancel={() => {
setOpen(false)
}}>
<Title level={5}>操作时间段:</Title>
<Text>{formatTime(props.selectTime[0])} - {formatTime(props.selectTime[1])}</Text>
<br/>
<Title level={5}>文件名:</Title>
<Text>{props.fileName}</Text>
</Modal>
</>
<Modal title={props.type === 'EXPORT' ? '导出文件' : '开始展示'} open={open} onOk={() => {
props.buttonFunction(props.type)
setOpen(false)
}} onCancel={() => {
setOpen(false)
}}>
<Title level={5}>操作时间段:</Title>
<Text>{formatTime(props.selectTime[0])} - {formatTime(props.selectTime[1])}</Text>
<br/>
{
props.type === 'EXPORT' &&
<>
<Title level={5}>文件名:</Title>
<Text>{props.fileName || 'default'}</Text>
</>
}
</Modal>
</>
}

export default OfflineDate
7 changes: 6 additions & 1 deletion front_end/src/views/demo/TestConfig/configDropContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import NumberGaugeChart from "@/components/Charts/NumberGaugeChart";
import GridLayout from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import React, {useEffect, useMemo} from "react";
import React, {useEffect, useMemo, useRef} from "react";
import LinesChart from "@/components/Charts/LinesChart/LinesChart.tsx";
import {DataSourceType} from "@/components/Charts/interface.ts";
import {IHistory} from "@/apis/standard/history.ts";
Expand Down Expand Up @@ -37,6 +37,8 @@ const ConfigDropContainer: React.FC<{
const [openItemId, setOpenItemId] = React.useState<string>("");
const [open, setOpen] = React.useState<boolean>(false);

const pathRef = useRef(window.location.pathname);


return (
<div className="dc_container">
Expand Down Expand Up @@ -79,6 +81,9 @@ const ConfigDropContainer: React.FC<{
}}
onContextMenu={(e) => {
e.preventDefault();
if (pathRef.current === "/offline-show"){
return
}
if (banModify) {
setOpen(true);
setOpenItemId(item.id);
Expand Down
Loading

0 comments on commit c763292

Please sign in to comment.