Antd 常见问题

Select

场景: 最多选三个值,最少选中一个。删除每一个值的时候都要弹窗并且Select组件下拉框支持选中。

import { useState, useEffect, useMemo } from 'react';
import { Form, Tag, Select, Checkbox, message } from 'antd';

const data = [
  {
    label: 'A',
    checked: false,
    disabled: false,
  },
  {
    label: 'B',
    checked: true,
    disabled: false,
  },
  {
    label: 'C',
    checked: false,
    disabled: false,
  },
  {
    label: 'D',
    checked: false,
    disabled: false,
];

const Index = () => {
  const [form] = Form.useForm();
  const [selectOptions, setSelectOptions] = useState(data);

  const handleChangeChecked = (name) => {
    const newSelectOptions = selectOptions.map((item) => {
      let { checked } = item;
      const { label, disabled } = item;
      if (item.label === name) {
        checked = !checked;
      }
      return { label, checked, disabled };
    });
    setSelectOptions(newSelectOptions);
  };

  const propertyIndustryList = useMemo(() => (form.getFieldValue('values') ? form.getFieldValue('values') : []), [
    form.getFieldValue('values'),
  ]);
  const selectOptionsEqualOne = useMemo(() => {
    const list = selectOptions.filter((item) => item.checked);
    return list.length === 1;
  }, [selectOptions]);

  const onChange = (e, name) => {
    if (selectOptionsEqualOne && !e.target.checked) {
      message.warning('至少保留一个值');
    } else {
      handleChangeChecked(name);
      if (e.target.checked) {
        form.setFieldsValue({ values: [...propertyIndustryList, name] });
      } else {
        form.setFieldsValue({ values: propertyIndustryList.filter((item) => item !== name) });
      }
    }
  };

  useEffect(() => {
    const list = selectOptions.map((item) => {
      let { disabled } = item;
      const { label, checked } = item;
      if (!propertyIndustryList.includes(label) && propertyIndustryList.length > 2) {
        disabled = true;
      } else {
        disabled = false;
      }
      return { label, checked, disabled };
    });
    setSelectOptions(list);
  }, [propertyIndustryList]);

  // 至少选中2个值
  const onMoreThanOneOk = (label: string) => {
    const idx = propertyIndustryList.indexOf(label);
    const newProList = [...propertyIndustryList];
    newProList.splice(idx, 1);
    handleChangeChecked(label);
    form.setFieldsValue({ values: newProList });
  };

  const tagRender = ({ label, closable }) => {
    const onPreventMouseDown = (event) => {
      event.preventDefault();
      event.stopPropagation();
    };
    const onCloseChange = (e) => {
      e.preventDefault();
      if (propertyIndustryList.length > 1) {
        ModalConfirm({
          title: '确定要删除该值吗?',
          content: '删除该值将清空所有与其相关的数据。',
          onOk: () => onMoreThanOneOk(label),
          type: 'confirm',
        });
      } else {
        message.warning('至少保留一个值');
      }
    };
    return (
      <Tag onMouseDown={onPreventMouseDown} closable={closable} onClose={onCloseChange} style={{ marginRight: 3 }}>
        {label}
      </Tag>
    );
  };

  const dropdownRender = () => (
    <div>
      {selectOptions.map((item) => {
        const { label, checked, disabled } = item;
        return (
          <div key={label} style={{ padding: '4px 8px' }}>
            <Checkbox disabled={disabled} checked={checked} onChange={(e) => onChange(e, label)}>
              {label}
            </Checkbox>
          </div>
        );
      })}
    </div>
  );

  return (
    <Form form={form}>
      <Form.Item label="" name="values" rules={[{ required: true, message: '' }]}>
        <Select
          mode="multiple"
          style={{ width: 352 }}
          tagRender={tagRender}
          dropdownRender={dropdownRender}
          showArrow
        />
      </Form.Item>
    </Form>
  );
};

export default Index;

antd Form.Item

Form.Item中的tooltip替换icon

<Form.Item
  label="替换icon"
  name="replace"
  tooltip={{ title: '成功替换', icon: <InfoCircleOutlined /> }}
>

antd Anchor

<div style={{ display: 'flex' }}>
  <div style={{ width: '80vw' }}>
    <div id="first" style={{ height: 800, border: '1px solid red' }}>
      第一行
    </div>
    <div id="second" style={{ height: 600, border: '1px solid green' }}>
      第二行
    </div>
    <div id="third" style={{ height: 480, border: '1px solid blue' }}>
      第三行
    </div>
    <div id="fourth" style={{ height: 100, border: '1px solid pink' }}>
      第四行
    </div>
  </div>
  <Anchor>
    <Link href="#first" title="第一行" />
    <Link href="#second" title="第二行" />
    <Link href="#third" title="第三行" />
    <Link href="#fourth" title="第四行" />
  </Anchor>
</div>

antd Tabs 闪动

记得查看是否是自己写的样式覆盖了自带的样式导致的!!!

antd Select

TIP

菜单渲染父节点。默认渲染到 body 上,如果遇到菜单滚动定位问题,修改滚动的区域的父元素,并设置相对其定位。

<div style={{ padding: 100, height: 1000, background: '#eee', position: 'relative' }} id="area">
  <h4>可滚动的区域 / scrollable area</h4>
  <Select
    defaultValue="lucy"
    style={{ width: 120 }}
    getPopupContainer={() => document.getElementById('area')}
  >
    <Option value="jack">Jack</Option>
    <Option value="lucy">Lucy</Option>
    <Option value="yiminghe">yiminghe</Option>
  </Select>
</div>

Input 组件取消自动显示输入历史

TIP

Form默认开启 autoComplete 功能,当submit时会记录输入历史, 取消自动补充功能,只需在 Form 上加 autoComplete="off" 即可关闭自动提示输入历史

Form

TIP

Form.Item组件里面的,如果声明在FormSelect.tsx文件,会导致在父组件中通过 form.validateFields()获取不到该字段的值,应该把<Form.Item></Form.Item>FormSelect.tsx文件。

// FormSelect.tsx
const FormSelect = <Item name="types" label='性别' rules={[{ required: true, message: `请选择性别!` }]}>
  <Select
    placeholder='请选择性别'
    style={{ width: 360 }}
    showArrow
    options={options}
    mode="multiple"
  />
</Item>


<Form form={form} autoComplete="off" labelCol={{ span: 5 }}>
  <Item name="name" label="姓名" rules={[{ required: true, message: '请填写姓名!' }]}>
    <Input placeholder="请填写姓名" style={{ width: 360 }} />
  </Item>
  <FormSelect form={form} />
</Form>