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 vote down vote up
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 vote down vote up
// 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
Option = AutoComplete.Option
Example #10
Source File: MyAutoComplete.jsx    From react-sendbird-messenger with GNU General Public License v3.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
Option = AutoComplete.Option
Example #13
Source File: TimeLine.js    From YApi-X with MIT License 5 votes vote down vote up
{ Option, OptGroup } = AutoComplete
Example #14
Source File: index.jsx    From spring-boot-plus-admin-react with Apache License 2.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
{ Option } = AutoComplete
Example #17
Source File: index.jsx    From egoshop with Apache License 2.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
                  接口路径&nbsp;
                  <Tooltip
                    title={
                      <div>
                        <p>
                          1. 支持动态路由,例如:
                          {DEMOPATH}
                        </p>
                        <p>
                          2. 支持 ?controller=xxx
                          的QueryRouter,非router的Query参数请定义到
                          Request设置-&#62;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:&nbsp;
                {!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'>
            返回数据设置&nbsp;
            {!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>
                  消息通知&nbsp;
                  <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>
                  开放接口&nbsp;
                  <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 vote down vote up
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 vote down vote up
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>
    );
  }