This is an automated email from the ASF dual-hosted git repository.

yangwenjie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new b709682a [feature] Transform the 'divide,' 'grpc,' and 'websocket' 
plugins for discovery (#388)
b709682a is described below

commit b709682adc63a9af56e9311558692f96f4f22db3
Author: lulu <[email protected]>
AuthorDate: Wed Jan 3 21:25:43 2024 +0800

    [feature] Transform the 'divide,' 'grpc,' and 'websocket' plugins for 
discovery (#388)
    
    * refactor: change "tcp" to "discovery"
    
    * refactor: Change the parameters of componentDidUpdate to adapt to the new 
version of react
    
    * feature: add tabPane to divide plugin selector
    
    * refactor: Modify variables with the same name
    
    * refactor: Completed the addition of the discovery page in the divide 
plugin
    
    * refactor: Completed the addition of the discovery page in the divide 
plugin
    
    * feature: addSelector success
    
    * feature: updateSelector success
    
    * refactor: refactor updateSelector success
    
    * fix: fix discovery upstream data acquisition
    
    * [feature] Add discovery tabPane to Websocket and Grpc
    
    * feature: add upstream api
    
    * fix: selector update bug
    
    * fix:upstream bug
    
    * Revert "fix:upstream bug"
    
    This reverts commit db7893c2d04e1d6b70b14e6e99ec2c300ea1ada3.
    
    * refactor:upstream update
    
    * fix:upstream bug
    
    * feature:config import from backend
    
    * fix:null config bug
    
    * refactor: change table to editableTable
    
    * refactor: remove unused code
    
    * refactor: remove protocol in upstreamTable, change tcp table to editable 
table
    
    * fix: add startupTime and warmupTime
    
    * refactor: change editableTable
    
    * fix: fix DiscoveryImportModal
    
    * fix: fix button
    
    * fix: remove unused code
    
    * fix: discovery props set
    
    * refactor: tcp editableTable
    
    * fix: add eureka discovery icon
    
    * fix: remove unused local variable
    
    * fix: remove unused code
    
    * fix: fix discovery props style bug and add i18n fields
    
    * fix: remove unused component
---
 src/locales/en-US.json                             |  11 +-
 src/locales/zh-CN.json                             |   9 +-
 src/models/common.js                               |   3 +-
 src/models/discovery.js                            |  41 +-
 src/routes/Plugin/Common/Selector.js               | 928 ++++++++++++++-------
 src/routes/Plugin/Common/index.js                  | 189 ++++-
 .../Discovery/{TcpCard.js => DiscoveryCard.js}     |  11 +-
 .../Plugin/Discovery/DiscoveryConfigModal.js       |  18 +-
 src/routes/Plugin/Discovery/DiscoveryIcon.js       |  15 +
 .../Plugin/Discovery/DiscoveryImportModal.js       |  94 +++
 .../Plugin/Discovery/DiscoveryUpstreamTable.js     | 287 +++++++
 src/routes/Plugin/Discovery/ProxySelectorModal.js  |  75 +-
 src/routes/Plugin/Discovery/UpstreamTable.js       | 213 -----
 .../Plugin/Discovery/{tcp.less => discovery.less}  |   2 +-
 src/routes/Plugin/Discovery/index.js               |  92 +-
 .../PluginRuleHandle/ParamPluginRuleHandle.js      |   6 +-
 src/services/api.js                                |  19 +
 src/utils/AuthRoute.js                             |   1 -
 18 files changed, 1383 insertions(+), 631 deletions(-)

diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index bbabfe67..a48e9021 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -405,12 +405,19 @@
   "SHENYU.DISCOVERY.SELECTOR.CONFIGURATION": "Discovery Configuration",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM": "Discovery Upstreams",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD": "Add Discovery Upstream",
-  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE": "Sure to delete?",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM": "Sure to delete?",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM": "Sure to cancel?",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION": "operation",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL": "Cancel",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE": "Save",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT": "Edit",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateCreated": "DateCreated",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateUpdated": "DateUpdated",
   "SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC": "Basic Config",
   "SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY": "Discovery Config",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT": "Import Background Discovery 
Config",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "Configuration Import 
Confirmation",
   "SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "Are you sure to delete this 
selector?",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "Import",
   "SHENYU.COMMON.REFRESH": "Refresh"
-}
\ No newline at end of file
+}
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index e457a42a..369f9913 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -408,12 +408,19 @@
   "SHENYU.DISCOVERY.SELECTOR.CONFIGURATION": "服务发现配置",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM": "服务下游列表",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD": "添加下游服务",
-  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE": "确定删除吗",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM": "确定删除吗",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM": "确定取消吗",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION": "操作",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL": "取消",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE": "保存",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT": "编辑",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateCreated": "创建时间",
   "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DateUpdated": "更新时间",
   "SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC": "基本配置",
   "SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY": "服务发现",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT": "导入后台服务发现配置",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM": "后台配置导入确认",
+  "SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE": "导入",
   "SHENYU.DISCOVERY.SELECTOR.DELETE.CONFIRM": "确定删除该选择器吗?",
   "SHENYU.COMMON.REFRESH": "刷新"
 }
diff --git a/src/models/common.js b/src/models/common.js
index 1c392de7..db1ae7f0 100644
--- a/src/models/common.js
+++ b/src/models/common.js
@@ -109,7 +109,8 @@ export default {
       const json = yield call(addSelector, payload);
       if (json.code === 200) {
         message.success(getIntlContent('SHENYU.COMMON.RESPONSE.ADD.SUCCESS'));
-        callback();
+        const selectorId = json.data;
+        callback(selectorId);
         yield put({ type: "reload", fetchValue });
       } else {
         message.warn(json.message);
diff --git a/src/models/discovery.js b/src/models/discovery.js
index fc198069..0eed512a 100644
--- a/src/models/discovery.js
+++ b/src/models/discovery.js
@@ -25,8 +25,9 @@ import {
   updateProxySelector,
   getDiscovery,
   refreshProxySelector,
-  deleteDiscovery
-
+  deleteDiscovery,
+  bindingSelector,
+  updateDiscoveryUpstream
 } from "../services/api";
 import {getIntlContent} from "../utils/IntlUtils";
 
@@ -35,7 +36,7 @@ export default {
 
   state: {
     typeEnums: [],
-    selectorList: [],
+    proxySelectorList: [],
     chosenType: '',
     totalPage: 0,
     currentPage: 1,
@@ -43,7 +44,7 @@ export default {
   },
 
   effects: {
-    * fetchSelector(params, {call, put}) {
+    * fetchProxySelectors(params, {call, put}) {
       const {payload} = params;
       const json = yield call(fetchProxySelector, payload);
       if (json.code === 200) {
@@ -80,6 +81,7 @@ export default {
         message.warn(json.message);
       }
     },
+
     * delete(params, {call, put}) {
       const {payload, fetchValue} = params;
       const { list } = payload;
@@ -92,6 +94,7 @@ export default {
         message.warn(json.message);
       }
     },
+
     * update(params, {call, put}) {
       const {payload, callback, fetchValue} = params;
       const json = yield call(updateProxySelector, payload);
@@ -103,11 +106,12 @@ export default {
         message.warn(json.message);
       }
     },
+
     * reload(params, {put}) {
       const {fetchValue} = params;
       const {name = '', currentPage, pageSize} = fetchValue;
       const payload = {name, currentPage, pageSize};
-      yield put({type: "fetchSelector", payload});
+      yield put({type: "fetchProxySelectors", payload});
     },
 
 
@@ -116,9 +120,9 @@ export default {
       const json = yield call(postDiscoveryInsertOrUpdate, payload);
       if (json.code === 200) {
         
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.CONFIGURATION.SUCCESS'));
-        const { data } = json;
+        const discoveryConfigData = json.data;
         if (callback) {
-          callback(data);
+          callback(discoveryConfigData);
         }
       } else {
         message.warn(json.message);
@@ -162,6 +166,27 @@ export default {
       }
     },
 
+    * bindSelector(params, {call}) {
+      const {payload} = params;
+      const json = yield call(bindingSelector, payload);
+      if (json.code === 200) {
+        // 
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.ADD.SUCCESS'));
+        // callback();
+      } else {
+        message.warn(json.message);
+      }
+    },
+
+    * updateDiscoveryUpstream(params, {call}) {
+      const { discoveryHandlerId, upstreams } = params.payload;
+      const json = yield call(updateDiscoveryUpstream, discoveryHandlerId, 
upstreams);
+      if (json.code === 200) {
+        // 
message.success(getIntlContent('SHENYU.COMMON.RESPONSE.UPDATE.SUCCESS'));
+      } else {
+        message.warn(json.message);
+      }
+    },
+
   },
 
   reducers: {
@@ -169,7 +194,7 @@ export default {
       return {
         ...state,
         totalPage: payload.total,
-        selectorList: payload.dataList,
+        proxySelectorList: payload.dataList,
       };
     },
 
diff --git a/src/routes/Plugin/Common/Selector.js 
b/src/routes/Plugin/Common/Selector.js
index 40e33413..021d2a09 100644
--- a/src/routes/Plugin/Common/Selector.js
+++ b/src/routes/Plugin/Common/Selector.js
@@ -32,15 +32,19 @@ import {
   Icon,
   InputNumber,
   DatePicker,
-  TimePicker
+  TimePicker, Tabs, Divider
 } from "antd";
 import { connect } from "dva";
 import classnames from "classnames";
 import styles from "../index.less";
 import { getIntlContent } from "../../../utils/IntlUtils";
 import SelectorCopy from "./SelectorCopy";
+import { findKeyByValue } from "../../../utils/utils";
+import DiscoveryImportModal from "../Discovery/DiscoveryImportModal";
+import EditableFormTable from "../Discovery/DiscoveryUpstreamTable.js";
 
 const { Item } = Form;
+const { TabPane } = Tabs;
 const { Option } = Select;
 
 const formItemLayout = {
@@ -54,10 +58,11 @@ const formCheckLayout = {
 
 let id = 0;
 
-@connect(({ pluginHandle, global, shenyuDict }) => ({
+@connect(({ pluginHandle, global, shenyuDict, discovery }) => ({
   pluginHandle,
   platform: global.platform,
-  shenyuDict
+  shenyuDict,
+  discovery
 }))
 class AddModal extends Component {
   constructor(props) {
@@ -75,6 +80,7 @@ class AddModal extends Component {
     }
 
     const { divideUpstreams = [], gray = false, serviceId = "" } = data;
+    const { discoveryUpstreams = [], isDiscovery, isAdd = true, 
discoveryConfig = {} } = this.props;
 
     if (pluginId === "8") {
       id = divideUpstreams.length;
@@ -85,18 +91,36 @@ class AddModal extends Component {
       gray,
       serviceId,
       divideUpstreams,
-
-      visible: false
+      visible: false,
+      pluginHandleList: [],
+      upstreams: discoveryUpstreams,
+      recordCount: discoveryUpstreams ? discoveryUpstreams.length : 0,
+      discoveryHandler: null,
+      defaultValueList: null,
+      configPropsJson: {},
+      selectedDiscoveryValue : 'local',
+      showDiscoveryImportModal: false,
+      importedDiscoveryId: ''
     };
 
     this.initSelectorCondition(props);
-    this.initDics();
+
+    if (isDiscovery && !isAdd){
+      this.state.configPropsJson = JSON.parse(discoveryConfig.props);
+      this.state.selectedDiscoveryValue = discoveryConfig.discoveryType;
+    }
   }
 
   componentDidMount() {
-    const { dispatch, pluginId, handle, multiSelectorHandle } = this.props;
+    const { dispatch, pluginId, handle, multiSelectorHandle, isDiscovery } = 
this.props;
     this.setState({ pluginHandleList: [] });
     let type = 1;
+    this.initDics();
+
+    dispatch({
+      type: "discovery/fetchEnumType"
+    })
+
     dispatch({
       type: "pluginHandle/fetchByPluginId",
       payload: {
@@ -106,6 +130,20 @@ class AddModal extends Component {
         isHandleArray: multiSelectorHandle,
         callBack: pluginHandles => {
           this.setPluginHandleList(pluginHandles);
+          if (isDiscovery && Object.keys(pluginHandles).length > 0) {
+            const filteredArray = pluginHandles[0].filter(item => item.field 
!== 'discoveryHandler');
+
+            const handlerArray = pluginHandles[0].filter(item => item.field 
=== 'discoveryHandler');
+            this.setState({discoveryHandler: handlerArray});
+
+            pluginHandles[0] = filteredArray;
+            this.setState({ pluginHandleList: pluginHandles });
+
+            if (handlerArray.length !== 0) {
+              let defaultValue = handlerArray[0].defaultValue;
+              this.setState({ defaultValueList: defaultValue.split(",")  });
+            }
+          }
         }
       }
     });
@@ -137,16 +175,22 @@ class AddModal extends Component {
     this.initDic("operator");
     this.initDic("matchMode");
     this.initDic("paramType");
+    this.initDic("discoveryMode");
   };
 
   initDic = type => {
-    const { dispatch } = this.props;
+    const { dispatch, isAdd = true} = this.props;
     dispatch({
       type: "shenyuDict/fetchByType",
       payload: {
         type,
         callBack: dics => {
           this.state[`${type}Dics`] = dics;
+          if (type === "discoveryMode" && isAdd ) {
+            let configProps = dics.filter(item => item.dictName === 
'zookeeper');
+            let propsEntries = JSON.parse(configProps[0]?.dictValue || "{}");
+            this.setState({configPropsJson: propsEntries});
+          }
         }
       }
     });
@@ -187,8 +231,8 @@ class AddModal extends Component {
 
   handleSubmit = e => {
     e.preventDefault();
-    const { form, handleOk, multiSelectorHandle, pluginId } = this.props;
-    const { selectorConditions, selectValue, pluginHandleList } = this.state;
+    const { form, handleOk, multiSelectorHandle, pluginId, isDiscovery } = 
this.props;
+    const { selectorConditions, selectValue, pluginHandleList, 
defaultValueList, configPropsJson, upstreams, importedDiscoveryId } = 
this.state;
     let handle = [];
 
     form.validateFieldsAndScroll((err, values) => {
@@ -196,38 +240,67 @@ class AddModal extends Component {
         const mySubmit =
           selectValue !== "0" && this.checkConditions(selectorConditions);
         if (mySubmit || selectValue === "0") {
-          pluginHandleList.forEach((handleList, index) => {
-            handle[index] = {};
-            handleList.forEach(item => {
-              if (pluginId === "8") {
-                const { keys, divideUpstreams } = values;
-                const data = {
-                  [item.field]: values[item.field],
-                  gray: values.gray
-                };
-
-                if (Array.isArray(divideUpstreams) && divideUpstreams.length) {
-                  data.divideUpstreams = keys.map(key => divideUpstreams[key]);
+          if ( isDiscovery ) {
+            // The discoveryProps refer to the attributes corresponding to 
each registration center mode
+            const discoveryPropsJson = {};
+            Object.entries(configPropsJson).forEach(([key]) => {
+              discoveryPropsJson[key] = form.getFieldValue(key);
+            });
+            const discoveryProps = JSON.stringify(discoveryPropsJson);
+
+            // The handler refers to the url, status, weight, protocol, etc. 
of the discovery module.
+            let handler = {};
+            if ( defaultValueList !== null) {
+              defaultValueList.forEach(item => {
+                if ((values[item]) !== undefined){
+                  handler[values[item]] = item;
                 }
-                handle[index] = data;
-                delete values[item.field];
-                delete values.divideUpstreams;
-                delete values.gray;
-                delete values.key;
-              } else {
-                handle[index][item.field] = values[item.field + index];
-                delete values[item.field + index];
-              }
+              });
+            }
+            handler = JSON.stringify(handler);
+
+            handleOk({...values,
+              sort: Number(values.sort),
+              selectorConditions,
+              handler,
+              discoveryProps,
+              upstreams,
+              importedDiscoveryId});
+
+          } else {
+            pluginHandleList.forEach((handleList, index) => {
+              handle[index] = {};
+              handleList.forEach(item => {
+                if (pluginId === "8") {
+                  const {keys, divideUpstreams} = values;
+                  const data = {
+                    [item.field]: values[item.field],
+                    gray: values.gray
+                  };
+
+                  if (Array.isArray(divideUpstreams) && 
divideUpstreams.length) {
+                    data.divideUpstreams = keys.map(key => 
divideUpstreams[key]);
+                  }
+                  handle[index] = data;
+                  delete values[item.field];
+                  delete values.divideUpstreams;
+                  delete values.gray;
+                  delete values.key;
+                } else {
+                  handle[index][item.field] = values[item.field + index];
+                  delete values[item.field + index];
+                }
+              });
+            });
+            handleOk({
+              ...values,
+              handle: multiSelectorHandle
+                ? JSON.stringify(handle)
+                : JSON.stringify(handle[0]),
+              sort: Number(values.sort),
+              selectorConditions
             });
-          });
-          handleOk({
-            ...values,
-            handle: multiSelectorHandle
-              ? JSON.stringify(handle)
-              : JSON.stringify(handle[0]),
-            sort: Number(values.sort),
-            selectorConditions
-          });
+          }
         }
       }
     });
@@ -332,10 +405,15 @@ class AddModal extends Component {
     const {
       form: { getFieldDecorator, getFieldValue, setFieldsValue },
       multiSelectorHandle,
-      pluginId
+      pluginId,
+      isDiscovery
     } = this.props;
     const labelWidth = 75;
 
+    if (isDiscovery) {
+      return;
+    }
+
     if (pluginId === "8") {
       getFieldDecorator("keys", {
         initialValue: Array.from({
@@ -754,10 +832,43 @@ class AddModal extends Component {
     this.setState({ visible: false, selectValue: formData.type });
   };
 
+  handleImportDiscoveryConfig = configData => {
+    const { form } = this.props;
+    const {
+      type = '',
+      serverList = '',
+      id: discoveryId = '',
+      props = '{}'
+    } = configData || {};
+    const formData = {
+      selectedDiscoveryType: type,
+      serverList
+    };
+    this.setState(
+      { selectedDiscoveryValue: type, showDiscoveryImportModal: false, 
importedDiscoveryId: discoveryId },
+      () => {
+        form.setFieldsValue(formData);
+        this.state.configPropsJson = JSON.parse(props);
+      }
+    );
+  };
+
   onDealChange = (value, item) => {
     item.value = value;
   };
 
+  handleTableChange = (newData) => {
+    this.setState({ upstreams: newData });
+  };
+
+  handleCountChange = (newCount) => {
+    this.setState({ recordCount: newCount });
+  };
+
+  handleDiscoveryValueChange = (value) => {
+    this.setState({selectedDiscoveryValue: value})
+  }
+
   renderOperatorOptions = (operators, paramType) => {
     if (operators && operators instanceof Array) {
       let operatorsFil = operators.map(operate => {
@@ -856,9 +967,18 @@ class AddModal extends Component {
     }
   }
 
-  render() {
+  handleOptions() {
+    const { discovery } = this.props;
+    if (!discovery || !Array.isArray(discovery.typeEnums)) {
+      return [];
+    }
+    return discovery.typeEnums.map(type =>
+      <Option key={type} value={type.toString()}>{type.toString()}</Option>
+    )
+  }
+
+  renderBasicConfig = () => {
     let {
-      onCancel,
       form,
       name = "",
       platform,
@@ -882,278 +1002,514 @@ class AddModal extends Component {
 
     type = `${type.toString()}` || "1";
     let { selectorTypeEnums } = platform;
-
     const { getFieldDecorator } = form;
-
-    return (
-      <Modal
-        width='900px'
-        centered
-        title={getIntlContent("SHENYU.SELECTOR.NAME")}
-        visible
-        okText={getIntlContent("SHENYU.COMMON.SURE")}
-        cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
-        onOk={this.handleSubmit}
-        onCancel={onCancel}
-      >
-        <Form onSubmit={this.handleSubmit} className="login-form">
-          <Item
-            label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
-            {...formItemLayout}
-          >
-            {getFieldDecorator("name", {
-              rules: [
-                {
+    return(
+      <>
+        <Item
+          label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
+          {...formItemLayout}
+        >
+          {getFieldDecorator("name", {
+            rules: [
+              {
                   required: true,
                   message: getIntlContent("SHENYU.COMMON.INPUTNAME")
-                }
-              ],
-              initialValue: name
-            })(
-              <Input
-                allowClear
-                placeholder={getIntlContent(
-                  "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME"
-                )}
-                addonAfter={
-                  <Button
-                    size="small"
-                    type="link"
-                    onClick={() => {
-                      this.setState({ visible: true });
-                    }}
-                  >
-                    {getIntlContent("SHENYU.SELECTOR.COPY")}
-                  </Button>
-                }
-              />
+              }
+            ],
+            initialValue: name
+        })(
+          <Input
+            allowClear
+            placeholder={getIntlContent(
+                "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME"
             )}
-          </Item>
-          <SelectorCopy
-            visible={visible}
-            onOk={this.handleCopyData}
-            onCancel={() => {
-              this.setState({ visible: false });
-            }}
+            addonAfter={
+              <Button
+                size="small"
+                type="link"
+                onClick={() => {
+                    this.setState({ visible: true });
+                }}
+              >
+                {getIntlContent("SHENYU.SELECTOR.COPY")}
+              </Button>
+            }
           />
-          <Item
-            label={getIntlContent("SHENYU.COMMON.TYPE")}
-            {...formItemLayout}
-          >
-            {getFieldDecorator("type", {
+          )}
+        </Item>
+        <SelectorCopy
+          visible={visible}
+          onOk={this.handleCopyData}
+          onCancel={() => {
+              this.setState({ visible: false });
+          }}
+        />
+        <Item
+          label={getIntlContent("SHENYU.COMMON.TYPE")}
+          {...formItemLayout}
+        >
+          {getFieldDecorator("type", {
               rules: [
-                {
-                  required: true,
-                  message: getIntlContent("SHENYU.COMMON.INPUTTYPE")
-                }
+                  {
+                      required: true,
+                      message: getIntlContent("SHENYU.COMMON.INPUTTYPE")
+                  }
               ],
               initialValue: type
-            })(
-              <Select
-                placeholder={getIntlContent("SHENYU.COMMON.TYPE")}
-                onChange={value => this.getSelectValue(value)}
-              >
-                {selectorTypeEnums.map(item => {
-                  return (
-                    <Option key={item.code} value={item.code.toString()}>
-                      {getIntlContent(
-                        
`SHENYU.COMMON.SELECTOR.TYPE.${item.name.toUpperCase()}`,
-                        item.name
-                      )}
-                    </Option>
-                  );
-                })}
-              </Select>
-            )}
-          </Item>
-          {selectValue !== "0" && (
-            <Fragment>
-              <Item
-                label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
-                {...formItemLayout}
-              >
-                {getFieldDecorator("matchMode", {
-                  rules: [
+          })(
+            <Select
+              placeholder={getIntlContent("SHENYU.COMMON.TYPE")}
+              onChange={value => this.getSelectValue(value)}
+            >
+              {selectorTypeEnums.map(item => {
+                return (
+                  <Option key={item.code} value={item.code.toString()}>
+                    {getIntlContent(
+                      `SHENYU.COMMON.SELECTOR.TYPE.${item.name.toUpperCase()}`,
+                      item.name
+                    )}
+                  </Option>
+                );
+              })}
+            </Select>
+          )}
+        </Item>
+        {selectValue !== "0" && (
+          <Fragment>
+            <Item
+              label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
+              {...formItemLayout}
+            >
+              {getFieldDecorator("matchMode", {
+                rules: [
                     {
-                      required: true,
-                      message: getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE")
+                        required: true,
+                        message: getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE")
                     }
-                  ],
-                  initialValue: `${matchMode}`
-                })(
-                  <Select
-                    placeholder={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
-                  >
-                    {matchModeDics &&
-                      matchModeDics.map(item => {
-                        return (
-                          <Option key={item.dictValue} value={item.dictValue}>
-                            {item.dictName}
-                          </Option>
-                        );
-                      })}
-                  </Select>
-                )}
-              </Item>
-              <div className={styles.condition}>
-                <Item
-                  label={getIntlContent("SHENYU.COMMON.CONDITION")}
-                  required
-                  {...formItemLayout}
+                ],
+                initialValue: `${matchMode}`
+              })(
+                <Select
+                  placeholder={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
                 >
-                  {selectorConditions.map((item, index) => {
-                    return (
-                      <Row key={index} gutter={8}>
-                        <Col span={5}>
-                          <Select
-                            onChange={value => {
+                  {matchModeDics &&
+                    matchModeDics.map(item => {
+                      return (
+                        <Option key={item.dictValue} value={item.dictValue}>
+                          {item.dictName}
+                        </Option>
+                      );
+                    })}
+                </Select>
+              )}
+            </Item>
+            <div className={styles.condition}>
+              <Item
+                label={getIntlContent("SHENYU.COMMON.CONDITION")}
+                required
+                {...formItemLayout}
+              >
+                {selectorConditions.map((item, index) => {
+                  return (
+                    <Row key={index} gutter={8}>
+                      <Col span={5}>
+                        <Select
+                          onChange={value => {
                               this.conditionChange(index, "paramType", value);
-                            }}
-                            value={item.paramType}
-                          >
-                            {this.renderParamTypeOptions(paramTypeDics)}
-                          </Select>
-                        </Col>
-                        <Col
-                          span={4}
-                          style={{
-                            display: this.state[`paramTypeValueEn${index}`]
-                              ? "none"
-                              : "block"
                           }}
+                          value={item.paramType}
                         >
-                          <Input
-                            allowClear
-                            onChange={e => {
-                              this.conditionChange(
+                          {this.renderParamTypeOptions(paramTypeDics)}
+                        </Select>
+                      </Col>
+                      <Col
+                        span={4}
+                        style={{
+                            display: this.state[`paramTypeValueEn${index}`]
+                                ? "none"
+                                : "block"
+                        }}
+                      >
+                        <Input
+                          allowClear
+                          onChange={e => {
+                            this.conditionChange(
                                 index,
                                 "paramName",
                                 e.target.value
-                              );
-                            }}
-                            placeholder={item.paramName}
-                          />
-                        </Col>
-                        <Col span={4}>
-                          <Select
-                            onChange={value => {
-                              this.conditionChange(index, "operator", value);
-                            }}
-                            value={item.operator}
-                          >
-                            {this.renderOperatorOptions(operatorDics, 
item.paramType)}
-                          </Select>
-                        </Col>
-
-                        <Col
-                          span={7}
-                          style={{
-                            display: item.operator === "isBlank"
-                              ? "none"
-                              : "block"
+                            );
+                          }}
+                          placeholder={item.paramName}
+                        />
+                      </Col>
+                      <Col span={4}>
+                        <Select
+                          onChange={value => {
+                            this.conditionChange(index, "operator", value);
                           }}
+                          value={item.operator}
                         >
-                          <Tooltip title={item.paramValue}>
-                            {this.getParamValueInput(item, index)}
-                          </Tooltip>
-                        </Col>
-                        <Col span={4}>
-                          <Button
-                            type="danger"
-                            onClick={() => {
+                          {this.renderOperatorOptions(operatorDics, 
item.paramType)}
+                        </Select>
+                      </Col>
+
+                      <Col
+                        span={7}
+                        style={{
+                            display: item.operator === "isBlank"
+                                ? "none"
+                                : "block"
+                        }}
+                      >
+                        <Tooltip title={item.paramValue}>
+                          {this.getParamValueInput(item, index)}
+                        </Tooltip>
+                      </Col>
+                      <Col span={4}>
+                        <Button
+                          type="danger"
+                          onClick={() => {
                               this.handleDelete(index);
-                            }}
-                            style={{ marginLeft: 10 }}
-                          >
-                            {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
-                          </Button>
-                        </Col>
-                      </Row>
-                    );
+                          }}
+                          style={{ marginLeft: 10 }}
+                        >
+                          {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
+                        </Button>
+                      </Col>
+                    </Row>
+                  );
                   })}
-                </Item>
-                <Item
-                  label={' '}
-                  colon={false}
-                  {...formItemLayout}
-                >
-                  <Button className={styles.addButton} 
onClick={this.handleAdd} type="primary">
-                    {getIntlContent("SHENYU.COMMON.ADD")} {" "}
-                    {getIntlContent("SHENYU.COMMON.CONDITION")}
-                  </Button>
-                </Item>
-              </div>
-
-            </Fragment>
-          )}
-          <div className={styles.layout}>
-            <Item
-              {...formCheckLayout}
-              label={getIntlContent("SHENYU.SELECTOR.CONTINUE")}
-            >
-              {getFieldDecorator("continued", {
-                initialValue: continued,
-                valuePropName: "checked",
-                rules: [{ required: true }]
-              })(<Switch />)}
-            </Item>
-            <Item
-              style={{ margin: "0 30px" }}
-              {...formCheckLayout}
-              label={getIntlContent("SHENYU.SELECTOR.PRINTLOG")}
-            >
-              {getFieldDecorator("loged", {
-                initialValue: loged,
-                valuePropName: "checked",
-                rules: [{ required: true }]
-              })(<Switch />)}
-            </Item>
-            <Item
-              {...formCheckLayout}
-              label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
-            >
-              {getFieldDecorator("enabled", {
-                initialValue: enabled,
-                valuePropName: "checked",
-                rules: [{ required: true }]
-              })(<Switch />)}
-            </Item>
-            <Item
-              style={{ margin: "0 30px" }}
-              {...formCheckLayout}
-              label={getIntlContent("SHENYU.SELECTOR.MATCHRESTFUL")}
-            >
-              {getFieldDecorator("matchRestful", {
+              </Item>
+              <Item
+                label={' '}
+                colon={false}
+                {...formItemLayout}
+              >
+                <Button className={styles.addButton} onClick={this.handleAdd} 
type="primary">
+                  {getIntlContent("SHENYU.COMMON.ADD")} {" "}
+                  {getIntlContent("SHENYU.COMMON.CONDITION")}
+                </Button>
+              </Item>
+            </div>
+          </Fragment>
+      )}
+        <div className={styles.layout}>
+          <Item
+            {...formCheckLayout}
+            label={getIntlContent("SHENYU.SELECTOR.CONTINUE")}
+          >
+            {getFieldDecorator("continued", {
+              initialValue: continued,
+              valuePropName: "checked",
+              rules: [{ required: true }]
+            })(<Switch />)}
+          </Item>
+          <Item
+            style={{ margin: "0 30px" }}
+            {...formCheckLayout}
+            label={getIntlContent("SHENYU.SELECTOR.PRINTLOG")}
+          >
+            {getFieldDecorator("loged", {
+              initialValue: loged,
+              valuePropName: "checked",
+              rules: [{ required: true }]
+            })(<Switch />)}
+          </Item>
+          <Item
+            {...formCheckLayout}
+            label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
+          >
+            {getFieldDecorator("enabled", {
+              initialValue: enabled,
+              valuePropName: "checked",
+              rules: [{ required: true }]
+            })(<Switch />)}
+          </Item>
+          <Item
+            style={{ margin: "0 30px" }}
+            {...formCheckLayout}
+            label={getIntlContent("SHENYU.SELECTOR.MATCHRESTFUL")}
+          >
+            {getFieldDecorator("matchRestful", {
                 initialValue: matchRestful,
                 valuePropName: "checked",
                 rules: [{ required: true }]
-              })(<Switch />)}
-            </Item>
-          </div>
-          {this.renderPluginHandler()}
-          <Item
-            label={getIntlContent("SHENYU.SELECTOR.EXEORDER")}
-            {...formItemLayout}
-          >
-            {getFieldDecorator("sort", {
-              initialValue: sort,
-              rules: [
-                {
+            })(<Switch />)}
+          </Item>
+        </div>
+        {this.renderPluginHandler()}
+        <Item
+          label={getIntlContent("SHENYU.SELECTOR.EXEORDER")}
+          {...formItemLayout}
+        >
+          {getFieldDecorator("sort", {
+            initialValue: sort,
+            rules: [
+              {
                   required: true,
                   message: getIntlContent("SHENYU.SELECTOR.INPUTNUMBER")
-                },
-                {
+              },
+              {
                   pattern: /^([1-9][0-9]{0,2}|1000)$/,
                   message: getIntlContent("SHENYU.SELECTOR.INPUTNUMBER")
-                }
-              ]
-            })(
-              <Input
-                allowClear
-                placeholder={getIntlContent("SHENYU.SELECTOR.INPUTORDER")}
+              }
+            ]
+          })(
+            <Input
+              allowClear
+              placeholder={getIntlContent("SHENYU.SELECTOR.INPUTORDER")}
+            />
+          )}
+        </Item>
+      </>
+    )
+  }
+
+  renderDiscoveryConfig = () => {
+    const { form, isAdd = true, discoveryConfig = {} } = this.props;
+    const { discoveryModeDics, upstreams, recordCount, discoveryHandler, 
defaultValueList, configPropsJson, selectedDiscoveryValue } = this.state;
+    const { getFieldDecorator } = form;
+    return(
+      <>
+        <Item label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")} 
{...formItemLayout}>
+          {getFieldDecorator('selectedDiscoveryType', {
+            rules: [{required: true, message: 
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}],
+            initialValue: discoveryConfig.discoveryType !== '' ? 
discoveryConfig.discoveryType : 'local'
+          })(
+            <Select
+              
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
+              disabled={!isAdd}
+              onChange={value => {
+                this.handleDiscoveryValueChange(value)
+                let configProps = discoveryModeDics.filter(item => 
item.dictName === value);
+                let propsEntries = JSON.parse(configProps[0]?.dictValue || 
"{}");
+                this.setState({configPropsJson: propsEntries})
+              }
+              }
+            >
+              {this.handleOptions()}
+            </Select>,
+          )}
+        </Item>
+
+        {
+          selectedDiscoveryValue !== 'local' ? (
+            <>
+              <Item 
label={getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE")} 
{...formItemLayout}>
+                {getFieldDecorator('listenerNode', {
+                  rules: [{required: true, message: 
getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE.INPUT")}],
+                  initialValue: discoveryConfig.listenerNode
+                })(<Input
+                  allowClear
+                  disabled={!isAdd}
+                  
placeholder={getIntlContent("SHENYU.DISCOVERY.SELECTOR.LISTENERNODE.INPUT")}
+                />)}
+              </Item>
+
+              {discoveryHandler !== null && Array.isArray(discoveryHandler) && 
discoveryHandler.length !== 0 && (
+                <Item 
label={getIntlContent("SHENYU.DISCOVERY.SELECTOR.HANDLER")} {...formItemLayout}>
+                  <div
+                    className={styles.handleWrap}
+                    style={{
+                      display: "flex"
+                    }}
+                  >
+                    <div
+                      style={{
+                        display: "flex",
+                        justifyContent: "space-between",
+                        flexDirection: "row"
+                      }}
+                    >
+                      <ul
+                        className={classnames({
+                          [styles.handleUl]: true,
+                          [styles.springUl]: true
+                        })}
+                        style={{ width: "100%" }}
+                      >
+                        {(() => {
+                          let item = discoveryHandler[0];
+                          let checkRule = item.checkRule;
+                          let required = item.required === "1";
+                          let rules = [];
+                          if (required) {
+                            rules.push({
+                              required: { required },
+                              message:
+                                  getIntlContent("SHENYU.COMMON.PLEASEINPUT") +
+                                  item.label
+                            });
+                          }
+                          if (checkRule) {
+                            rules.push({
+                              // eslint-disable-next-line no-eval
+                              pattern: eval(checkRule),
+                              message: `${getIntlContent(
+                                  "SHENYU.PLUGIN.RULE.INVALID"
+                              )}:(${checkRule})`
+                            });
+                          }
+                          if (defaultValueList != null) {
+                            return defaultValueList.map((value, index) => (
+                              <li key={index}>
+                                <Item>
+                                  {getFieldDecorator(value, {
+                                    initialValue: isAdd === true ? 
findKeyByValue(discoveryConfig.handler, value): 
findKeyByValue(JSON.parse(discoveryConfig.handler), value),
+                                    rules
+                                  })(
+                                    <Input
+                                      allowClear
+                                      disabled={!isAdd}
+                                      addonAfter={
+                                        <div style={{ width: '50px' }}>
+                                          {value}
+                                        </div>
+                                      }
+                                      placeholder={`Your ${value}`}
+                                      key={value}
+                                    />
+                                  )}
+                                </Item>
+                              </li>
+                            ));
+                          }
+                        })()}
+                      </ul>
+                    </div>
+                  </div>
+                </Item>
+              )}
+
+              <Item 
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST")} 
{...formItemLayout}>
+                {getFieldDecorator('serverList', {
+                  rules: [{required: true, message: 
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST.INPUT")}],
+                  initialValue: discoveryConfig.serverList
+                })(<Input
+                  allowClear
+                  disabled={!isAdd}
+                  
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST.INPUT")}
+                />)}
+              </Item>
+
+              {Object.keys(configPropsJson).length > 0 && (
+                <div style={{ marginLeft: '60px', marginTop: '15px', 
marginBottom: '15px', fontWeight: '500' }}>
+                  {getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.PROPS")}
+                  <span style={{ marginLeft: '2px', fontWeight: '500' 
}}>:</span>
+                </div>
+              )}
+
+              <div style={{ marginLeft: '40px', marginRight: '35px', display: 
'flex', alignItems: 'baseline' }}>
+                <div style={{ marginLeft: '8px', width: '100%' }}>
+                  <Row gutter={[16, 4]} justify="center">
+                    {Object.entries(configPropsJson).map(([key, value]) => (
+                      <Col span={12} key={key}>
+                        <Item>
+                          {getFieldDecorator(key, {
+                            initialValue: value
+                          })(
+                            <Input
+                              allowClear
+                              disabled={!isAdd}
+                              placeholder={isAdd ? `Enter ${key}` : ''}
+                              addonBefore={key}
+                              style={{ width: '100%' }}
+                            />
+                          )}
+                        </Item>
+                      </Col>
+                    ))}
+                  </Row>
+                </div>
+              </div>
+
+              {
+                isAdd !== true ? (
+                  <>
+                    
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
+                    <EditableFormTable
+                      isLocal={false}
+                      dataSource={upstreams}
+                      recordCount={recordCount}
+                      onTableChange={this.handleTableChange}
+                      onCountChange={this.handleCountChange}
+                    />
+                    {/* <Table dataSource={upstreams} columns={columns} />; */}
+                  </>
+                ):null
+              }
+            </>
+          ) : (
+            <>
+              
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
+              <EditableFormTable
+                isLocal={true}
+                dataSource={upstreams}
+                recordCount={recordCount}
+                onTableChange={this.handleTableChange}
+                onCountChange={this.handleCountChange}
               />
-            )}
-          </Item>
+            </>
+          )
+        }
+      </>
+    )
+
+  }
+
+
+  render() {
+    const {
+      onCancel,
+      isDiscovery,
+      isAdd = true
+    } = this.props;
+    const operations = (
+      <Button
+        type="primary"
+        icon="import"
+        onClick={() => {
+          this.setState({ showDiscoveryImportModal: true });
+        }}
+        disabled={!isAdd}
+      >
+        {getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT")}
+      </Button>
+    );
+    return (
+      <Modal
+        width='1100px'
+        centered
+        title={getIntlContent("SHENYU.SELECTOR.NAME")}
+        // visible here defaults to true, because the visibility of modal is 
determined by the popup attribute in index.js
+        visible
+        okText={getIntlContent("SHENYU.COMMON.SURE")}
+        cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+        onOk={this.handleSubmit}
+        onCancel={onCancel}
+      >
+        <Form onSubmit={this.handleSubmit} className="login-form">
+          {// divide, grpc, websocket plugin
+            isDiscovery ? (
+              <Tabs defaultActiveKey="1" size="small" 
tabBarExtraContent={operations}>
+                <TabPane 
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.BASIC")} key="1">
+                  {this.renderBasicConfig()}
+                </TabPane>
+                <TabPane 
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY")} key="2">
+                  {this.renderDiscoveryConfig()}
+                  <DiscoveryImportModal
+                    pluginName={this.props.pluginName}
+                    disabled={!isAdd}
+                    visible={this.state.showDiscoveryImportModal}
+                    onOk={this.handleImportDiscoveryConfig}
+                    onCancel={() => {
+                      this.setState({ showDiscoveryImportModal: false });
+                    }}
+                  />
+                </TabPane>
+              </Tabs>
+            ) : this.renderBasicConfig()
+          }
         </Form>
       </Modal>
     );
diff --git a/src/routes/Plugin/Common/index.js 
b/src/routes/Plugin/Common/index.js
index 1323218d..574a6619 100644
--- a/src/routes/Plugin/Common/index.js
+++ b/src/routes/Plugin/Common/index.js
@@ -65,7 +65,8 @@ export default class Common extends Component {
     }
   }
 
-  componentDidUpdate(prevProps) {
+  /* eslint-disable no-unused-vars */
+  componentDidUpdate(prevProps,  prevState, snapshot) {
     const preId = prevProps.match.params.id;
     const newId = this.props.match.params.id;
     const { selectorPage, selectorPageSize } = this.state;
@@ -89,6 +90,7 @@ export default class Common extends Component {
       }
     }
   }
+  /* eslint-enable no-unused-vars */
 
   componentWillUnmount() {
     const { dispatch } = this.props;
@@ -179,25 +181,96 @@ export default class Common extends Component {
     const { id: pluginId, config } = plugin;
     const multiSelectorHandle =
       this.getPluginConfigField(config, "multiSelectorHandle") === "1";
-    this.setState({
-      popup: (
-        <Selector
-          pluginId={pluginId}
-          multiSelectorHandle={multiSelectorHandle}
-          handleOk={selector => {
-            dispatch({
-              type: "common/addSelector",
-              payload: { pluginId, ...selector },
-              fetchValue: { pluginId, currentPage: selectorPage, pageSize: 
selectorPageSize },
-              callback: () => {
-                this.closeModal();
-              }
-            });
-          }}
-          onCancel={this.closeModal}
-        />
-      )
-    });
+    const isDiscovery = ["5", "15", "26"].includes(pluginId);
+    if (isDiscovery) {
+      let discoveryConfig = {
+        discoveryType: '',
+        serverList: '',
+        handler: {},
+        listenerNode: '',
+        props: {}
+      }
+      let typeValue =
+        name === "divide" ? "http" :
+          name === "websocket" ? "ws" :
+            name === "grpc" ? "grpc" : "http";
+      this.setState({
+        popup: (
+          <Selector
+            pluginName={name}
+            pluginId={pluginId}
+            multiSelectorHandle={multiSelectorHandle}
+            isAdd={true}
+            discoveryConfig={discoveryConfig}
+            isDiscovery={true}
+            handleOk={selector => {
+              const { name: selectorName, listenerNode, serverList, 
selectedDiscoveryType, discoveryProps, handler, upstreams, importedDiscoveryId 
} = selector;
+              const upstreamsWithProps = upstreams.map(item => ({
+                protocol: item.protocol,
+                url: item.url,
+                status: parseInt(item.status, 10),
+                weight: item.weight,
+                startupTime: item.startupTime,
+                props: JSON.stringify({
+                  warmupTime: item.warmupTime
+                })
+              }));
+              dispatch({
+                type: "common/addSelector",
+                payload: { pluginId, ...selector, upstreams: 
upstreamsWithProps },
+                fetchValue: { pluginId, currentPage: selectorPage, pageSize: 
selectorPageSize },
+                callback: (selectorId) => {
+                  dispatch({
+                    type: "discovery/bindSelector",
+                    payload: {
+                      selectorId,
+                      name: selectorName,
+                      pluginName: name,
+                      listenerNode,
+                      handler,
+                      type: typeValue,
+                      discoveryUpstreams: upstreamsWithProps,
+                      discovery: {
+                        id: importedDiscoveryId,
+                        discoveryType: selectedDiscoveryType,
+                        serverList,
+                        props: discoveryProps,
+                        name: selectorName
+                      }
+                    }
+                  })
+                  this.closeModal();
+                }
+              });
+            }}
+            onCancel={this.closeModal}
+          />
+        )
+      });
+    }else {
+      this.setState({
+        popup: (
+          <Selector
+            pluginName={name}
+            pluginId={pluginId}
+            multiSelectorHandle={multiSelectorHandle}
+            isDiscovery={false}
+            handleOk={selector => {
+              dispatch({
+                type: "common/addSelector",
+                payload: { pluginId, ...selector },
+                fetchValue: { pluginId, currentPage: selectorPage, pageSize: 
selectorPageSize },
+                callback: () => {
+                  this.closeModal();
+                }
+              });
+            }}
+            onCancel={this.closeModal}
+          />
+        )
+      });
+    }
+
   };
 
   searchRuleOnchange = e => {
@@ -297,6 +370,7 @@ export default class Common extends Component {
     const { id: pluginId, config } = plugin;
     const multiSelectorHandle =
       this.getPluginConfigField(config, "multiSelectorHandle") === "1";
+    const isDiscovery = ["5", "15", "26"].includes(pluginId);
     const { id } = record;
     dispatch({
       type: "common/fetchSeItem",
@@ -304,11 +378,37 @@ export default class Common extends Component {
         id
       },
       callback: selector => {
+       if ( isDiscovery ){
+        let discoveryConfig = {
+          props: selector.discoveryVO ? selector.discoveryVO.props: "{}",
+          discoveryType: selector.discoveryVO ? selector.discoveryVO.type: 
'local',
+          serverList: selector.discoveryVO ? selector.discoveryVO.serverList: 
'',
+          handler: selector.discoveryHandler ? 
selector.discoveryHandler.handler: "{}",
+          listenerNode: selector.discoveryHandler ? 
selector.discoveryHandler.listenerNode : '',
+        }
+        let updateArray = [];
+        if (selector.discoveryUpstreams) {
+          updateArray = selector.discoveryUpstreams.map((item) => {
+            let propsObj = JSON.parse(item.props || "{}");
+            if (item.props === null) {
+              propsObj = {
+                warmupTime: 10,
+              };
+            }
+            return { ...item, key: item.id, warmupTime: propsObj.warmupTime };
+          });
+        }
+        let discoveryHandlerId = selector.discoveryHandler ? 
selector.discoveryHandler.id : '';
         this.setState({
           popup: (
             <Selector
+              pluginName={name}
               {...selector}
               multiSelectorHandle={multiSelectorHandle}
+              discoveryConfig={discoveryConfig}
+              discoveryUpstreams={updateArray}
+              isAdd={false}
+              isDiscovery={true}
               handleOk={values => {
                 dispatch({
                   type: "common/updateSelector",
@@ -323,6 +423,24 @@ export default class Common extends Component {
                     pageSize: selectorPageSize
                   },
                   callback: () => {
+                    const {upstreams} = values
+                    const upstreamsWithHandlerId = upstreams.map(item => ({
+                      protocol: item.protocol,
+                      url: item.url,
+                      status: parseInt(item.status, 10),
+                      weight: item.weight,
+                      props: JSON.stringify({
+                        warmupTime: item.warmupTime
+                      }),
+                      discoveryHandlerId
+                    }));
+                    dispatch({
+                      type: "discovery/updateDiscoveryUpstream",
+                      payload: {
+                        discoveryHandlerId,
+                        upstreams: upstreamsWithHandlerId
+                      }
+                    })
                     this.closeModal();
                   }
                 });
@@ -331,6 +449,37 @@ export default class Common extends Component {
             />
           )
         });
+        } else {
+          this.setState({
+            popup: (
+              <Selector
+                pluginName={name}
+                {...selector}
+                multiSelectorHandle={multiSelectorHandle}
+                isDiscovery={false}
+                handleOk={values => {
+                  dispatch({
+                    type: "common/updateSelector",
+                    payload: {
+                      pluginId,
+                      ...values,
+                      id
+                    },
+                    fetchValue: {
+                      pluginId,
+                      currentPage: selectorPage,
+                      pageSize: selectorPageSize
+                    },
+                    callback: () => {
+                      this.closeModal();
+                    }
+                  });
+                }}
+                onCancel={this.closeModal}
+              />
+            )
+          });
+        }
       }
     });
   };
diff --git a/src/routes/Plugin/Discovery/TcpCard.js 
b/src/routes/Plugin/Discovery/DiscoveryCard.js
similarity index 92%
rename from src/routes/Plugin/Discovery/TcpCard.js
rename to src/routes/Plugin/Discovery/DiscoveryCard.js
index 55d79b12..93640896 100644
--- a/src/routes/Plugin/Discovery/TcpCard.js
+++ b/src/routes/Plugin/Discovery/DiscoveryCard.js
@@ -19,16 +19,16 @@ import React, {Component} from "react";
 import {Card, Popover, Typography, Skeleton, Icon, Popconfirm} from "antd";
 
 import {getIntlContent} from "../../../utils/IntlUtils";
-import tcpStyles from "./tcp.less";
+import discoveryStyles from "./discovery.less";
 
 import { formatTimestamp } from "../../../utils/utils";
-import {ConsulIcon, EtcdIcon, LocalIcon, NacosIcon, ZkIcon} from 
"./DiscoveryIcon";
+import {ConsulIcon, EtcdIcon, LocalIcon, NacosIcon, ZkIcon, EurekaIcon} from 
"./DiscoveryIcon";
 import AuthButton from "../../../utils/AuthButton";
 
 const { Text } = Typography;
 const { Meta } = Card;
 
-export class TcpCard extends Component {
+export class DiscoveryCard extends Component {
 
   render() {
     const { updateSelector, data, handleDelete, handleRefresh } = this.props
@@ -56,6 +56,7 @@ export class TcpCard extends Component {
       nacos: <NacosIcon style={{ fontSize: '40px', color: '#354458' }} />,
       consul: <ConsulIcon style={{ fontSize: '40px', color: '#354458' }} />,
       etcd: <EtcdIcon style={{ fontSize: '40px', color: '#354458' }} />,
+      eureka: <EurekaIcon style={{ fontSize: '40px', color: '#354458' }} />,
     };
 
     const getAvatarIcon = () => {
@@ -67,7 +68,7 @@ export class TcpCard extends Component {
         <Card
           title={<div style={{ fontSize: '17px', lineHeight: 
'1.5'}}>{data.name}</div>}
           bordered={false}
-          className={tcpStyles.tcpCard}
+          className={discoveryStyles.discoveryCard}
           actions={[
             <AuthButton perms="plugin:tcp:modify">
               <Icon type="reload" key="reload" style={{color: '#2E496E', 
fontSize: "17px"}} onClick={() => handleRefresh(data.discoveryHandlerId)} />
@@ -85,7 +86,7 @@ export class TcpCard extends Component {
             >
               <AuthButton perms="plugin:tcpSelector:delete">
                 <Icon type="delete" key="delete" style={{color: "#CC0000", 
fontSize: "17px"}} />
-              </AuthButton>,
+              </AuthButton>
             </Popconfirm>
           ]}
           extra={<div style={{ fontSize: '15px', lineHeight: '1.5', 
marginRight: '14px'}}>{formatTimestamp(createTime)}</div>}
diff --git a/src/routes/Plugin/Discovery/DiscoveryConfigModal.js 
b/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
index 5c6be0fd..0ad11ff2 100644
--- a/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
+++ b/src/routes/Plugin/Discovery/DiscoveryConfigModal.js
@@ -59,13 +59,13 @@ class DiscoveryConfigModal extends Component {
     e.preventDefault();
     form.validateFieldsAndScroll((err, values) => {
       if (!err) {
-        let { name, serverList, tcpType } = values;
+        let { name, serverList, discoveryType } = values;
         const propsjson = {};
         Object.entries(configPropsJson).forEach(([key]) => {
           propsjson[key] = form.getFieldValue(key);
         });
         const props = JSON.stringify(propsjson);
-        handleOk({ name, serverList, props, tcpType});
+        handleOk({ name, serverList, props, discoveryType});
       }
     });
   };
@@ -82,7 +82,7 @@ class DiscoveryConfigModal extends Component {
   render() {
     const { handleCancel, form, data, isSetConfig, handleConfigDelete, 
dispatch } = this.props
     const { getFieldDecorator } = form;
-    const { name, serverList, type: tcpType, id} = data || {};
+    const { name, serverList, type: discoveryType, id} = data || {};
     const { configPropsJson, discoveryDicts } = this.state;
     const formItemLayout = {
       labelCol: {
@@ -109,8 +109,8 @@ class DiscoveryConfigModal extends Component {
               placement="topLeft"
               title={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.DELETE")}
               onConfirm={() => handleConfigDelete(id)}
-              okText="Yes"
-              cancelText="No"
+              okText={getIntlContent("SHENYU.COMMON.SURE")}
+              cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
               key="popconfirm"
             >
               <Button key="delete" type="danger" style={{ marginRight: '10px' 
}}>
@@ -129,9 +129,9 @@ class DiscoveryConfigModal extends Component {
       >
         <Form onSubmit={this.handleSubmit}>
           <Form.Item 
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")} 
{...formItemLayout}>
-            {getFieldDecorator('tcpType', {
+            {getFieldDecorator('discoveryType', {
               rules: [{ required: true, message: 
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT") }],
-              initialValue: tcpType !== "" ? tcpType : undefined
+              initialValue: discoveryType !== "" ? discoveryType : undefined
             })(
               <Select
                 
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
@@ -179,7 +179,7 @@ class DiscoveryConfigModal extends Component {
             <span style={{ marginLeft: '2px', fontWeight: '500' }}>:</span>
           </div>
           <div style={{ marginLeft: '35px', display: 'flex', alignItems: 
'baseline' }}>
-            <div style={{ marginLeft: '8px' }}>
+            <div style={{ marginLeft: '8px', width: '100%' }}>
               <Row gutter={[16, 4]} justify="center">
                 {Object.entries(configPropsJson).map(([key, value]) => (
                   <Col span={12} key={key}>
@@ -190,7 +190,7 @@ class DiscoveryConfigModal extends Component {
                         <Input
                           allowClear
                           disabled={isSetConfig}
-                          placeholder={`Enter ${key}`}
+                          placeholder={!isSetConfig ? `Enter ${key}` : ''}
                           addonBefore={key}
                         />
                       )}
diff --git a/src/routes/Plugin/Discovery/DiscoveryIcon.js 
b/src/routes/Plugin/Discovery/DiscoveryIcon.js
index bd9edaf2..2f3fbcd4 100644
--- a/src/routes/Plugin/Discovery/DiscoveryIcon.js
+++ b/src/routes/Plugin/Discovery/DiscoveryIcon.js
@@ -100,11 +100,26 @@ const EtcdSvg = () => (
   </svg>
 )
 
+const EurekaSvg = () => (
+  <svg
+    className="icon"
+    viewBox="0 0 1024 1024"
+    width="1em"
+    height="1em"
+  >
+    <path
+      d="M985.417388 0H38.457749C17.231069 0 0 17.231069 0 
38.457749v947.084502c0 21.22668 17.231069 38.457749 38.457749 
38.457749h947.084502c21.22668 0 38.332886-17.231069 
38.457749-38.332886V38.457749C1023.875137 17.231069 1006.644068 0 985.417388 
0zM744.182417 
820.723326H279.69272V203.151811h449.506158v92.897939H390.32118V449.506158h311.158152v92.897939H390.32118v185.42129H744.182417v92.897939z"
+      fill="currentColor"
+    />
+  </svg>
+)
+
 export const ZkIcon = props => <Icon component={ZkSvg} {...props} />;
 export const LocalIcon = props => <Icon component={LocalSvg} {...props} />;
 export const NacosIcon = props => <Icon component={NacosSvg} {...props} />;
 export const ConsulIcon = props => <Icon component={ConsulSvg} {...props} />;
 export const EtcdIcon = props => <Icon component={EtcdSvg} {...props} />;
+export const EurekaIcon = props => <Icon component={EurekaSvg} {...props} />;
 
 
 
diff --git a/src/routes/Plugin/Discovery/DiscoveryImportModal.js 
b/src/routes/Plugin/Discovery/DiscoveryImportModal.js
new file mode 100644
index 00000000..7e9c03f6
--- /dev/null
+++ b/src/routes/Plugin/Discovery/DiscoveryImportModal.js
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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, { Component } from "react";
+import { Modal } from "antd";
+import { connect } from "dva";
+import { getIntlContent } from "../../../utils/IntlUtils";
+
+@connect(({ discovery }) => ({
+  discovery
+}))
+
+class DiscoveryImportModal extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      configList: {},
+      loading: false
+    };
+  }
+
+  componentDidMount() {
+    const { dispatch, pluginName } = this.props
+    dispatch({
+      type: "discovery/fetchDiscovery",
+      payload: {
+        pluginName,
+        level: "1"
+      },
+      callback: discoveryConfigList => {
+        this.setState({configList: discoveryConfigList})
+      }
+    })
+  }
+
+  handleCancel = () => {
+    const { onCancel } = this.props;
+    // eslint-disable-next-line no-unused-expressions
+    onCancel && onCancel();
+  };
+
+  handleOk = async () => {
+    const { onOk } = this.props;
+    const { configList } = this.state;
+    // eslint-disable-next-line no-unused-expressions
+    onOk && onOk(configList);
+  };
+
+  render() {
+    const { visible = false } = this.props;
+    const { loading, configList } = this.state;
+    const { type = '', serverList =  '', props: discoveryProps = '{}' } = 
configList || {};
+    return (
+      <Modal
+        visible={visible}
+        centered
+        
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.CONFIRM")}
+        onCancel={this.handleCancel}
+        onOk={this.handleOk}
+        confirmLoading={loading}
+        okText={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.IMPORT.SURE")}
+        cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+      >
+        <ul>
+          <li style={{ marginBottom: '8px' }}>
+            
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")}:</strong> {type}
+          </li>
+          <li style={{ marginBottom: '8px' }}>
+            
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.SERVERLIST")}:</strong> 
{serverList}
+          </li>
+          <li>
+            
<strong>{getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.PROPS")}:</strong> 
<pre><code>{JSON.stringify(JSON.parse(discoveryProps), null, 4)}</code></pre>
+          </li>
+        </ul>
+      </Modal>
+    );
+  }
+}
+
+export default DiscoveryImportModal;
diff --git a/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js 
b/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js
new file mode 100644
index 00000000..33357b71
--- /dev/null
+++ b/src/routes/Plugin/Discovery/DiscoveryUpstreamTable.js
@@ -0,0 +1,287 @@
+import React, {Component} from "react";
+import {Button, Form, Input, InputNumber, Popconfirm, Select, Table} from 
'antd';
+import {getIntlContent} from "../../../utils/IntlUtils";
+
+const EditableContext = React.createContext();
+const { Option } = Select;
+
+class EditableCell extends Component {
+  getInput = () => {
+    if (this.props.inputType === 'number') {
+      return <InputNumber />;
+    } else if (this.props.inputType === 'dropdown') {
+      // return <Select />;
+      return (
+        <Select>
+          <Option value="0">open</Option>
+          <Option value="1">close</Option>
+        </Select>
+      );
+    }
+    return <Input />;
+  };
+
+  renderCell = ({ getFieldDecorator }) => {
+    const {
+      editing,
+      dataIndex,
+      title,
+      inputType,
+      record,
+      index,
+      children,
+      ...restProps
+    } = this.props;
+    return (
+      <td {...restProps}>
+        {editing ? (
+          <Form.Item style={{ margin: 0 }}>
+            {getFieldDecorator(dataIndex, {
+              rules: [
+                {
+                  required: true,
+                  message: `Please Input ${title}!`,
+                },
+              ],
+              initialValue: record[dataIndex],
+            })(this.getInput())}
+          </Form.Item>
+        ) : (
+          children
+        )}
+      </td>
+    );
+  };
+
+  render() {
+    return 
<EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>;
+  }
+}
+
+class EditableTable extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      editingKey: '',
+      isLocal: this.props.isLocal
+    };
+    this.columns = [
+      {
+        title: 'protocol',
+        dataIndex: 'protocol',
+        editable: true,
+        width: '25%',
+        align: 'center'
+      },
+      {
+        title: 'url',
+        dataIndex: 'url',
+        editable: this.state.isLocal,
+        width: '35%',
+        align: 'center'
+      },
+      {
+        title: 'status',
+        dataIndex: 'status',
+        editable: true,
+        width: '19%',
+        align: 'center',
+        render: (text) => {
+          return text === 0 || text === '0' ? 'open' : 'close';
+        },
+      },
+      {
+        title: 'weight',
+        dataIndex: 'weight',
+        editable: true,
+        align: 'center'
+      },
+      {
+        title: 'startupTime',
+        dataIndex: 'startupTime',
+        editable: this.state.isLocal,
+        align: 'center'
+      },
+      {
+        title: 'warmupTime',
+        dataIndex: 'warmupTime',
+        editable: true,
+        align: 'center'
+      },
+      {
+        title: getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION"),
+        dataIndex: 'operation',
+        width: '18%',
+        align: 'center',
+        render: (text, record) => {
+          const { editingKey } = this.state;
+          const editable = this.isEditing(record);
+          return (
+            <span>
+              {editable ? (
+                <span>
+                  <EditableContext.Consumer>
+                    {form => (
+                      <a
+                        onClick={() => this.save(form, record.key)}
+                        style={{ marginRight: 8 }}
+                      >
+                        
{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SAVE")}
+                      </a>
+                    )}
+                  </EditableContext.Consumer>
+                  <Popconfirm
+                    
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL.CONFIRM")}
+                    onConfirm={() => this.cancel(record.key)}
+                    okText={getIntlContent("SHENYU.COMMON.SURE")}
+                    cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+                  >
+                    
<a>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.CANCEL")}</a>
+                  </Popconfirm>
+                </span>
+                ) : (
+                  <span>
+                    <Button
+                      type="link"
+                      disabled={editingKey !== ''}
+                      onClick={() => this.edit(record.key)}
+                    >
+                      
{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.EDIT")}
+                    </Button>
+                    {' '}
+                    {this.props.dataSource.length >= 1 && this.state.isLocal ? 
(
+                      <Popconfirm
+                        
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE.CONFIRM")}
+                        onConfirm={() => this.handleDelete(record.key)}
+                        okText={getIntlContent("SHENYU.COMMON.SURE")}
+                        cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+                      >
+                        <a>{getIntlContent("SHENYU.BUTTON.SYSTEM.DELETE")}</a>
+                      </Popconfirm>
+                    ) : null}
+                  </span>
+                )
+              }
+            </span>
+          );
+        },
+      },
+    ];
+  }
+
+  isEditing = record => record.key === this.state.editingKey;
+
+  cancel = () => {
+    this.setState({ editingKey: '' });
+  };
+
+  handleDelete = key => {
+    const { dataSource } = this.props;
+    const newData = dataSource.filter(item => item.key !== key);
+    this.props.onTableChange(newData);
+  };
+
+  handleAdd = () => {
+    const { dataSource, recordCount } = this.props;
+    const newRecordCount = recordCount + 1;
+    const newData = {
+      key: newRecordCount,
+      protocol: 'http://',
+      url: 'localhost:',
+      status: 0,
+      weight: 50,
+      startupTime: 0,
+      warmupTime: 10
+    };
+    this.props.onTableChange([...dataSource, newData]);
+    this.props.onCountChange(newRecordCount);
+  };
+
+  save(form, key) {
+    form.validateFields((error, row) => {
+      if (error) {
+        return;
+      }
+      const newData = [...this.props.dataSource];
+      const index = newData.findIndex(item => key === item.key);
+      if (index > -1) {
+        const item = newData[index];
+        newData.splice(index, 1, {
+          ...item,
+          ...row,
+        });
+        this.props.onTableChange(newData);
+        this.setState({ editingKey: '' });
+      } else {
+        const { recordCount } = this.props;
+        row.key = recordCount + 1;
+        newData.push(row);
+        this.props.onCountChange(recordCount + 1);
+        this.props.onTableChange(newData);
+        this.setState({ editingKey: '' });
+      }
+    });
+  }
+
+  edit(key) {
+    this.setState({ editingKey: key });
+  }
+
+  render() {
+    const components = {
+      body: {
+        cell: EditableCell,
+      },
+    };
+
+    const columns = this.columns.map(col => {
+      if (!col.editable) {
+        return col;
+      }
+
+      let inputType = 'text';
+      if (col.dataIndex === 'weight' || col.dataIndex === 'startupTime' || 
col.dataIndex === 'warmupTime') {
+        inputType = 'number';
+      } else if (col.dataIndex === 'status') {
+        inputType = 'dropdown';
+      }
+      return {
+        ...col,
+        onCell: record => ({
+          record,
+          inputType,
+          dataIndex: col.dataIndex,
+          title: col.title,
+          editing: this.isEditing(record),
+        }),
+      };
+    });
+
+    return (
+      <div>
+        {this.state.isLocal && (
+          <Button onClick={this.handleAdd} type="primary" style={{ 
marginBottom: 16 }}>
+            {getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD")}
+          </Button>
+        )}
+        <EditableContext.Provider value={this.props.form}>
+          <Table
+            components={components}
+            bordered
+            dataSource={this.props.dataSource}
+            columns={columns}
+            rowClassName="editable-row"
+            pagination={{
+              onChange: this.cancel,
+            }}
+          />
+        </EditableContext.Provider>
+      </div>
+
+    );
+  }
+}
+
+const EditableFormTable = Form.create()(EditableTable);
+export default EditableFormTable;
+
diff --git a/src/routes/Plugin/Discovery/ProxySelectorModal.js 
b/src/routes/Plugin/Discovery/ProxySelectorModal.js
index 7132bfd8..01e3cf1e 100644
--- a/src/routes/Plugin/Discovery/ProxySelectorModal.js
+++ b/src/routes/Plugin/Discovery/ProxySelectorModal.js
@@ -17,13 +17,13 @@
 
 import React, {Component} from "react";
 import {connect} from "dva";
-import {Button, Col, Divider, Form, Input, Modal, Row, Select, Table, Tabs, 
Tooltip} from "antd";
+import {Button, Col, Divider, Form, Input, Modal, Row, Select, Tabs, Tooltip} 
from "antd";
 import classnames from "classnames";
 import {getIntlContent} from "../../../utils/IntlUtils";
-import EditableTable from './UpstreamTable';
 import styles from "../index.less";
 import ProxySelectorCopy from "./ProxySelectorCopy.js";
 import {findKeyByValue} from "../../../utils/utils";
+import EditableFormTable from "./DiscoveryUpstreamTable";
 
 
 const FormItem = Form.Item;
@@ -53,7 +53,7 @@ class ProxySelectorModal extends Component {
   }
 
   componentDidMount() {
-    const { isAdd, isSetConfig, tcpType, data, pluginId, dispatch } = 
this.props;
+    const { isAdd, isSetConfig, discoveryType, data, pluginId, dispatch } = 
this.props;
     const { discoveryDicts } = this.state;
     const { props } = this.props.data || {};
 
@@ -62,7 +62,7 @@ class ProxySelectorModal extends Component {
       dispatch({
         type: 'discovery/saveGlobalType',
         payload: {
-          chosenType: tcpType
+          chosenType: discoveryType
         }
       });
     }else{
@@ -88,10 +88,13 @@ class ProxySelectorModal extends Component {
         callBack: (pluginHandles) => {
           if  (Object.keys(pluginHandles).length > 0) {
             const filteredArray = pluginHandles[0].filter(item => item.field 
!== 'discoveryHandler');
+
             const handlerArray = pluginHandles[0].filter(item => item.field 
=== 'discoveryHandler');
-            pluginHandles[0] = filteredArray;
             this.setState({discoveryHandler: handlerArray});
+
+            pluginHandles[0] = filteredArray;
             this.setState({ pluginHandleList: pluginHandles });
+
             let defaultValue = handlerArray[0].defaultValue;
             this.setState({ defaultValueList: defaultValue.split(",")  });
           }
@@ -106,12 +109,15 @@ class ProxySelectorModal extends Component {
     e.preventDefault();
     form.validateFieldsAndScroll((err, values) => {
       if (!err) {
-        let {name, forwardPort, listenerNode, serverList, discoveryType} = 
values;
+        let {name, forwardPort, listenerNode, serverList, 
selectedDiscoveryType} = values;
         const discoveryPropsJson = {};
         Object.entries(configPropsJson).forEach(([key]) => {
           discoveryPropsJson[key] = form.getFieldValue(key);
         });
+        // The discoveryProps refer to the attributes corresponding to each 
registration center mode
         const discoveryProps = JSON.stringify(discoveryPropsJson);
+
+        // The handler refers to the url, status, weight, protocol, etc. of 
the discovery module.
         let handler = {};
         if ( defaultValueList !== null) {
           defaultValueList.forEach(item => {
@@ -120,6 +126,8 @@ class ProxySelectorModal extends Component {
             }
           });
         }
+        handler = JSON.stringify(handler);
+
         let handleResult = [];
         handleResult[0] = {};
         if(Object.keys(pluginHandleList).length > 0){
@@ -127,9 +135,9 @@ class ProxySelectorModal extends Component {
             handleResult[0][item.field] = values[item.field + 0];
           });
         }
-        handler = JSON.stringify(handler);
+        // The props refer to the properties of each plug-in
         let props = JSON.stringify(handleResult[0]);
-        handleOk({name, forwardPort, props, listenerNode, handler, 
discoveryProps, serverList, discoveryType, upstreams});
+        handleOk({name, forwardPort, props, listenerNode, handler, 
discoveryProps, serverList, selectedDiscoveryType, upstreams});
       }
     });
   };
@@ -141,7 +149,7 @@ class ProxySelectorModal extends Component {
       name,
       forwardPort,
       listenerNode,
-      discoveryType: discovery.type,
+      selectedDiscoveryType: discovery.type,
       serverList: discovery.serverList
     };
     dispatch({
@@ -176,7 +184,7 @@ class ProxySelectorModal extends Component {
 
 
   render() {
-    const { tcpType, form, handleCancel, isSetConfig, isAdd, chosenType, 
dispatch } = this.props;
+    const { discoveryType, form, handleCancel, isSetConfig, isAdd, chosenType, 
dispatch } = this.props;
     const { recordCount, upstreams, pluginHandleList, visible, 
discoveryHandler, defaultValueList, discoveryDicts, configPropsJson } = 
this.state;
     const { getFieldDecorator } = form;
     const { name, forwardPort, listenerNode, discovery, handler } = 
this.props.data || {};
@@ -189,32 +197,6 @@ class ProxySelectorModal extends Component {
         sm: { span: 19 }
       }
     };
-    const columns = [
-      {
-        title: 'protocol',
-        dataIndex: 'protocol',
-        key: 'protocol',
-        align: 'center'
-      },
-      {
-        title: 'url',
-        dataIndex: 'url',
-        key: 'url',
-        align: 'center'
-      },
-      {
-        title: 'status',
-        dataIndex: 'status',
-        key: 'status',
-        align: 'center'
-      },
-      {
-        title: 'weight',
-        dataIndex: 'weight',
-        key: 'weight',
-        align: 'center'
-      },
-    ];
 
     return (
       <Modal
@@ -426,9 +408,9 @@ class ProxySelectorModal extends Component {
             </TabPane>
             <TabPane 
tab={getIntlContent("SHENYU.DISCOVERY.SELECTOR.CONFIG.DISCOVERY")} key="2">
               <FormItem 
label={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE")} 
{...formItemLayout}>
-                {getFieldDecorator('discoveryType', {
+                {getFieldDecorator('selectedDiscoveryType', {
                   rules: [{required: true, message: 
getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}],
-                  initialValue: tcpType !== '' ? tcpType : undefined
+                  initialValue: discoveryType !== '' ? discoveryType : 
undefined
                 })(
                   <Select
                     
placeholder={getIntlContent("SHENYU.DISCOVERY.CONFIGURATION.TYPE.INPUT")}
@@ -526,7 +508,7 @@ class ProxySelectorModal extends Component {
                                                 {value}
                                               </div>
                                             }
-                                            placeholder={`Your ${value}`}
+                                            placeholder={isAdd? `Your 
${value}` : ''}
                                             key={value}
                                           />
                                         )}
@@ -560,7 +542,7 @@ class ProxySelectorModal extends Component {
                             <span style={{ marginLeft: '2px', fontWeight: 
'500' }}>:</span>
                           </div>
                           <div style={{ marginLeft: '35px', display: 'flex', 
alignItems: 'baseline' }}>
-                            <div style={{ marginLeft: '8px' }}>
+                            <div style={{ marginLeft: '8px', width: '100%' }}>
                               <Row gutter={[16, 4]} justify="center">
                                 {Object.entries(configPropsJson).map(([key, 
value]) => (
                                   <Col span={12} key={key}>
@@ -571,7 +553,7 @@ class ProxySelectorModal extends Component {
                                         <Input
                                           allowClear
                                           disabled={!isAdd}
-                                          placeholder={`Enter ${key}`}
+                                          placeholder={isAdd ? `Enter ${key}` 
: ''}
                                           addonBefore={key}
                                         />
                                       )}
@@ -589,7 +571,13 @@ class ProxySelectorModal extends Component {
                       isAdd !== true ? (
                         <>
                           
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
-                          <Table dataSource={upstreams} columns={columns} />;
+                          <EditableFormTable
+                            isLocal={false}
+                            dataSource={upstreams}
+                            recordCount={recordCount}
+                            onTableChange={this.handleTableChange}
+                            onCountChange={this.handleCountChange}
+                          />
                         </>
                       ):null
                     }
@@ -597,7 +585,8 @@ class ProxySelectorModal extends Component {
                 ) : (
                   <>
                     
<Divider>{getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM")}</Divider>
-                    <EditableTable
+                    <EditableFormTable
+                      isLocal={true}
                       dataSource={upstreams}
                       recordCount={recordCount}
                       onTableChange={this.handleTableChange}
diff --git a/src/routes/Plugin/Discovery/UpstreamTable.js 
b/src/routes/Plugin/Discovery/UpstreamTable.js
deleted file mode 100644
index d1cc7f1a..00000000
--- a/src/routes/Plugin/Discovery/UpstreamTable.js
+++ /dev/null
@@ -1,213 +0,0 @@
-import React, {Component, createContext} from "react";
-import { Table, Input, Button, Popconfirm, Form } from 'antd';
-import AuthButton from "../../../utils/AuthButton";
-import {getIntlContent} from "../../../utils/IntlUtils";
-
-
-const EditableContext = createContext();
-
-const EditableRow = ({ form, index, ...props }) => (
-  <EditableContext.Provider value={form}>
-    <tr {...props} />
-  </EditableContext.Provider>
-);
-
-const EditableFormRow = Form.create()(EditableRow);
-
-class EditableCell extends Component {
-  state = {
-    editing: false,
-  };
-
-  toggleEdit = () => {
-    this.setState(prevState => ({ editing: !prevState.editing }), () => {
-      if (this.state.editing) {
-        this.input.focus();
-      }
-    });
-  };
-
-  save = e => {
-    const { record, handleSave } = this.props;
-    this.form.validateFields((error, values) => {
-      if (error && error[e.currentTarget.id]) {
-        return;
-      }
-      this.toggleEdit();
-      handleSave({ ...record, ...values });
-    });
-  };
-
-
-  renderCell = form => {
-    this.form = form;
-    const { children, dataIndex, record, title } = this.props;
-    const { editing } = this.state;
-    return editing ? (
-      <Form.Item style={{ margin: 0 }}>
-        {form.getFieldDecorator(dataIndex, {
-          rules: [
-            {
-              required: true,
-              message: `${title} is required.`,
-            },
-          ],
-          initialValue: record[dataIndex],
-        })(<Input allowClear ref={node => { this.input = node }} 
onPressEnter={this.save} onBlur={this.save} />
-        )}
-      </Form.Item>
-    ) : (
-      <div
-        className="editable-cell-value-wrap"
-        style={{ paddingRight: 24 }}
-        onClick={this.toggleEdit}
-      >
-        {children}
-      </div>
-    );
-  };
-
-  render() {
-    const {
-      editable,
-      dataIndex,
-      title,
-      record,
-      index,
-      handleSave,
-      children,
-      ...restProps
-    } = this.props;
-    return (
-      <td {...restProps}>
-        {editable ? (
-          
<EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>
-        ) : (
-          children
-        )}
-      </td>
-    );
-  }
-}
-
-
-export default class EditableTable extends Component {
-  constructor(props) {
-    super(props);
-    this.columns = [
-      {
-        title: 'protocol',
-        dataIndex: 'protocol',
-        editable: true,
-        // width: '20%',
-        align: 'center'
-      },
-      {
-        title: 'url',
-        dataIndex: 'url',
-        editable: true,
-        width: '33%',
-        align: 'center'
-      },
-      {
-        title: 'status',
-        dataIndex: 'status',
-        editable: true,
-        // width: '19%',
-        align: 'center'
-      },
-      {
-        title: 'weight',
-        dataIndex: 'weight',
-        editable: true,
-        // width: '19%',
-        align: 'center'
-      },
-      {
-        title: getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.OPERATION"),
-        dataIndex: 'operation',
-        align: 'center',
-        render: (text, record) =>
-          this.props.dataSource.length >= 1 ? (
-            <Popconfirm 
title={getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DELETE")} 
onConfirm={() => this.handleDelete(record.key)}>
-              <a>{getIntlContent("SHENYU.BUTTON.SYSTEM.DELETE")}</a>
-            </Popconfirm>
-          ) : null,
-      },
-    ];
-
-  }
-
-  handleDelete = key => {
-    const { dataSource } = this.props;
-    const newData = dataSource.filter(item => item.key !== key);
-    this.props.onTableChange(newData);
-  };
-
-  handleAdd = () => {
-    const { dataSource, recordCount} = this.props;
-    const newRecordCount = recordCount + 1;
-    const newData = {
-      key: newRecordCount,
-      protocol: 'protocol',
-      url: 'url',
-      status: '0',
-      weight: '0',
-    };
-    this.props.onTableChange([...dataSource, newData]);
-    this.props.onCountChange(newRecordCount);
-  };
-
-  handleSave = row => {
-    const newData = [...this.props.dataSource];
-    const index = newData.findIndex(item => row.key === item.key);
-    const item = newData[index];
-    newData.splice(index, 1, {
-      ...item,
-      ...row,
-    });
-    this.props.onTableChange(newData);
-  };
-
-  render() {
-    const { dataSource } = this.props;
-    const components = {
-      body: {
-        row: EditableFormRow,
-        cell: EditableCell,
-      },
-    };
-    const columns = this.columns.map(col => {
-      if (!col.editable) {
-        return col;
-      }
-      return {
-        ...col,
-        onCell: record => ({
-          record,
-          editable: col.editable,
-          dataIndex: col.dataIndex,
-          title: col.title,
-          handleSave: this.handleSave,
-        }),
-      };
-    });
-    return (
-      <div>
-        <AuthButton perms="plugin:tcpSelector:add">
-          <Button onClick={this.handleAdd} type="primary" style={{ 
marginBottom: 16 }}>
-            {getIntlContent("SHENYU.DISCOVERY.SELECTOR.UPSTREAM.ADD")}
-          </Button>
-        </AuthButton>
-        <Table
-          components={components}
-          rowClassName={() => 'editable-row'}
-          bordered
-          dataSource={dataSource}
-          columns={columns}
-        />
-      </div>
-    );
-  }
-}
-
diff --git a/src/routes/Plugin/Discovery/tcp.less 
b/src/routes/Plugin/Discovery/discovery.less
similarity index 99%
rename from src/routes/Plugin/Discovery/tcp.less
rename to src/routes/Plugin/Discovery/discovery.less
index ba2d5ae7..eddaaf46 100644
--- a/src/routes/Plugin/Discovery/tcp.less
+++ b/src/routes/Plugin/Discovery/discovery.less
@@ -79,7 +79,7 @@
 }
 
 
-.tcpCard {
+.discoveryCard {
   :global {
     .ant-card-body {
       padding: 10px
diff --git a/src/routes/Plugin/Discovery/index.js 
b/src/routes/Plugin/Discovery/index.js
index 6d9efe9a..baaf7c6d 100644
--- a/src/routes/Plugin/Discovery/index.js
+++ b/src/routes/Plugin/Discovery/index.js
@@ -19,10 +19,10 @@ import React, {Component} from 'react';
 import {connect} from 'dva';
 import {Button, Pagination, Row, Switch, Tag, Input, Typography, message} from 
"antd";
 import {getIntlContent} from "../../../utils/IntlUtils";
-import tcpStyles from './tcp.less'
+import discoveryStyles from './discovery.less'
 import DiscoveryConfigModal from "./DiscoveryConfigModal";
 import ProxySelectorModal from "./ProxySelectorModal";
-import {TcpCard} from "./TcpCard";
+import {DiscoveryCard} from "./DiscoveryCard";
 import AuthButton from "../../../utils/AuthButton";
 import { getUpdateModal, updatePluginsEnabled } from "../../../utils/plugin";
 
@@ -35,13 +35,13 @@ const {Title} = Typography;
   ...shenyuDict,
   loading: loading.effects["global/fetchPlatform"]
 }))
-export default class TCPProxy extends Component {
+export default class DiscoveryProxy extends Component {
   constructor(props) {
     super(props);
     this.state = {
       searchKey: '',
       cardData: {
-        tcpType: '',
+        discoveryType: '',
         name: '',
         forwardPort: '',
         type: 'tcp',
@@ -71,7 +71,7 @@ export default class TCPProxy extends Component {
   componentDidMount() {
     const {dispatch, currentPage, pageSize} = this.props
     dispatch({
-      type: "discovery/fetchSelector",
+      type: "discovery/fetchProxySelectors",
       payload: {
         name: '',
         currentPage,
@@ -95,9 +95,9 @@ export default class TCPProxy extends Component {
   }
 
   // eslint-disable-next-line react/sort-comp
-  renderCards(selectorList = []) {
-    return selectorList.map(selector =>
-      <TcpCard key={selector.id} updateSelector={this.updateSelector} 
data={selector} handleDelete={this.handleDelete} 
handleRefresh={this.handleRefresh} />
+  renderCards(proxySelectorList = []) {
+    return proxySelectorList.map(selector =>
+      <DiscoveryCard key={selector.id} updateSelector={this.updateSelector} 
data={selector} handleDelete={this.handleDelete} 
handleRefresh={this.handleRefresh} />
     );
   }
 
@@ -111,7 +111,7 @@ export default class TCPProxy extends Component {
     })
     const {searchKey} = this.state;
     this.props.dispatch({
-      type: "discovery/fetchSelector",
+      type: "discovery/fetchProxySelectors",
       payload: {
         currentPage: page,
         pageSize,
@@ -152,7 +152,7 @@ export default class TCPProxy extends Component {
     this.setState({popup: ""});
     this.setState({
       cardData: {
-        tcpType: '',
+        discoveryType: '',
         name: '',
         forwardPort: '',
         type: 'tcp',
@@ -163,15 +163,7 @@ export default class TCPProxy extends Component {
           serverList: '',
           props: {}
         },
-        discoveryUpstreams: [
-          // {
-          //   protocol: '1',
-          //   url: '1',
-          //   status:'1',
-          //   weight: '1',
-          //   key: '1'
-          // }
-        ]
+        discoveryUpstreams: []
       }
     });
   };
@@ -201,11 +193,11 @@ export default class TCPProxy extends Component {
               discoveryDicts={discoveryDics}
               handleConfigDelete={this.handleConfigDelete}
               handleOk={values => {
-                const {name, serverList, props, tcpType} = values;
+                const {name, serverList, props, discoveryType} = values;
                 dispatch({
                   type: "discovery/set",
                   payload: {
-                    type: tcpType,
+                    type: discoveryType,
                     serverList,
                     name,
                     props,
@@ -257,7 +249,7 @@ export default class TCPProxy extends Component {
     const {searchKey} = this.state;
     const {currentPage, pageSize} = this.props
     this.props.dispatch({
-      type: "discovery/fetchSelector",
+      type: "discovery/fetchProxySelectors",
       payload: {
         currentPage,
         pageSize,
@@ -278,11 +270,11 @@ export default class TCPProxy extends Component {
         level: "1"
       },
       callback: discoveryConfigList => {
-        let tcpType = '';
+        let discoveryType = '';
         let id = null;
         let isSetConfig = false;
         if (discoveryConfigList !== null) {
-          tcpType = discoveryConfigList.type;
+          discoveryType = discoveryConfigList.type;
           id = discoveryConfigList.id
           isSetConfig = true;
         }
@@ -294,12 +286,22 @@ export default class TCPProxy extends Component {
               typeEnums={typeEnums}
               data={cardData}
               discoveryUpstreams={cardData.discoveryUpstreams}
-              tcpType={tcpType}
+              discoveryType={discoveryType}
               isAdd={true}
               isSetConfig={isSetConfig}
               discoveryDicts={discoveryDics}
               handleOk={values => {
-                const {name, forwardPort, props, listenerNode, handler, 
discoveryProps, serverList, discoveryType, upstreams} = values;
+                const {name, forwardPort, props, listenerNode, handler, 
discoveryProps, serverList, selectedDiscoveryType, upstreams} = values;
+                const upstreamsWithProps = upstreams.map(item => ({
+                  protocol: item.protocol,
+                  url: item.url,
+                  status: parseInt(item.status, 10),
+                  weight: item.weight,
+                  startupTime: item.startupTime,
+                  props: JSON.stringify({
+                    warmupTime: item.warmupTime
+                  })
+                }));
                 dispatch({
                   type: 'discovery/add',
                   payload: {
@@ -314,11 +316,11 @@ export default class TCPProxy extends Component {
                       id,
                       level: "0", // 0 selector
                       pluginName,
-                      discoveryType,
+                      discoveryType: selectedDiscoveryType,
                       serverList,
                       props: discoveryProps
                     },
-                    discoveryUpstreams: upstreams
+                    discoveryUpstreams: upstreamsWithProps
                   },
                   callback: () => {
                     this.closeModal();
@@ -340,9 +342,9 @@ export default class TCPProxy extends Component {
   }
 
   updateSelector = (id) => {
-    const {dispatch, selectorList, tcpType: discoveryType, currentPage, 
pageSize, plugins, typeEnums} = this.props;
+    const {dispatch, proxySelectorList, discoveryType, currentPage, pageSize, 
plugins, typeEnums} = this.props;
     const { discoveryDics, pluginName } = this.state;
-    const data = selectorList.find(value => value.id === id)
+    const data = proxySelectorList.find(value => value.id === id)
     const plugin = this.getPlugin(plugins, pluginName);
     let isSetConfig = false
     this.setState({
@@ -352,14 +354,20 @@ export default class TCPProxy extends Component {
       isSetConfig = true
     }
     const updateArray = data.discoveryUpstreams.map((item) => {
-      return { ...item, key: item.id };
+      let propsObj = JSON.parse(item.props || "{}");
+      if (item.props === null) {
+        propsObj = {
+          warmupTime: 10,
+        };
+      }
+      return { ...item, key: item.id, warmupTime: propsObj.warmupTime };
     });
     this.setState({
       popup: (
         <ProxySelectorModal
           recordCount={updateArray.length}
           discoveryUpstreams={updateArray}
-          tcpType={data.discovery.type}
+          discoveryType={data.discovery.type}
           typeEnums={typeEnums}
           isAdd={false}
           isSetConfig={isSetConfig}
@@ -368,6 +376,16 @@ export default class TCPProxy extends Component {
           discoveryDicts={discoveryDics}
           handleOk={values => {
             const {name, forwardPort, props, listenerNode, handler, 
discoveryProps, serverList, upstreams} = values;
+            const upstreamsWithProps = upstreams.map(item => ({
+              protocol: item.protocol,
+              url: item.url,
+              status: parseInt(item.status, 10),
+              weight: item.weight,
+              startupTime: item.startupTime,
+              props: JSON.stringify({
+                warmupTime: item.warmupTime
+              })
+            }));
             dispatch({
               type: 'discovery/update',
               payload: {
@@ -384,7 +402,7 @@ export default class TCPProxy extends Component {
                   serverList,
                   props: discoveryProps
                 },
-                discoveryUpstreams: upstreams
+                discoveryUpstreams: upstreamsWithProps
               },
               callback: () => {
                 this.closeUpdateModal();
@@ -446,14 +464,14 @@ export default class TCPProxy extends Component {
 
   render() {
     const {popup} = this.state;
-    const {selectorList, totalPage, currentPage, pageSize} = this.props;
+    const {proxySelectorList, totalPage, currentPage, pageSize} = this.props;
     const tag = {
       text: this.state.isPluginEnabled ? getIntlContent("SHENYU.COMMON.OPEN") 
: getIntlContent("SHENYU.COMMON.CLOSE"),
       color: this.state.isPluginEnabled ? 'green' : 'red'
     }
     return (
       <>
-        <div className={tcpStyles.main}>
+        <div className={discoveryStyles.main}>
           <Row style={{marginBottom: '0px', display: 'flex', justifyContent: 
'space-between', alignItems: 'center'}}>
             <div style={{display: 'flex', alignItems: 'end', flex: 1, margin: 
0}}>
               <Title level={2} style={{textTransform: 'capitalize', margin: '0 
20px 0 0'}}>
@@ -476,7 +494,7 @@ export default class TCPProxy extends Component {
           </Row>
 
           <Row>
-            <div className={tcpStyles["header-bar"]}>
+            <div className={discoveryStyles["header-bar"]}>
               <h3 style={{overflow: "visible", margin: 0}}>
                 {getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.TITLE")}
               </h3>
@@ -529,7 +547,7 @@ export default class TCPProxy extends Component {
               alignItems: 'stretch'
             }}
             >
-              {this.renderCards(selectorList)}
+              {this.renderCards(proxySelectorList)}
             </div>
           </Row>
 
diff --git a/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js 
b/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
index 7728ac87..2aa8af2d 100644
--- a/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
+++ b/src/routes/Plugin/PluginRuleHandle/ParamPluginRuleHandle.js
@@ -16,7 +16,7 @@
  */
 
 import React, {Component} from "react";
-import {Form, Select, Row, Col, Input, Button, Tabs, Table} from "antd";
+import {Button, Col, Form, Input, Row, Select, Table, Tabs} from "antd";
 import {getIntlContent} from '../../../utils/IntlUtils'
 
 const {Option} = Select;
@@ -96,7 +96,6 @@ class ParamPluginRuleConfig extends Component {
     const data = {};
     const currentData = this.getCurrentData();
     const valueStr = JSON.stringify(currentData);
-    // console.log(currentData);
     if (value !== undefined) {
       try {
         Object.assign(data, JSON.parse(value));
@@ -315,8 +314,7 @@ export default class ParamPluginRuleHandle extends 
Component {
     const {
       form: {getFieldValue}
     } = this.props;
-    const value = getFieldValue("handle");
-    return value;
+    return getFieldValue("handle");
   };
 
   render() {
diff --git a/src/services/api.js b/src/services/api.js
index 28bb80ca..e6b3ee36 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -1081,3 +1081,22 @@ export function fetchAlertReport(params) {
     }
   });
 }
+
+export function bindingSelector(params) {
+  return request(`${baseUrl}/proxy-selector/binding`,
+    {
+      method: `POST`,
+      body: params
+    });
+}
+
+export function updateDiscoveryUpstream(discoveryHandlerId, upstreams) {
+  return request(`${baseUrl}/discovery-upstream/${discoveryHandlerId}`,
+    {
+        method: `PUT`,
+        body: upstreams
+    });
+}
+
+
+
diff --git a/src/utils/AuthRoute.js b/src/utils/AuthRoute.js
index 50b9ff9c..9380412a 100644
--- a/src/utils/AuthRoute.js
+++ b/src/utils/AuthRoute.js
@@ -241,7 +241,6 @@ const setMenuIconAndSort = (menus, permissions) => {
       if (Array.isArray(menuArr)) {
         menuArr.forEach(menu => {
           const currentMenu = iconAndSortMap[formatRouteUrl(menu.path)];
-          // console.log(menu, currentMenu, menu.path);
           if (
             currentMenu &&
             currentMenu.meta.icon &&

Reply via email to