optimize node top metric and change cluster config editing logic

This commit is contained in:
liugq 2021-12-10 12:23:08 +08:00
parent 740cda1ff7
commit 475dd606e8
15 changed files with 166 additions and 39 deletions

View File

@ -448,7 +448,7 @@ export default [
hideInMenu: true, hideInMenu: true,
}, },
{ {
path: "/system/cluster/edit", path: "/system/cluster/:id/edit",
name: "editCluster", name: "editCluster",
component: "./System/Cluster/Form", component: "./System/Cluster/Form",
hideInMenu: true, hideInMenu: true,

View File

@ -16,6 +16,7 @@ class DropdownSelect extends React.Component {
overlayVisible: false, overlayVisible: false,
data: (props.data || []).slice(0, props.size), data: (props.data || []).slice(0, props.size),
dataSource: [...props.data], dataSource: [...props.data],
selectedIndex: -1,
}; };
} }
@ -75,9 +76,38 @@ class DropdownSelect extends React.Component {
hasMore: newData.length > this.props.size, hasMore: newData.length > this.props.size,
}); });
}; };
selectOffset = (offset) => {
let { selectedIndex, data } = this.state;
const len = data.length;
selectedIndex = (selectedIndex + offset + len) % len;
this.setState({
selectedIndex,
});
};
onKeyDown = (e) => {
const { which } = e;
switch (which) {
case 38:
this.selectOffset(-1);
e.preventDefault();
e.stopPropagation();
break;
case 40:
this.selectOffset(1);
e.stopPropagation();
break;
case 13:
const { data, selectedIndex } = this.state;
if (selectedIndex > -1) {
this.handleItemClick(data[selectedIndex]);
this.setState({ overlayVisible: false });
}
break;
}
};
render() { render() {
let me = this;
const { labelField, clusterStatus } = this.props; const { labelField, clusterStatus } = this.props;
let value = this.props.value || this.state.value; let value = this.props.value || this.state.value;
let displayVaue = value[labelField]; let displayVaue = value[labelField];
@ -86,6 +116,10 @@ class DropdownSelect extends React.Component {
<div <div
className={styles.infiniteContainer} className={styles.infiniteContainer}
style={{ height: this.props.height }} style={{ height: this.props.height }}
onMouseEnter={() => {
this.searchInputRef.focus();
}}
onKeyDown={this.onKeyDown}
> >
<div <div
className={styles.filter} className={styles.filter}
@ -97,6 +131,9 @@ class DropdownSelect extends React.Component {
onChange={this.handleInputChange} onChange={this.handleInputChange}
placeholder="输入集群名称查找" placeholder="输入集群名称查找"
value={this.state.displayValue || ""} value={this.state.displayValue || ""}
ref={(ref) => {
this.searchInputRef = ref;
}}
/> />
</div> </div>
<InfiniteScroll <InfiniteScroll
@ -118,7 +155,7 @@ class DropdownSelect extends React.Component {
匹配不到集群(匹配规则为前缀匹配) 匹配不到集群(匹配规则为前缀匹配)
</div> </div>
)} )}
{(this.state.data || []).map((item) => { {(this.state.data || []).map((item, idx) => {
// return <div className={styles.item}> // return <div className={styles.item}>
// <Button key={item[labelField]} // <Button key={item[labelField]}
// onClick={() => { // onClick={() => {
@ -132,6 +169,7 @@ class DropdownSelect extends React.Component {
key={item.id} key={item.id}
isSelected={item.id === value.id} isSelected={item.id === value.id}
clusterItem={item} clusterItem={item}
isSelected={this.state.selectedIndex == idx}
clusterStatus={cstatus} clusterStatus={cstatus}
onClick={() => { onClick={() => {
this.handleItemClick(item); this.handleItemClick(item);

View File

@ -37,6 +37,7 @@ interface props {
initialText: string; initialText: string;
paneKey: string; paneKey: string;
height: number; height: number;
isActive: boolean;
} }
const INITIAL_PANEL_WIDTH = 50; const INITIAL_PANEL_WIDTH = 50;
@ -47,6 +48,7 @@ const ConsoleWrapper = ({
saveEditorContent, saveEditorContent,
initialText, initialText,
paneKey, paneKey,
isActive,
height, height,
}: props) => { }: props) => {
const { const {
@ -84,6 +86,7 @@ const ConsoleWrapper = ({
saveEditorContent={saveEditorContent} saveEditorContent={saveEditorContent}
initialText={initialText} initialText={initialText}
paneKey={paneKey} paneKey={paneKey}
isActive={isActive}
/> />
<div <div
style={{ style={{

View File

@ -71,6 +71,7 @@ interface ConsoleInputProps {
initialText: string | undefined; initialText: string | undefined;
saveEditorContent: (content: string) => void; saveEditorContent: (content: string) => void;
paneKey: string; paneKey: string;
isActive: boolean;
height?: string; height?: string;
} }
@ -87,6 +88,7 @@ const ConsoleInputUI = ({
saveEditorContent, saveEditorContent,
paneKey, paneKey,
height = "100%", height = "100%",
isActive,
}: ConsoleInputProps) => { }: ConsoleInputProps) => {
const editorRef = useRef<HTMLDivElement | null>(null); const editorRef = useRef<HTMLDivElement | null>(null);
const editorActionsRef = useRef<HTMLDivElement | null>(null); const editorActionsRef = useRef<HTMLDivElement | null>(null);
@ -103,6 +105,11 @@ const ConsoleInputUI = ({
const { const {
services: { settings }, services: { settings },
} = useServicesContext(); } = useServicesContext();
// if (isActive) {
// if (aceEditorRef.current) {
// aceEditorRef.current.focus();
// }
// }
useEffect(() => { useEffect(() => {
const aceEditor = ace.edit(editorRef.current!); const aceEditor = ace.edit(editorRef.current!);

View File

@ -90,7 +90,7 @@ export default {
"form.button.pre": "上一步", "form.button.pre": "上一步",
"form.button.goback": "返回", "form.button.goback": "返回",
"form.button.reset": "重置", "form.button.reset": "重置",
"form.label.search-keyword": "观检测", "form.label.search-keyword": "关键词",
"component.globalHeader.search": "站内搜索", "component.globalHeader.search": "站内搜索",
"component.globalHeader.search.example1": "搜索提示一", "component.globalHeader.search.example1": "搜索提示一",

View File

@ -246,6 +246,7 @@ export default {
updateCluster(state, { payload }) { updateCluster(state, { payload }) {
let idx = state.clusterList.findIndex((item) => item.id === payload.id); let idx = state.clusterList.findIndex((item) => item.id === payload.id);
idx > -1 && (state.clusterList[idx].name = payload.name); idx > -1 && (state.clusterList[idx].name = payload.name);
state.clusterStatus[payload.id].config.monitored = payload.monitored;
return state; return state;
}, },
changeClusterById(state, { payload }) { changeClusterById(state, { payload }) {

View File

@ -16,6 +16,7 @@ import {
} from "antd"; } from "antd";
import moment from "moment"; import moment from "moment";
import router from "umi/router"; import router from "umi/router";
import Link from "umi/link";
import "@elastic/charts/dist/theme_only_light.css"; import "@elastic/charts/dist/theme_only_light.css";
import { import {
@ -38,6 +39,7 @@ import NodeMetric from "./components/node_metric";
import IndexMetric from "./components/index_metric"; import IndexMetric from "./components/index_metric";
import ClusterMetric from "./components/cluster_metric"; import ClusterMetric from "./components/cluster_metric";
import QueueMetric from "./components/queue_metric"; import QueueMetric from "./components/queue_metric";
// import StorageMetric from "./components/storage_metric";
import { formatter, getFormatter, getNumFormatter } from "./format"; import { formatter, getFormatter, getNumFormatter } from "./format";
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
@ -452,8 +454,10 @@ class ClusterMonitor extends PureComponent {
}); });
let clusterAvailable = true; let clusterAvailable = true;
const { clusterStatus: cstatus, selectedCluster } = this.props; const { clusterStatus: cstatus, selectedCluster } = this.props;
let clusterMonitored = true;
if (cstatus && selectedCluster && cstatus[selectedCluster.id]) { if (cstatus && selectedCluster && cstatus[selectedCluster.id]) {
clusterAvailable = cstatus[selectedCluster.id].available; clusterAvailable = cstatus[selectedCluster.id].available;
clusterMonitored = cstatus[selectedCluster.id].config.monitored;
} }
return ( return (
@ -476,10 +480,26 @@ class ClusterMonitor extends PureComponent {
Last data collection time: {clusterStats?.timestamp} Last data collection time: {clusterStats?.timestamp}
</div> </div>
</div> </div>
) : !clusterMonitored ? (
<div className={styles.mask}>
<div>
Cluster is not monitored.{" "}
<Button type="primary">
<Link to={`/system/cluster/${selectedCluster.id}/edit`}>
Go to open
</Link>
</Button>
</div>
<div className={styles.time}>
Last data collection time: {clusterStats?.timestamp}
</div>
</div>
) : null} ) : null}
<Row <Row
gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]} gutter={[16, { xs: 8, sm: 16, md: 24, lg: 32 }]}
className={!clusterAvailable ? styles.metricMask : ""} className={
!clusterAvailable || !clusterMonitored ? styles.metricMask : ""
}
> >
<Col md={2} xs={4}> <Col md={2} xs={4}>
<Statistic <Statistic
@ -510,7 +530,11 @@ class ClusterMonitor extends PureComponent {
</Col> </Col>
<Col md={2} xs={4}> <Col md={2} xs={4}>
<Statistic <Statistic
valueStyle={vstyle} valueStyle={{
...vstyle,
display: "flex",
alignItems: "center",
}}
title={formatMessage({ title={formatMessage({
id: "cluster.monitor.summary.health", id: "cluster.monitor.summary.health",
})} })}
@ -649,6 +673,18 @@ class ClusterMonitor extends PureComponent {
handleTimeChange={this.handleTimeChange} handleTimeChange={this.handleTimeChange}
/> />
</Tabs.TabPane> </Tabs.TabPane>
{/* <Tabs.TabPane
key="storage"
tab={formatMessage({
id: "cluster.monitor.queue.storage",
})}
>
<StorageMetric
clusterID={this.props.selectedCluster.id}
timezone={timezone}
timeRange={this.state.timeRange}
/>
</Tabs.TabPane> */}
</Tabs> </Tabs>
</div> </div>
</div> </div>

View File

@ -23,10 +23,10 @@ import MetricContainer from "./metric_container";
import _ from "lodash"; import _ from "lodash";
const gorupOrder = [ const gorupOrder = [
"storage",
"document",
"operations", "operations",
"latency", "latency",
"storage",
"document",
"memory", "memory",
"cache", "cache",
]; ];

View File

@ -23,10 +23,10 @@ import MetricContainer from "./metric_container";
import _ from "lodash"; import _ from "lodash";
const gorupOrder = [ const gorupOrder = [
"system",
"transport",
"operations", "operations",
"latency", "latency",
"system",
"transport",
"storage", "storage",
"document", "document",
"http", "http",

View File

@ -23,8 +23,8 @@ import MetricContainer from "./metric_container";
import _ from "lodash"; import _ from "lodash";
const gorupOrder = [ const gorupOrder = [
"thread_pool_search",
"thread_pool_write", "thread_pool_write",
"thread_pool_search",
"thread_pool_index", "thread_pool_index",
"thread_pool_bulk", "thread_pool_bulk",
"thread_pool_get", "thread_pool_get",

View File

@ -436,6 +436,7 @@ export const ConsoleUI = ({
paneKey={pane.key} paneKey={pane.key}
saveEditorContent={saveEditorContent} saveEditorContent={saveEditorContent}
initialText={pane.content} initialText={pane.content}
isActive={pane.key == tabState.activeKey}
/> />
{/* {pane.content} */} {/* {pane.content} */}
</TabPane> </TabPane>

View File

@ -25,23 +25,36 @@ import { formatMessage } from "umi/locale";
class ClusterForm extends React.Component { class ClusterForm extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
let editValue = this.props.clusterConfig.editValue;
let needAuth = false;
if (
editValue.basic_auth &&
typeof editValue.basic_auth.username !== "undefined" &&
editValue.basic_auth.username !== ""
) {
needAuth = true;
}
this.state = { this.state = {
confirmDirty: false, confirmDirty: false,
needAuth: needAuth,
isLoading: false, isLoading: false,
}; };
} }
componentDidMount() { componentDidMount() {
//console.log(this.props.clusterConfig.editMode) //console.log(this.props.clusterConfig.editMode)
const { match, dispatch, clusterConfig } = this.props;
dispatch({
type: "clusterConfig/fetchCluster",
payload: {
id: match.params.id,
},
}).then((res) => {
if (res && res.found) {
let editValue = res._source;
let needAuth = false;
if (
editValue.basic_auth &&
typeof editValue.basic_auth.username !== "undefined"
) {
needAuth = true;
}
this.setState({
needAuth: needAuth,
});
}
});
} }
compareToFirstPassword = (rule, value, callback) => { compareToFirstPassword = (rule, value, callback) => {
@ -62,7 +75,7 @@ class ClusterForm extends React.Component {
}; };
handleSubmit = () => { handleSubmit = () => {
const { form, dispatch, clusterConfig } = this.props; const { form, dispatch, clusterConfig, history } = this.props;
form.validateFields((errors, values) => { form.validateFields((errors, values) => {
if (errors) { if (errors) {
return; return;
@ -100,7 +113,8 @@ class ClusterForm extends React.Component {
}).then(function(rel) { }).then(function(rel) {
if (rel) { if (rel) {
message.success("修改成功"); message.success("修改成功");
router.push("/system/cluster"); // router.push("/system/cluster");
history.go(-1);
} }
}); });
} }
@ -256,7 +270,7 @@ class ClusterForm extends React.Component {
})} })}
> >
<Switch <Switch
defaultChecked={this.state.needAuth} checked={this.state.needAuth}
onChange={this.handleAuthChange} onChange={this.handleAuthChange}
checkedChildren={<Icon type="check" />} checkedChildren={<Icon type="check" />}
unCheckedChildren={<Icon type="close" />} unCheckedChildren={<Icon type="close" />}

View File

@ -178,7 +178,7 @@ class Index extends React.Component {
render: (text, record) => ( render: (text, record) => (
<div> <div>
<Link <Link
to="/system/cluster/edit" to={`/system/cluster/${record.id}/edit`}
onClick={() => { onClick={() => {
this.handleEditClick(record); this.handleEditClick(record);
}} }}

View File

@ -4,6 +4,7 @@ import {
updateClusterConfig, updateClusterConfig,
deleteClusterConfig, deleteClusterConfig,
tryConnect, tryConnect,
getClusterConfig,
} from "@/services/cluster"; } from "@/services/cluster";
import { message } from "antd"; import { message } from "antd";
import { formatESSearchResult } from "@/lib/elasticsearch/util"; import { formatESSearchResult } from "@/lib/elasticsearch/util";
@ -36,6 +37,23 @@ export default {
}, },
}); });
}, },
*fetchCluster({ payload }, { call, put, select }) {
let res = yield call(getClusterConfig, payload);
if (res.error) {
message.error(res.error);
return false;
}
yield put({
type: "saveData",
payload: {
editValue: {
...res._source,
id: res._id,
},
},
});
return res;
},
*addCluster({ payload }, { call, put, select }) { *addCluster({ payload }, { call, put, select }) {
let res = yield call(createClusterConfig, payload); let res = yield call(createClusterConfig, payload);
if (res.error) { if (res.error) {
@ -76,20 +94,22 @@ export default {
return false; return false;
} }
let { data } = yield select((state) => state.clusterConfig); let { data } = yield select((state) => state.clusterConfig);
let idx = data.findIndex((item) => { if (data) {
return item.id === res._id; let idx = data.findIndex((item) => {
}); return item.id === res._id;
});
data[idx] = {
...data[idx],
...res._source,
};
yield put({
type: "saveData",
payload: {
data,
},
});
}
data[idx] = {
...data[idx],
...res._source,
};
yield put({
type: "saveData",
payload: {
data,
},
});
//handle global cluster logic //handle global cluster logic
yield put({ yield put({
@ -97,9 +117,9 @@ export default {
payload: { payload: {
id: res._id, id: res._id,
name: res._source.name, name: res._source.name,
monitored: res._source.monitored,
}, },
}); });
return res; return res;
}, },
*deleteCluster({ payload }, { call, put, select }) { *deleteCluster({ payload }, { call, put, select }) {

View File

@ -72,3 +72,10 @@ export async function tryConnect(params) {
body: params, body: params,
}); });
} }
export async function getClusterConfig(params) {
let url = `${ESPrefix}/${params.id}`;
return request(url, {
method: "GET",
});
}