antd state使用_uform使用笔记

上周开始尝试着用uform去写一些简单的demo,然后提供给其他同事参考使用。以下算是一些总结。

先要搞清楚一个事,就是 uform是一个form的解决方案,并不是UI组件。因此,我们在使用时,通常会结合 antd或者其他UI库(需要自己去结合uform搞一套 @uform/xxx)一起使用。

官方有提供了一套 @uform/antd,所以我们就用这个来实现以下的一些场景。

先搞明白 labelColwrapperCol

比如下面的写法:

<SchemaForm
  labelCol={5}
  wrapperCol={8}
/> 

我一开始的理解是, labelCol占整个form的5份, wrapperCol是占整个form的8份。就相当于 labelCol是5/13, wrapperCol是8/13。

其实不是的,它正确的理解是整个form是分成24份, labelCol占了5份, wrapperCol占了8份,也就是说剩下的11份是右边的留白空间。

表单项的内容为文字

比如这样的情况:

616ceb33b49cf5a1ab8e2d9bf04263d6.png

写法如下:

<Field
  type="string"
  title="测试"
  name="test"
  default="haha"
  x-props = {{
    editable: false
  }}
/>

需要注意的是,在submit的时候, values里面也会包含这个字段,可能需要自己进行过滤一下。

是否可编辑差不多的,还有一个 是否可见。也许你会这样来写:

<Field
  type="string"
  title="aa"
  name="aa"
  x-props = {{
    visible: false
  }}
/>

但经测试,是行不通的。。这个得写到 effect里面的 onFormMount事件里面,对单个字段或者批量的字段设置 visible

01f8ec3d5f1198b4bd88c74b6ade2e3a.png

input表单项

写法如下:

<Field 
  type="string"
  title="用户名"
  name="username"
/>

规则rule

通常我们需要有一些规则,比如必填、字符个数、正则判断(比如只能是中文)、在输入的同时要校验后端是否存在了这个值(如果存在,则要提示错误)

方式就是通过 x-rules,它的写法其实和 antd的form那一套差不多,不过似乎增加了一种更为人性化的方式,那就是 function

x-rules = {[
  (val) => {
    if (value === "你指定的值") {
      return "错误的message信息"
    }
    return "";
  }
]}

return内容为空字符串时,表示校验通过。

说一个具体场景吧:比如说上传文件,你可以校验上传的file size,当超过1M时,需要error,则可以采用上面的写法。

当然上面的是同步的,异步的写法,相信大家也猜到了,通过 Promise

x-rules = {[
  val =>
    new Promise(resolve => {
      // 在这里可以发起请求
      setTimeout(() => {
        if (val === '1111') {
          resolve('不允许重复')
        } else {
          resolve()
        }
      }, 300);
    })
]}

resolve空字符串时,则表示校验通过。

password

antd的Input组件有提供 type属性,当传入 password时,自然会渲染成密码框。

所以我们可以通过 x-props传入 type属性即可。

不过呢, @uform/antd提供了更好的UI:

6774b716cd5138bbb7bd26eda5b5ed0a.png

写法如下:

<Field
  type="password"
  title="密码"
  name="password"
  x-props={{
    checkStrength: true,
    autoComplete: "new-password"
  }}
/>

checkStrength是用来显示 密码强度那一块的内容的, autoComplete则是为了解决控制台的 warning

cb811bf94f4fa42b075e890e3355a54e.png

表单项旁边有一些文字

如图所示:

7b62cf71813019af149f0683079e9de0.png

这个东西,我翻了很多文档,硬是没找着怎么写,最后在钉钉群里面问了白玄,他给了我一个方案才搞定的:

import { Form } from 'antd';

const TextBox = createVirtualBox("text-box", ({ text, children, ...props }) => {
    return <Form.Item {...props}>{children}<span style={{marginLeft: 10}}>{text}</span></Form.Item>
});

// ...
<TextBox
  label="测试2"
  labelCol={{ span: 5 }}
  wrapperCol={{ span: 8 }}
  required={true}
  text="这是一段描述"
>
  <Field 
     x-props={{autoComplete: "new-password"}} 
     type="string" 
     required 
     name="aa"
   />
</TextBox>

我以为这样就结束了,但事实上并没有,还有一些样式上的问题:

antd的 label后面的冒号是通过伪类来实现的,但是 @uform/antd的并不是,它是通过html来实现的,导致了有一些px的差距。

不过在解决问题的过程中,学到了伪类中的 content的空格写法,要用unicode来处理: 00a0

Field的description属性

0f898280ce8b6942a3296bb953d26016.png

但我发现这个不同的type, description所在的区域不太一样,譬如日期的:

30b023aab8566c2007d1f0bff6567ba0.png

有些奇怪,所以 不推荐使用这个属性。

另外, title属性是可以使用 jsx的,所以像上面的密码后面的 ?,其实也可以写在 title里面,我个人是感觉上面(密码)的样式比较奇怪。

下拉Select

input差不多,不同点在于它的 enum属性为数组。

<Field
  type="string"
  title="应用列表"
  name="application"
  enum={[]}
  x-props={{
    placeholder: "请选择应用列表"
  }}
/>

然后在 effectonFormMount事件里面去请求加载数据:

effects={($, { setFieldState }) => {
  $('onFormMount').subscribe(() => {
    setTimeout(() => {
      setFieldState("application", state => {
        state.props.enum = [
          {label: "应用一", value: "1"},
          {label: "应用二", value: "2"},
          {label: "应用三", value: "3"},
          {label: "应用四", value: "4"},
          {label: "应用五", value: "5"},
          {label: "应用六", value: "6"} 
        ] 
       }) 
     }, 300);
   }) 
}}

可输入搜索下拉框

antdSelect有实现,所以只要传入props即可:

<Field
  type="string"
  title="应用列表(可输入)"
  name="application2"
  enum={[]}
  x-props={{
    placeholder: "请选择应用列表",
    showSearch: true,
    optionFilterProp: "children",
    filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
  }}
/>

多选

也是普通 Select的扩展:

<Field
  type="string"
  title="应用列表(多选)"
  name="application3"
  enum={[]}
  x-props={{
    placeholder: "请选择应用列表",
    mode: "multiple"
  }}
/>

Checkbox

<Field
  type="checkbox"
  enum={[
    {label: "吃饭", value: "1"},
    {label: "睡觉", value: "2"},
  ]}
  required
  title="兴趣爱好"
  name="hobby"
/>

Radio

<Field
  type="radio"
  enum={[
    {label: "男", value: "1"},
    {label: "女", value: "2"}
  ]}
  title="性别"
  name="sex"
  x-rules={[
   {required: true, message: "请选择性别"}
  ]}
/>

日期

<Field type="date" title="日期选择" name="date" />
这里有一个注意点,就是传入的值是一个字符串,在antd里面,我们的日期传入值必须是moment对象,但这里不是。。

说到 moment,这里可能会有一个中英文的问题,所以需要在入口文件那里指定:

import moment from "moment";
moment.locale('zh-cn');

Input file

OK,这个可以来讲讲了。。如果你的组件是选择文件的时候,直接和后端交互,那么是没问题的。

但是如果你希望选择文件后,不与后端交互,和其他字段一起提交到后端,那么现有的方式是有问题。

问题在于: beforeUpload需要 returnfalse,但是这个会导致 required不起作用,也就是说当你选择了一个文件之后,它也会提示未选择文件。

所以我就自己自定义了一个 my-file的组件:

import React, { useState } from 'react';
import { Upload, Button, Icon } from 'antd';
import { registerFormField, connect } from '@uform/antd';

const SelectFile = React.forwardRef((props, ref) => {
 const { onChange, accept, value = {} } = props;
 const [fileList, setFileList] = useState(value.name ? [
    {
      uid: '-1',
      name: value.name,
      status: 'done',
      url: value.url,
    }
  ]: []);
  return (
     <Upload
        fileList={fileList}
        accept={accept}
        beforeUpload={
          (file) => {
             setFileList([file]);
             onChange(file);
             return false;
           }
        }
        onRemove={
          () => {
            setFileList([]);
            onChange("");
          }
        }
      >
        <Button>
           <Icon type="upload" /> Select File
        </Button>
      </Upload>
   )
});

registerFormField("my-file", connect()(SelectFile));

当然上面的业务,我并没有做得比较通用,这是针对我之前写的业务代码的一个简单抽象(它适合只能上传单个文件)。

使用:

<Field
  type="string"
  title="单文件上传"
  name="upload"
  required
  x-props={{
    listType: 'text',
    accept: ".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
  }}
  x-component="my-file"
  x-rules={[
    {required: true, message: "请上传文件"},
    (file) => {
       const { name } = file;
       if (!name) {
          return '';
       }
       const extname = path.extname(name);
       if (extname !== '.xlsx' && extname !== '.xls') {
          return '后缀名必须是excel格式';
       }
    }
  ]}
/>

列表

效果如下:

312f09c0855a975da324c191ee87898b.png

这个也需要自己自定义组件,可以参考着array和table来改。

其他

其实上周就差不多写了这些:

d783bcc4d83c5f6348a62bf3a7289472.png

剩下的几个,大家翻翻文档就基本上搞定了。

感觉下来,除了样式这一块,其他近乎完美。

其实拿我个人来说,我最喜欢的就是:json schema和可视化平台。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
要实现可滑动的侧边栏,可以使用 Ant Design 的 Layout 组件。具体步骤如下: 1. 引入 Layout 组件 首先,在组件中引入 Layout 组件: ```jsx import { Layout } from 'antd'; const { Sider, Content } = Layout; ``` 2. 创建 Sider 和 Content 使用 Sider 组件创建侧边栏,使用 Content 组件创建主内容区域。例如: ```jsx <Layout> <Sider width={200} style={{ background: '#fff' }}> 左侧菜单 </Sider> <Content style={{ padding: '0 24px', minHeight: 280 }}> 主内容区域 </Content> </Layout> ``` 其中,Sider 的 width 属性设置侧边栏宽度,Content 的 style 属性设置主内容区域样式。 3. 设置 Sider 的可滑动 为了使 Sider 可以滑动,需要为其添加 trigger 和 collapsible 属性。例如: ```jsx <Sider width={200} style={{ background: '#fff' }} trigger={null} collapsible collapsed={this.state.collapsed}> 左侧菜单 </Sider> ``` 其中,trigger={null} 表示隐藏默认的 trigger,collapsible 表示是否可收缩,collapsed 则表示是否默认收缩。 4. 设置 trigger 如果需要显示自定义的 trigger,可以在 Sider 中添加一个按钮,并设置 trigger={this.trigger()},其中 trigger() 返回一个函数,用于切换侧边栏的收缩状态。 ```jsx <Sider width={200} style={{ background: '#fff' }} trigger={this.trigger()} collapsible collapsed={this.state.collapsed}> 左侧菜单 </Sider> trigger = () => { return ( <div onClick={this.toggle}> <Icon type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'} /> </div> ); }; toggle = () => { this.setState({ collapsed: !this.state.collapsed, }); }; ``` 这样就可以创建一个可滑动的侧边栏了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值