diff --git a/public/index.html b/public/index.html index 9b38967..5ab8445 100644 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,6 @@ name="description" content="Web site created using create-react-app" /> - АСБ Vision diff --git a/src/App.js b/src/App.js index 5f1893d..1f74ae1 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,4 @@ import './styles/App.less' -import React /*, { useContext, createContext, useState }*/ from "react" import { BrowserRouter as Router, Switch, diff --git a/src/components/LoaderPortal.jsx b/src/components/LoaderPortal.jsx new file mode 100644 index 0000000..c84fc6c --- /dev/null +++ b/src/components/LoaderPortal.jsx @@ -0,0 +1,12 @@ +import Loader from './Loader' + +export default function LoaderPortal({show, fade=true, children}){ + return( +
+
+ {children} +
+ {show && fade &&
} + {show &&
} +
) +} \ No newline at end of file diff --git a/src/components/Well.jsx b/src/components/Well.jsx index ee45576..64e6ecc 100644 --- a/src/components/Well.jsx +++ b/src/components/Well.jsx @@ -17,7 +17,7 @@ export default function Well() { }> diff --git a/src/components/charts/ChartTimeBase.tsx b/src/components/charts/ChartTimeBase.tsx index 5412b15..e56104e 100644 --- a/src/components/charts/ChartTimeBase.tsx +++ b/src/components/charts/ChartTimeBase.tsx @@ -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 | 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) - return 'day' + if(intervalSec <= 32*60*60) + return 'minute' - if(intervalSec < 365*24*60*60) + if(intervalSec <= 32*12*60*60) + return 'hour' + + if(intervalSec <= 32*24*60*60) + return 'day' + + 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 = ({options, dataParams}) => { const chartRef = useRef(null) const [chart, setChart] = useState() @@ -130,18 +191,19 @@ export const ChartTimeBase: React.FC = ({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) + 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 } } diff --git a/src/pages/Archive.jsx b/src/pages/Archive.jsx index d3497e3..6c5d4f0 100644 --- a/src/pages/Archive.jsx +++ b/src/pages/Archive.jsx @@ -13,6 +13,7 @@ import {generateUUID} from '../services/UidGenerator' import { ArchiveColumn } from '../components/ArchiveColumn' import moment from 'moment' import notify from '../components/notify' +import LoaderPortal from '../components/LoaderPortal' const { RangePicker } = DatePicker; @@ -31,7 +32,8 @@ export default function Archive() { const [saubData, setSaubData] = useState([]) const [chartsCfgs, setChartsCfgs] = useState([]) const [rangeDate, setRangeDate] = useState([moment().subtract(3,'hours'), moment()]) - const [geometry, setGeometry] = useState({ratioRest:1, ratio1st:1, wRest:.5, w1st:.5}) + const [geometry, setGeometry] = useState({ratioRest:1, ratio1st:1, wRest:.5, w1st:.5}) + const [loader, setLoader] = useState(false) const chartsCfgsKey = 'chartsCfgs' const chartsContainerRef = useRef(); @@ -95,12 +97,14 @@ export default function Archive() { let interval = (rangeDate[1] - rangeDate[0]) / 1000 let startDate = rangeDate[0].toISOString() + setLoader(true) DataService.getData(id, startDate, interval, 2048) .then(handleReceiveDataSaub) .catch(error => { notify(`Не удалось загрузить данные по скважине (${id}) c ${rangeDate[0]} по ${rangeDate[1]}`, 'error') console.error(error) }) + .finally(()=>setLoader(false)) }, [id, rangeDate]); let charts = null @@ -137,8 +141,10 @@ export default function Archive() { value = {rangeDate} /> - - {charts} - + + + {charts} + + ) } \ No newline at end of file diff --git a/src/pages/LayoutPortal.jsx b/src/pages/LayoutPortal.jsx index eb3c37e..b1ff72e 100644 --- a/src/pages/LayoutPortal.jsx +++ b/src/pages/LayoutPortal.jsx @@ -1,32 +1,9 @@ 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 ( diff --git a/src/pages/Messages.jsx b/src/pages/Messages.jsx index 7fcb2fa..0b410c6 100644 --- a/src/pages/Messages.jsx +++ b/src/pages/Messages.jsx @@ -1,14 +1,16 @@ -import {Button, Table, Select} from 'antd'; +import {Table, Select, DatePicker, ConfigProvider} from 'antd'; import {MessageService} from '../services/api' import {useState, useEffect} from 'react' import {useParams} from 'react-router-dom' -import {Subscribe} from '../services/signalr' -import Loader from '../components/Loader' -import moment from 'moment' +import LoaderPortal from '../components/LoaderPortal' import '../styles/message.css' import notify from '../components/notify' +import locale from "antd/lib/locale/ru_RU"; +import moment from 'moment' const {Option} = Select +const pageSize = 26 +const {RangePicker} = DatePicker; // Словарь категорий для строк таблицы const categoryDictionary = { @@ -17,7 +19,6 @@ const categoryDictionary = { 3: {title: 'Информация'}, } -// Конфигурация таблицы const columns = [ { title: 'Дата', @@ -31,11 +32,7 @@ const columns = [ dataIndex: 'categoryId', render: (_, item) => categoryDictionary[item.categoryId].title, style: (_, item) => categoryDictionary[item.categoryId].style, - filters: [ - {text: 'Авария', value: '1'}, - {text: 'Предупреждение', value: '2'}, - {text: 'Информация', value: '3'}, - ] + ellipsis: true, }, { title: 'Сообщение', @@ -49,76 +46,103 @@ const columns = [ }, ]; -// Опции для фильра +const filterOptions = [ + {label: 'Авария', value: 1}, + {label: 'Предупреждение', value: 2}, + {label: 'Информация', value: 3}, +] // Данные для таблицы export default function Messages() { let {id} = useParams() const [messages, setMessages] = useState([]) - const [loader] = useState(false) + const [pagination, setPagination] = useState(null) + const [page, setPage] = useState(1) + const [range, setRange] = useState([]) + const [categories, setCategories] = useState([]) - const filterOptions = [ - {label: 'Авария', value: 1}, - {label: 'Предупреждение', value: 2}, - {label: 'Информация', value: 3}, - ] + const [loader, setLoader] = useState(false) - const children = filterOptions.map((line) => ()) + const children = filterOptions.map((line) => ) - const handleReceiveMessages = (messages) => { - if (messages) { - setMessages(messages.items) - } + const onChangeRange = (range) => { + setRange(range) } - + useEffect(() => { - MessageService.getMessage(id) - .then(handleReceiveMessages) - .catch((ex) => { + const GetMessages = async () => { + setLoader(true) + try { + let begin = null + let end = null + if (range?.length > 1) { + begin = range[0].toISOString() + end = range[1].toISOString() + } + let paginatedMessages = await MessageService.getMessage(`${id}`, + (page - 1) * pageSize, + pageSize, + categories, + begin, + end) + setMessages(paginatedMessages.items.map(m => { + return { + key: m.id, + categoryids: categoryDictionary[m.categoryId], + begin: m.date, + ...m + } + })) + setPagination({ + total: paginatedMessages.count, + current: Math.floor(paginatedMessages.skip / pageSize), + }) + } catch (ex) { notify(`Не удалось загрузить сообщения по скважине "${id}"`, 'error') console.log(ex) - }) - - let unSubscribeMessagesHub = Subscribe('ReceiveMessages', `well_${id}`, handleReceiveMessages) - return () => { - unSubscribeMessagesHub() + } + setLoader(false) } - }, [id]) + GetMessages() + }, [id, page, categories, range]) return ( <> -

Сообщения

-
-

Фильтр сообщений

- - - - `event_message_${record.categoryId} event_message`} - size={'small'} - pagination={{pageSize: 26}} - rowKey={(record) => record.id} - /> - {loader && } + +
+

Фильтр сообщений

+ + + + +
+ +
`event_message_${record.categoryId} event_message`} + size={'small'} + pagination={{ + pageSize: pageSize, + showSizeChanger: false, + total: pagination?.total, + current: page, + onChange: (page) => setPage(page) + }} + rowKey={(record) => record.id} + /> + ) } \ No newline at end of file diff --git a/src/pages/TelemetryView.jsx b/src/pages/TelemetryView.jsx index 9bdf2e6..63ed45f 100644 --- a/src/pages/TelemetryView.jsx +++ b/src/pages/TelemetryView.jsx @@ -2,7 +2,7 @@ import {useState, useEffect} from 'react' import {useParams} from 'react-router-dom' import {Row, Col, Select, Table} from 'antd' import {ChartTimeOnline} from '../components/charts/ChartTimeOnline' -import Loader from '../components/Loader' +import LoaderPortal from '../components/LoaderPortal' import {ChartTimeOnlineFooter} from '../components/ChartTimeOnlineFooter' import {CustomColumn} from '../components/CustomColumn' import {UserOfWells} from '../components/UserOfWells' @@ -189,7 +189,7 @@ export default function TelemetryView(props) { const [chartInterval, setChartInterval] = useState(600) const [messages, setMessages] = useState([]) - const [loader] = useState(false) // , setLoader + const [loader, setLoader] = useState(false) // , setLoader const handleReceiveDataSaub = (data) => { if (data) { @@ -204,20 +204,23 @@ export default function TelemetryView(props) { } useEffect(() => { - DataService.getData(id) + setLoader(true) + let promiseData = DataService.getData(id) .then(handleReceiveDataSaub) .catch((ex) => { notify(`Не удалось загрузить данные по скважине "${id}"`, 'error') console.log(ex) }) - MessageService.getMessage(id) + let promiseMessages = MessageService.getMessage(id) .then(handleReceiveMessages) .catch((ex) => { notify(`Не удалось загрузить сообщения по скважине "${id}"`, 'error') console.log(ex) }) + Promise.all([promiseData, promiseMessages]).then(()=>setLoader(false)) + let unSubscribeDataSaubHub = Subscribe('ReceiveDataSaub', `well_${id}`, handleReceiveDataSaub) let unSubscribeMessagesHub = Subscribe('ReceiveMessages', `well_${id}`, handleReceiveMessages) return () => { @@ -227,14 +230,16 @@ export default function TelemetryView(props) { }, [id]); useEffect(() => { + setLoader(true) DataService.getData(id, null, chartInterval) .then(handleReceiveDataSaub) .catch(error => console.error(error)) + .finally(()=>setLoader(false)) }, [id, chartInterval]); const colSpan = 24 / (paramsGroups.length) - return (
+ return (
@@ -278,6 +283,5 @@ export default function TelemetryView(props) { pagination={false} rowKey={(record) => record.id} /> - {loader && } - ) + ) } \ No newline at end of file diff --git a/src/pages/Wells.jsx b/src/pages/Wells.jsx index 0f5b97e..4bf4e9a 100644 --- a/src/pages/Wells.jsx +++ b/src/pages/Wells.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { WellService } from '../services/api' -import Loader from '../components/Loader' +import LoaderPortal from '../components/LoaderPortal' import { Table } from 'antd' // TreeSelect import { useHistory } from 'react-router-dom' import notify from '../components/notify' @@ -37,21 +37,22 @@ 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){ notify('Не удалось загрузить список скважин', 'error') - console.error(`${e.message}`); + console.error(`${e}`); } setLoader(false) } - useEffect(()=>updateWellsList()) + useEffect(()=>updateWellsList(), []) return(<>

Скважины

-
+
{ @@ -59,6 +60,6 @@ export default function Wells(props){ onClick: event => {history.push(`/well/${record.id}/`)}, }; }}/> - {loader&&} + ) } \ No newline at end of file diff --git a/src/styles/loader.css b/src/styles/loader.css index fece98b..e8b0844 100644 --- a/src/styles/loader.css +++ b/src/styles/loader.css @@ -31,4 +31,39 @@ height: 72px; opacity: 0; } -} \ No newline at end of file +} + +.loader-container{ + display: grid; + grid-template-columns: 1fr 1fr 1fr; +} + +.loader-content{ + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; +} + +.loader-overlay{ + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; + place-self: center; + align-self: center; + justify-self: center; + z-index: 1; + height: 80px; + width: 80px; + border-radius: 40px; +} + +.loader-fade{ + grid-column-start: 1; + grid-column-end: span 3; + grid-row-start: 1; + background-color: rgba(255, 255, 255, 0.4); + align-self: stretch; + justify-self: stretch; + z-index: 1; + box-shadow: 0px 0px 6px 5px rgba(255, 254, 254, 0.4); +} diff --git a/src/styles/message.css b/src/styles/message.css index 329182b..fb003b0 100644 --- a/src/styles/message.css +++ b/src/styles/message.css @@ -1,11 +1,3 @@ -/*.ant-table.ant-table-small .ant-table-tbody > tr > td {*/ -/* padding: 0;*/ -/*}*/ - -/*.ant-table-tbody > tr > td {*/ -/* border-bottom: 0.5px;*/ -/*}*/ - .event_message > td { font-size: 14px; padding: 1px !important; @@ -37,6 +29,15 @@ background: #505060; } +.filter-group { + margin: 0 0 5px 0; +} + +.filter-group__heading { + margin: 5px auto; + align-items: center; +} + td.ant-table-column-sort { color: black; background-color: rgb(221, 247, 221);