Compare commits

...

1 Commits

4 changed files with 147 additions and 1 deletions

View File

@ -0,0 +1,86 @@
import { createContext, ReactNode, useCallback, useContext, useMemo, useState } from 'react'
import { CloseOutlined } from '@ant-design/icons'
import { Button } from 'antd'
import { FunctionalValue, getFunctionalValue, isDev } from '@utils'
import '@styles/components/live_log.less'
export type LogType = 'info' | 'warn' | 'error' | 'critical'
export type VarType = { value: any, type: LogType }
export type LogContext = (varName: string, value: FunctionalValue<(prev?: any) => any>, type?: FunctionalValue<(prev: LogType) => LogType>) => void
export const LiveLogContext = createContext<LogContext>(() => {})
const renderVar = (name: string, value: any, type: LogType, remove: (name: string) => void) => {
if (typeof value === 'object')
value = JSON.stringify(value)
return (
<div className={`log-row log-${type}`}>
<span className={'log-close'} title={'Удалить значение'} onClick={() => remove(name)}><CloseOutlined /></span>
<span className={'log-name'}>{name}:</span>
<span className={'log-value'}>{String(value)}</span>
</div>
)
}
export type LiveLogProps = {
placement?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
children: JSX.Element
}
export const LiveLog = ({ placement = 'top-left', children }: LiveLogProps) => {
if (!isDev()) return children
const [logMsgs, setLogMsgs] = useState<Record<string, VarType | undefined>>({})
const clear = useCallback(() => setLogMsgs({}), [])
const removeVar = useCallback((name: string) => {
setLogMsgs((prev) => {
if (!(name in prev)) return prev
const newMsgs = { ...prev }
delete newMsgs[name]
return newMsgs
})
}, [])
const log: LogContext = useCallback((varName, value, type) => {
setLogMsgs((prev) => {
const newValue = getFunctionalValue(value)(prev[varName]?.value)
const newType = getFunctionalValue(type)(prev[varName]?.type)
return {
...prev,
[varName]: {
type: newType || 'info',
value: newValue,
}
}
})
}, [])
const logInner = useMemo(() => Object.entries(logMsgs).map(([name, value]) => value && renderVar(name, value.value, value.type, removeVar)), [logMsgs, removeVar])
const isEmpty = useMemo(() => Object.keys(logMsgs).length <= 0, [logMsgs])
return (
<LiveLogContext.Provider value={log}>
{isEmpty || (
<div className={`log-box log-box-${placement}`}>
<div className={'log-box-label'}>
<span className={'log-box-label'}>Непрерывные значения</span>
<Button type={'link'} title={'Очистить'} onClick={clear}><CloseOutlined /></Button>
</div>
<div className={'log-box-content'}>
{logInner}
</div>
</div>
)}
{children}
</LiveLogContext.Provider>
)
}
export const useLiveLog = () => useContext(LiveLogContext)
export default LiveLog

View File

@ -3,6 +3,7 @@ import { ConfigProvider } from 'antd'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import React from 'react' import React from 'react'
import LiveLog from '@components/LiveLog'
import { getUser } from '@utils' import { getUser } from '@utils'
import { OpenAPI } from '@api' import { OpenAPI } from '@api'
@ -22,7 +23,9 @@ const root = createRoot(container)
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<ConfigProvider locale={locale}> <ConfigProvider locale={locale}>
<LiveLog>
<App /> <App />
</LiveLog>
</ConfigProvider> </ConfigProvider>
</React.StrictMode> </React.StrictMode>
) )

View File

@ -33,6 +33,7 @@ import SpinPicDisabled from '@images/SpinDisabled.png'
import '@styles/pages/telemetry_view.less' import '@styles/pages/telemetry_view.less'
import '@styles/pages/message.less' import '@styles/pages/message.less'
import { useLiveLog } from '@asb/components/LiveLog'
const { Option } = Select const { Option } = Select
@ -90,6 +91,8 @@ const TelemetryView = memo(() => {
const [archiveMode, setArchiveMode] = useState(false) const [archiveMode, setArchiveMode] = useState(false)
const log = useLiveLog()
const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well]) const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well])
const handleDataSaub = useCallback((data, replace = false) => { const handleDataSaub = useCallback((data, replace = false) => {
@ -116,6 +119,7 @@ const TelemetryView = memo(() => {
}, []) }, [])
const onWheel = useCallback((e) => { const onWheel = useCallback((e) => {
log('Wheel count', (prev) => prev ? prev + 1 : 1, 'warn')
if (!archiveMode && e.deltaY < 0) { if (!archiveMode && e.deltaY < 0) {
setArchiveMode(true) setArchiveMode(true)
} else if (archiveMode) { } else if (archiveMode) {

View File

@ -0,0 +1,53 @@
.log-box {
position: fixed;
z-index: 1000;
background-color: rgba(0,0,0,.5);
border-radius: 5px;
padding: 10px;
color: white;
display: flex;
flex-direction: column;
align-items: stretch;
overflow-x: hidden;
width: 300px;
&.log-box-top-left { top: 10px; left: 10px; }
&.log-box-top-right { top: 10px; right: 10px; }
&.log-box-bottom-left { bottom: 10px; left: 10px; }
&.log-box-bottom-right { bottom: 10px; right: 10px; }
& .log-box-label {
display: flex;
justify-content: space-between;
& span {
font-size: 1.25em;
}
}
& .log-box-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 5px;
& .log-row {
display: flex;
gap: 10px;
& .log-close {
cursor: pointer;
}
& .log-name {
text-decoration: underline;
}
&.log-info { color: white }
&.log-warn { color: #faad14 }
&.log-error { color: #ff4d4f }
&.log-critical { color: #ff4d4f; font-weight: bolder; }
}
}
}