# antd

返回:开发笔记

# antd 组件中 default 属性的坑

default 属性只在第一次渲染时有效,只要组件渲染了 2 次(或以上)则此属性无效,解决方式就是类似如下:

# Tree

const generateTree = () => {
  return treeData.length === 0 ? null : (
    <Tree treeData={treeData} onSelect={onSelect} defaultExpandAll></Tree>
  );
};
1
2
3
4
5

# Select

placeholder

如果需要显示【placeholder】信息,则 value 值必须为undefined,否则不会显示,即使 value 值为''或者null

generateUserSelect = () => {
  const otherProps = this.props;
  const { fetching, data, value } = this.state;
  if (!!data && data.length > 0) {
    return (
      <Select
        showSearch
        showArrow={false}
        value={value}
        notFoundContent={fetching ? <Spin size="small" /> : null}
        filterOption={false}
        onSearch={this.fetchUser}
        onChange={this.onChange}
        style={{ width: "120px" }}
        {...otherProps}
      >
        {data.map((d) => (
          <Option key={d.id}>{d.name}</Option>
        ))}
      </Select>
    );
  }
  return <Spin size="small" />;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

支持搜索功能:Option 增加自定义属性,如下为:name,Select 中增加optionFilterProp,并指定其过滤属性即可

<Select
  placeholder={formatMessage({ id: "all.circuits" })}
  showArrow
  style={{ width: "141px" }}
  getPopupContainer={(triggerNode) => triggerNode.parentElement}
  allowClear
  optionFilterProp="name"
  showSearch
>
  <Option value="" disabled>
    {formatMessage({ id: "all.circuits" })}
  </Option>
  {linkedDevices.length > 0 &&
    linkedDevices.map((item) => (
      <Option
        key={item.id}
        name={`${item.name}${item.no}`}
      >{`${item.name}-${item.no}`}</Option>
    ))}
</Select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# antd 清除 select 内容

通过给 Select 组件新增 allowClear 属性注意:allowClear 也会触发 onChange 方法,所以,也要单独处理一下,因为 value 和 element 为 undefined。

<Select {...this.props} placeholder="请选择" allowClear={true}>
  ...
</Select>
1
2
3

# 自定义 antd Select 组件样式

.ant-select-dropdown {
  /* 定义下拉框背景色 */
  background: #4e5458;
  /* 定义下拉框中文字颜色 */
  .ant-select-dropdown-menu-item {
    color: #ffffff;
  }
  /* 定义选中的选项的背景色 */
  .ant-select-dropdown-menu-item-selected {
    background: #457173;
  }
}
/* 定义鼠标移动到选项上时,背景色 */
.ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
  background: #163240;
}
/* 定义展开下拉框时,第一个选项的背景色 */
.ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled) {
  background: #163240;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Input

const inputRef = useRef();

useEffect(() => {
  const { types, keyWord } = defaultSelected;
  setCheckedList(types);
  // console.info({ types }, '--------------');
  // console.info({ keyWord }, '+++++++++++++++++++');
  // console.info({ selectCount }, '+++++++++++++');
  setIndeterminate(types && types.length > 0 && types.length < selectCount);
  setCheckAll(types && types.length > 0 && types.length === selectCount);
  // console.info(defaultSelected.length, selectCount, '00000000000000000');
  setDefaultKeyWord(keyWord);
  // inputRef.current = keyWord;
  if (keyWord) {
    inputRef.current.state.value = keyWord;
    // console.info(inputRef.current?.state?.value, '000000000000');
  }
}, [defaultSelected, selectCount]);

<Input
  placeholder={formatMessage({ id: "oneMapComp.device.detail.keyWord" })}
  onChange={changeValue}
  style={{ marginLeft: 10, width: 160 }}
  ref={inputRef}
/>;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

form

当然如果使用 antd 的 form 组件的话,可以使用 initialValue 来显示初始值

# input-defaultvalue

关于 defaultValue

由于 defaultValue 只在第一次绘制时会启用。
这意味着,如果首次绘制为"",而后面绘制为"value",则 defalutValue 始终为""

# maxLength

<Input
  placeholder="请输入账号"
  disabled={account}
  maxLength="30"
  autoComplete="off"
/>
1
2
3
4
5
6

# inputNumber

<InputNumber
  formatter={(value) => value}
  parser={(value) => parseInt(value) || ""}
  style={{ width: "100%" }}
  step={1}
  onChange={(val) => this.onChangeIpt(1, val)}
/>
1
2
3
4
5
6
7

# 关于数字的校验问题

  • 增加whitespace为 true
rules: [
  {
    required: true,
    whitespace: true,
    message: formatMessage({
      id: 'devices.device.form.factory',
    }),
  },
],
1
2
3
4
5
6
7
8
9

此时即使存在初始值,也会报错,此时可采取两种解决方法

  1. 删掉whitespace: true,
  2. 增加type: 'number'
rules: [
  {
    required: true,
    whitespace: true,
    type: 'number',
    message: formatMessage({
      id: 'devices.device.form.factory',
    }),
  },
],
1
2
3
4
5
6
7
8
9
10

Type Indicates the type of validator to use. Recognised type values are:

  • string: Must be of type string. This is the default type.
  • number: Must be of type number.
  • boolean: Must be of type boolean.
  • method: Must be of type function.
  • regexp: Must be an instance of RegExp or a string that does not generate an exception when creating a new - RegExp.
  • integer: Must be of type number and an integer.
  • float: Must be of type number and a floating point number.
  • array: Must be an array as determined by Array.isArray.
  • object: Must be of type object and not Array.isArray.
  • enum: Value must exist in the enum.
  • date: Value must be valid as determined by Date
  • url: Must be of type url.
  • hex: Must be of type hex.
  • email: Must be of type email.
  • any: Can be any type.

# 关于 Tab 组件

  • 删除 tab 组件下方的线条
.ant-tabs-card-bar {
  border: none;
}
1
2
3

# 路由

 {
  path: '/weibao',
  name: 'wb',
  routes: [
    {
      path: '/weibao/im',
      name: 'im',
      routes: [
        {
          path: '/weibao/im/items',
          name: 'items',
          component: './wb/im/items',
        },
        {
          path: '/weibao/im/train',
          name: 'train',
          component: './wb/im/train',
        },
      ],
    },
    {
      path: 'weibao/dm',
      name: 'dm',
    }
  ],
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

设置成上述,则可以刷新页面时正常,但是设置成下述,则刷新时会 404

 {
  path: '/wb',
  name: 'wb',
  routes: [
    {
      path: '/wb/im',
      name: 'im',
      routes: [
        {
          path: '/wb/im/items',
          name: 'items',
          component: './wb/im/items',
        },
        {
          path: '/wb/im/train',
          name: 'train',
          component: './wb/im/train',
        },
      ],
    },
    {
      path: 'wb/dm',
      name: 'dm',
    }
  ],
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 路由跳转总结

  • 1、直接点击跳转页面:
<a href="跳转路径>点击跳转</a>

<Link to="跳转路径">点击跳转</Link>
1
2
3
  • 2、点击按钮之后跳转:

引入:

import { hashHistory } from 'react-router';

// 在方法里面加入:
hashHistory.push('跳转路径')
1
2
3
4
  • 3、browserHistory 带参数跳转
import{browserHistory}from'dva/router';

browserHistory.push('/orders/orderdetail?oderId='+item.order);

// 在router里面设置路径的时候不需要加入参数

path:'orders/orderdetail',
1
2
3
4
5
6
7
  • 4、关于路由获取参数的问题:
location.pathname  设置或获取对象指定的文件名或路径。
location.href  设置或获取整个 URL 为字符串。
location.port 设置或获取与 URL 关联的端口号码。
location.protocol 设置或获取 URL 的协议部分。
location.hash 设置或获取 href 属性中在井号“#”后面的分段。
location.host 设置或获取 location 或 URL 的 hostname 和 port 号码。
location.search 设置或获取 href 属性中跟在问号后面的部分。
1
2
3
4
5
6
7
  • 5、routerRedux 路由跳转

1、不带参数跳转:

dispatch(
  routerRedux.push({
    pathname: "/couponDetail",
  })
);
1
2
3
4
5

2、带参数跳转

dispatch(
  routerRedux.push({ pathname: "/couponDetail", query: 要携带的参数object })
);
// 注意:通过location.query.参数字段来获取参数值
1
2
3
4

3、在 effect 里面跳转

yield put (routerRedux.replace({ pathname: '/domains/buy/pay', query: payload }));
1

书写 modal 的位置,以防出现不必要的点击事件,放到最外层的节点中去

  <ShowDeviceLocation
    onCancel={hideLocation}
    visible={locationVisible}
    deviceId={deviceData.id}
  />
  <Modal
    visible={energyVisible}
    closable
    destroyOnClose
    onCancel={hideEnergy}
    width={1024}
    height={400}
    footer={null}
    title={null}
    maskClosable={false}
    className={styles.blackModal}
    closeIcon={<img alt="" src={closeIcon} />}
  >
    {generateTabPane()}
  </Modal>
</React.Fragment>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

不要放到父组件的某个节点中

    <ShowDeviceLocation
      onCancel={hideLocation}
      visible={locationVisible}
      deviceId={deviceData.id}
    />
    <Modal
      visible={energyVisible}
      closable
      destroyOnClose
      onCancel={hideEnergy}
      width={1024}
      height={400}
      footer={null}
      title={null}
      maskClosable={false}
      className={styles.blackModal}
      closeIcon={<img alt="" src={closeIcon} />}
    >
      {generateTabPane()}
    </Modal>
  </div>
</React.Fragment>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# modelFoot

一般应用场景,详情不需要底部按钮,新增和修改需要。
通过父组件传递一个空的字符串或者 {footer: null} 给 Modal 组件进行属性解构。

# 自定义 Modal

查看元素可知,Modal 是在界面构建完成之后,由 js 控制,动态的添加,所以想事先获取 ant-modal-body 中 DOM 元素的节点是不可能的,但是一般情况也不会去获取它。
自定义 Modal,解决上述的问题。

  • 1:因为我们使用的是 antd,所以,下面的样式是不需要引入的。这个跟 antd 的 Modal 样式重复。
  • 2:Modal 的隐藏和显示,是通过控制 class 为 ant-modal-mask 和 ant-modal-wrap 两个 div 的显示和隐藏。
    • 通过给 ant-modal-mask 的 div,添加另外一个 className:ant-modal-mask-hidden,来控制其隐藏,也可以通过 display 来控制。
    • 通过给 ant-modal-wrap 设置行内样式 display: none,来控制其隐藏。不过,也可以使用 className,随便都可以。
// 界面布局:

<div className="ant-modal-mask" ></div>
<div tabIndex="-1" className="ant-modal-wrap " role="dialog" aria-labelledby="rcDialogTitle0" style={{}}>
  <div role="document" className="ant-modal" style={{ width: '920px' }}>
    <div className="ant-modal-content">
      <div className="ant-modal-header">
       ...
      </div>
      <div className="ant-modal-body" style={{ background: 'rgb(16, 16, 17)' }}>
        ...
      </div>
    </div>
  </div>
  <div tabIndex="0" style={{ width: '0px', height: '0px', overflow: 'hidden' }}>
    sentinel
    </div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* // 样式: */
.ant-modal-mask {
  /* // 遮罩层 */
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.65);
  height: 100%;
  z-index: 1000;
  filter: alpha(opacity=50);
}

.ant-modal-wrap {
  position: fixed;
  overflow: auto;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  -webkit-overflow-scrolling: touch;
  outline: 0;
}

.ant-modal {
  font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  font-size: 14px;
  font-variant: tabular-nums;
  line-height: 1.5;
  color: rgba(0, 0, 0, 0.65);
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
  width: 920px;
  margin: 0 auto;
  top: 100px;
  padding-bottom: 24px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 左侧菜单调整宽度设置

🔝🔝开发笔记

通过在 Sider 组件,设置 width,调整菜单的宽度,通过设置 collapsedWidth,调整调整缩进的宽度。

<Sider
  style={{ background: "#1D2023", height: "100%" }}
  trigger={null}
  collapsible
  collapsed={this.state.collapsed}
  width={140}
  collapsedWidth={40}
>
  <BaseMenu
    toggle={this.toggle}
    collapsed={this.state.collapsed}
    history={history}
    location={location}
  />
</Sider>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

🔝🔝开发笔记

menu 必须放在 Sider 中,才能实现缩回去的,这个有特定的布局。

<Sider
  style={{ background: "#1D2023", height: "100%" }}
  trigger={null}
  collapsible
  collapsed={this.state.collapsed}
  width={140}
  collapsedWidth={40}
>
  <BaseMenu
    toggle={this.toggle}
    collapsed={this.state.collapsed}
    history={history}
    location={location}
  />
</Sider>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Button 组件

Antd Select 组件(onBlur)+Button 按钮 (handleClick)如何让 handleClick 优先触发

TIP

按钮不要用onClick,用onMouseDown,可以保证在 onBlur 事件之前触发。

# 过渡组件 dva-loading

back

该组件仅仅监听异步加载状态,这从它的调用方式就可以看出来 const isLoading = loading.effects['user/query'],其中 user/query 是 model 中的异步请求方法

loading 在异步请求发出那一刻会持续监听该异步请求方法的状态,在异步请求结束之前 isLoading 的值一直是 true,当此次异步请求结束时 isLoading 的值变成 false,同时 loading 对象停止监听。

loading 有三个方法,其中 loading.effects['user/query'] 为监听单一异步请求状态,当页面处于异步加载状态时该值为 true,当页面加载完成时,自动监听该值为 false。

# Progress

针对【type="dashboard"】的仪表盘类型的进度条,如何调整未覆盖进度的背景色(svg 中的 class="ant-progress-circle-trail"的 stroke 属性)

.ant-progress-circle-trail {
  stroke: transparent !important;
}
1
2
3

# Upload

# Upload错误集锦

  • must set key for <rc-animate> children

初始回填的文件列表是从后端拿的,直接传给了upload组件,虽然设置的uid为filename但是名字有''的情况,此时识别为无uid。antd文档写了uid是唯一标识符,没有uid导致了此错误