diff --git a/web/src/assets/elasticsearch.svg b/web/src/assets/elasticsearch.svg new file mode 100644 index 00000000..b95507cd --- /dev/null +++ b/web/src/assets/elasticsearch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/components/GlobalHeader/DropdownSelect.js b/web/src/components/GlobalHeader/DropdownSelect.js index 7c98db2b..e27e904f 100644 --- a/web/src/components/GlobalHeader/DropdownSelect.js +++ b/web/src/components/GlobalHeader/DropdownSelect.js @@ -7,11 +7,16 @@ import {DropdownItem} from './DropdownItem'; import {HealthStatusCircle} from '@/components/infini/health_status_circle' class DropdownSelect extends React.Component{ - state={ - value: this.props.defaultValue, - loading: false, - hasMore: true, - overlayVisible: false, + constructor(props){ + super(props) + this.state={ + value: props.defaultValue, + loading: false, + hasMore: props.data.length > props.size, + overlayVisible: false, + data: (props.data || []).slice(0, props.size), + dataSource: [...props.data], + } } handleItemClick = (item)=>{ @@ -28,47 +33,35 @@ class DropdownSelect extends React.Component{ } componentDidMount(){ - let me = this; - this.fetchData().then((data)=>{ - let hasMore = true; - if(data.length < this.props.size){ - hasMore = false; - } - me.setState({ - hasMore - }) - }) - } - fetchData = (name)=>{ - let me = this; - const {fetchData, size} = this.props; - let data = this.props.data || []; - return fetchData(name || '', size); } - handleInfiniteOnLoad = (name) => { - let { data } = this.props; + handleInfiniteOnLoad = (current) => { + let {size } = this.props; + let targetLength = current * size; + let {hasMore, dataSource} = this.state; + if(dataSource.length < targetLength){ + targetLength = dataSource.length; + hasMore = false + } + const newData = this.state.dataSource.slice(0, targetLength); + this.setState({ - loading: true, + data: newData, + hasMore: hasMore, }) - this.fetchData(name).then((newdata)=>{ - let newState = { - loading: false, - }; - if(newdata.length < this.props.size){ - //message.info("no more data"); - newState.hasMore = false; - } - this.setState(newState); - }); } handleInputChange = (e) =>{ const name = e.target.value; + const newData = this.props.data.filter(item=>{ + return item.name.includes(name); + }); this.setState({ displayValue: name, + dataSource: newData, + data: newData, + hasMore: newData.length > this.props.size, }) - this.handleInfiniteOnLoad(name); } @@ -79,7 +72,7 @@ class DropdownSelect extends React.Component{ let displayVaue = value[labelField]; const menu = (
-
+
- {(!this.props.data || !this.props.data.length)&&
匹配不到集群(匹配规则为前缀匹配)
} - {(this.props.data || []).map((item)=>{ + {(!this.state.data || !this.state.data.length)&&
匹配不到集群(匹配规则为前缀匹配)
} + {(this.state.data || []).map((item)=>{ // return
//
- + + +
+ +
+ +
+ + + + + {lastDatum?.request.header} + + + + + {lastDatum?.response.header} + + + + +
{selectedCluster.version}
); -const [headerInfoVisible, setHeaderInfoVisible] = React.useState(false) if (requestInProgress) { content = ( @@ -140,12 +139,6 @@ const [headerInfoVisible, setHeaderInfoVisible] = React.useState(false)
-
- {/* */} -
); @@ -154,32 +147,7 @@ const [headerInfoVisible, setHeaderInfoVisible] = React.useState(false) return (
{left?
{clusterContent}
: - [
{content}
, - {setHeaderInfoVisible(false)}} - > - - -
- - {requestResult?.requestHeader} - - -
-
- - - {requestResult?.responseHeader} - - -
-
] + [
{content}
,] }
diff --git a/web/src/pages/DevTool/Console.tsx b/web/src/pages/DevTool/Console.tsx index 777c8f21..823573b4 100644 --- a/web/src/pages/DevTool/Console.tsx +++ b/web/src/pages/DevTool/Console.tsx @@ -1,7 +1,8 @@ import Console from '../../components/kibana/console/components/Console'; import {connect} from 'dva'; -import {Button, Icon, Menu, Dropdown, Tabs} from 'antd'; -// import Tabs from '@/components/infini/tabs'; +import {Button, Icon, Menu, Dropdown, } from 'antd'; +import Tabs from '@/components/infini/tabs'; +import {DraggableTabs} from '@/components/infini/tabs/DraggableTabs'; import {useState, useReducer, useCallback, useEffect, useMemo, useRef, useLayoutEffect} from 'react'; import {useLocalStorage} from '@/lib/hooks/storage'; import {setClusterID} from '../../components/kibana/console/modules/mappings/mappings'; @@ -9,6 +10,7 @@ import {TabTitle} from './console_tab_title'; import '@/assets/utility.scss'; import { Resizable } from "re-resizable"; import {ResizeBar} from '@/components/infini/resize_bar'; +import NewTabMenu from './NewTabMenu'; const { TabPane } = Tabs; @@ -94,6 +96,11 @@ const consoleTabReducer = (state: any, action: any) => { panes, }); break; + case 'saveOrder': + newState = ({ + ...state, + order: action.payload.order, + }); default: } // setLocalState(newState); @@ -123,8 +130,8 @@ export const ConsoleUI = ({selectedCluster, return cm; } (clusterList || []).map((cluster: any)=>{ - cluster.status = clusterStatus[cluster.id].health?.status; - if(!clusterStatus[cluster.id].available){ + cluster.status = clusterStatus[cluster.id]?.health?.status; + if(!clusterStatus[cluster.id]?.available){ cluster.status = 'unavailable'; } cm[cluster.id] = cluster; @@ -184,7 +191,7 @@ export const ConsoleUI = ({selectedCluster, }; const newTabClick = useCallback((param: any)=>{ - const cluster = clusterList.find(item=>item.id == param.key); + const cluster = clusterList.find(item=>item.id == param.id); if(!cluster){ console.log('cluster not found') return; @@ -198,11 +205,16 @@ export const ConsoleUI = ({selectedCluster, },[clusterList]) const menu = ( - - {(clusterList||[]).map((cluster:any)=>{ - return {cluster.name} - })} - + // + // {(clusterList||[]).map((cluster:any)=>{ + // return {cluster.name} + // })} + // + ); const rootRef = useRef(null); @@ -220,26 +232,35 @@ export const ConsoleUI = ({selectedCluster, setIsFullscreen(!isFullscreen) } - const tabBarExtra =( + const tabBarExtra ={ + left:
+ {minimize? :null} +
, + right:(
- - - {isFullscreen? : - } - {minimize? :null}
- ); + ), + append:( +
+ + + +
+ ) +}; setClusterID(tabState.activeKey?.split(':')[0]); const panes = tabState.panes.filter((pane: any)=>{ @@ -268,6 +289,14 @@ export const ConsoleUI = ({selectedCluster, const enableWindowScroll = ()=>{ document.body.style.overflow = ''; } + const onTabNodeMoved=(newOrder:string[])=>{ + dispatch({ + type:'saveOrder', + payload: { + order: newOrder, + } + }) + } return ( @@ -275,7 +304,7 @@ export const ConsoleUI = ({selectedCluster, defaultSize={{ height: editorHeight||'50vh' }} - minHeight={200} + minHeight={70} maxHeight="100vh" handleComponent={{ top: }} onResize={onResize} @@ -294,13 +323,15 @@ export const ConsoleUI = ({selectedCluster, onMouseOut={enableWindowScroll} id="console" ref={rootRef} > - {panes.map(pane => ( {saveTitle(pane.key, title)}}/>} key={pane.key} closable={pane.closable}> @@ -308,7 +339,7 @@ export const ConsoleUI = ({selectedCluster, {/* {pane.content} */} ))} - +
); diff --git a/web/src/pages/DevTool/NewTabMenu.jsx b/web/src/pages/DevTool/NewTabMenu.jsx new file mode 100644 index 00000000..43511351 --- /dev/null +++ b/web/src/pages/DevTool/NewTabMenu.jsx @@ -0,0 +1,100 @@ +import { Button, Dropdown, List, Spin, message, Icon, Input } from 'antd'; +import * as React from 'react'; +import InfiniteScroll from 'react-infinite-scroller'; +import styles from '@/components/GlobalHeader/DropdownSelect.less'; +import _ from "lodash"; +import {DropdownItem} from '@/components/GlobalHeader/DropdownItem'; +import {HealthStatusCircle} from '@/components/infini/health_status_circle' + +class NewTabMenu extends React.Component{ + handleItemClick = (item)=>{ + const onItemClick = this.props.onItemClick; + if(onItemClick && typeof onItemClick == 'function'){ + onItemClick(item) + } + } + constructor(props){ + super(props); + this.state={ + loading: false, + hasMore: (props.data || []).length > props.size, + data: (props.data || []).slice(0, props.size || 10), + initialLoad: true, + dataSource: [...props.data], + dataSourceKey: 1, + } + } + + componentDidMount(){ + } + handleInfiniteOnLoad = (current) => { + let {size } = this.props; + let targetLength = current * size; + let {hasMore, dataSource} = this.state; + if(dataSource.length < targetLength){ + targetLength = dataSource.length; + hasMore = false + } + const newData = this.state.dataSource.slice(0, targetLength); + + this.setState({ + data: newData, + hasMore: hasMore, + }) + + } + + handleInputChange = (e) =>{ + const name = e.target.value; + const newData = this.props.data.filter(item=>{ + return item.name.includes(name); + }); + this.setState({ + displayValue: name, + dataSource: newData, + data: newData, + hasMore: newData.length > this.props.size, + }) + + } + + + render(){ + const {clusterStatus} = this.props; + return (
+
+
+ +
+ +
+ {(!this.state.data || !this.state.data.length)&&
匹配不到集群(匹配规则为前缀匹配)
} + {(this.state.data || []).map((item)=>{ + const cstatus = clusterStatus ? clusterStatus[item.id] : null; + return { + this.handleItemClick(item) + }} + /> + })} +
+
+
+ {!this.state.loading && this.state.hasMore && ( +
+ pull load more +
+ )} +
); + } + +} + +export default NewTabMenu; \ No newline at end of file diff --git a/web/src/pages/DevTool/console_tab_title.tsx b/web/src/pages/DevTool/console_tab_title.tsx index 1f994503..b92b2890 100644 --- a/web/src/pages/DevTool/console_tab_title.tsx +++ b/web/src/pages/DevTool/console_tab_title.tsx @@ -1,5 +1,11 @@ import {useState, useRef, useEffect} from 'react'; import './console_tab_title.scss'; +import ElasticSvg from '@/assets/elasticsearch.svg'; +import {Icon} from 'antd'; + +const ElasticIcon = () => ( + +); interface TabTitleProps { title: string, @@ -28,7 +34,8 @@ export const TabTitle = ({title, onTitleChange}: TabTitleProps)=>{ onBlur={()=>{ setEditable(false) }} - onChange={onValueChange}/>:value} + onChange={onValueChange}/>: +
{value}
} ) }