forked from ddrilling/asb_cloud_front
* Выделен стиль блока фильтрации
* Исправлена работа страница "Наработка"
This commit is contained in:
parent
39c1289d32
commit
7f2d337b4f
@ -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
|
||||
|
128
src/pages/Telemetry/OperationTime/index.jsx
Normal file
128
src/pages/Telemetry/OperationTime/index.jsx
Normal file
@ -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) => (
|
||||
<div className={'table_color'} style={{ 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 (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<div className={'filter-group'}>
|
||||
<h3 className={'head'}>Фильтр подсистем</h3>
|
||||
<div className={'body'}>
|
||||
<Select
|
||||
allowClear
|
||||
mode={'multiple'}
|
||||
options={typeOptions}
|
||||
className={'filter-selector'}
|
||||
onChange={setSelected}
|
||||
value={selected}
|
||||
/>
|
||||
<DateRangeWrapper
|
||||
onCalendarChange={setDateRange}
|
||||
disabledDate={disabledDates}
|
||||
disabledTime={disabledTimes}
|
||||
value={dateRange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<D3HorizontalPercentChart
|
||||
data={chartData}
|
||||
colors={subsystemColors}
|
||||
width={'100%'}
|
||||
height={'50vh'}
|
||||
/>
|
||||
<Table
|
||||
bordered
|
||||
size={'small'}
|
||||
columns={tableColumns}
|
||||
dataSource={selectedData}
|
||||
scroll={{ y: '25vh', x: true }}
|
||||
pagination={false}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
})
|
||||
|
||||
export default wrapPrivateComponent(OperationTime, {
|
||||
requirements: [], // SubsystemOperationTime.get
|
||||
title: 'Наработка',
|
||||
route: 'operation_time',
|
||||
key: 'operation_time',
|
||||
})
|
@ -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) => (
|
||||
<div style={{ backgroundColor: color, padding: '5px 0' }} />
|
||||
) }),
|
||||
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<DataType[]>([])
|
||||
const [dateRange, setDateRange] = useState<Moment[]>([])
|
||||
const [childrenData, setChildrenData] = useState<ReactNode[]>([])
|
||||
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) => (
|
||||
<Option key={item.subsystemName}>
|
||||
{item.subsystemName}
|
||||
</Option>
|
||||
)))
|
||||
}, [data])
|
||||
|
||||
const selectChange = (value: string[]) => {
|
||||
|
||||
setData(data.reduce((previousValue: DataType[], currentValue) => {
|
||||
if (value.includes(currentValue.subsystemName)) {
|
||||
previousValue.push(currentValue)
|
||||
}
|
||||
return previousValue
|
||||
}, []))
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<h3 className={'filter-group-heading'}>Фильтр подсистем</h3>
|
||||
<Row align={'middle'} justify={'space-between'} wrap={false} style={{ backgroundColor: 'white' }}>
|
||||
<Col span={18}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
defaultValue={data.map(d => d.subsystemName)}
|
||||
onChange={selectChange}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{childrenData}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<DateRangeWrapper
|
||||
onCalendarChange={(dateRange: any) => setDateRange(dateRange)}
|
||||
value={[dateRange[0], dateRange[1]]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<div style={{width: '100%', height: '50vh'}}>
|
||||
<D3HorizontalChart
|
||||
data={data.map(item => ({name: item.subsystemName, percent: item.kUsage * 100}))}
|
||||
colors={subsystemColors}
|
||||
width={'100%'}
|
||||
height={'50vh'}
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
size={'small'}
|
||||
columns={tableColumns}
|
||||
dataSource={data.map((d, i) => ({...d, color: subsystemColors[i]}))}
|
||||
scroll={{ y: '25vh', x: true }}
|
||||
pagination={false}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
||||
export default wrapPrivateComponent(OperationTime, {
|
||||
requirements: [],
|
||||
title: 'Наработка',
|
||||
route: 'operation_time',
|
||||
})
|
@ -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) {
|
||||
|
22
src/styles/filter.less
Normal file
22
src/styles/filter.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
3
src/styles/operation_time.less
Normal file
3
src/styles/operation_time.less
Normal file
@ -0,0 +1,3 @@
|
||||
.table_color {
|
||||
padding: 5px 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user