modified system cluster and global cluster selector
This commit is contained in:
parent
523ba2fbec
commit
c981e6537d
|
@ -69,7 +69,7 @@ export default {
|
||||||
// },
|
// },
|
||||||
proxy: {
|
proxy: {
|
||||||
'/_search-center/': {
|
'/_search-center/': {
|
||||||
target: 'http://localhost:2900',
|
target: 'http://localhost:9000',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
// pathRewrite: { '^/server': '' },
|
// pathRewrite: { '^/server': '' },
|
||||||
},
|
},
|
||||||
|
|
|
@ -304,7 +304,7 @@ export default [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/system/cluster/edit',
|
path: '/system/cluster/edit',
|
||||||
name: 'edit-cluster',
|
name: 'editCluster',
|
||||||
component: './System/Cluster/Form',
|
component: './System/Cluster/Form',
|
||||||
hideInMenu: true
|
hideInMenu: true
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
export default {
|
||||||
|
// 'post /_search-center/system/cluster/_search': function(req, res){
|
||||||
|
// res.send({
|
||||||
|
// "took": 0,
|
||||||
|
// "timed_out": false,
|
||||||
|
// "hits": {
|
||||||
|
// "total": {
|
||||||
|
// "relation": "eq",
|
||||||
|
// "value": 1
|
||||||
|
// },
|
||||||
|
// "max_score": 1,
|
||||||
|
// "hits": [
|
||||||
|
// {
|
||||||
|
// "_index": ".infini-search-center_cluster",
|
||||||
|
// "_type": "_doc",
|
||||||
|
// "_id": "c0oc4kkgq9s8qss2uk50",
|
||||||
|
// "_source": {
|
||||||
|
// "basic_auth": {
|
||||||
|
// "password": "123",
|
||||||
|
// "username": "medcl"
|
||||||
|
// },
|
||||||
|
// "created": "2021-02-20T16:03:30.867084+08:00",
|
||||||
|
// "description": "xx业务集群1",
|
||||||
|
// "enabled": false,
|
||||||
|
// "endpoint": "http://localhost:9200",
|
||||||
|
// "name": "cluster1",
|
||||||
|
// "updated": "2021-02-20T16:03:30.867084+08:00"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// 'post /_search-center/system/cluster': function(req, res){
|
||||||
|
// res.send({
|
||||||
|
// "_id": "c0obhd4gq9s7akom0o60",
|
||||||
|
// "_source": {
|
||||||
|
// "name": "cluster1",
|
||||||
|
// "endpoint": "http://localhost:9200",
|
||||||
|
// "basic_auth": {
|
||||||
|
// "username": "medcl",
|
||||||
|
// "password": "123"
|
||||||
|
// },
|
||||||
|
// "description": "xx业务集群1",
|
||||||
|
// "enabled": false,
|
||||||
|
// "created": "2021-02-20T15:12:50.984062+08:00",
|
||||||
|
// "updated": "2021-02-20T15:12:50.984062+08:00"
|
||||||
|
// },
|
||||||
|
// "result": "created"
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// 'put /_search-center/system/cluster/:id': function(req, res){
|
||||||
|
// res.send({
|
||||||
|
// "_id": "c0obhd4gq9s7akom0o60",
|
||||||
|
// "_source": {
|
||||||
|
// "basic_auth": {
|
||||||
|
// "password": "456",
|
||||||
|
// "username": "medcl"
|
||||||
|
// },
|
||||||
|
// "description": "xx业务集群2",
|
||||||
|
// "endpoint": "http://localhost:9201",
|
||||||
|
// "name": "cluster2",
|
||||||
|
// "updated": "2021-02-20T15:25:12.159789+08:00"
|
||||||
|
// },
|
||||||
|
// "result": "updated"
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// 'delete /_search-center/system/cluster/:id': function(req, res){
|
||||||
|
// res.send({
|
||||||
|
// "_id": "c0obk7cgq9s7hi05aou0",
|
||||||
|
// "result": "deleted"
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ import styles from './DropdownSelect.less';
|
||||||
class DropdownSelect extends React.Component{
|
class DropdownSelect extends React.Component{
|
||||||
state={
|
state={
|
||||||
value: this.props.defaultValue,
|
value: this.props.defaultValue,
|
||||||
data: this.props.data || [],
|
|
||||||
loading: false,
|
loading: false,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
}
|
}
|
||||||
|
@ -24,26 +23,23 @@ class DropdownSelect extends React.Component{
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
let data = [];
|
let me = this;
|
||||||
for(let i = 1; i<=28; i++){
|
this.fetchData().then((data)=>{
|
||||||
data.push('cluster'+i)
|
let hasMore = true;
|
||||||
}
|
if(data.length < this.props.size){
|
||||||
this.setState({
|
hasMore = false;
|
||||||
data: data,
|
}
|
||||||
|
me.setState({
|
||||||
|
hasMore
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fetchData = ()=>{
|
fetchData = ()=>{
|
||||||
let me = this;
|
let me = this;
|
||||||
return new Promise(resolve => {
|
const {fetchData, size} = this.props;
|
||||||
setTimeout(() => {
|
let data = this.props.data || [];
|
||||||
let start = me.state.data.length;
|
let from = data.length;
|
||||||
let data =[]
|
return fetchData(from, size);
|
||||||
for(let i = start + 1; i<start+11; i++){
|
|
||||||
data.push('cluster'+i)
|
|
||||||
}
|
|
||||||
resolve(data)
|
|
||||||
}, 2000)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInfiniteOnLoad = (page) => {
|
handleInfiniteOnLoad = (page) => {
|
||||||
|
@ -51,28 +47,24 @@ class DropdownSelect extends React.Component{
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
})
|
})
|
||||||
if (data.length > 50) {
|
|
||||||
message.warning('No more data');
|
|
||||||
this.setState({
|
|
||||||
hasMore: false,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.fetchData().then((newdata)=>{
|
this.fetchData().then((newdata)=>{
|
||||||
data = data.concat(newdata);
|
let newState = {
|
||||||
this.setState({
|
|
||||||
data,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
};
|
||||||
|
if(newdata.length < this.props.size){
|
||||||
|
message.info("no more data");
|
||||||
|
newState.hasMore = false;
|
||||||
|
}
|
||||||
|
this.setState(newState);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let me = this;
|
let me = this;
|
||||||
const menu = (<div className={styles.dropmenu}>
|
const {labelField} = this.props;
|
||||||
<div className={styles.infiniteContainer}>
|
const menu = (<div className={styles.dropmenu} style={{width: this.props.width}}>
|
||||||
|
<div className={styles.infiniteContainer} style={{height: this.props.height}}>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
initialLoad={false}
|
initialLoad={false}
|
||||||
loadMore={this.handleInfiniteOnLoad}
|
loadMore={this.handleInfiniteOnLoad}
|
||||||
|
@ -84,10 +76,10 @@ class DropdownSelect extends React.Component{
|
||||||
gutter: 8,
|
gutter: 8,
|
||||||
column: 4,
|
column: 4,
|
||||||
}}
|
}}
|
||||||
dataSource={this.state.data}
|
dataSource={this.props.data}
|
||||||
renderItem={item => (
|
renderItem={item => (
|
||||||
<List.Item key={item}>
|
<List.Item key={item[labelField]}>
|
||||||
<Button onClick={()=>{this.handleItemClick(item)}} className={styles.btnitem}>{item}</Button>
|
<Button onClick={()=>{this.handleItemClick(item)}} className={styles.btnitem}>{item[labelField]}</Button>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -106,9 +98,11 @@ class DropdownSelect extends React.Component{
|
||||||
)}
|
)}
|
||||||
</div>);
|
</div>);
|
||||||
return(
|
return(
|
||||||
<Dropdown overlay={menu} placement="bottomLeft">
|
this.props.visible ?
|
||||||
<Button className={styles['btn-ds']}>{this.state.value} <Icon style={{float:'right', marginTop:3}} type="caret-down"/></Button>
|
(<Dropdown overlay={menu} placement="bottomLeft">
|
||||||
</Dropdown>
|
<Button className={styles['btn-ds']}>{this.state.value[labelField]} <Icon style={{float: 'right', marginTop: 3}}
|
||||||
|
type="caret-down"/></Button>
|
||||||
|
</Dropdown>) : ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
.infiniteContainer {
|
.infiniteContainer {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
.loadingContainer {
|
.loadingContainer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default class GlobalHeader extends PureComponent {
|
||||||
this.triggerResizeEvent();
|
this.triggerResizeEvent();
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { collapsed, isMobile, logo } = this.props;
|
const { collapsed, isMobile, logo, clusterVisible, clusterList } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
|
@ -37,9 +37,34 @@ export default class GlobalHeader extends PureComponent {
|
||||||
type={collapsed ? 'menu-unfold' : 'menu-fold'}
|
type={collapsed ? 'menu-unfold' : 'menu-fold'}
|
||||||
onClick={this.toggle}
|
onClick={this.toggle}
|
||||||
/>
|
/>
|
||||||
<DropdownSelect defaultValue="Select cluster"
|
<DropdownSelect defaultValue={{name:"Select cluster"}}
|
||||||
onChange={(item)=>{}}
|
labelField="name"
|
||||||
data={['cluster1', 'cluster2','cluster3', 'cluster4','cluster5', 'cluster6']}/>
|
visible={clusterVisible}
|
||||||
|
onChange={(item)=>{
|
||||||
|
this.props.handleSaveGlobalState({
|
||||||
|
selectedCluster: item
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
size={56}
|
||||||
|
fetchData={
|
||||||
|
this.props.onFetchClusterList
|
||||||
|
// (from, size)=>{
|
||||||
|
// return new Promise(resolve => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// let start = from;
|
||||||
|
// let data =[]
|
||||||
|
// for(let i = start + 1; i<start+size+1; i++){
|
||||||
|
// if(start+size > 56){
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// data.push('cluster'+i)
|
||||||
|
// }
|
||||||
|
// resolve(data)
|
||||||
|
// }, 2000)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
data={clusterList}/>
|
||||||
<RightContent {...this.props} />
|
<RightContent {...this.props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -114,6 +114,25 @@ class HeaderView extends PureComponent {
|
||||||
this.ticking = false;
|
this.ticking = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleFetchClusterList = (from, size) => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
return dispatch({
|
||||||
|
type: 'global/fetchClusterList',
|
||||||
|
payload: {
|
||||||
|
from,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSaveGlobalState = (newState) => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
return dispatch({
|
||||||
|
type: 'global/saveData',
|
||||||
|
payload: newState
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isMobile, handleMenuCollapse, setting } = this.props;
|
const { isMobile, handleMenuCollapse, setting } = this.props;
|
||||||
const { navTheme, layout, fixedHeader } = setting;
|
const { navTheme, layout, fixedHeader } = setting;
|
||||||
|
@ -139,6 +158,8 @@ class HeaderView extends PureComponent {
|
||||||
onNoticeClear={this.handleNoticeClear}
|
onNoticeClear={this.handleNoticeClear}
|
||||||
onMenuClick={this.handleMenuClick}
|
onMenuClick={this.handleMenuClick}
|
||||||
onNoticeVisibleChange={this.handleNoticeVisibleChange}
|
onNoticeVisibleChange={this.handleNoticeVisibleChange}
|
||||||
|
onFetchClusterList={this.handleFetchClusterList}
|
||||||
|
handleSaveGlobalState={this.handleSaveGlobalState}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -158,4 +179,6 @@ export default connect(({ user, global, setting, loading }) => ({
|
||||||
fetchingNotices: loading.effects['global/fetchNotices'],
|
fetchingNotices: loading.effects['global/fetchNotices'],
|
||||||
notices: global.notices,
|
notices: global.notices,
|
||||||
setting,
|
setting,
|
||||||
|
clusterVisible: global.clusterVisible,
|
||||||
|
clusterList: global.clusterList,
|
||||||
}))(HeaderView);
|
}))(HeaderView);
|
||||||
|
|
|
@ -120,6 +120,7 @@ export default {
|
||||||
|
|
||||||
'menu.system': 'SYSTEM',
|
'menu.system': 'SYSTEM',
|
||||||
'menu.system.cluster': 'CLUSTER',
|
'menu.system.cluster': 'CLUSTER',
|
||||||
|
'menu.system.editCluster': 'EDIT CLUSTER',
|
||||||
'menu.system.settings': 'SETTINGS',
|
'menu.system.settings': 'SETTINGS',
|
||||||
'menu.system.settings.global': 'GLOBAL',
|
'menu.system.settings.global': 'GLOBAL',
|
||||||
'menu.system.settings.gateway': 'GATEWAY',
|
'menu.system.settings.gateway': 'GATEWAY',
|
||||||
|
|
|
@ -127,6 +127,7 @@ export default {
|
||||||
|
|
||||||
'menu.system': '系统管理',
|
'menu.system': '系统管理',
|
||||||
'menu.system.cluster': '集群管理',
|
'menu.system.cluster': '集群管理',
|
||||||
|
'menu.system.editCluster': '集群编辑',
|
||||||
'menu.system.settings': '系统设置',
|
'menu.system.settings': '系统设置',
|
||||||
'menu.system.settings.global': '全局设置',
|
'menu.system.settings.global': '全局设置',
|
||||||
'menu.system.settings.gateway': '网关设置',
|
'menu.system.settings.gateway': '网关设置',
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { queryNotices } from '@/services/api';
|
import { queryNotices } from '@/services/api';
|
||||||
|
import {message} from "antd";
|
||||||
|
import {searchClusterConfig} from "@/services/clusterConfig";
|
||||||
|
import {formatESSearchResult} from '@/lib/elasticsearch/util';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespace: 'global',
|
namespace: 'global',
|
||||||
|
@ -6,6 +10,9 @@ export default {
|
||||||
state: {
|
state: {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
notices: [],
|
notices: [],
|
||||||
|
clusterVisible: true,
|
||||||
|
clusterList: [],
|
||||||
|
selectedCluster: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
|
@ -31,6 +38,29 @@ export default {
|
||||||
payload: count,
|
payload: count,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
*fetchClusterList({payload}, {call, put, select}){
|
||||||
|
let res = yield call(searchClusterConfig, payload);
|
||||||
|
if(res.error){
|
||||||
|
message.error(res.error)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
res = formatESSearchResult(res)
|
||||||
|
let clusterList = yield select(state => state.global.clusterList);
|
||||||
|
let data = res.data.map((item)=>{
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
id: item.id,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: 'saveData',
|
||||||
|
payload: {
|
||||||
|
clusterList: clusterList.concat(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return data;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
reducers: {
|
reducers: {
|
||||||
|
@ -52,12 +82,43 @@ export default {
|
||||||
notices: state.notices.filter(item => item.type !== payload),
|
notices: state.notices.filter(item => item.type !== payload),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
saveData(state, {payload}){
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeCluster(state, {payload}){
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
clusterList: state.clusterList.filter(item => item.id !== payload.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addCluster(state, {payload}){
|
||||||
|
state.clusterList.push(payload)
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
updateCluster(state, {payload}){
|
||||||
|
let idx = state.clusterList.findIndex(item => item.id === payload.id);
|
||||||
|
idx > -1 && (state.clusterList[idx].name = payload.name);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
setup({ history }) {
|
setup({ history, dispatch }) {
|
||||||
// Subscribe history(url) change, trigger `load` action if pathname is `/`
|
// Subscribe history(url) change, trigger `load` action if pathname is `/`
|
||||||
return history.listen(({ pathname, search }) => {
|
return history.listen(({ pathname, search }) => {
|
||||||
|
let clusterVisible = true;
|
||||||
|
if(pathname.startsWith("/system")){
|
||||||
|
clusterVisible = false;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
type: 'saveData',
|
||||||
|
payload: {
|
||||||
|
clusterVisible,
|
||||||
|
}
|
||||||
|
})
|
||||||
if (typeof window.ga !== 'undefined') {
|
if (typeof window.ga !== 'undefined') {
|
||||||
window.ga('send', 'pageview', pathname + search);
|
window.ga('send', 'pageview', pathname + search);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
import React, {Fragment} from 'react';
|
import React, {Fragment} from 'react';
|
||||||
import {Card, Divider, Popconfirm, Table} from "antd";
|
import {Card, Divider, Popconfirm, Row, Col, Table, Descriptions} from "antd";
|
||||||
import {Link} from "umi"
|
import {Link} from "umi"
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import styles from "./Overview.less";
|
||||||
|
import {connect} from "dva";
|
||||||
|
|
||||||
|
let HealthCircle = (props)=>{
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
background: props.color,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: 12,
|
||||||
|
display: "inline-block",
|
||||||
|
marginRight: 3,
|
||||||
|
}}></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@connect(({global}) => ({
|
||||||
|
selectedCluster: global.selectedCluster
|
||||||
|
}))
|
||||||
class Overview extends React.Component {
|
class Overview extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
data: [{id:"JFpIbacZQamv9hkgQEDZ2Q", name:"single-es", endpoint:"http://localhost:9200", health: "green", version: "7.10.0", uptime:"320883955"}]
|
data: [{id:"JFpIbacZQamv9hkgQEDZ2Q", name:"single-es", endpoint:"http://localhost:9200", health: "green", version: "7.10.0", uptime:"320883955"}]
|
||||||
|
@ -39,13 +57,43 @@ class Overview extends React.Component {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
render() {
|
render() {
|
||||||
return (<Card>
|
return (<Card title={this.props.selectedCluster?this.props.selectedCluster.name:''}>
|
||||||
<Table
|
<Row gutter={[16,16]}>
|
||||||
bordered
|
<Col xs={24} sm={12} md={12} lg={8} >
|
||||||
dataSource={this.state.data}
|
<Card title="Summary" size={"small"}>
|
||||||
columns={this.clusterColumns}
|
<Descriptions column={1} bordered colon={false} className={styles.overview}>
|
||||||
rowKey="id"
|
<Descriptions.Item label="Health"><HealthCircle color="green"/>Healthy</Descriptions.Item>
|
||||||
/>
|
<Descriptions.Item label="Version">7.10.0</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="Uptime">3 天</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="License">Basic</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col xs={24} sm={12} md={12} lg={8}>
|
||||||
|
<Card title="Nodes:2" size={"small"}>
|
||||||
|
<Descriptions column={1} bordered colon={false} size="small" className={styles.overview}>
|
||||||
|
<Descriptions.Item label="Disk Available">
|
||||||
|
83.21%
|
||||||
|
<p className={styles.light}>775.1 GB / 931.5 GB</p>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="JVM Heap">
|
||||||
|
27.60%
|
||||||
|
<p className={styles.light}>565.3 GB / 2.0 GB</p>
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col xs={24} sm={12} md={12} lg={8}>
|
||||||
|
<Card title="Indices:27" size={"small"}>
|
||||||
|
<Descriptions column={1} bordered colon={false} className={styles.overview}>
|
||||||
|
<Descriptions.Item label="Documents">20,812,087</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="Disk Usage">1.1 GB</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="Primary Shards">28</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="Replica Shards">26</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
</Card>)
|
</Card>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.overview{
|
||||||
|
:global(.ant-descriptions-row){
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
:global(.ant-descriptions-item-label){
|
||||||
|
border-right: none;
|
||||||
|
background-color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
:global(.ant-descriptions-view){
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
:global(.ant-descriptions-item-content){
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.light{
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Card, Form, Icon, Input, InputNumber, Button, Switch} from 'antd';
|
import {Card, Form, Icon, Input, InputNumber, Button, Switch, message} from 'antd';
|
||||||
import router from 'umi/router';
|
import router from 'umi/router';
|
||||||
|
|
||||||
import styles from './Form.less';
|
import styles from './Form.less';
|
||||||
|
@ -42,15 +42,23 @@ class ClusterForm extends React.Component{
|
||||||
}
|
}
|
||||||
//console.log(values);
|
//console.log(values);
|
||||||
let newVals = {
|
let newVals = {
|
||||||
...values
|
name: values.name,
|
||||||
|
endpoint: values.endpoint,
|
||||||
|
basic_auth: {
|
||||||
|
username: values.username,
|
||||||
|
password: values.password,
|
||||||
|
},
|
||||||
|
description: values.description,
|
||||||
|
enabled: values.enabled,
|
||||||
|
order: values.order,
|
||||||
}
|
}
|
||||||
delete(newVals['confirm']);
|
|
||||||
if(clusterConfig.editMode === 'NEW') {
|
if(clusterConfig.editMode === 'NEW') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'clusterConfig/addCluster',
|
type: 'clusterConfig/addCluster',
|
||||||
payload: newVals,
|
payload: newVals,
|
||||||
}).then(function (rel){
|
}).then(function (rel){
|
||||||
if(rel){
|
if(rel){
|
||||||
|
message.success("添加成功")
|
||||||
router.push('/system/cluster');
|
router.push('/system/cluster');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -61,6 +69,7 @@ class ClusterForm extends React.Component{
|
||||||
payload: newVals,
|
payload: newVals,
|
||||||
}).then(function (rel){
|
}).then(function (rel){
|
||||||
if(rel){
|
if(rel){
|
||||||
|
message.success("修改成功")
|
||||||
router.push('/system/cluster');
|
router.push('/system/cluster');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -124,34 +133,21 @@ class ClusterForm extends React.Component{
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="ES 用户名">
|
<Form.Item label="ES 用户名">
|
||||||
{getFieldDecorator('username', {
|
{getFieldDecorator('username', {
|
||||||
initialValue: editValue.username,
|
initialValue: editValue.basic_auth.username,
|
||||||
rules: [
|
rules: [
|
||||||
],
|
],
|
||||||
})(<Input autoComplete='off' />)}
|
})(<Input autoComplete='off' />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="ES 密码" hasFeedback>
|
<Form.Item label="ES 密码" hasFeedback>
|
||||||
{getFieldDecorator('password', {
|
{getFieldDecorator('password', {
|
||||||
initialValue: editValue.password,
|
initialValue: editValue.basic_auth.password,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
validator: this.validateToNextPassword,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})(<Input.Password />)}
|
})(<Input.Password />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="ES 确认密码" hasFeedback>
|
|
||||||
{getFieldDecorator('confirm', {
|
|
||||||
initialValue: editValue.password,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
validator: this.compareToFirstPassword,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})(<Input.Password onBlur={this.handleConfirmBlur} />)}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="排序权重">
|
<Form.Item label="排序权重">
|
||||||
{getFieldDecorator('order', {
|
{getFieldDecorator('order', {
|
||||||
initialValue: editValue.order,
|
initialValue: editValue.order || 0,
|
||||||
})(<InputNumber />)}
|
})(<InputNumber />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="描述">
|
<Form.Item label="描述">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Button, Card, Col, Divider, Form, Input, Row, Table, Switch, Icon, Popconfirm} from "antd";
|
import {Button, Card, Col, Divider, Form, Input, Row, Table, Switch, Icon, Popconfirm, message} from "antd";
|
||||||
import Link from "umi/link";
|
import Link from "umi/link";
|
||||||
import {connect} from "dva";
|
import {connect} from "dva";
|
||||||
|
|
||||||
|
@ -18,12 +18,15 @@ class Index extends React.Component {
|
||||||
key: 'endpoint',
|
key: 'endpoint',
|
||||||
},{
|
},{
|
||||||
title: '用户名',
|
title: '用户名',
|
||||||
dataIndex: 'username',
|
dataIndex: 'basic_auth.username',
|
||||||
key: 'username',
|
key: 'username',
|
||||||
},{
|
},{
|
||||||
title: '密码',
|
title: '密码',
|
||||||
dataIndex: 'password',
|
dataIndex: 'basic_auth.password',
|
||||||
key: 'password',
|
key: 'password',
|
||||||
|
render: (val) =>{
|
||||||
|
return "******";
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
title: '排序权重',
|
title: '排序权重',
|
||||||
dataIndex: 'order',
|
dataIndex: 'order',
|
||||||
|
@ -60,7 +63,9 @@ class Index extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchData({})
|
if(typeof this.props.clusterConfig.data === 'undefined') {
|
||||||
|
this.fetchData({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchClick = ()=>{
|
handleSearchClick = ()=>{
|
||||||
|
@ -77,6 +82,10 @@ class Index extends React.Component {
|
||||||
payload: {
|
payload: {
|
||||||
id: record.id
|
id: record.id
|
||||||
}
|
}
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
message.success("删除成功");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +101,7 @@ class Index extends React.Component {
|
||||||
handleNewClick = () => {
|
handleNewClick = () => {
|
||||||
this.saveData({
|
this.saveData({
|
||||||
editMode: 'NEW',
|
editMode: 'NEW',
|
||||||
editValue: {},
|
editValue: {basic_auth: {}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
handleEditClick = (record)=>{
|
handleEditClick = (record)=>{
|
||||||
|
|
|
@ -27,6 +27,28 @@ export default {
|
||||||
message.error(res.error)
|
message.error(res.error)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
let {data, total} = yield select(state => state.clusterConfig);
|
||||||
|
data.unshift({
|
||||||
|
...res._source,
|
||||||
|
id: res._id,
|
||||||
|
});
|
||||||
|
yield put({
|
||||||
|
type: 'saveData',
|
||||||
|
payload: {
|
||||||
|
data,
|
||||||
|
total: {
|
||||||
|
...total,
|
||||||
|
value: total.value + 1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
yield put({
|
||||||
|
type: 'global/addCluster',
|
||||||
|
payload: {
|
||||||
|
id: res._id,
|
||||||
|
name: res._source.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
*updateCluster({payload}, {call, put, select}) {
|
*updateCluster({payload}, {call, put, select}) {
|
||||||
|
@ -35,6 +57,27 @@ export default {
|
||||||
message.error(res.error)
|
message.error(res.error)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
let {data} = yield select(state => state.clusterConfig);
|
||||||
|
let idx = data.findIndex((item)=>{
|
||||||
|
return item.id === res._id;
|
||||||
|
});
|
||||||
|
data[idx] = {
|
||||||
|
...data[idx],
|
||||||
|
...res._source
|
||||||
|
};
|
||||||
|
yield put({
|
||||||
|
type: 'saveData',
|
||||||
|
payload: {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
yield put({
|
||||||
|
type: 'global/updateCluster',
|
||||||
|
payload: {
|
||||||
|
id: res._id,
|
||||||
|
name: res._source.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
*deleteCluster({payload}, {call, put, select}) {
|
*deleteCluster({payload}, {call, put, select}) {
|
||||||
|
@ -51,7 +94,16 @@ export default {
|
||||||
type: 'saveData',
|
type: 'saveData',
|
||||||
payload: {
|
payload: {
|
||||||
data,
|
data,
|
||||||
total: total -1,
|
total: {
|
||||||
|
...total,
|
||||||
|
value: total.value + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
yield put({
|
||||||
|
type: 'global/removeCluster',
|
||||||
|
payload: {
|
||||||
|
id: payload.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -9,7 +9,9 @@ export async function createClusterConfig(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateClusterConfig(params) {
|
export async function updateClusterConfig(params) {
|
||||||
return request(`${pathPrefix}/system/cluster/${params.id}`, {
|
let id = params.id;
|
||||||
|
delete(params['id']);
|
||||||
|
return request(`${pathPrefix}/system/cluster/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: params,
|
body: params,
|
||||||
});
|
});
|
||||||
|
@ -23,7 +25,7 @@ export async function deleteClusterConfig(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchClusterConfig(params) {
|
export async function searchClusterConfig(params) {
|
||||||
let url = `${pathPrefix}/system/cluster`;
|
let url = `${pathPrefix}/system/cluster/_search`;
|
||||||
let args = buildQueryArgs({
|
let args = buildQueryArgs({
|
||||||
name: params.name,
|
name: params.name,
|
||||||
enabled: params.enabled
|
enabled: params.enabled
|
||||||
|
|
Loading…
Reference in New Issue