diff --git a/src/pages/Telemetry/Messages.jsx b/src/pages/Telemetry/Messages.jsx
index 824e38d..bc845d4 100755
--- a/src/pages/Telemetry/Messages.jsx
+++ b/src/pages/Telemetry/Messages.jsx
@@ -11,6 +11,7 @@ import { makeColumn, makeDateColumn, makeNumericColumn, makeNumericSorter, makeT
import { wrapPrivateComponent } from '@utils'
import { MessageService } from '@api'
+import '@styles/filter.less'
import '@styles/message.less'
const pageSize = 26
diff --git a/src/pages/Telemetry/OperationTime/index.jsx b/src/pages/Telemetry/OperationTime/index.jsx
new file mode 100644
index 0000000..9351aae
--- /dev/null
+++ b/src/pages/Telemetry/OperationTime/index.jsx
@@ -0,0 +1,128 @@
+import { memo, useEffect, useMemo, useState } from 'react'
+import { Select } from 'antd'
+import moment from 'moment'
+
+import { useWell } from '@asb/context'
+import LoaderPortal from '@components/LoaderPortal'
+import { invokeWebApiWrapperAsync } from '@components/factory'
+import D3HorizontalPercentChart from '@components/d3/D3HorizontalPercentChart'
+import { DateRangeWrapper, makeColumn, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table'
+import { arrayOrDefault, range, wrapPrivateComponent } from '@utils'
+import { SubsystemOperationTimeService } from '@api'
+
+import '@styles/filter.less'
+import '@styles/operation_time.less'
+
+const subsystemColors = [
+ '#1abc9c', '#16a085', '#2ecc71', '#27ae60',
+ '#3498db', '#2980b9', '#9b59b6', '#8e44ad',
+ '#34495e', '#2c3e50', '#f1c40f', '#f39c12',
+ '#e67e22', '#d35400', '#e74c3c', '#c0392b',
+ '#ecf0f1', '#bdc3c7', '#95a5a6', '#7f8c8d',
+]
+
+const tableColumns = [
+ makeColumn('Цвет', 'color', { width: 50, render: (backgroundColor) => (
+
+ )}),
+ makeTextColumn('Подсистема', 'subsystemName'),
+ makeNumericColumn('Использование, %', 'kUsage', undefined, undefined, val => (+val * 100).toFixed(2)),
+ makeNumericColumn('Проходка, м', 'sumDepthInterval'),
+ makeNumericColumn('Время работы, ч', 'usedTimeHours'),
+ makeNumericColumn('Кол-во запусков', 'operationCount', undefined, undefined, makeNumericRender(0)),
+]
+
+// Выбор доступен только до текущей даты
+const disabledDates = (current) => current && moment(current).isAfter(moment(), 'day', '[]')
+// Выбор доступен только до текущего времени
+const disabledTimes = (date) => ({
+ disabledHours: () => range(24).filter(h => date && moment(date).hours(h).isAfter(moment(), 'hour', '[]')),
+ disabledMinutes: () => range(60).filter(m => date && moment(date).minutes(m).isAfter(moment(), 'minute', '[]')),
+ disabledSeconds: () => range(60).filter(s => date && moment(date).seconds(s).isBetween(moment(), 'second', '[]'))
+})
+
+export const OperationTime = memo(() => {
+ const [showLoader, setShowLoader] = useState(false)
+ const [data, setData] = useState([])
+ const [selected, setSelected] = useState([])
+ const [dateRange, setDateRange] = useState([])
+ const [well] = useWell()
+
+ // Создаём массив пунктов для селектора подсистем
+ const typeOptions = useMemo(() => data.map((d) => ({ label: d.subsystemName, value: d.idSubsystem })), [data])
+ // Фильтруем данные по выбранным подсистемам
+ const selectedData = useMemo(() => data.filter((d) => selected.includes(d.idSubsystem)), [selected, data])
+ // Подготавливаем данные для отображения на графике
+ const chartData = useMemo(() => selectedData.map((d) => ({
+ name: d.subsystemName,
+ percent: d.kUsage * 100,
+ color: d.color,
+ })), [selectedData])
+
+ useEffect(() => {
+ invokeWebApiWrapperAsync(
+ async () => {
+ if (!well.id) return
+
+ // Ограничение задаётся только если выбраны обе даты
+ const startDate = dateRange[1] ? dateRange[0]?.toISOString() : undefined
+ const endDate = dateRange[1]?.toISOString()
+
+ const data = await SubsystemOperationTimeService.getStat(well.id, undefined, startDate, endDate)
+ // Выбираем цвета для подсистем (если цветов не хватает начинаем сначала)
+ const coloredData = arrayOrDefault(data).map((d, i) => ({ ...d, color: subsystemColors[i % subsystemColors.length] }))
+
+ setData(coloredData)
+ setSelected(data.map((d) => d.idSubsystem)) // По-умолчанию выбираем все подсистемы
+ },
+ setShowLoader,
+ `Не удалось загрузить данные`,
+ { actionName: 'Получение данных по скважине', well }
+ )
+ }, [dateRange, well])
+
+ return (
+
+
+
Фильтр подсистем
+
+
+
+
+
+
+
+
+ )
+})
+
+export default wrapPrivateComponent(OperationTime, {
+ requirements: [], // SubsystemOperationTime.get
+ title: 'Наработка',
+ route: 'operation_time',
+ key: 'operation_time',
+})
diff --git a/src/pages/Telemetry/OperationTime/index.tsx b/src/pages/Telemetry/OperationTime/index.tsx
deleted file mode 100644
index d442d29..0000000
--- a/src/pages/Telemetry/OperationTime/index.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import React, { ReactNode, useEffect, useState } from 'react'
-import { Col, Row, Select } from 'antd'
-import { Moment } from 'moment'
-
-import { DateRangeWrapper, makeColumn, makeNumericRender, Table } from '@components/Table'
-import LoaderPortal from '@components/LoaderPortal'
-import { arrayOrDefault, wrapPrivateComponent } from '@utils'
-import D3HorizontalChart from '@components/d3/D3HorizontalChart'
-import { useWell } from '@asb/context'
-import { invokeWebApiWrapperAsync } from '@components/factory'
-import { SubsystemOperationTimeService } from '@api'
-
-const { Option } = Select;
-
-type DataType = {
- idSubsystem: number
- subsystemName: string
- usedTimeHours: number
- kUsage: number
- sumDepthInterval: number
- operationCount: number
-}
-
-const subsystemColors = [
- '#1abc9c',
- '#16a085',
- '#2ecc71',
- '#27ae60',
- '#3498db',
- '#2980b9',
- '#9b59b6',
- '#8e44ad',
- '#34495e',
- '#2c3e50',
- '#f1c40f',
- '#f39c12',
- '#e67e22',
- '#d35400',
- '#e74c3c',
- '#c0392b',
- '#ecf0f1',
- '#bdc3c7',
- '#95a5a6',
- '#7f8c8d',
-]
-
-const tableColumns = [
- makeColumn('Цвет', 'color', { width: 50, render: (color) => (
-
- ) }),
- makeColumn('Подсистема', 'subsystemName'),
- makeColumn('% использования', 'kUsage', { render: val => (+val * 100).toFixed(2) }),
- makeColumn('Проходка, м', 'sumDepthInterval', {render: makeNumericRender(2)}),
- makeColumn('Время работы, ч', 'usedTimeHours', {render: makeNumericRender(2)}),
- makeColumn('Кол-во запусков', 'operationCount'),
-]
-
-const OperationTime = () => {
- const [showLoader, setShowLoader] = useState(false)
- const [data, setData] = useState([])
- const [dateRange, setDateRange] = useState([])
- const [childrenData, setChildrenData] = useState([])
- const [well] = useWell()
-
- const errorNotifyText = `Не удалось загрузить данные`
-
- useEffect(() => {
-
- invokeWebApiWrapperAsync(
- async () => {
- if (!well.id) return
- try {
- setData(arrayOrDefault(await SubsystemOperationTimeService.getStat(
- well.id,
- undefined,
- dateRange[1] ? dateRange[0]?.toISOString() : undefined,
- dateRange[1]?.toISOString(),
- )))
- } catch(e) {
- setData([])
- throw e
- }
- },
- setShowLoader,
- errorNotifyText,
- { actionName: 'Получение данных по скважине', well }
- )
- }, [dateRange])
-
- useEffect(() => {
- setChildrenData(data.map((item) => (
-
- )))
- }, [data])
-
- const selectChange = (value: string[]) => {
-
- setData(data.reduce((previousValue: DataType[], currentValue) => {
- if (value.includes(currentValue.subsystemName)) {
- previousValue.push(currentValue)
- }
- return previousValue
- }, []))
-
- }
-
- return (
-
- Фильтр подсистем
-
-
-
-
-
- setDateRange(dateRange)}
- value={[dateRange[0], dateRange[1]]}
- />
-
-
-
- ({name: item.subsystemName, percent: item.kUsage * 100}))}
- colors={subsystemColors}
- width={'100%'}
- height={'50vh'}
- />
-
- ({...d, color: subsystemColors[i]}))}
- scroll={{ y: '25vh', x: true }}
- pagination={false}
- />
-
- )
-}
-
-export default wrapPrivateComponent(OperationTime, {
- requirements: [],
- title: 'Наработка',
- route: 'operation_time',
-})
\ No newline at end of file
diff --git a/src/styles/d3.less b/src/styles/d3.less
index 2e48ca4..90db40d 100644
--- a/src/styles/d3.less
+++ b/src/styles/d3.less
@@ -116,9 +116,9 @@
}
}
-.grid-line line {
+.grid-line:not(:first-of-type):not(:last-of-type) line {
stroke: #ddd;
- stroke-dasharray: 4
+ stroke-dasharray: 4;
}
@media (max-width: 1800px) {
diff --git a/src/styles/filter.less b/src/styles/filter.less
new file mode 100644
index 0000000..16618cc
--- /dev/null
+++ b/src/styles/filter.less
@@ -0,0 +1,22 @@
+.filter-group {
+ margin: 0 0 5px 0;
+
+ & .head {
+ margin: 5px auto;
+ align-items: center;
+ }
+
+ & .body {
+ width: 100%;
+ display: flex;
+ gap: 5px;
+
+ & .type-filter {
+ width: 25%;
+ }
+
+ & .filter-selector {
+ flex: 1;
+ }
+ }
+}
diff --git a/src/styles/message.less b/src/styles/message.less
index 513d06f..be78867 100644
--- a/src/styles/message.less
+++ b/src/styles/message.less
@@ -39,29 +39,6 @@
background: #505060;
}
-.filter-group {
- margin: 0 0 5px 0;
-
- & .head {
- margin: 5px auto;
- align-items: center;
- }
-
- & .body {
- width: 100%;
- display: flex;
- gap: 5px;
-
- & .type-filter {
- width: 25%;
- }
-
- & .filter-selector {
- flex: 1;
- }
- }
-}
-
td.ant-table-column-sort {
color: black;
background-color: #fafafa;
diff --git a/src/styles/operation_time.less b/src/styles/operation_time.less
new file mode 100644
index 0000000..d35e54f
--- /dev/null
+++ b/src/styles/operation_time.less
@@ -0,0 +1,3 @@
+.table_color {
+ padding: 5px 0;
+}