разрешение конфликта и merge filter-message

This commit is contained in:
Alexey 2021-06-01 15:02:29 +05:00
commit 2184ae71a1
16 changed files with 192 additions and 101 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 354 KiB

View File

@ -10,34 +10,11 @@
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>АСБ Vision</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,14 +1,14 @@
import { useState, useEffect } from 'react';
import { Button, Select, Tag, Popover, Row, Tooltip } from 'antd';
import { ChartTimeArchive } from './charts/ChartTimeArchive';
//import { SlidersOutlined } from '@ant-design/icons';
import { DeleteOutlined } from '@ant-design/icons';
const { Option } = Select;
const linesCollection = [
{ label: "Глубина забоя", xAccessorName: "wellDepth", color: '#f00' },
{ label: "Положение инструмента", xAccessorName: "bitDepth", color: '#ff0' },
{ label: "Высота талевого блока", xAccessorName: "blockPosition", color: '#f0f' },
{ label: "Положение талевого блока", xAccessorName: "blockPosition", color: '#f0f' },
{ label: "Талевый блок. Мин положение", xAccessorName: "blockPositionMin", color: '#0ff' },
{ label: "Талевый блок. Макс положение", xAccessorName: "blockPositionMax", color: '#0f0' },
{ label: "Скорость талевого блока", xAccessorName: "blockSpeed", color: '#00f' },
@ -54,7 +54,6 @@ const tagRender = ({ label, value, closable, onClose }) =>{
let color = linesCollection.find(l=>l.xAccessorName === value)?.color
return (
<Tag
//color={color}
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
@ -86,7 +85,8 @@ export function ArchiveColumn({ data, config, rangeDate, chartRatio, onRemoveCha
mode="multiple"
placeholder="Выберите линии"
value={selectedValues}
//showArrow
allowClear={false}
showArrow
bordered={false}
tagRender={tagRender}
onChange={handleLinesSetChange}
@ -101,7 +101,7 @@ export function ArchiveColumn({ data, config, rangeDate, chartRatio, onRemoveCha
const popBar = <Row>
{select}
<Tooltip title="Удалить этот график">
<Button onClick={() => onRemoveChart(config.id)}>X</Button>
<Button onClick={() => onRemoveChart(config.id)}><DeleteOutlined /></Button>
</Tooltip>
</Row>

View File

@ -4,6 +4,7 @@ import { WellService } from '../services/api'
import Loader from '../components/Loader'
import { TreeSelect } from 'antd'
import { useHistory } from 'react-router-dom'
import notify from './notify'
const groupBy = (table, ...keys) => {
let key = keys[0]
@ -49,6 +50,7 @@ export default function WellTreeSelector(props) {
setWellsTree(wellsTree)
}
catch (e) {
notify('Не удалось загрузить список скважин', 'error')
console.error(`${e.message}`);
}
setLoader(false)
@ -65,11 +67,10 @@ export default function WellTreeSelector(props) {
return (
<>
<TreeSelect
style={{
width: '25%',
alignItems: 'center'
}}
placeholder="Выберите месторождение"
className='header-tree-select'
bordered={false}
dropdownMatchSelectWidth={false}
placeholder='Выберите месторождение'
treeData={wellsTree}
treeDefaultExpandAll
onSelect={onSelect}

View File

@ -30,11 +30,11 @@ const defaultOptions = {
millisecond: 'HH:mm:ss.SSS',
second: 'HH:mm:ss',
minute: 'HH:mm:ss',
hour: 'dd HH:mm:ss',
day: 'MM.dd HH:mm',
week: 'yy.MM.dd HH:mm',
month: 'yyyy.MM.dd',
quarter: 'yyyy.MM.dd',
hour: 'DD HH:mm:ss',
day: 'MM.DD HH:mm',
week: 'yy.MM.DD HH:mm',
month: 'yyyy.MM.DD',
quarter: 'yyyy.MM.DD',
year: 'yyyy.MM',
},
},
@ -90,19 +90,80 @@ export type ChartTimeBaseProps = {
options?: ChartOptions<keyof ChartTypeRegistry> | any,
}
const timeUnitByInterval = (intervalSec:number):String =>{
if(intervalSec < 24*60*60)
export type TimeParams = {
unit: String
stepSize: number
}
const linesPerInterval = 32
const timeUnitByInterval = (intervalSec:number):String => {
if(intervalSec <= 60)
return 'millisecond'
if(intervalSec <= 32*60)
return 'second'
if(intervalSec < 30*24*60*60)
if(intervalSec <= 32*60*60)
return 'minute'
if(intervalSec <= 32*12*60*60)
return 'hour'
if(intervalSec <= 32*24*60*60)
return 'day'
if(intervalSec < 365*24*60*60)
if(intervalSec <= 32*7*24*60*60)
return 'week'
if(intervalSec <= 32*30.4375*24*60*60)
return 'month'
if(intervalSec <= 32*121.75*24*60*60)
return 'quarter'
else
return 'year'
}
const timeParamsByInterval = (intervalSec:number) :TimeParams => {
let stepSize = intervalSec
let unit = timeUnitByInterval(intervalSec)
switch(unit){
case "millisecond":
stepSize *= 1000
break;
case "second":
//stepSize *= 1
break;
case "minute":
stepSize /= 60
break;
case "hour":
stepSize /= 60*60
break;
case "day":
stepSize /= 24*60*60
break;
case "week":
stepSize /= 7*24*60*60
break;
case "month":
stepSize /= 30*24*60*60
break;
case "quarter":
stepSize /= 91*24*60*60
break;
case "year":
stepSize /= 365.25*24*60*60
break;
}
stepSize = Math.round(stepSize/linesPerInterval)
stepSize = stepSize > 0 ? stepSize : 1;
return {unit, stepSize}
}
export const ChartTimeBase: React.FC<ChartTimeBaseProps> = ({options, dataParams}) => {
const chartRef = useRef<HTMLCanvasElement>(null)
const [chart, setChart] = useState<any>()
@ -130,18 +191,19 @@ export const ChartTimeBase: React.FC<ChartTimeBaseProps> = ({options, dataParams
chart.data = dataParams.data
chart.options.aspectRatio = options?.aspectRatio
if(dataParams.yStart){
let interval = Number(dataParams.yInterval ?? 600)
let start = new Date(dataParams.yStart)
let end = new Date(dataParams.yStart)
end.setSeconds(end.getSeconds() + interval)
let {unit, stepSize} = timeParamsByInterval(interval)
if(chart.options.scales?.y){
chart.options.scales.y.max = end.getTime()
chart.options.scales.y.min = start.getTime()
chart.options.scales.y.ticks.display = dataParams.displayLabels ?? true
chart.options.scales.y.time.unit = timeUnitByInterval(interval)
chart.options.scales.y.time.stepSize = Math.round(interval/32)
chart.options.scales.y.time.unit = unit
chart.options.scales.y.time.stepSize = stepSize
}
}

View File

@ -11,9 +11,9 @@ const typeDictionary = {
* @param body string или ReactNode
* @param type для параметра типа. Допустимые значение 'error', 'warning', 'info'
*/
export default function Notification(body, type='info'){
export default function notify(body, type='info'){
notification.info({
notification[type]({
description: body,
message: typeDictionary[type],
type,

View File

@ -12,6 +12,7 @@ import locale from 'antd/lib/locale/ru_RU'
import {generateUUID} from '../services/UidGenerator'
import { ArchiveColumn } from '../components/ArchiveColumn'
import moment from 'moment'
import notify from '../components/notify'
const { RangePicker } = DatePicker;
@ -96,7 +97,10 @@ export default function Archive() {
DataService.getData(id, startDate, interval, 2048)
.then(handleReceiveDataSaub)
.catch(error => console.error(error))
.catch(error => {
notify(`Не удалось загрузить данные по скважине (${id}) c ${rangeDate[0]} по ${rangeDate[1]}`, 'error')
console.error(error)
})
}, [id, rangeDate]);
let charts = null
@ -128,6 +132,7 @@ export default function Archive() {
<ConfigProvider locale={locale}>
<RangePicker
showTime
allowClear={false}
onChange = {onChangeRange}
value = {rangeDate}
/>

View File

@ -6,7 +6,7 @@ import WellTreeSelector from '../components/WellTreeSelector'
const { Header } = Layout
export default function PageHeader(props){
export default function PageHeader({title='Мониторинг', wellsList}){
const login = localStorage['login']
let handleLogout = () => {
@ -14,19 +14,18 @@ export default function PageHeader(props){
localStorage.removeItem('token')
}
return(
<Layout>
<Header className="header">
<img src={logo} alt="АСБ" className="logo"/>
<WellTreeSelector />
<h1 className="title">Мониторинг</h1>
<Link to="/login" onClick={handleLogout}>
<Button icon={<UserOutlined/>}>
({login}) Выход
</Button>
</Link>
</Header>
</Layout>
)
};
return(
<Layout>
<Header className="header">
<img src={logo} alt="АСБ" className="logo"/>
<WellTreeSelector wellsList={wellsList}/>
<h1 className="title">{title}</h1>
<Link to="/login" onClick={handleLogout}>
<Button icon={<UserOutlined/>}>
({login}) Выход
</Button>
</Link>
</Header>
</Layout>
)
};

View File

@ -0,0 +1,39 @@
import {Layout} from 'antd'
import PageHeader from './Header'
// import { useState, useEffect, createContext} from 'react'
// import { useParams } from 'react-router-dom'
const {Content} = Layout
export default function LayoutPortal({title, children}) {
// const [wells, setWells] = useState([])
// let { id } = useParams();
// let updateWellsList = async () => {
// setLoader(true)
// try {
// let newWells = (await WellService.getWells()).map(w => { return { key: w.id, ...w } })
// setWells(newWells)
// }
// catch (e) {
// console.error(`${e.message}`);
// }
// setLoader(false)
// }
// useEffect(() => {
// updateWellsList()
// }, [])
// const WellsContext = createContext({wells});
return (
<Content>
<PageHeader title={title}/>
<Layout>
<Content className="site-layout-background sheet">
{children}
</Content>
</Layout>
</Content>)
}

View File

@ -1,31 +1,25 @@
import {Layout} from 'antd'
import Wells from './Wells'
import PageHeader from './Header'
import Well from "../components/Well";
import LayoutPortal from './LayoutPortal'
import {Redirect, Route, Switch} from "react-router-dom";
const {Content} = Layout
export default function Main() {
return (
<Content>
<PageHeader/>
<Layout>
<Content className="site-layout-background sheet">
<Switch>
<Route path="/well/:id/">
<Well/>
</Route>
<Route path="/well">
<Wells/>
</Route>
<Route path="/">
<Redirect to={{pathname: `/well`}}/>
</Route>
</Switch>
</Content>
</Layout>
</Content>
<Switch>
<Route path="/well/:id/">
<LayoutPortal>
<Well/>
</LayoutPortal>
</Route>
<Route path="/well">
<LayoutPortal>
<Wells/>
</LayoutPortal>
</Route>
<Route path="/">
<Redirect to={{pathname: `/well`}}/>
</Route>
</Switch>
)
}

View File

@ -12,7 +12,7 @@ import moment from 'moment'
import {Subscribe} from '../services/signalr'
import {DataService, MessageService} from '../services/api'
import '../styles/message.css'
import Notification from "../components/Notification";
import notify from "../components/notify";
const {Option} = Select
@ -207,14 +207,14 @@ export default function TelemetryView(props) {
DataService.getData(id)
.then(handleReceiveDataSaub)
.catch((ex) => {
Notification(`Не удалось загрузить данные по скважине "${id}"`, 'error')
notify(`Не удалось загрузить данные по скважине "${id}"`, 'error')
console.log(ex)
})
MessageService.getMessage(id)
.then(handleReceiveMessages)
.catch((ex) => {
Notification(`Не удалось загрузить сообщения по скважине "${id}"`, 'error')
notify(`Не удалось загрузить сообщения по скважине "${id}"`, 'error')
console.log(ex)
})

View File

@ -3,6 +3,7 @@ import { WellService } from '../services/api'
import Loader from '../components/Loader'
import { Table } from 'antd' // TreeSelect
import { useHistory } from 'react-router-dom'
import notify from '../components/notify'
const columns = [
{
@ -36,16 +37,17 @@ export default function Wells(props){
setLoader(true)
try{
let newWells = (await WellService.getWells()).map(w =>{return {key:w.id, ...w}})
console.log(Wells.wellsTree)
console.log(newWells)
setWells( newWells )
}
catch(e){
console.error(`${e.message}`);
notify('Не удалось загрузить список скважин', 'error')
console.error(`${e}`);
}
setLoader(false)
}
useEffect(()=>updateWellsList())
useEffect(()=>updateWellsList(), [])
return(<>
<h2>Скважины</h2>

View File

@ -61,7 +61,7 @@ html {
.sheet{
padding: 5px 24px;
min-height: 280px;
margin: 0 15px;
margin: 0 15px 15px 15px;
}
.site-layout-background {
@ -145,3 +145,18 @@ tr.table_row_size {
width: 50%;
margin-right: 5px;
}
.header-tree-select *{
color: #fff;
font-size: '1.5rem';
}
.header-tree-select{
border: 1px solid rgba(255, 255, 255, 0.2);
background-color: rgba(0, 0, 0, 0.3);
}
.header-tree-select:hover{
border: 1px solid rgba(255, 255, 255, 0.8);
background-color: rgba(0, 0, 0, 0.5);
}

View File

@ -1,3 +0,0 @@
.content-sheet {
display: inline-flex;
}