antd#AutoComplete JavaScript Examples
The following examples show how to use
antd#AutoComplete.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js From online-test-platform with Apache License 2.0 | 6 votes |
render() {
const { className, placeholder, open, ...restProps } = this.props;
const { searchMode, value } = this.state;
delete restProps.defaultOpen; // for rc-select not affected
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<span
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
const { onVisibleChange } = this.props;
onVisibleChange(searchMode);
}
}}
>
<Icon type="search" key="Icon" />
<AutoComplete
key="AutoComplete"
{...restProps}
className={inputClass}
value={value}
onChange={this.onChange}
>
<Input
ref={node => {
this.input = node;
}}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode}
/>
</AutoComplete>
</span>
);
}
Example #2
Source File: Search.js From YApi-X with MIT License | 6 votes |
// getDataSource(groupList){
// const groupArr =[];
// groupList.forEach(item =>{
// groupArr.push("group: "+ item["group_name"]);
// })
// return groupArr;
// }
render() {
const { dataSource } = this.state;
return (
<div className="search-wrapper">
<AutoComplete
className="search-dropdown"
dataSource={dataSource}
style={{ width: '100%' }}
defaultActiveFirstOption={false}
onSelect={this.onSelect}
onSearch={this.handleSearch}
// filterOption={(inputValue, option) =>
// option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
// }
>
<Input
prefix={<Icon type="search" className="srch-icon" />}
placeholder="搜索分组/项目/接口"
className="search-input"
/>
</AutoComplete>
</div>
);
}
Example #3
Source File: index.js From camel-store-admin with Apache License 2.0 | 6 votes |
render() {
const { className, placeholder, open, ...restProps } = this.props;
const { searchMode, value } = this.state;
delete restProps.defaultOpen; // for rc-select not affected
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<span
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
const { onVisibleChange } = this.props;
onVisibleChange(searchMode);
}
}}
>
<Icon type="search" key="Icon" />
<AutoComplete
key="AutoComplete"
{...restProps}
className={inputClass}
value={value}
onChange={this.onChange}
>
<Input
ref={node => {
this.input = node;
}}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode}
/>
</AutoComplete>
</span>
);
}
Example #4
Source File: HighlightInput.js From 4IZ268-2021-2022-ZS with MIT License | 6 votes |
HighlightInput = () => {
const clubList = useDataStore((store) => store.clubList)
const setHighlightClub = usePersonalSettings((store) => store.setHighlightClub)
const onChange = (val) => {
setHighlightClub(val)
}
const onSelect = (val) => {
setHighlightClub(val)
}
return (
<AutoComplete
options={clubList.map((str) => { return { label: str, value: str } })}
onSelect={onSelect}
onChange={onChange}
style={{width: 200}}
/>
)
}
Example #5
Source File: autocomplete_filter.js From art-dashboard-ui with Apache License 2.0 | 6 votes |
render() {
return (
<AutoComplete style={{
width: "90%",
paddingRight: "10%"
}}
allowClear={true}
options={this.state.options}
placeholder={this.state.placeholder}
value={this.state.value}
onSelect={this.onChange}
filterOption={(inputValue, option) =>
option.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>
)
}
Example #6
Source File: index.jsx From vpp with MIT License | 5 votes |
HeaderSearch = props => {
const {
className,
defaultValue,
onVisibleChange,
placeholder,
open,
defaultOpen,
...restProps
} = props;
const inputRef = useRef(null);
const [value, setValue] = useMergeValue(defaultValue, {
value: props.value,
onChange: props.onChange,
});
const [searchMode, setSearchMode] = useMergeValue(defaultOpen || false, {
value: props.open,
onChange: onVisibleChange,
});
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<div
className={classNames(className, styles.headerSearch)}
onClick={() => {
setSearchMode(true);
if (searchMode && inputRef.current) {
inputRef.current.focus();
}
}}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
if (onVisibleChange) {
onVisibleChange(searchMode);
}
}
}}
>
<SearchOutlined
key="Icon"
style={{
cursor: 'pointer',
}}
/>
<AutoComplete
key="AutoComplete"
className={inputClass}
value={value}
style={{
height: 28,
marginTop: -6,
}}
options={restProps.options}
onChange={setValue}
>
<Input
ref={inputRef}
defaultValue={defaultValue}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={e => {
if (e.key === 'Enter') {
if (restProps.onSearch) {
restProps.onSearch(value);
}
}
}}
onBlur={() => {
setSearchMode(false);
}}
/>
</AutoComplete>
</div>
);
}
Example #7
Source File: index.js From QiskitFlow with Apache License 2.0 | 5 votes |
FilterForm = ({
filter,
setFilterDateStart,
setFilterDateEnd,
setFilterQuery,
}) => {
const onSearch = q => {
setFilterQuery(q);
};
const onSelect = selection => {
console.log('Selection:', selection);
};
const onDateChange = dates => {
const format = 'DD-MM-YYYY';
const [start, end] = dates;
setFilterDateStart(start.format(format));
setFilterDateEnd(end.format(format));
};
return(<span/>);
return (
<Form layout="inline" name="basic" style={{ padding: '16px' }}>
<Form.Item label="Search" name="query">
<AutoComplete
options={[]}
value={filter.query}
style={{ width: 500 }}
onSelect={onSelect}
onSearch={debounce(onSearch, 500)}
placeholder="Name..."
/>
</Form.Item>
<Form.Item name="dates" label="Dates">
<RangePicker onChange={onDateChange} />
</Form.Item>
</Form>
);
}
Example #8
Source File: index.js From scalable-form-platform with MIT License | 5 votes |
render() {
const popupContainer = this.props.formContext && this.props.formContext.popupContainer;
const {schema, id, options, disabled, readonly, autofocus, placeholder} = this.props;
// 对于value值为空字符串的情况,value的值传入undefined,这样才能显示组件的placeholder
let {value} = this.state;
if (value === '') {
value = undefined;
}
// enumOptions会由react-jsonschema-form注入到组件的options中 https://github.com/mozilla-services/react-jsonschema-form#custom-widget-components note
let enumOptions = options.enumOptions || [];
if (typeof schema.data !== 'undefined' && schema.data.length >= 0) {
enumOptions = schema.data;
}
// 这个idPrefix决定所有字段的id的前缀,与react-jsonschema-form组件中的idPrefix属性相同https://github.com/mozilla-services/react-jsonschema-form#id-prefix
const idPrefix = 'root';
const fieldCode = id.slice(idPrefix.length + 1);
//解析schema中约定的_errorType字段,用来标识是validate中哪种类型校验不通过
let validateMessage = '';
let _errorType = options._errorType || '';
if (_errorType !== '' && typeof options.validate !== 'undefined') {
validateMessage = this._getValidateMessage(_errorType, options.validate);
}
return (
<div className={classnames({
'ant-form-item-control': true,
'xform-custom-widget': true,
'xform-custom-suggest-select': true,
'has-error': _errorType !== ''
})}>
<AutoComplete
defaultActiveFirstOption={false}
filterOption={false}
value={value}
disabled={disabled}
readonly={readonly}
onSearch={(value) => {
this.handleKeywordChange(fieldCode, value);
}}
onSelect={this.handleSelectChange}
autoFocus={autofocus}
placeholder={placeholder}
getPopupContainer={popupContainer}
{...options}
>
{enumOptions.map((enums) => {
return <Option key={enums.value} value={enums.value}>{enums.label}</Option>
})}
</AutoComplete>
<div className="ant-form-explain">{validateMessage}</div>
</div>
);
}
Example #9
Source File: index.js From scalable-form-platform with MIT License | 5 votes |
Option = AutoComplete.Option
Example #10
Source File: MyAutoComplete.jsx From react-sendbird-messenger with GNU General Public License v3.0 | 5 votes |
export function MyAutoComplete({
style,
options = [],
value = '',
onSelect = () => {},
onSearch = () => {},
onChange = () => {},
placeholder = 'Search for people',
}) {
return (
<div style={style}>
<AutoComplete
options={options}
style={{ width: '100%' }}
onSelect={onSelect}
onSearch={onSearch}
filterOption={(inputValue, option) =>
option.value
.toUpperCase()
.indexOf(inputValue.toUpperCase()) !== -1
}
notFoundContent={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
allowClear={true}
backfill={true}
// autoFocus={true}
>
<Input
value={value}
onChange={onChange}
placeholder={placeholder}
prefix={
<SearchOutlined
style={{
fontSize: 16,
color: '#d9d9d9',
}}
/>
}
/>
</AutoComplete>
</div>
)
}
Example #11
Source File: K8SClusterSetting.jsx From juno with Apache License 2.0 | 5 votes |
function ClusterForm(props) {
const { form, onOk } = props;
const { data: zoneEnvResp, loading: zoneEnvLoading } = useRequest(zoneEnvTree);
const zoneCodes = {};
const envs = [];
zoneEnvResp &&
Object.keys(zoneEnvResp.data).forEach((env) => {
envs.push(env);
zoneEnvResp.data[env].forEach((zone) => {
zoneCodes[zone.zone_code] = zone.zone_name;
});
});
return (
<Form form={form} onFinish={onOk}>
<Form.Item label={'名称'} name={'name'}>
<Input />
</Form.Item>
<Form.Item label={'Env'} name={'env'}>
<Select mode={'tags'} loading={zoneEnvLoading}>
{envs.map((env) => (
<Select.Option value={env}>{env}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={'Zone Code'} name={'zone_code'}>
<AutoComplete>
{Object.keys(zoneCodes).map((code) => {
return (
<AutoComplete.Option value={code}>
<span>{zoneCodes[code]}</span>
<span style={{ fontWeight: 'bold', marginLeft: '10px' }}>[{code}]</span>
</AutoComplete.Option>
);
})}
</AutoComplete>
</Form.Item>
<Form.Item label={'Zone Name'} name={'zone_name'}>
<Input />
</Form.Item>
<Form.Item label={'domain'} name={'domain'}>
<Input />
</Form.Item>
<Form.Item label={'token'} name={'token'}>
<Input />
</Form.Item>
</Form>
);
}
Example #12
Source File: Search.js From YApi-X with MIT License | 5 votes |
Option = AutoComplete.Option
Example #13
Source File: TimeLine.js From YApi-X with MIT License | 5 votes |
{ Option, OptGroup } = AutoComplete
Example #14
Source File: index.jsx From spring-boot-plus-admin-react with Apache License 2.0 | 5 votes |
render() {
const { className, defaultValue, placeholder, open, ...restProps } = this.props;
const { searchMode, value } = this.state;
delete restProps.defaultOpen; // for rc-select not affected
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<span
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
const { onVisibleChange } = this.props;
onVisibleChange(searchMode);
}
}}
>
<Icon type="search" key="Icon" />
<AutoComplete
key="AutoComplete"
{...restProps}
className={inputClass}
value={value}
onChange={this.onChange}
>
<Input
ref={node => {
this.inputRef = node;
}}
defaultValue={defaultValue}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode}
/>
</AutoComplete>
</span>
);
}
Example #15
Source File: index.js From gobench with Apache License 2.0 | 5 votes |
render() { const { result } = this.state const children = result.map(email => <Option key={email}>{email}</Option>) return ( <div> <div className="row"> <div className="col-lg-6 mb-5"> <h5 className="mb-3"> <strong>Basic</strong> </h5> <AutoComplete dataSource={this.state.dataSource} style={{ width: 200 }} onSelect={onSelect} onSearch={this.onSearch} placeholder="input here" /> <br /> <br /> <AutoComplete value={this.state.value} dataSource={this.state.dataSource} style={{ width: 200 }} onSelect={onSelect} onSearch={this.onSearch} onChange={this.onChange} placeholder="control mode" /> </div> <div className="col-lg-6 mb-5"> <h5 className="mb-3"> <strong>Customized</strong> </h5> <AutoComplete style={{ width: 200 }} onSearch={this.handleSearch} placeholder="input here" > {children} </AutoComplete> </div> </div> </div> ) }
Example #16
Source File: index.js From gobench with Apache License 2.0 | 5 votes |
{ Option } = AutoComplete
Example #17
Source File: index.jsx From egoshop with Apache License 2.0 | 5 votes |
render() {
const { className, placeholder, open, ...restProps } = this.props;
const { searchMode, value } = this.state;
delete restProps.defaultOpen; // for rc-select not affected
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<span
className={classNames(className, styles.headerSearch)}
onClick={this.enterSearchMode}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
const { onVisibleChange } = this.props;
onVisibleChange(searchMode);
}
}}
>
<Icon type="search" key="Icon" />
<AutoComplete
key="AutoComplete"
{...restProps}
className={inputClass}
value={value}
onChange={this.onChange}
>
<Input
ref={node => {
this.inputRef = node;
}}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={this.onKeyDown}
onBlur={this.leaveSearchMode}
/>
</AutoComplete>
</span>
);
}
Example #18
Source File: GeneSearchBar.jsx From ui with MIT License | 5 votes |
GeneSearchBar = (props) => {
const { plotUuid, experimentId, searchBarUuid } = props;
const dispatch = useDispatch();
const geneList = useSelector((state) => state.genes.properties.views[searchBarUuid]?.data);
const config = useSelector((state) => state.componentConfig[plotUuid]?.config);
const [options, setOptions] = useState([]);
// pass reactive component as value (search text) to allow auto clear on select
const [value, setValue] = useState('');
const onSelect = (newGene) => {
if (!geneList.includes(newGene) || config?.selectedGenes.includes(newGene)) {
return;
}
const genes = _.clone(config?.selectedGenes);
genes.push(newGene);
dispatch(loadGeneExpression(experimentId, genes, plotUuid));
setValue('');
};
const onSearch = (searchText) => {
setValue(searchText);
setOptions(!searchText ? [] : filterGenes(searchText, geneList, config?.selectedGenes));
};
return (
<AutoComplete
allowClear
value={value}
options={options}
style={{ width: '100%' }}
onSelect={onSelect}
onSearch={onSearch}
placeholder='Search for genes...'
/>
);
}
Example #19
Source File: index.js From bank-client with MIT License | 5 votes |
function Recipient({ intl, onValidateFields }) {
const { recipients } = useSelector(stateSelector);
const dispatch = useDispatch();
const onChangeRecipientAccountBill = (name, value) =>
dispatch(changeInputAction({ name, value }));
const onSearchRecipient = (value) =>
value && dispatch(searchRecipientAction(value));
const checkStringConsistsNumbersOnly = (_, value) => {
if (value && !numberValidation(value)) {
return Promise.reject(
new Error(intl.formatMessage(messages.validationNumber)),
);
}
if (!value) {
return Promise.reject(new Error(intl.formatMessage(messages.validation)));
}
return Promise.resolve();
};
const options = recipients.map((recipient) => ({
label: (
<>
<div>
{recipient.user.firstName} {recipient.user.lastName}
</div>
<div>{recipient.accountBillNumber}</div>
</>
),
value: recipient.accountBillNumber.replace(/ /g, ''),
}));
return (
<StyledFormItem
label={intl.formatMessage(messages.label)}
name="recipientBill"
rules={[{ validator: checkStringConsistsNumbersOnly }]}
>
<AutoComplete
onSearch={onSearchRecipient}
onChange={(value) =>
onChangeRecipientAccountBill('recipientAccountBillNumber', value)
}
options={options}
notFoundContent={
<div>
<FormattedMessage {...messages.notFoundContent} />
</div>
}
>
<Input
onPressEnter={onValidateFields}
onKeyPress={disabledSpacesInput}
maxLength="26"
placeholder={intl.formatMessage(messages.placeholder)}
suffix={
<Tooltip title={intl.formatMessage(messages.tooltip)}>
<QuestionCircleOutlined />
</Tooltip>
}
/>
</AutoComplete>
</StyledFormItem>
);
}
Example #20
Source File: Playlists.js From remixr with MIT License | 5 votes |
export default function Playlist() {
const [accessToken] = useState(Cookies.get('access_token'));
const [playlists, setPlaylists] = useState([]);
const [filteredPlaylists, setFilteredPlaylists] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
if (accessToken) {
ReactGA.pageview('/playlists');
let url = process.env.REACT_APP_API_URL + '/playlists';
transport.get(url).then(
(response) => {
setPlaylists(response.data.playlists);
setFilteredPlaylists(response.data.playlists);
setLoading(false);
},
(error) => {
setError(true);
}
);
}
}, []);
if (!accessToken) {
return <Redirect to="/" />;
}
const filter = (searchTerm) => {
setFilteredPlaylists(playlists.filter((item) => item.name.toLowerCase().includes(searchTerm.toLowerCase())));
ReactGA.event({
category: 'Playlist',
action: 'Search playlists',
});
};
if (error) {
return <ErrorScreen />;
}
return (
<div className="playlists">
<Navbar />
<Title> Select playlist </Title>
<AutoComplete className="searchBox" onSearch={filter}>
<Input className="rounded" size="large" placeholder="Filter Playlists" />
</AutoComplete>
<Divider />
<div>
<Row gutter={[16, 24]}>
{loading && <Skeleton active />}
{filteredPlaylists &&
filteredPlaylists.map((item) => {
return <PlaylistCard playlist={item} key={item.id} />;
})}
</Row>
</div>
</div>
);
}
Example #21
Source File: align.jsx From virtuoso-design-system with MIT License | 5 votes |
storiesOf('antd/Input', module).add('align', () =>
<>
<Mentions style={{ width: 100 }} rows={1} />
<Input.TextArea rows={1} style={{ width: 100 }} />
<Button type="primary">Button</Button>
<Input style={{ width: 100 }} />
<Text copyable>Ant Design</Text>
<Input prefix="1" suffix="2" style={{ width: 100 }} />
<Input addonBefore="1" addonAfter="2" style={{ width: 100 }} />
<InputNumber style={{ width: 100 }} />
<DatePicker style={{ width: 100 }} />
<TimePicker style={{ width: 100 }} />
<Select style={{ width: 100 }} defaultValue="jack">
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="disabled" disabled>
Disabled
</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
<TreeSelect style={{ width: 100 }} />
<Cascader defaultValue={['zhejiang', 'hangzhou', 'xihu']} options={options} />
<RangePicker />
<DatePicker picker="month" />
<Radio.Group defaultValue="a">
<Radio.Button value="a">Hangzhou</Radio.Button>
<Radio.Button value="b">Shanghai</Radio.Button>
</Radio.Group>
<AutoComplete style={{ width: 100 }} placeholder="input here" />
<br />
<Input prefix="$" addonBefore="Http://" addonAfter=".com" defaultValue="mysite" />
<Input style={narrowStyle} suffix="Y" />
<Input style={narrowStyle} />
<Input style={narrowStyle} defaultValue="1" suffix="Y" />
</>,
{ docs: { page: () => (<><hr />
<p>order: 99
title:
zh-CN: 文本对齐
en-US: Text Align</p>
<h2 id="debugtrue">debug: true</h2></>) } });
Example #22
Source File: index.jsx From prometheusPro with MIT License | 5 votes |
HeaderSearch = props => {
const {
className,
defaultValue,
onVisibleChange,
placeholder,
open,
defaultOpen,
...restProps
} = props;
const inputRef = useRef(null);
const [value, setValue] = useMergeValue(defaultValue, {
value: props.value,
onChange: props.onChange,
});
const [searchMode, setSearchMode] = useMergeValue(defaultOpen || false, {
value: props.open,
onChange: onVisibleChange,
});
const inputClass = classNames(styles.input, {
[styles.show]: searchMode,
});
return (
<div
className={classNames(className, styles.headerSearch)}
onClick={() => {
setSearchMode(true);
if (searchMode && inputRef.current) {
inputRef.current.focus();
}
}}
onTransitionEnd={({ propertyName }) => {
if (propertyName === 'width' && !searchMode) {
if (onVisibleChange) {
onVisibleChange(searchMode);
}
}
}}
>
<SearchOutlined
key="Icon"
style={{
cursor: 'pointer',
}}
/>
<AutoComplete
key="AutoComplete"
className={inputClass}
value={value}
style={{
height: 28,
marginTop: -6,
}}
options={restProps.options}
onChange={setValue}
>
<Input
ref={inputRef}
size="middle"
defaultValue={defaultValue}
aria-label={placeholder}
placeholder={placeholder}
onKeyDown={e => {
if (e.key === 'Enter') {
if (restProps.onSearch) {
restProps.onSearch(value);
}
}
}}
onBlur={() => {
setSearchMode(false);
}}
/>
</AutoComplete>
</div>
);
}
Example #23
Source File: index.jsx From juno with Apache License 2.0 | 4 votes |
render() {
const {
app_service_tree,
use_cases,
editor,
selected_user_case,
node_addr_list,
selected_service,
right_menu_visible,
right_menu_position,
right_menu,
dispatch,
response,
active_tab,
selected_history_id,
public_cases,
settings,
setting_dialog_visible,
service_bind_dialog_visible,
proto_list,
appList,
use_case_loading,
} = this.props;
let addrOptions = node_addr_list?.hosts
? node_addr_list.hosts
.filter((i) => i.env !== 'prod' && i.env !== 'gray')
.map((item) => {
return (
<AutoComplete.Option key={item.addr} value={item.addr + ':' + node_addr_list.port}>
<Tag>{item.env}</Tag>
<span>
{item.addr}:{node_addr_list.port}
</span>
</AutoComplete.Option>
);
})
: [];
if (settings && settings.localhost) {
addrOptions.push(
<AutoComplete.Option
key={settings.localhost}
value={settings.localhost + ':' + node_addr_list.port}
>
<Tag>local</Tag>
<span>
{settings.localhost}:{node_addr_list.port}
</span>
</AutoComplete.Option>,
);
}
let metadata = editor.form.metadata || [];
if (!(metadata instanceof Array)) {
try {
metadata = JSON.parse(metadata);
} catch (e) {
metadata = [];
}
}
return (
<div className={styles.debugPage}>
<div className={styles.layoutHeader}>
<Cascader
showSearch
value={selected_service}
className={styles.serviceCascader}
displayRender={(labels) => {
return labels.join('/');
}}
placeholder="切换服务"
options={(app_service_tree || []).map((app) => {
return {
label: app.app_name,
value: app.app_name,
children: (app.services || []).map((service) => {
return {
value: '' + service.id,
label: service.name,
};
}),
};
})}
onChange={this.onChangeService}
/>
<Button
shape="circle"
icon={<SettingOutlined />}
className={styles.settingButton}
onClick={this.onShowSettingDialog}
/>
<Button
shape="circle"
icon={<LinkOutlined />}
className={styles.bindServiceButton}
onClick={this.onShowServiceBindDialog}
/>
</div>
<div className={styles.main}>
<div className={styles.layoutSider}>
<Tabs
type={'card'}
className={styles.leftTabs}
activeKey={active_tab}
onChange={this.onTabChange}
renderTabBar={(props, DefaultTabBar) => {
return (
<DefaultTabBar
{...props}
style={{
backgroundColor: 'rgb(250,250,250)',
padding: '10px 0 0 10px',
margin: '0',
flex: '0 0 50px',
}}
/>
);
}}
>
<Tabs.TabPane key="history" tab="History">
{active_tab === 'history' ? (
<History
selectedService={selected_service}
onChange={(id) => {
dispatch({ type: 'grpcDebugModel/loadHistoryItem', payload: id }).then(
(res) => {
if (res.code === 0) {
this.form.current.setFieldsValue({
case_name: editor.form.case_name,
address: res.data.addr,
});
}
},
);
}}
activeId={selected_history_id}
/>
) : null}
</Tabs.TabPane>
<Tabs.TabPane key="api" tab="API">
<ScrollArea style={{ height: '830px' }}>
<DirectoryTree
onRightClick={this.onUserCaseTreeRightClicked}
defaultExpandAll
onSelect={this.onSelectUserCaseTree}
selectedKeys={[selected_user_case]}
style={{ marginTop: '10px' }}
>
{(use_cases || []).map((method, id) => {
return (
<TreeNode
title={
method.description ? (
<Popover content={method.description}>{method.name}</Popover>
) : (
method.name
)
}
key={`method:${method.id}`}
>
<TreeNode
icon={<PlusOutlined />}
key={`new:${method.id}`}
title="New"
isLeaf
/>
{method.use_cases
? method.use_cases.map((tc, id) => {
return <TreeNode title={tc.name} key={`case:${tc.id}`} isLeaf />;
})
: null}
</TreeNode>
);
})}
</DirectoryTree>
</ScrollArea>
</Tabs.TabPane>
</Tabs>
</div>
{editor.form.method_id ? (
<Spin spinning={use_case_loading} wrapperClassName={styles.formSpin}>
{this.renderForm(editor, addrOptions, metadata, response)}
</Spin>
) : (
<div style={{ flex: '1 1 auto', padding: '100px' }}>
<Empty desciption={'请选择用例'} />
</div>
)}
</div>
<SettingDialog
onCancel={() => {
dispatch({
type: 'grpcDebugModel/showSettings',
payload: false,
});
}}
settings={settings}
onSave={this.onSaveSettings}
visible={setting_dialog_visible}
/>
<ServiceBindDialog
visible={service_bind_dialog_visible}
protoList={proto_list}
appList={appList}
onSubmit={this.onBindAppService}
onCancel={() => {
dispatch({
type: 'grpcDebugModel/showServiceBindDialog',
payload: false,
});
}}
/>
</div>
);
}
Example #24
Source File: index.jsx From juno with Apache License 2.0 | 4 votes |
renderForm(editor, addrOptions, metadata, response) {
const { request_loading } = this.props;
return (
<Form ref={this.form} className={styles.layoutContent}>
<div className={styles.caseNameLine}>
<Form.Item
rules={[{ required: true }]}
name={'case_name'}
initialValue={editor.form.case_name}
>
<Input placeholder="请输入用例名称" addonBefore={'Name'} />
</Form.Item>
<Popover content="Ctrl-S">
<Button icon={<FileAddOutlined />} onClick={this.onSave}>
Save
</Button>
</Popover>
</div>
<div className={styles.caseAddrLine}>
<Form.Item name={'address'}>
<AutoComplete optionLabelProp="value" dataSource={addrOptions}>
<Input addonBefore={'Address'} placeholder="请输入地址" />
</AutoComplete>
</Form.Item>
<Button
icon={<RocketOutlined />}
type="primary"
loading={request_loading}
onClick={this.onSendRequest}
>
Send
</Button>
</div>
<div className={styles.basicInfoLine}>
<Descriptions bordered size="small">
<Descriptions.Item label="App">{editor.info.app_name}</Descriptions.Item>
<Descriptions.Item label="Service">{editor.info.service_name}</Descriptions.Item>
<Descriptions.Item label="Method">{editor.info.method_name}</Descriptions.Item>
</Descriptions>
</div>
<div className={styles.inputOutputLayout}>
<div className={styles.inputContainer}>
<Tabs
className={styles.inputTabs}
tabBarStyle={{ height: '100%' }}
defaultActiveKey={'payload'}
onChange={(tab) => {
let dimension = { width: '100%', height: 'auto' };
setTimeout(() => {
switch (tab) {
case 'payload':
this.payloadEditor?.layout();
break;
case 'script':
this.scriptEditor?.layout();
break;
}
});
}}
renderTabBar={(props, DefaultTab) => {
return (
<DefaultTab
{...props}
style={{
padding: '0 10px',
margin: '0',
backgroundColor: 'rgb(250,250,250)',
}}
/>
);
}}
>
<Tabs.TabPane tab={'Payload'} key={'payload'}>
<div className={styles.grpcPayload}>
<MonacoEditor
width={'100%'}
height={'663px'}
value={editor.form.input}
onChange={(val) => {
this.onGrpcInputChange(val);
}}
language={'json'}
theme={'vs'}
options={{
automaticLayout: true,
}}
editorDidMount={(editor) => {
this.payloadEditor = editor;
}}
/>
</div>
</Tabs.TabPane>
<Tabs.TabPane tab={'Metadata'} key={'metadata'}>
<div className={styles.metadataInputLine}>
<KeyValueEditor data={metadata} onChange={this.onMetadataChange} />
</div>
</Tabs.TabPane>
<Tabs.TabPane tab={'Test Script'} key={'script'}>
<MonacoEditor
width={'100%'}
height={'663px'}
value={editor.form.script || DefaultScript}
language={'javascript'}
theme={'vs'}
onChange={(val) => {
this.onTestScriptChange(val);
}}
editorDidMount={(editor) => {
this.scriptEditor = editor;
}}
/>
</Tabs.TabPane>
</Tabs>
</div>
<div className={styles.outputContainer}>
<Tabs
className={styles.outputTabs}
tabBarStyle={{ height: '100%' }}
defaultActiveKey={'response'}
renderTabBar={(props, DefaultTab) => {
return (
<DefaultTab
{...props}
style={{
padding: '0 10px',
margin: '0',
backgroundColor: 'rgb(250,250,250)',
}}
/>
);
}}
>
<Tabs.TabPane tab={'Response'} key={'response'}>
<div className={styles.responseContainer}>
{response === null ? (
<div style={{ textAlign: 'center', padding: '40px', color: '#c3c3c3' }}>
<RocketOutlined style={{ fontSize: '48px' }} />
<p style={{ marginTop: '20px' }}>发起请求获取响应</p>
</div>
) : (
<Spin spinning={request_loading}>
<div
className={
styles.responseStatusBar +
(response.status === 'success' ? '' : ' ' + styles.responseStatusBarFail)
}
>
<span className={styles.statusBlock}>
<span>Test: </span>
<span>
{response.test_passed ? (
<span className={styles.statusSuccess}>passed</span>
) : (
<span className={styles.statusFail}>failed</span>
)}
</span>
</span>
<span className={styles.statusBlock}>
<span>Status: </span>
<span>
{response.status === 'success' ? (
<span className={styles.statusSuccess}>success</span>
) : (
<span className={styles.statusFail}>fail</span>
)}
</span>
</span>
<span className={styles.statusBlock}>
<span>Time: </span>
<span className={styles.statusSuccess}>{response.time_cost}ms</span>
</span>
</div>
{response.status === 'success' ? (
// 成功
<div className={styles.responseSuccess}>
<MonacoEditor
width={'100%'}
height={'621px'}
value={response.output}
language={'json'}
theme={'vs'}
options={{
readOnly: true,
automaticLayout: true,
}}
/>
</div>
) : (
// 失败
<div className={styles.responseFail}>
<Tag color="red">error</Tag>
{response.error}
</div>
)}
</Spin>
)}
</div>
</Tabs.TabPane>
<Tabs.TabPane tab={'Logs'} key={'logs'}>
{response?.logs && Object.keys(response?.logs).length ? (
<Descriptions size={'small'} bordered style={{ margin: '10px' }}>
{Object.keys(response?.logs || {}).map((key, idx) => {
return (
<Descriptions.Item label={key} key={idx} span={24}>
{response.logs[key]}
</Descriptions.Item>
);
})}
</Descriptions>
) : (
<Empty style={{ margin: '10px' }} />
)}
</Tabs.TabPane>
</Tabs>
</div>
</div>
</Form>
);
}
Example #25
Source File: editor.jsx From juno with Apache License 2.0 | 4 votes |
function Editor(props) {
const { request, dispatch, httpPort, addrList, currentAppName, response, sendStatus } = props;
const [nameEditing, nameEditAction] = useBoolean(false);
const onFieldChange = (fields) => {
dispatch({
type: 'HttpDebug/updateCurrentRequest',
payload: {
...fields,
},
});
};
const onSave = () => {
if (request.id) {
dispatch({
type: 'HttpDebug/saveTestCase',
payload: request,
}).then((r) => {
dispatch({
type: 'HttpDebug/fetchCollections',
payload: {
appName: currentAppName,
},
});
});
} else {
dispatch({
type: 'HttpDebug/showModalNewTestCase',
payload: {
visible: true,
},
});
}
};
const onSend = () => {
if (!request.method || !request.url) {
return message.error('请输入Method和 url');
}
dispatch({
type: 'HttpDebug/sendRequest',
payload: request,
});
};
const isSuccessCode = (code) => {
return code >= 200 && code < 300;
};
const renderRequestResult = () => {
const { response, responseStatus, responseError } = props;
if (sendStatus === 'done') {
if (responseStatus === 'success') {
let success = isSuccessCode(response.code);
return (
<div>
{/*<div*/}
{/* className={styles.responseStatusBar + (success ? '' : ' ' + styles.responseStatusBarFail)}>*/}
{/* <span className={styles.statusBlock}>*/}
{/* <span>Status: </span>*/}
{/* <span>*/}
{/* {success ? <span className={styles.statusSuccess}>{response.code}</span>*/}
{/* : <span className={styles.statusFail}>{response.code}</span>}*/}
{/* </span>*/}
{/* </span>*/}
{/* <span className={styles.statusBlock}>*/}
{/* <span>Time: </span>*/}
{/* <span className={styles.statusSuccess}>*/}
{/* {response.time_cost}ms*/}
{/* </span>*/}
{/* </span>*/}
{/*</div>*/}
<div className={styles.responseSuccess}>
<Tabs
size={'small'}
renderTabBar={(props, DefaultTabBar) => {
return (
<DefaultTabBar {...props} style={{ paddingLeft: '10px', margin: '0px' }} />
);
}}
>
<Tabs.TabPane tab={'Body'} key={'body'}>
<MonacoEditor
width={'100%'}
height={'348px'}
language={'javascript'}
value={response.body}
theme={'vs'}
options={{
readOnly: true,
automaticLayout: true,
}}
/>
</Tabs.TabPane>
<Tabs.TabPane tab={'Header'} key={'header'}>
<ResponseHeaders headers={response.headers} style={{ height: '340px' }} />
</Tabs.TabPane>
<Tabs.TabPane tab={'Logs'} key={'logs'}>
<TestLog logs={response.logs} style={{ height: '340px' }} />
</Tabs.TabPane>
</Tabs>
</div>
</div>
);
} else {
// 失败
return (
<div className={styles.responseFail}>
<Tag color={'red'}>{responseStatus}</Tag>
{responseError}
</div>
);
}
}
if (sendStatus === 'not_start') {
return (
<div style={{ textAlign: 'center', padding: '40px', color: '#c3c3c3' }}>
<RocketOutlined />
<p style={{ marginTop: '20px' }}>发起请求获取响应</p>
</div>
);
}
if (sendStatus === 'sending') {
return (
<div style={{ textAlign: 'center', padding: '40px' }}>
<Spin tip={'请求中...'} />
</div>
);
}
};
return (
<div className={styles.httpDebugContainer}>
<div className={styles.nameBar}>
{!nameEditing ? (
<>
{request?.name || 'Untitled'}
<span>
<Button
type={'link'}
onClick={() => {
nameEditAction.setTrue();
}}
>
<EditOutlined />
</Button>
</span>
</>
) : (
<Input
onChange={(ev) => {
onFieldChange({ name: ev.target.value });
}}
onBlur={() => {
onSave();
nameEditAction.setFalse();
}}
style={{ maxWidth: 200 }}
defaultValue={request.name}
/>
)}
</div>
<div className={styles.methodUrlLine}>
<Select
defaultValue={'GET'}
value={request.method}
onChange={(val) => {
onFieldChange({ method: val });
}}
>
{['GET', 'POST', 'PUT', 'PATCH'].map((item, idx) => (
<Select.Option value={item} key={idx}>
{item}
</Select.Option>
))}
</Select>
<AutoComplete
value={request.url}
onChange={(val) => {
onFieldChange({ url: val });
}}
>
{(addrList || []).map((item) => {
return (
<AutoComplete.Option value={`http://${item.addr}:${httpPort}`}>
<Tag>{item.env}</Tag> {`http://${item.addr}:${httpPort}`}
</AutoComplete.Option>
);
})}
</AutoComplete>
<Button
type={'primary'}
onClick={() => {
onSend();
}}
>
Send
</Button>
<Button
onClick={() => {
onSave();
}}
>
Save
</Button>
</div>
<div className={styles.requestParamEditBox}>
<Tabs
size={'small'}
renderTabBar={(tabBarProps, DefaultTabBar) => {
return (
<div style={{ position: 'relative' }}>
<div
style={{
position: 'absolute',
width: '100px',
height: '50px',
right: '10px',
top: '0px',
zIndex: 1,
paddingTop: '10px',
}}
>
<Button
type={'link'}
onClick={() => {
props.dispatch({
type: 'HttpDebug/showModalScriptEditor',
payload: true,
});
}}
>
Test Script
</Button>
</div>
<DefaultTabBar
{...tabBarProps}
style={{
backgroundColor: 'rgb(250,250,250)',
padding: '10px 0 0 10px',
}}
/>
</div>
);
}}
>
<Tabs.TabPane tab={'Params'} key={'params'}>
<ReactScroll horizontal={true} style={{ height: '200px', width: '100%' }}>
<KeyValueEditor
onChange={(data) => {
onFieldChange({ query: data });
}}
data={request.query}
/>
</ReactScroll>
</Tabs.TabPane>
<Tabs.TabPane tab={'Headers'} key={'headers'}>
<ReactScroll style={{ height: '200px', width: '100%' }}>
<KeyValueEditor
onChange={(data) => {
onFieldChange({ headers: data });
}}
data={request.headers}
/>
</ReactScroll>
</Tabs.TabPane>
<Tabs.TabPane tab={'Body'} key={'body'}>
<ReactScroll style={{ height: '200px', width: '100%' }}>
<BodyTabPane />
</ReactScroll>
</Tabs.TabPane>
</Tabs>
</div>
<div className={styles.responseTitleBar}>
<span>Response</span>
{response && sendStatus === 'done' && (
<div style={{ textAlign: 'right' }}>
<span>
{response.success ? (
<span className={styles.statusSuccess}>Test Passed</span>
) : (
<span className={styles.statusFail}>Test Failed</span>
)}
</span>
<span className={styles.statusBlock}>
<span>Status: </span>
<span>{response.code}</span>
</span>
<span className={styles.statusBlock}>
<span>Time: </span>
<span className={styles.statusSuccess}>{response.time_cost}ms</span>
</span>
</div>
)}
</div>
<div>{renderRequestResult()}</div>
</div>
);
}
Example #26
Source File: TimeLine.js From YApi-X with MIT License | 4 votes |
render() {
let data = this.props.newsData ? this.props.newsData.list : [];
const curDiffData = this.state.curDiffData;
let logType = {
project: '项目',
group: '分组',
interface: '接口',
interface_col: '接口集',
user: '用户',
other: '其他'
};
const children = this.state.apiList.map(item => {
let methodColor = variable.METHOD_COLOR[item.method ? item.method.toLowerCase() : 'get'];
return (
<Option title={item.title} value={item._id + ''} path={item.path} key={item._id}>
{item.title}{' '}
<Tag
style={{ color: methodColor ? methodColor.color : '#cfefdf', backgroundColor: methodColor ? methodColor.bac : '#00a854', border: 'unset' }}
>
{item.method}
</Tag>
</Option>
);
});
children.unshift(
<Option value="" key="all">
选择全部
</Option>
);
if (data && data.length) {
data = data.map((item, i) => {
let interfaceDiff = false;
// 去掉了 && item.data.interface_id
if (item.data && typeof item.data === 'object') {
interfaceDiff = true;
}
return (
<Timeline.Item
dot={
<Link to={`/user/profile/${item.uid}`}>
<Avatar src={`/api/user/avatar?uid=${item.uid}`} />
</Link>
}
key={i}
>
<div className="logMesHeade">
<span className="logoTimeago">{timeago(item.add_time)}</span>
{/*<span className="logusername"><Link to={`/user/profile/${item.uid}`}><Icon type="user" />{item.username}</Link></span>*/}
<span className="logtype">{logType[item.type]}动态</span>
<span className="logtime">{formatTime(item.add_time)}</span>
</div>
<span className="logcontent" dangerouslySetInnerHTML={{ __html: item.content }} />
<div style={{ padding: '10px 0 0 10px' }}>
{interfaceDiff && <Button onClick={() => this.openDiff(item.data)}>改动详情</Button>}
</div>
</Timeline.Item>
);
});
} else {
data = '';
}
let pending =
this.props.newsData.total <= this.props.curpage ? (
<a className="logbidden">以上为全部内容</a>
) : (
<a className="loggetMore" onClick={this.getMore.bind(this)}>
查看更多
</a>
);
if (this.state.loading) {
pending = <Spin />;
}
let diffView = showDiffMsg(jsondiffpatch, formattersHtml, curDiffData);
return (
<section className="news-timeline">
<Modal
style={{ minWidth: '800px' }}
title="Api 改动日志"
visible={this.state.visible}
footer={null}
onCancel={this.handleCancel}
>
<i>注: 绿色代表新增内容,红色代表删除内容</i>
<div className="project-interface-change-content">
{diffView.map((item, index) => {
return (
<AddDiffView
className="item-content"
title={item.title}
key={index}
content={item.content}
/>
);
})}
{diffView.length === 0 && <ErrMsg type="noChange" />}
</div>
</Modal>
{this.props.type === 'project' && (
<Row className="news-search">
<Col span="3">选择查询的 Api:</Col>
<Col span="10">
<AutoComplete
onSelect={this.handleSelectApi}
style={{ width: '100%' }}
placeholder="Select Api"
optionLabelProp="title"
filterOption={(inputValue, options) => {
if (options.props.value == '') return true;
if (
options.props.path.indexOf(inputValue) !== -1 ||
options.props.title.indexOf(inputValue) !== -1
) {
return true;
}
return false;
}}
>
{/* {children} */}
<OptGroup label="other">
<Option value="wiki" path="" title="wiki">
wiki
</Option>
</OptGroup>
<OptGroup label="api">{children}</OptGroup>
</AutoComplete>
</Col>
</Row>
)}
{data ? (
<Timeline className="news-content" pending={pending}>
{data}
</Timeline>
) : (
<ErrMsg type="noData" />
)}
</section>
);
}
Example #27
Source File: InterfaceEditForm.js From YApi-X with MIT License | 4 votes |
render() {
const { getFieldDecorator } = this.props.form
const { custom_field, projectMsg } = this.props
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
}
const res_body_use_schema_editor =
checkIsJsonSchema(this.state.res_body) || ''
const req_body_other_use_schema_editor =
checkIsJsonSchema(this.state.req_body_other) || ''
const queryTpl = (data, index) => {
return (
<Row key={index} className='interface-edit-item-content'>
<Col
span='1'
easy_drag_sort_child='true'
className='interface-edit-item-content-col interface-edit-item-content-col-drag'>
<Icon type='bars' />
</Col>
<Col
span='4'
draggable='false'
className='interface-edit-item-content-col'>
{getFieldDecorator('req_query[' + index + '].name', {
initialValue: data.name,
})(<Input placeholder='参数名称' />)}
</Col>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_query[' + index + '].type', {
initialValue: data.type,
})(
<Select>
<Option value='string'>string</Option>
<Option value='number'>number</Option>
</Select>,
)}
</Col>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_query[' + index + '].required', {
initialValue: data.required,
})(
<Select>
<Option value='1'>必需</Option>
<Option value='0'>非必需</Option>
</Select>,
)}
</Col>
<Col span='6' className='interface-edit-item-content-col'>
{getFieldDecorator('req_query[' + index + '].example', {
initialValue: data.example,
})(<TextArea autosize={true} placeholder='参数示例' />)}
</Col>
<Col span='6' className='interface-edit-item-content-col'>
{getFieldDecorator('req_query[' + index + '].desc', {
initialValue: data.desc,
})(<TextArea autosize={true} placeholder='备注' />)}
</Col>
<Col span='1' className='interface-edit-item-content-col'>
<Icon
type='delete'
className='interface-edit-del-icon'
onClick={() => this.delParams(index, 'req_query')}
/>
</Col>
</Row>
)
}
const headerTpl = (data, index) => {
return (
<Row key={index} className='interface-edit-item-content'>
<Col
span='1'
easy_drag_sort_child='true'
className='interface-edit-item-content-col interface-edit-item-content-col-drag'>
<Icon type='bars' />
</Col>
<Col span='4' className='interface-edit-item-content-col'>
{getFieldDecorator('req_headers[' + index + '].name', {
initialValue: data.name,
})(
<AutoComplete
dataSource={HTTP_REQUEST_HEADER}
filterOption={(inputValue, option) =>
option.props.children
.toUpperCase()
.indexOf(inputValue.toUpperCase()) !== -1
}
placeholder='参数名称'
/>,
)}
</Col>
<Col span='5' className='interface-edit-item-content-col'>
{getFieldDecorator('req_headers[' + index + '].value', {
initialValue: data.value,
})(<Input placeholder='参数值' />)}
</Col>
<Col span='5' className='interface-edit-item-content-col'>
{getFieldDecorator('req_headers[' + index + '].example', {
initialValue: data.example,
})(<TextArea autosize={true} placeholder='参数示例' />)}
</Col>
<Col span='8' className='interface-edit-item-content-col'>
{getFieldDecorator('req_headers[' + index + '].desc', {
initialValue: data.desc,
})(<TextArea autosize={true} placeholder='备注' />)}
</Col>
<Col span='1' className='interface-edit-item-content-col'>
<Icon
type='delete'
className='interface-edit-del-icon'
onClick={() => this.delParams(index, 'req_headers')}
/>
</Col>
</Row>
)
}
const requestBodyTpl = (data, index) => {
return (
<Row key={index} className='interface-edit-item-content'>
<Col
span='1'
easy_drag_sort_child='true'
className='interface-edit-item-content-col interface-edit-item-content-col-drag'>
<Icon type='bars' />
</Col>
<Col span='4' className='interface-edit-item-content-col'>
{getFieldDecorator('req_body_form[' + index + '].name', {
initialValue: data.name,
})(<Input placeholder='name' />)}
</Col>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_body_form[' + index + '].type', {
initialValue: data.type,
})(
<Select>
<Option value='text'>text</Option>
<Option value='file'>file</Option>
</Select>,
)}
</Col>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_body_form[' + index + '].required', {
initialValue: data.required,
})(
<Select>
<Option value='1'>必需</Option>
<Option value='0'>非必需</Option>
</Select>,
)}
</Col>
<Col span='5' className='interface-edit-item-content-col'>
{getFieldDecorator('req_body_form[' + index + '].example', {
initialValue: data.example,
})(<TextArea autosize={true} placeholder='参数示例' />)}
</Col>
<Col span='7' className='interface-edit-item-content-col'>
{getFieldDecorator('req_body_form[' + index + '].desc', {
initialValue: data.desc,
})(<TextArea autosize={true} placeholder='备注' />)}
</Col>
<Col span='1' className='interface-edit-item-content-col'>
<Icon
type='delete'
className='interface-edit-del-icon'
onClick={() => this.delParams(index, 'req_body_form')}
/>
</Col>
</Row>
)
}
const paramsTpl = (data, index) => {
return (
<Row key={index} className='interface-edit-item-content'>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_params[' + index + '].name', {
initialValue: data.name,
})(<Input disabled placeholder='参数名称' />)}
</Col>
<Col span='3' className='interface-edit-item-content-col'>
{getFieldDecorator('req_params[' + index + '].type', {
initialValue: data.type,
})(
<Select>
<Option value='string'>string</Option>
<Option value='number'>number</Option>
</Select>,
)}
</Col>
<Col span='7' className='interface-edit-item-content-col'>
{getFieldDecorator('req_params[' + index + '].example', {
initialValue: data.example,
})(<TextArea autosize={true} placeholder='参数示例' />)}
</Col>
<Col span='11' className='interface-edit-item-content-col'>
{getFieldDecorator('req_params[' + index + '].desc', {
initialValue: data.desc,
})(<TextArea autosize={true} placeholder='备注' />)}
</Col>
</Row>
)
}
const paramsList = this.state.req_params.map((item, index) => {
return paramsTpl(item, index)
})
const QueryList = this.state.req_query.map((item, index) => {
return queryTpl(item, index)
})
const headerList = this.state.req_headers
? this.state.req_headers.map((item, index) => {
return headerTpl(item, index)
})
: []
const requestBodyList = this.state.req_body_form.map((item, index) => {
return requestBodyTpl(item, index)
})
const DEMOPATH = '/api/user/{id}'
return (
<div>
<Modal
title='批量添加参数'
width={680}
visible={this.state.visible}
onOk={this.handleBulkOk}
onCancel={this.handleBulkCancel}
okText='导入'>
<div>
<TextArea
placeholder='每行一个name:examples'
autosize={{ minRows: 6, maxRows: 10 }}
value={this.state.bulkValue}
onChange={this.handleBulkValueInput}
/>
</div>
</Modal>
<Modal
title='批量添加Header'
width={680}
visible={this.state.headerBulkAddVisible}
onOk={this.handleHeaderBulkAddConfirm}
onCancel={this.handleHeaderBulkAddCancel}
okText='导入'>
<div>
<div style={{ marginBottom: 8 }}>
每行一个,支持以冒号(:)、制表符(TAB)分割键值:
</div>
<TextArea
placeholder='请输入'
autosize={{ minRows: 6, maxRows: 10 }}
value={this.state.headerBulkValue}
onChange={this.handleHeaderBulkValueInput}
/>
</div>
</Modal>
<Form onSubmit={this.handleSubmit}>
<h2 className='interface-title' style={{ marginTop: 0 }}>
基本设置
</h2>
<div className='panel-sub'>
<FormItem
className='interface-edit-item'
{...formItemLayout}
label='接口名称'>
{getFieldDecorator('title', {
initialValue: this.state.title,
rules: nameLengthLimit('接口'),
})(<Input id='title' placeholder='接口名称' />)}
</FormItem>
<FormItem
className='interface-edit-item'
{...formItemLayout}
label='选择分类'>
{getFieldDecorator('catid', {
initialValue: this.state.catid + '',
rules: [{ required: true, message: '请选择一个分类' }],
})(
<Select placeholder='请选择一个分类'>
{this.props.cat.map(item => {
return (
<Option key={item._id} value={item._id + ''}>
{item.name}
</Option>
)
})}
</Select>,
)}
</FormItem>
<FormItem
className='interface-edit-item'
{...formItemLayout}
label={
<span>
接口路径
<Tooltip
title={
<div>
<p>
1. 支持动态路由,例如:
{DEMOPATH}
</p>
<p>
2. 支持 ?controller=xxx
的QueryRouter,非router的Query参数请定义到
Request设置->Query
</p>
</div>
}>
<Icon type='question-circle-o' style={{ width: '10px' }} />
</Tooltip>
</span>
}>
<InputGroup compact>
<Select
value={this.state.method}
onChange={this.onChangeMethod}
style={{ width: '15%' }}>
{HTTP_METHOD_KEYS.map(item => {
return (
<Option key={item} value={item}>
{item}
</Option>
)
})}
</Select>
<Tooltip
title='接口基本路径,可在 项目设置 里修改'
style={{
display: this.props.basepath == '' ? 'block' : 'none',
}}>
<Input
disabled
value={this.props.basepath}
readOnly
onChange={() => {}}
style={{ width: '25%' }}
/>
</Tooltip>
{getFieldDecorator('path', {
initialValue: this.state.path,
rules: [
{
required: true,
message: '请输入接口路径!',
},
],
})(
<Input
onChange={this.handlePath}
placeholder='/path'
style={{ width: '60%' }}
/>,
)}
</InputGroup>
<Row className='interface-edit-item'>
<Col span={24} offset={0}>
{paramsList}
</Col>
</Row>
</FormItem>
<FormItem
className='interface-edit-item'
{...formItemLayout}
label='Tag'>
{getFieldDecorator('tag', { initialValue: this.state.tag })(
<Select placeholder='请选择 tag ' mode='multiple'>
{projectMsg.tag.map(item => {
return (
<Option value={item.name} key={item._id}>
{item.name}
</Option>
)
})}
<Option
value='tag设置'
disabled
style={{ cursor: 'pointer', color: '#2395f1' }}>
<Button type='primary' onClick={this.props.onTagClick}>
Tag设置
</Button>
</Option>
</Select>,
)}
</FormItem>
<FormItem
className='interface-edit-item'
{...formItemLayout}
label='状态'>
{getFieldDecorator('status', { initialValue: this.state.status })(
<Select>
<Option value='done'>已完成</Option>
<Option value='undone'>未完成</Option>
</Select>,
)}
</FormItem>
{custom_field.enable && (
<FormItem
className='interface-edit-item'
{...formItemLayout}
label={custom_field.name}>
{getFieldDecorator('custom_field_value', {
initialValue: this.state.custom_field_value,
})(<Input placeholder='请输入' />)}
</FormItem>
)}
</div>
<h2 className='interface-title'>请求参数设置</h2>
<div className='container-radiogroup'>
<RadioGroup
value={this.state.req_radio_type}
size='large'
className='radioGroup'
onChange={this.changeRadioGroup}>
{HTTP_METHOD[this.state.method].request_body ? (
<RadioButton value='req-body'>Body</RadioButton>
) : null}
<RadioButton value='req-query'>Query</RadioButton>
<RadioButton value='req-headers'>Headers</RadioButton>
</RadioGroup>
</div>
<div className='panel-sub'>
<FormItem
className={
'interface-edit-item ' + this.state.hideTabs.req.query
}>
<Row type='flex' justify='space-around'>
<Col span={12}>
<Button
size='small'
type='primary'
onClick={() => this.addParams('req_query')}>
添加Query参数
</Button>
</Col>
<Col span={12}>
<div
className='bulk-import'
onClick={() => this.showBulk('req_query')}>
批量添加
</div>
</Col>
</Row>
</FormItem>
<Row
className={
'interface-edit-item ' + this.state.hideTabs.req.query
}>
<Col>
<EasyDragSort
data={() => this.props.form.getFieldValue('req_query')}
onChange={this.handleDragMove('req_query')}
onlyChild='easy_drag_sort_child'>
{QueryList}
</EasyDragSort>
</Col>
</Row>
<FormItem
className={
'interface-edit-item ' + this.state.hideTabs.req.headers
}>
<Row type='flex' justify='space-around'>
<Col span={12}>
<Button
size='small'
type='primary'
onClick={() => this.addParams('req_headers')}>
添加Header
</Button>
</Col>
<Col span={12}>
<div
className='bulk-import'
onClick={this.handleHeaderBulkAddClick}>
批量添加
</div>
</Col>
</Row>
</FormItem>
<Row
className={
'interface-edit-item ' + this.state.hideTabs.req.headers
}>
<Col>
<EasyDragSort
data={() => this.props.form.getFieldValue('req_headers')}
onChange={this.handleDragMove('req_headers')}
onlyChild='easy_drag_sort_child'>
{headerList}
</EasyDragSort>
</Col>
</Row>
{HTTP_METHOD[this.state.method].request_body ? (
<div>
<FormItem
className={
'interface-edit-item ' + this.state.hideTabs.req.body
}>
{getFieldDecorator('req_body_type', {
initialValue: this.state.req_body_type,
})(
<RadioGroup>
<Radio value='form'>form</Radio>
<Radio value='json'>json</Radio>
<Radio value='file'>file</Radio>
<Radio value='raw'>raw</Radio>
</RadioGroup>,
)}
</FormItem>
<Row
className={
'interface-edit-item ' +
(this.props.form.getFieldValue('req_body_type') === 'form'
? this.state.hideTabs.req.body
: 'hide')
}>
<Col style={{ minHeight: '50px' }}>
<Row type='flex' justify='space-around'>
<Col span='12' className='interface-edit-item'>
<Button
size='small'
type='primary'
onClick={() => this.addParams('req_body_form')}>
添加form参数
</Button>
</Col>
<Col span='12'>
<div
className='bulk-import'
onClick={() => this.showBulk('req_body_form')}>
批量添加
</div>
</Col>
</Row>
<EasyDragSort
data={() =>
this.props.form.getFieldValue('req_body_form')
}
onChange={this.handleDragMove('req_body_form')}
onlyChild='easy_drag_sort_child'>
{requestBodyList}
</EasyDragSort>
</Col>
</Row>
</div>
) : null}
<Row
className={
'interface-edit-item ' +
(this.props.form.getFieldValue('req_body_type') === 'json'
? this.state.hideTabs.req.body
: 'hide')
}>
<span>
JSON-SCHEMA:
{!projectMsg.is_json5 && (
<Tooltip title='项目 -> 设置 开启 json5'>
<Icon type='question-circle-o' />{' '}
</Tooltip>
)}
</span>
{getFieldDecorator('req_body_is_json_schema', {
valuePropName: 'checked',
initialValue:
this.state.req_body_is_json_schema || !projectMsg.is_json5,
})(
<Switch
checkedChildren='开'
unCheckedChildren='关'
disabled={!projectMsg.is_json5}
/>,
)}
<Col
style={{ marginTop: '5px' }}
className='interface-edit-json-info'>
{!this.props.form.getFieldValue('req_body_is_json_schema') ? (
<span>
基于 Json5, 参数描述信息用注释的方式实现{' '}
<Tooltip title={<pre>{Json5Example}</pre>}>
<Icon
type='question-circle-o'
style={{ color: '#086dbf' }}
/>
</Tooltip>
“全局编辑”或 “退出全屏” 请按 F9
</span>
) : (
<ReqBodySchema
onChange={text => {
this.setState({
req_body_other: text,
})
if (new Date().getTime() - this.startTime > 1000) {
EditFormContext.props.changeEditStatus(true)
}
}}
isMock={true}
data={req_body_other_use_schema_editor}
/>
)}
</Col>
<Col>
{!this.props.form.getFieldValue('req_body_is_json_schema') && (
<AceEditor
className='interface-editor'
data={this.state.req_body_other}
onChange={this.handleReqBody}
fullScreen={true}
/>
)}
</Col>
</Row>
{this.props.form.getFieldValue('req_body_type') === 'file' &&
this.state.hideTabs.req.body !== 'hide' ? (
<Row className='interface-edit-item'>
<Col className='interface-edit-item-other-body'>
{getFieldDecorator('req_body_other', {
initialValue: this.state.req_body_other,
})(<TextArea placeholder='' autosize={true} />)}
</Col>
</Row>
) : null}
{this.props.form.getFieldValue('req_body_type') === 'raw' &&
this.state.hideTabs.req.body !== 'hide' ? (
<Row>
<Col>
{getFieldDecorator('req_body_other', {
initialValue: this.state.req_body_other,
})(<TextArea placeholder='' autosize={{ minRows: 8 }} />)}
</Col>
</Row>
) : null}
</div>
{/* ----------- Response ------------- */}
<h2 className='interface-title'>
返回数据设置
{!projectMsg.is_json5 && (
<Tooltip title='项目 -> 设置 开启 json5'>
<Icon type='question-circle-o' className='tooltip' />{' '}
</Tooltip>
)}
{getFieldDecorator('res_body_is_json_schema', {
valuePropName: 'checked',
initialValue:
this.state.res_body_is_json_schema || !projectMsg.is_json5,
})(
<Switch
checkedChildren='json-schema'
unCheckedChildren='json'
disabled={!projectMsg.is_json5}
/>,
)}
</h2>
<div className='container-radiogroup'>
{getFieldDecorator('res_body_type', {
initialValue: this.state.res_body_type,
})(
<RadioGroup size='large' className='radioGroup'>
<RadioButton value='json'>JSON</RadioButton>
<RadioButton value='raw'>RAW</RadioButton>
</RadioGroup>,
)}
</div>
<div className='panel-sub'>
<Row
className='interface-edit-item'
style={{
display:
this.props.form.getFieldValue('res_body_type') === 'json'
? 'block'
: 'none',
}}>
<Col>
<Tabs
size='large'
defaultActiveKey='tpl'
onChange={this.handleJsonType}>
<TabPane tab='模板' key='tpl' />
<TabPane tab='预览' key='preview' />
</Tabs>
<div style={{ marginTop: '10px' }}>
{!this.props.form.getFieldValue('res_body_is_json_schema') ? (
<div style={{ padding: '10px 0', fontSize: '15px' }}>
<span>
基于 mockjs 和 json5,使用注释方式写参数说明{' '}
<Tooltip title={<pre>{Json5Example}</pre>}>
<Icon
type='question-circle-o'
style={{ color: '#086dbf' }}
/>
</Tooltip>{' '}
,具体使用方法请{' '}
<span
className='href'
onClick={() =>
window.open(
'https://hellosean1025.github.io/yapi/documents/mock.html',
'_blank',
)
}>
查看文档
</span>
</span>
,“全局编辑”或 “退出全屏” 请按{' '}
<span style={{ fontWeight: '500' }}>F9</span>
</div>
) : (
<div
style={{
display:
this.state.jsonType === 'tpl' ? 'block' : 'none',
}}>
<ResBodySchema
onChange={text => {
this.setState({
res_body: text,
})
if (new Date().getTime() - this.startTime > 1000) {
EditFormContext.props.changeEditStatus(true)
}
}}
isMock={true}
data={res_body_use_schema_editor}
/>
</div>
)}
{!this.props.form.getFieldValue('res_body_is_json_schema') &&
this.state.jsonType === 'tpl' && (
<AceEditor
className='interface-editor'
data={this.state.res_body}
onChange={this.handleResBody}
ref={editor => (this.resBodyEditor = editor)}
fullScreen={true}
/>
)}
<div
id='mock-preview'
style={{
backgroundColor: '#eee',
lineHeight: '20px',
minHeight: '300px',
display:
this.state.jsonType === 'preview' ? 'block' : 'none',
}}
/>
</div>
</Col>
</Row>
<Row
className='interface-edit-item'
style={{
display:
this.props.form.getFieldValue('res_body_type') === 'raw'
? 'block'
: 'none',
}}>
<Col>
{getFieldDecorator('res_body', {
initialValue: this.state.res_body,
})(<TextArea style={{ minHeight: '150px' }} placeholder='' />)}
</Col>
</Row>
</div>
{/* ----------- remark ------------- */}
<h2 className='interface-title'>备 注</h2>
<div className='panel-sub'>
<FormItem className={'interface-edit-item'}>
<div>
<div
id='desc'
style={{ lineHeight: '20px' }}
className='remark-editor'
/>
</div>
</FormItem>
</div>
{/* ----------- email ------------- */}
<h2 className='interface-title'>其 他</h2>
<div className='panel-sub'>
<FormItem
className={'interface-edit-item'}
{...formItemLayout}
label={
<span>
消息通知
<Tooltip title={'开启消息通知,可在 项目设置 里修改'}>
<Icon type='question-circle-o' style={{ width: '10px' }} />
</Tooltip>
</span>
}>
{getFieldDecorator('switch_notice', {
valuePropName: 'checked',
initialValue: this.props.noticed,
})(<Switch checkedChildren='开' unCheckedChildren='关' />)}
</FormItem>
<FormItem
className={'interface-edit-item'}
{...formItemLayout}
label={
<span>
开放接口
<Tooltip title={'用户可以在 数据导出 时选择只导出公开接口'}>
<Icon type='question-circle-o' style={{ width: '10px' }} />
</Tooltip>
</span>
}>
{getFieldDecorator('api_opened', {
valuePropName: 'checked',
initialValue: this.state.api_opened,
})(<Switch checkedChildren='开' unCheckedChildren='关' />)}
</FormItem>
</div>
<FormItem
className='interface-edit-item'
style={{ textAlign: 'center', marginTop: '16px' }}>
{/* <Button type="primary" htmlType="submit">保存1</Button> */}
<Affix offsetBottom={0}>
<Button
className='interface-edit-submit-button'
disabled={this.state.submitStatus}
size='large'
htmlType='submit'>
保存
</Button>
</Affix>
</FormItem>
</Form>
</div>
)
}
Example #28
Source File: ProjectEnvContent.js From YApi-X with MIT License | 4 votes |
render() {
const { projectMsg } = this.props;
const { getFieldDecorator } = this.props.form;
const headerTpl = (item, index) => {
const headerLength = this.state.header.length - 1;
return (
<Row gutter={2} key={index}>
<Col span={10}>
<FormItem>
{getFieldDecorator('header[' + index + '].name', {
validateTrigger: ['onChange', 'onBlur'],
initialValue: item.name || ''
})(
<AutoComplete
style={{ width: '200px' }}
allowClear={true}
dataSource={constants.HTTP_REQUEST_HEADER}
placeholder="请输入header名称"
onChange={() => this.addHeader(item, index, 'header')}
filterOption={(inputValue, option) =>
option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem>
{getFieldDecorator('header[' + index + '].value', {
validateTrigger: ['onChange', 'onBlur'],
initialValue: item.value || ''
})(<Input placeholder="请输入参数内容" style={{ width: '90%', marginRight: 8 }} />)}
</FormItem>
</Col>
<Col span={2} className={index === headerLength ? ' env-last-row' : null}>
{/* 新增的项中,只有最后一项没有有删除按钮 */}
<Icon
className="dynamic-delete-button delete"
type="delete"
onClick={e => {
e.stopPropagation();
this.delHeader(index, 'header');
}}
/>
</Col>
</Row>
);
};
const commonTpl = (item, index, name) => {
const length = this.state[name].length - 1;
return (
<Row gutter={2} key={index}>
<Col span={10}>
<FormItem>
{getFieldDecorator(`${name}[${index}].name`, {
validateTrigger: ['onChange', 'onBlur'],
initialValue: item.name || ''
})(
<Input
placeholder={`请输入 ${name} Name`}
style={{ width: '200px' }}
onChange={() => this.addHeader(item, index, name)}
/>
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem>
{getFieldDecorator(`${name}[${index}].value`, {
validateTrigger: ['onChange', 'onBlur'],
initialValue: item.value || ''
})(<Input placeholder="请输入参数内容" style={{ width: '90%', marginRight: 8 }} />)}
</FormItem>
</Col>
<Col span={2} className={index === length ? ' env-last-row' : null}>
{/* 新增的项中,只有最后一项没有有删除按钮 */}
<Icon
className="dynamic-delete-button delete"
type="delete"
onClick={e => {
e.stopPropagation();
this.delHeader(index, name);
}}
/>
</Col>
</Row>
);
};
const envTpl = data => {
return (
<div>
<h3 className="env-label">环境名称</h3>
<FormItem required={false}>
{getFieldDecorator('env.name', {
validateTrigger: ['onChange', 'onBlur'],
initialValue: data.name === '新环境' ? '' : data.name || '',
rules: [
{
required: false,
whitespace: true,
validator(rule, value, callback) {
if (value) {
if (value.length === 0) {
callback('请输入环境名称');
} else if (!/\S/.test(value)) {
callback('请输入环境名称');
} else {
return callback();
}
} else {
callback('请输入环境名称');
}
}
}
]
})(
<Input
onChange={e => this.props.handleEnvInput(e.target.value)}
placeholder="请输入环境名称"
style={{ width: '90%', marginRight: 8 }}
/>
)}
</FormItem>
<h3 className="env-label">环境域名</h3>
<FormItem required={false}>
{getFieldDecorator('env.domain', {
validateTrigger: ['onChange', 'onBlur'],
initialValue: data.domain ? data.domain.split('//')[1] : '',
rules: [
{
required: false,
whitespace: true,
validator(rule, value, callback) {
if (value) {
if (value.length === 0) {
callback('请输入环境域名!');
} else if (/\s/.test(value)) {
callback('环境域名不允许出现空格!');
} else {
return callback();
}
} else {
callback('请输入环境域名!');
}
}
}
]
})(
<Input
placeholder="请输入环境域名"
style={{ width: '90%', marginRight: 8 }}
addonBefore={getFieldDecorator('env.protocol', {
initialValue: data.domain ? data.domain.split('//')[0] + '//' : 'http://',
rules: [
{
required: true
}
]
})(
<Select>
<Option value="http://">{'http://'}</Option>
<Option value="https://">{'https://'}</Option>
</Select>
)}
/>
)}
</FormItem>
<h3 className="env-label">Header</h3>
{this.state.header.map((item, index) => {
return headerTpl(item, index);
})}
<h3 className="env-label">Cookie</h3>
{this.state.cookie.map((item, index) => {
return commonTpl(item, index, 'cookie');
})}
<h3 className="env-label">
global
<a
target="_blank"
rel="noopener noreferrer"
href="https://hellosean1025.github.io/yapi/documents/project.html#%E9%85%8D%E7%BD%AE%E7%8E%AF%E5%A2%83"
style={{ marginLeft: 8 }}
>
<Tooltip title="点击查看文档">
<Icon type="question-circle-o" style={{fontSize: '13px'}}/>
</Tooltip>
</a>
</h3>
{this.state.global.map((item, index) => {
return commonTpl(item, index, 'global');
})}
</div>
);
};
return (
<div>
{envTpl(projectMsg)}
<div className="btnwrap-changeproject">
<Button
className="m-btn btn-save"
icon="save"
type="primary"
size="large"
onClick={this.handleOk}
>
保 存
</Button>
</div>
</div>
);
}
Example #29
Source File: CaseDesModal.js From YApi-X with MIT License | 4 votes |
render() {
const { getFieldDecorator, getFieldValue } = this.props.form;
const { isAdd, visible, onCancel } = this.props;
const {
name,
code,
headers,
ip,
ip_enable,
params,
paramsArr,
paramsForm,
res_body,
delay
} = this.state;
this.props.form.initialValue;
const valuesTpl = (values, title) => {
const dataSource = this.getParamsKey();
const display = paramsForm === 'json' ? 'none' : '';
return values.map((item, index) => (
<div key={index} className="paramsArr" style={{ display }}>
<FormItem
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
wrapperCol={index === 0 ? { span: 19 } : { span: 19, offset: 5 }}
label={index ? '' : title}
>
<Row gutter={8}>
<Col span={10}>
<FormItem>
{getFieldDecorator(`paramsArr[${index}].name`, { initialValue: item.name })(
<AutoComplete
dataSource={dataSource}
placeholder="参数名称"
filterOption={(inputValue, option) =>
option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>
)}
</FormItem>
</Col>
<Col span={10}>
<FormItem>
{getFieldDecorator(`paramsArr[${index}].value`, { initialValue: item.value })(
<Input placeholder="参数值" />
)}
</FormItem>
</Col>
<Col span={4}>
{values.length > 1 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
onClick={() => this.removeValues('paramsArr', index)}
/>
) : null}
</Col>
</Row>
</FormItem>
</div>
));
};
const headersTpl = (values, title) => {
const dataSource = constants.HTTP_REQUEST_HEADER;
return values.map((item, index) => (
<div key={index} className="headers">
<FormItem
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
wrapperCol={index === 0 ? { span: 19 } : { span: 19, offset: 5 }}
label={index ? '' : title}
>
<Row gutter={8}>
<Col span={10}>
<FormItem>
{getFieldDecorator(`headers[${index}].name`, { initialValue: item.name })(
<AutoComplete
dataSource={dataSource}
placeholder="参数名称"
filterOption={(inputValue, option) =>
option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>
)}
</FormItem>
</Col>
<Col span={10}>
<FormItem>
{getFieldDecorator(`headers[${index}].value`, { initialValue: item.value })(
<Input placeholder="参数值" />
)}
</FormItem>
</Col>
<Col span={4}>
{values.length > 1 ? (
<Icon
className="dynamic-delete-button"
type="minus-circle-o"
onClick={() => this.removeValues('headers', index)}
/>
) : null}
</Col>
</Row>
</FormItem>
</div>
));
};
return (
<Modal
title={isAdd ? '添加期望' : '编辑期望'}
visible={visible}
maskClosable={false}
onOk={this.handleOk}
width={780}
onCancel={() => onCancel()}
afterClose={() => this.setState({ paramsForm: 'form' })}
className="case-des-modal"
>
<Form onSubmit={this.handleOk}>
<h2 className="sub-title" style={{ marginTop: 0 }}>
基本信息
</h2>
<FormItem {...formItemLayout} label="期望名称">
{getFieldDecorator('name', {
initialValue: name,
rules: [{ required: true, message: '请输入期望名称!' }]
})(<Input placeholder="请输入期望名称" />)}
</FormItem>
<FormItem {...formItemLayout} label="IP 过滤" className="ip-filter">
<Col span={6} className="ip-switch">
<FormItem>
{getFieldDecorator('ip_enable', {
initialValue: ip_enable,
valuePropName: 'checked',
rules: [{ type: 'boolean' }]
})(<Switch />)}
</FormItem>
</Col>
<Col span={18}>
<div style={{ display: getFieldValue('ip_enable') ? '' : 'none' }} className="ip">
<FormItem>
{getFieldDecorator(
'ip',
getFieldValue('ip_enable')
? {
initialValue: ip,
rules: [
{
pattern: constants.IP_REGEXP,
message: '请填写正确的 IP 地址',
required: true
}
]
}
: {}
)(<Input placeholder="请输入过滤的 IP 地址" />)}
</FormItem>
</div>
</Col>
</FormItem>
<Row className="params-form" style={{ marginBottom: 8 }}>
<Col {...{ span: 12, offset: 5 }}>
<Switch
size="small"
checkedChildren="JSON"
unCheckedChildren="JSON"
checked={paramsForm === 'json'}
onChange={bool => {
this.setState({ paramsForm: bool ? 'json' : 'form' });
}}
/>
</Col>
</Row>
{valuesTpl(paramsArr, '参数过滤')}
<FormItem
wrapperCol={{ span: 6, offset: 5 }}
style={{ display: paramsForm === 'form' ? '' : 'none' }}
>
<Button
size="default"
type="primary"
onClick={() => this.addValues('paramsArr')}
style={{ width: '100%' }}
>
<Icon type="plus" /> 添加参数
</Button>
</FormItem>
<FormItem
{...formItemLayout}
wrapperCol={{ span: 17 }}
label="参数过滤"
style={{ display: paramsForm === 'form' ? 'none' : '' }}
>
<AceEditor className="pretty-editor" data={params} onChange={this.handleParams} />
<FormItem>
{getFieldDecorator(
'params',
paramsForm === 'json'
? {
rules: [
{ validator: this.jsonValidator, message: '请输入正确的 JSON 字符串!' }
]
}
: {}
)(<Input style={{ display: 'none' }} />)}
</FormItem>
</FormItem>
<h2 className="sub-title">响应</h2>
<FormItem {...formItemLayout} required label="HTTP Code">
{getFieldDecorator('code', {
initialValue: code
})(
<Select showSearch>
{httpCodes.map(code => (
<Option key={'' + code} value={'' + code}>
{'' + code}
</Option>
))}
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} label="延时">
{getFieldDecorator('delay', {
initialValue: delay,
rules: [{ required: true, message: '请输入延时时间!', type: 'integer' }]
})(<InputNumber placeholder="请输入延时时间" min={0} />)}
<span>ms</span>
</FormItem>
{headersTpl(headers, 'HTTP 头')}
<FormItem wrapperCol={{ span: 6, offset: 5 }}>
<Button
size="default"
type="primary"
onClick={() => this.addValues('headers')}
style={{ width: '100%' }}
>
<Icon type="plus" /> 添加 HTTP 头
</Button>
</FormItem>
<FormItem {...formItemLayout} wrapperCol={{ span: 17 }} label="Body" required>
<FormItem>
<AceEditor
className="pretty-editor"
data={res_body}
mode={this.props.currInterface.res_body_type === 'json' ? null : 'text'}
onChange={this.handleRequestBody}
/>
</FormItem>
</FormItem>
</Form>
</Modal>
);
}