src/routes/Plugin/Common/Selector.js (1,665 lines of code) (raw):
/*
* 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, Fragment } from "react";
import {
Modal,
Form,
Select,
Input,
Switch,
Button,
message,
Tooltip,
Popconfirm,
Row,
Col,
Card,
Icon,
InputNumber,
DatePicker,
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,
formatDate,
formatTime,
formatDateString,
formatTimeString,
} 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 = {
labelCol: { sm: { span: 3 } },
wrapperCol: { sm: { span: 21 } },
};
const formCheckLayout = {
labelCol: { sm: { span: 18 } },
wrapperCol: { sm: { span: 4 } },
};
const isDivideUpstreamsRequiresForPlugin = (pluginId) => {
// 8: springCloud
return ["8"].includes(pluginId);
};
let id = 0;
@connect(({ pluginHandle, global, shenyuDict, discovery }) => ({
pluginHandle,
platform: global.platform,
shenyuDict,
discovery,
}))
class AddModal extends Component {
constructor(props) {
super(props);
const { handle, pluginId } = props;
let selectValue = `${props.type}` || null;
let data = {};
if (handle) {
try {
data = JSON.parse(handle);
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
}
const { divideUpstreams = [], gray = false, serviceId = "" } = data;
const {
discoveryUpstreams = [],
isDiscovery,
isAdd = true,
discoveryConfig = {},
} = this.props;
if (isDivideUpstreamsRequiresForPlugin(pluginId)) {
id = divideUpstreams.length;
}
this.state = {
selectValue,
gray,
serviceId,
divideUpstreams,
visible: false,
pluginHandleList: [],
upstreams: discoveryUpstreams,
recordCount: discoveryUpstreams ? discoveryUpstreams.length : 0,
discoveryHandler: null,
defaultValueList: null,
configPropsJson: {},
selectedDiscoveryValue: "local",
showDiscoveryImportModal: false,
importedDiscoveryId: "",
};
this.initSelectorCondition(props);
if (isDiscovery && !isAdd) {
this.state.configPropsJson = JSON.parse(discoveryConfig.props);
this.state.selectedDiscoveryValue = discoveryConfig.discoveryType;
}
}
componentDidMount() {
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: {
pluginId,
type,
handle,
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(",") });
}
}
},
},
});
}
handleSubmit = (e) => {
e.preventDefault();
const { form, handleOk, multiSelectorHandle, pluginId, isDiscovery } =
this.props;
const {
selectorConditions,
selectValue,
pluginHandleList,
defaultValueList,
configPropsJson,
upstreams,
importedDiscoveryId,
} = this.state;
let handle = [];
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const mySubmit =
selectValue !== "0" && this.checkConditions(selectorConditions);
if (mySubmit || selectValue === "0") {
if (isDiscovery) {
if (
new Set(upstreams.map((item) => item.url)).size !==
upstreams.length
) {
message.destroy();
message.error(`Contains duplicate urls`);
return;
}
// 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;
}
});
}
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 (isDivideUpstreamsRequiresForPlugin(pluginId)) {
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,
});
}
}
}
});
};
handleAdd = () => {
let { selectorConditions } = this.state;
selectorConditions.push({
paramType: "uri",
operator: "pathPattern",
paramName: "/",
paramValue: "",
});
this.setState({ selectorConditions }, () => {
let len = selectorConditions.length || 0;
let key = `paramTypeValueEn${len - 1}`;
this.setState({ [key]: true });
});
};
handleDelete = (index) => {
let { selectorConditions } = this.state;
if (selectorConditions && selectorConditions.length > 1) {
selectorConditions.splice(index, 1);
} else {
message.destroy();
message.error("At least one condition");
}
this.setState({ selectorConditions });
};
handleAddHandle = () => {
let { pluginHandleList } = this.state;
let pluginHandle = pluginHandleList[0];
let toAddPluginHandle = pluginHandle.map((e) => {
return { ...e, value: null };
});
pluginHandleList.push(toAddPluginHandle);
this.setState({
pluginHandleList,
});
};
handleDeleteHandle = (index) => {
let { pluginHandleList } = this.state;
if (pluginHandleList.length === 1) {
message.destroy();
message.error(getIntlContent("SHENYU.PLUGIN.HANDLE.TIP"));
} else {
pluginHandleList.splice(index, 1);
this.setState({ pluginHandleList });
}
};
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>
));
}
conditionChange = (index, name, value) => {
let { selectorConditions } = this.state;
selectorConditions[index][name] = value;
if (name === "paramType") {
let key = `paramTypeValueEn${index}`;
if (
value === "uri" ||
value === "host" ||
value === "ip" ||
value === "req_method" ||
value === "domain"
) {
this.setState({ [key]: true });
selectorConditions[index].paramName = "/";
} else {
this.setState({ [key]: false });
}
if (value === "post") {
selectorConditions[index].paramName = "filedName";
}
if (value === "query") {
selectorConditions[index].paramName = "paramName";
}
if (value === "header") {
selectorConditions[index].paramName = "headerName";
}
if (value === "cookie") {
selectorConditions[index].paramName = "cookieName";
}
if (value === "uri") {
selectorConditions[index].operator = "pathPattern";
} else if (value === "req_method") {
selectorConditions[index].operator = "=";
} else {
selectorConditions[index].operator = "";
}
}
this.setState({ selectorConditions });
};
checkConditions = (selectorConditions) => {
let result = true;
if (selectorConditions) {
selectorConditions.forEach((item, index) => {
const { paramType, operator, paramName, paramValue } = item;
if (
!paramType ||
!operator ||
(operator !== "isBlank" && !paramValue)
) {
message.destroy();
message.error(`Line ${index + 1} condition is incomplete`);
result = false;
}
if (paramType === "uri" || paramType === "host" || paramType === "ip") {
// aaa
} else {
// eslint-disable-next-line no-lonely-if
if (!paramName) {
message.destroy();
message.error(`Line ${index + 1} condition is incomplete`);
result = false;
}
}
});
} else {
message.destroy();
message.error(`Incomplete condition`);
result = false;
}
return result;
};
initSelectorCondition = (props) => {
const selectorConditions = props.selectorConditions || [
{
paramType: "uri",
operator: "pathPattern",
paramName: "/",
paramValue: "",
},
];
selectorConditions.forEach((item, index) => {
const { paramType } = item;
let key = `paramTypeValueEn${index}`;
if (
paramType === "uri" ||
paramType === "host" ||
paramType === "ip" ||
paramType === "req_method" ||
paramType === "domain"
) {
this.state[key] = true;
selectorConditions[index].paramName = "/";
} else {
this.state[key] = false;
}
});
this.state.selectorConditions = selectorConditions;
};
initDics = () => {
this.initDic("operator");
this.initDic("matchMode");
this.initDic("paramType");
this.initDic("discoveryMode");
};
initDic = (type) => {
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 });
}
},
},
});
};
getSelectValue = (value) => {
this.setState({
selectValue: value,
});
};
renderPluginHandler = () => {
const { pluginHandleList, divideUpstreams, gray, serviceId } = this.state;
const {
form: { getFieldDecorator, getFieldValue, setFieldsValue },
multiSelectorHandle,
pluginId,
isDiscovery,
} = this.props;
const labelWidth = 75;
if (isDiscovery) {
return;
}
if (isDivideUpstreamsRequiresForPlugin(pluginId)) {
getFieldDecorator("keys", {
initialValue: Array.from({
length: divideUpstreams.length,
}).map((_, i) => i),
});
const keys = getFieldValue("keys");
const Rule = keys.map((key, index) => (
<Item
required
key={key}
{...(index === 0
? { labelCol: { span: 3 }, wrapperCol: { span: 21 } }
: { wrapperCol: { span: 21, offset: 3 } })}
label={index === 0 ? "divideUpstreams" : ""}
>
<Card>
<div
style={{
display: "flex",
alignItems: "center",
justifycontent: "space-between",
}}
>
<div style={{ flex: 1 }}>
<Row gutter={30}>
<Col span={10}>
<Item
label="protocol"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(`divideUpstreams[${key}].protocol`, {
initialValue: divideUpstreams[key]
? divideUpstreams[key].protocol
: "",
rules: [
{
required: true,
message: "protocol is required",
},
],
})(<Input allowClear />)}
</Item>
</Col>
<Col span={14} style={{ marginLeft: "-20px" }}>
<Item
label="upstreamUrl"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(
`divideUpstreams[${key}].upstreamUrl`,
{
initialValue: divideUpstreams[key]
? divideUpstreams[key].upstreamUrl
: "",
rules: [
{
required: true,
message: "upstreamUrl is required",
},
],
},
)(<Input allowClear />)}
</Item>
</Col>
</Row>
<Row gutter={30}>
<Col span={10}>
<Item
label="weight"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(`divideUpstreams[${key}].weight`, {
initialValue: divideUpstreams[key]
? divideUpstreams[key].weight
: "",
rules: [
{
required: true,
message: "weight is required",
},
],
})(
<InputNumber
min={0}
max={100}
style={{ width: "100%" }}
/>,
)}
</Item>
</Col>
<Col span={14} style={{ marginLeft: "-20px" }}>
<Item
label="timestamp"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(`divideUpstreams[${key}].timestamp`, {
initialValue: divideUpstreams[key]
? divideUpstreams[key].timestamp
: "",
rules: [
{
required: true,
message: "timestamp is required",
},
],
})(<InputNumber style={{ width: "100%" }} />)}
</Item>
</Col>
</Row>
<Row gutter={30}>
<Col span={10}>
<Item
label="status"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(`divideUpstreams[${key}].status`, {
initialValue: divideUpstreams[key]
? divideUpstreams[key].status
: false,
valuePropName: "checked",
rules: [
{
required: true,
message: "status is required",
},
],
})(<Switch />)}
</Item>
</Col>
<Col span={14} style={{ marginLeft: "-20px" }}>
<Item
label="warmup"
{...{ labelCol: { span: 9 }, wrapperCol: { span: 15 } }}
>
{getFieldDecorator(`divideUpstreams[${key}].warmup`, {
initialValue: divideUpstreams[key]
? divideUpstreams[key].warmup
: "",
rules: [
{
required: true,
message: "warmup is required",
},
],
})(<InputNumber style={{ width: "100%" }} />)}
</Item>
</Col>
</Row>
</div>
<div style={{ width: 64, textAlign: "right" }}>
<Icon
onClick={() => {
setFieldsValue({
keys: keys.filter((k) => k !== key),
});
}}
type="minus-circle-o"
style={{
fontSize: 18,
color: "#ff0000",
cursor: "pointer",
}}
/>
</div>
</div>
</Card>
</Item>
));
return (
<div className={styles.springCloud}>
<Item label="serviceId" {...formItemLayout}>
<div style={{ display: "flex", alignItems: "center" }}>
{getFieldDecorator("serviceId", {
initialValue: serviceId,
rules: [
{
required: true,
},
],
})(
<Input
style={{ width: "50%" }}
allowClear
placeholder="serviceId"
/>,
)}
<Item label="gray" {...formCheckLayout} style={{ margin: 0 }}>
{getFieldDecorator("gray", {
valuePropName: "checked",
initialValue: gray,
rules: [
{
required: true,
},
],
})(<Switch />)}
</Item>
</div>
</Item>
{Rule}
<Item label="" {...{ wrapperCol: { span: 21, offset: 3 } }}>
<Button
className={styles.addButton}
type="primary"
onClick={() => {
const keysData = getFieldValue("keys");
// eslint-disable-next-line no-plusplus
const nextKeys = keysData.concat(id++);
setFieldsValue({
keys: nextKeys,
});
}}
>
Add divide upstream
</Button>
</Item>
</div>
);
}
if (Array.isArray(pluginHandleList) && pluginHandleList.length) {
return (
<Item
label={getIntlContent("SHENYU.COMMON.DEAL")}
{...formItemLayout}
className={styles.rootFormItem}
>
<div
className={styles.handleWrap}
style={{
marginTop: 0,
}}
>
<div>
{pluginHandleList.map((handleList, index) => {
return (
<div
key={index}
style={{
display: "flex",
justifyContent: "space-between",
flexDirection: "row",
}}
>
<ul
className={classnames({
[styles.handleUl]: true,
[styles.handleSelectorUl]: true,
[styles.springUl]: true,
})}
style={{
width: "100%",
padding: 0,
marginBottom: "-6px",
}}
>
{handleList &&
handleList.map((item) => {
let required = item.required === "1";
let defaultValue =
item.value === 0 || item.value === false
? item.value
: item.value || item.defaultValue;
let placeholder = item.label || item.placeholder;
let checkRule = item.checkRule;
let fieldName = item.field + index;
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 (item.dataType === 1) {
return (
<li key={fieldName}>
<Tooltip title={placeholder}>
<Item>
{getFieldDecorator(fieldName, {
rules,
initialValue: defaultValue,
})(
<Input
allowClear
addonBefore={
<div style={{ width: labelWidth }}>
{item.label}
</div>
}
placeholder={placeholder}
key={fieldName}
/>,
)}
</Item>
</Tooltip>
</li>
);
} else if (item.dataType === 3 && item.dictOptions) {
return (
<li key={fieldName}>
<Tooltip title={placeholder}>
<Item>
{getFieldDecorator(fieldName, {
rules,
initialValue:
defaultValue === true
? "true"
: defaultValue === false
? "false"
: defaultValue,
})(
<Select
placeholder={placeholder}
style={{ width: "100%" }}
>
{item.dictOptions.map((option) => {
const optionValue =
option.dictValue === true
? "true"
: option.dictValue === false
? "false"
: option.dictValue;
return (
<Option
key={optionValue}
value={optionValue}
>
{option.dictName} ({item.label})
</Option>
);
})}
</Select>,
)}
</Item>
</Tooltip>
</li>
);
} else {
return (
<li key={fieldName}>
<Tooltip title={item.label}>
<Item>
{getFieldDecorator(fieldName, {
rules,
initialValue: defaultValue,
})(
<Input
allowClear
addonBefore={
<div style={{ width: labelWidth }}>
{item.label}
</div>
}
placeholder={placeholder}
key={fieldName}
onChange={(e) => {
this.onDealChange(
e.target.value,
item,
);
}}
/>,
)}
</Item>
</Tooltip>
</li>
);
}
})}
</ul>
{multiSelectorHandle && (
<div style={{ width: 80 }}>
<Popconfirm
title={getIntlContent("SHENYU.COMMON.DELETE")}
placement="bottom"
onCancel={(e) => {
e.stopPropagation();
}}
onConfirm={(e) => {
e.stopPropagation();
this.handleDeleteHandle(index);
}}
okText={getIntlContent("SHENYU.COMMON.SURE")}
cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
>
<Button type="danger">
{getIntlContent("SHENYU.COMMON.DELETE.NAME")}
</Button>
</Popconfirm>
</div>
)}
</div>
);
})}
</div>
{multiSelectorHandle && (
<div style={{ width: 80, marginLeft: 5 }}>
<Button onClick={this.handleAddHandle} type="primary">
{getIntlContent("SHENYU.COMMON.ADD")}
</Button>
</div>
)}
</div>
</Item>
);
}
return null;
};
setPluginHandleList = (pluginHandles) => {
this.setState({ pluginHandleList: pluginHandles });
};
handleCopyData = (copyData) => {
if (!copyData) {
this.setState({ visible: false });
return;
}
const { form } = this.props;
const {
selectorConditions,
name,
type,
matchMode,
continued,
loged,
enabled,
sort,
} = copyData;
const formData = {
name,
type: type.toString(),
continued,
loged,
enabled,
sort,
};
if (formData.type === "1") {
formData.matchMode = matchMode.toString();
this.initSelectorCondition({
selectorConditions: selectorConditions.map((v) => {
const {
id: rawId,
selectorId,
dateCreated,
dateUpdated,
...condition
} = v;
return condition;
}),
});
}
form.setFieldsValue(formData);
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) => {
return (
<Option key={operate.dictValue} value={operate.dictValue}>
{operate.dictName}
</Option>
);
});
if (paramType !== "uri") {
operatorsFil = operatorsFil.filter((operate) => {
return operate.key !== "pathPattern" ? operate : "";
});
}
if (
paramType !== "post" &&
paramType !== "query" &&
paramType !== "header" &&
paramType !== "cookie"
) {
operatorsFil = operatorsFil.filter((operate) => {
return operate.key !== "isBlank" ? operate : "";
});
}
if (
paramType === "uri" ||
paramType === "host" ||
paramType === "ip" ||
paramType === "cookie" ||
paramType === "domain"
) {
operatorsFil = operatorsFil.filter((operate) => {
return operate.key !== "TimeBefore" && operate.key !== "TimeAfter"
? operate
: "";
});
}
if (paramType === "req_method") {
operatorsFil = operatorsFil.filter((operate) => {
return operate.key === "=" ? operate : "";
});
}
return operatorsFil;
}
return "";
};
renderParamTypeOptions = (paramTypes = []) => {
if (paramTypes && paramTypes instanceof Array) {
return paramTypes.map((typeItem) => {
const { dictValue, dictName } = typeItem;
return (
<Option key={dictValue} value={dictValue}>
{dictName}
</Option>
);
});
}
return "";
};
getParamValueInput = (item, index) => {
if (item.operator === "TimeBefore" || item.operator === "TimeAfter") {
let date = new Date();
const defaultDay = date
.getFullYear()
.toString()
.concat("-")
.concat(date.getMonth() + 1)
.concat("-")
.concat(date.getDate());
return (
<Input.Group compact style={{ top: -2 }}>
<DatePicker
style={{ width: "51%" }}
onChange={(e) => {
let day = e
? e
.eraYear()
.toString()
.concat("-")
.concat(e.month() + 1)
.concat("-")
.concat(e.date())
: defaultDay;
this.conditionChange(
index,
"paramValue",
`${formatDateString(day)} ${formatTimeString(item.paramValue)}`,
);
}}
value={formatDate(item.paramValue)}
/>
<TimePicker
style={{ width: "49%" }}
onChange={(e) => {
let time = e
? ""
.concat(" ")
.concat(e.hours())
.concat(":")
.concat(e.minutes())
.concat(":")
.concat(e.seconds())
: "";
this.conditionChange(
index,
"paramValue",
`${formatDateString(item.paramValue)} ${formatTimeString(time)}`,
);
}}
value={formatTime(item.paramValue)}
/>
</Input.Group>
);
} else {
return (
<Input
allowClear
onChange={(e) => {
this.conditionChange(index, "paramValue", e.target.value);
}}
value={item.paramValue}
/>
);
}
};
renderBasicConfig = () => {
let {
form,
name = "",
platform,
type = "",
matchMode = "",
continued = true,
loged = true,
enabled = true,
matchRestful = false,
sort,
} = this.props;
const {
selectorConditions,
selectValue,
operatorDics,
matchModeDics,
paramTypeDics,
visible,
} = this.state;
type = `${type.toString()}` || "1";
let { selectorTypeEnums } = platform;
const { getFieldDecorator } = form;
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>
}
/>,
)}
</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"),
},
],
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" && (
<>
<Item
label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
{...formItemLayout}
>
{getFieldDecorator("matchMode", {
rules: [
{
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}
>
{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",
}}
>
<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",
}}
>
<Tooltip title={item.paramValue}>
{this.getParamValueInput(item, index)}
</Tooltip>
</Col>
<Col span={4}>
<Button
type="danger"
onClick={() => {
this.handleDelete(index);
}}
>
{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>
</>
)}
{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")}
/>,
)}
</Item>
<Item label={" "} colon={false} {...formItemLayout}>
<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>
</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}
/>
</>
)}
</>
);
};
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>
);
}
}
export default Form.create()(AddModal);