Skip to content

Commit

Permalink
[ISSUE-3917] Enhance service detail console web, add metadata filter (#…
Browse files Browse the repository at this point in the history
…4439)

* add metadata filter at back-end of nacos naming console

* code enhance

* 添加input,tag等组件,初步布局

* 实现页面交互,可添加,删除filter

* 更新filter向后端发起请求获取数据

* 实现更新更改过滤条件,刷新表格

* 添加国际化配置

* 输入完后按空格直接添加,没输入的input给出响应

* 提交单元测试,更新打包后的静态资源

* 修改配置 解决生产环境上sourcemap不生效的问题

* 用hook重构组件InstanceFilter

* 多个集群各自使用过滤

* 解决多集群时,元数据过滤无法单独使用的问题

* revert backend code

* revert metadata filter test

* 只从客户端已获取到的实例中过滤

* 变动更新到打包后的mian.js

Co-authored-by: jzhishu <john1994@foxmail.com>
  • Loading branch information
horizonzy and jzhishu authored Dec 21, 2020
1 parent 30a04f9 commit 638d89b
Show file tree
Hide file tree
Showing 12 changed files with 602 additions and 343 deletions.
6 changes: 3 additions & 3 deletions console-ui/build/webpack.prod.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ module.exports = Object.assign({}, base, {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
parallel: true
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'eval-source-map',
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns:[
Expand All @@ -54,5 +54,5 @@ module.exports = Object.assign({}, base, {
chunkFilename: '[id].css',
}),
],
mode: 'production',
mode: 'production'
});
5 changes: 5 additions & 0 deletions console-ui/src/locales/en-US.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ const I18N_CONF = {
serviceNameRequired: 'Please enter a service name',
protectThresholdRequired: 'Please enter a protect threshold',
},
InstanceFilter: {
title: 'Metadata Filter',
addFilter: 'Add Filter',
clear: 'Clear',
},
InstanceTable: {
operation: 'Operation',
port: 'Port',
Expand Down
5 changes: 5 additions & 0 deletions console-ui/src/locales/zh-CN.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ const I18N_CONF = {
serviceNameRequired: '请输入服务名',
protectThresholdRequired: '请输入保护阈值',
},
InstanceFilter: {
title: '元数据过滤',
addFilter: '添加过滤',
clear: '清空',
},
InstanceTable: {
operation: '操作',
port: '端口',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { request } from '../../../globalLib';
import { Button, ConfigProvider, Message, Pagination, Table } from '@alifd/next';
import { HEALTHY_COLOR_MAPPING } from './constant';
import EditInstanceDialog from './EditInstanceDialog';
import { isDiff } from './util';

@ConfigProvider.config
class InstanceTable extends React.Component {
Expand All @@ -30,6 +31,11 @@ class InstanceTable extends React.Component {
clusterName: PropTypes.string,
serviceName: PropTypes.string,
groupName: PropTypes.string,
filters: PropTypes.object,
};

static defaultProps = {
filters: new Map(),
};

constructor(props) {
Expand All @@ -38,6 +44,7 @@ class InstanceTable extends React.Component {
this.state = {
loading: false,
instance: { count: 0, list: [] },
// tableData: {},
pageNum: 1,
pageSize: 10,
};
Expand All @@ -56,7 +63,8 @@ class InstanceTable extends React.Component {
}

getInstanceList() {
const { clusterName, serviceName, groupName } = this.props;
const { clusterName, serviceName, groupName, filters } = this.props;

if (!clusterName) return;
const { pageSize, pageNum } = this.state;
request({
Expand Down Expand Up @@ -118,9 +126,16 @@ class InstanceTable extends React.Component {
const { locale = {} } = this.props;
const { clusterName, serviceName, groupName } = this.props;
const { instance, pageSize, loading } = this.state;
return instance.count ? (
const instanceList = instanceFilter(instance.list, this.props.filters);

const _instance = {
count: instanceList.length,
list: instanceList,
};

return _instance.count ? (
<div>
<Table dataSource={instance.list} loading={loading} getRowProps={this.rowColor}>
<Table dataSource={_instance.list} loading={loading} rowProps={this.rowColor}>
<Table.Column width={138} title="IP" dataIndex="ip" />
<Table.Column width={100} title={locale.port} dataIndex="port" />
<Table.Column
Expand Down Expand Up @@ -170,10 +185,10 @@ class InstanceTable extends React.Component {
)}
/>
</Table>
{instance.count > pageSize ? (
{_instance.count > pageSize ? (
<Pagination
className="pagination"
total={instance.count}
total={_instance.count}
pageSize={pageSize}
onChange={currentPage => this.onChangePage(currentPage)}
/>
Expand All @@ -192,4 +207,20 @@ class InstanceTable extends React.Component {
}
}

const instanceFilter = function(array, filters) {
return array.filter(item => {
const { metadata } = item;
let isTargetInstance = true;

filters.forEach((value, key) => {
if (value !== metadata[key]) {
isTargetInstance = false;
return isTargetInstance;
}
});

return isTargetInstance;
});
};

export default InstanceTable;
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { request } from '@/globalLib';
import { Input, Button, Card, ConfigProvider, Form, Loading, Message } from '@alifd/next';
import { Input, Button, Card, ConfigProvider, Form, Loading, Message, Tag } from '@alifd/next';
import EditServiceDialog from './EditServiceDialog';
import EditClusterDialog from './EditClusterDialog';
import InstanceTable from './InstanceTable';
import { getParameter } from 'utils/nacosutil';
import MonacoEditor from 'components/MonacoEditor';
import { MONACO_READONLY_OPTIONS, METADATA_ENTER } from './constant';
import InstanceFilter from './InstanceFilter';
import './ServiceDetail.scss';

const FormItem = Form.Item;
Expand Down Expand Up @@ -56,6 +57,7 @@ class ServiceDetail extends React.Component {
service: {},
pageSize: 10,
pageNum: {},
instanceFilters: new Map(),
};
}

Expand Down Expand Up @@ -94,9 +96,19 @@ class ServiceDetail extends React.Component {
this.editClusterDialog.current.getInstance().show(cluster);
}

setFilters = clusterName => filters => {
const { instanceFilters } = this.state;
const newFilters = new Map(Array.from(instanceFilters));
newFilters.set(clusterName, filters);

this.setState({
instanceFilters: newFilters,
});
};

render() {
const { locale = {} } = this.props;
const { serviceName, groupName, loading, service = {}, clusters } = this.state;
const { serviceName, groupName, loading, service = {}, clusters, instanceFilters } = this.state;
const { metadata = {}, selector = {} } = service;
let metadataText = '';
if (Object.keys(metadata).length) {
Expand Down Expand Up @@ -175,10 +187,12 @@ class ServiceDetail extends React.Component {
</Button>
}
>
<InstanceFilter setFilters={this.setFilters(cluster.name)} />
<InstanceTable
clusterName={cluster.name}
serviceName={serviceName}
groupName={groupName}
filters={instanceFilters.get(cluster.name)}
/>
</Card>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
.cluster-card {
margin-bottom: 30px;
}
.inner-card {
margin-bottom: 10px;
}
}

.service-detail-edit-dialog,
Expand Down
137 changes: 137 additions & 0 deletions console-ui/src/pages/ServiceManagement/ServiceDetail/instanceFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Input, ConfigProvider, Button, Form, Tag, Card } from '@alifd/next';
import { isDiff } from './util';

const { Group: TagGroup, Closeable: CloseableTag } = Tag;
const FormItem = Form.Item;

function InstanceFilter(props) {
const [key, setKey] = useState('');
const [value, setValue] = useState('');
const [keyState, setKeyState] = useState('');
const [valueState, setValueState] = useState('');
const [filters, setFilters] = useState(new Map());
const { locale = {} } = props;

const addFilter = () => {
updateInput();

if (key && value) {
const newFilters = new Map(Array.from(filters)).set(key, value);

setFilters(newFilters);
setKeyState('');
setValueState('');

clearInput();
}
};

const removeFilter = key => {
const newFilters = new Map(Array.from(filters));
newFilters.delete(key);

setFilters(newFilters);
};

const clearFilters = () => {
setFilters(new Map());
};

const clearInput = () => {
setKey('');
setValue('');
};

const updateInput = () => {
if (!key) {
setKeyState('error');
} else {
setKeyState('');
}

if (!value) {
setValueState('error');
} else {
setValueState('');
}
};

useEffect(() => {
props.setFilters(filters);
}, [filters]);

return (
<Card contentHeight="auto" className="inner-card">
<Form inline size="small">
<FormItem label={locale.title}>
<FormItem>
<Input
placeholder={'key'}
value={key}
trim
onChange={key => setKey(key)}
onPressEnter={addFilter}
state={keyState}
/>
</FormItem>
<FormItem>
<Input
placeholder={'value'}
value={value}
trim
onChange={value => setValue(value)}
onPressEnter={addFilter}
state={valueState}
/>
</FormItem>
<FormItem label="">
<Button type="primary" onClick={addFilter} style={{ marginRight: 10 }}>
{locale.addFilter}
</Button>
{filters.size > 0 ? (
<Button type="primary" onClick={clearFilters}>
{locale.clear}
</Button>
) : (
''
)}
</FormItem>
</FormItem>
</Form>
<TagGroup>
{Array.from(filters).map(filter => {
return (
<CloseableTag size="medium" key={filter[0]} onClose={() => removeFilter(filter[0])}>
{`${filter[0]} : ${filter[1]}`}
</CloseableTag>
);
})}
</TagGroup>
</Card>
);
}

InstanceFilter.propTypes = {
locale: PropTypes.object,
setFilters: PropTypes.func.isRequired,
};

export default ConfigProvider.config(InstanceFilter);
26 changes: 26 additions & 0 deletions console-ui/src/pages/ServiceManagement/ServiceDetail/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export const isDiff = function(prev, next) {
if (prev.size !== next.size) return true;

let isDiff = false;
next.forEach((value, key) => {
isDiff = value !== prev.get(key);
});

return isDiff;
};
Loading

0 comments on commit 638d89b

Please sign in to comment.