【React入门实践】结合Ant-Design 从0带你手把手开发 带【复杂操作】的表格列表以及【Modal弹框】的使用

252 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1. 看页面效果

功能总结:

(1)查询栏的展开收起 【React入门实践】复杂搜索表单的【展开-收起】功能_Sabrina_cc的博客-CSDN博客

(2)页面列表显示数据 【React入门实践】结合Ant-Design 从0带你手把手开发包含【列表】和【搜索栏】的简单【用户管理】页面_Sabrina_cc的博客-CSDN博客

(3)列表对应不同标签显示,以及操作的分情况显示

(4)查看详情 -- 需要通过点击事件传参查询该用户详情信息显示

(5)冻结/解冻账号 --  开关操作

(6)创建账号 -- 弹框新建并校验 新建后刷新列表

2. 分析接口文档

(1)冻结、解冻接口

改接口只需要传入账号相应的id即可,后端实现置反操作。前端只需要处理好显示即可

(2)查看详情接口

(3)分配角色接口

(4)创建用户接口

 

 3. 参照UI编写前端页面

(1)页面整体布局

Search是查询部分;CollectionCreateForm是新建部分弹框;DetailForm是详情弹框部分;RoleForm是角色分配弹框部分;StandardTable是列表部分;

    <PageHeaderLayout title="列表" className={'commonList'}>
      <Search init={init} dispatch={dispatch} />
      {visible && (
        <CollectionCreateForm
          setVisible={setVisible}
          visible={visible}
          dispatch={dispatch}
          init={init}
          toggleForm={toggleForm}
        />
      )}
      {detailVisible && (
        <DetailForm
          setDetVisible={setDetVisible}
          detailVisible={detailVisible}
          dispatch={dispatch}
          init={init}
          userId={userId}
          toggleDetail={toggleDetail}
          detailData={detailData}
        />
      )}
      {roleVisible && (
        <RoleForm
          setRoleVisible={setRoleVisible}
          roleVisible={roleVisible}
          dispatch={dispatch}
          init={init}
          userId={userId}
          userRole={userRole}
          toggleRole={toggleRole}
        />
      )}
      <Card
        style={{ marginTop: 1 }}
        bordered={false}
        bodyStyle={{ padding: '8px 32px 32px 32px' }}
      >
        <div className={'commonListOperator'}>
          <Button
            type="primary"
            onClick={() => toggleForm(-1)}
            className={styles.new}
          >
            创建账号
          </Button>
        </div>
        <StandardTable
          loading={loading}
          data={listData}
          columns={columns}
          onChange={handleStandardTableChange}
        />
      </Card>
    </PageHeaderLayout>
  )

(2)列表及复杂操作

其中,角色的显示:接口返回值为roles:[1,2] 数组,分别表示‘前台用户’和‘后台技术服务’,在此需要进行前端字符串拼接,并且最大显示五个字符。‘手机号’‘企业名称’同理。

    () => [
      {
        title: '序号',
        dataIndex: '_id'
      },
      {
        title: '用户名',
        dataIndex: 'username'
      },
      {
        title: '角色',
        width: 80,
        dataIndex: 'roles', //['1','2']  ['1']
        // render: (value: any) => (
        //   <>
        //     {value === 1 && <>前台用户</>}
        //     {value === 2 && <>后台技术服务</>}
        //   </>
        // )
        render: (value: any) => {
          var str = ''
          if (value.length == 1) {
            value.forEach((element: number) => {
              if (element === 1) {
                str = '前台用户'
              } else {
                str = '后台技术服务 '
              }
            })
          } else {
            value.forEach((element: number) => {
              // console.log(element)
              if (element === 1) {
                str = str + '前台用户,'
              } else {
                str = str + '后台技术服务 '
              }
            })
          }
          if (str.length <= 5) {
            return str
          } else {
            return <Tooltip title={str}>{str.substring(0, 5) + '...'}</Tooltip>
          }
        }
      },
 
      {
        title: '企业名称',
        dataIndex: 'firmName',
        render: (record: any) => {
          if (record.length <= 5) {
            return record
          } else {
            return (
              <Tooltip title={record}>{record.substring(0, 5) + '...'}</Tooltip>
            )
          }
        }
      },
      {
        title: '手机号码',
        dataIndex: 'mobile',
        render: (record: any) => {...},
      {
        title: '邮箱',
        dataIndex: 'email',
        render: (record: any) => {...}
      },
      {
        title: '地址',
        dataIndex: 'address',
        width: 100,
        render: (record: any) => {...}
      },
      {
        title: '认证状态',
        dataIndex: 'status',
        render: (value: any) => (
          <>
            {value === 3 && <Tag color="blue">未提交</Tag>}
            {value === 0 && <Tag color="orange">待审核</Tag>}
            {value === 1 && <Tag color="red">不通过</Tag>}
            {value === 2 && <Tag color="green">通过</Tag>}
          </>
        )
      },
      {
        title: '账号状态',
        dataIndex: 'isFreeze',
        render: (value: any) => (
          <>
            {value == 0 && <Tag color="green">正常</Tag>}
            {value == 1 && <Tag color="red">冻结</Tag>}
          </>
        )
      },
      {
        title: '操作',
        render: (value: any) => (
          <>
            {value.isFreeze == 1 && (
              <Popconfirm
                title="确定解冻账号吗"
                onConfirm={() => {
                  changeFreeze(value.id)
                }}
                // onConfirm={changeFreeze(value.id)}
                style={{ width: 400 }}
              >
                <a type="primary">解冻账号</a>
              </Popconfirm>
            )}
            {value.isFreeze == 0 && (
              <Popconfirm
                title="确定冻结账号吗"
                onConfirm={() => {
                  changeFreeze(value.id)
                }}
              >
                <a type="primary">冻结账号</a>
              </Popconfirm>
            )}
            <Divider type="vertical" />
            <a type="primary" onClick={() => toggleDetail(value.id)}>
              查看详情
            </a>
            {value.isFreeze == 0 && <Divider type="vertical" />}
            {value.isFreeze == 0 && (
              <a
                type="primary"
                onClick={() => toggleRole(value.id, value.roles)}
              >
                分配角色
              </a>
            )}
          </>
        )
      }
    ],
    [changeFreeze, toggleDetail, toggleRole]
  )

操作的显示:isFreeze控制显示是‘冻结’还是‘解冻’,并对应changeFreeze(value.id)函数实现功;查看详情是每种用户都有的操作,并绑定函数toggleDetail(value.id);对于非冻结的用户才可以进行角色分配功能,对应onClick={() => toggleRole(value.id, value.roles)}

4. 冻结与解冻功能

  const changeFreeze = useCallback(
    (index: any) => {
      dispatch({
        type: 'usermanage/changeFreeze',
        params: [{ id: index }]
      }).then((v: any) => {
        if (v.code === 200) {
          message.success(v.message)
          init()
        }
      })
    },
    [dispatch, init]
  )
      const data = await dispatch({
        type: 'usermanage/get',
        params: ['/api/manage/user/freeze/' + remoteValue.id]
      })
      return data
    }

5. 新建功能

(1)点击‘创建账号’弹出弹框

 在page页面同目录下 新建页面new.tsx,并在主页面引入import CollectionCreateForm from './new'

  visible,setVisible,toggleForm,dispatch,userId,itemData,init,form
}: any) {
  const { getFieldDecorator, validateFields } = form
 
  return (
    <Modal
      title={'新增'}
      visible={visible}
      onCancel={toggleForm}
      footer={[
        <Button
          key="back"
          onClick={() => {
            setVisible(false)
          }}
        >
          取消
        </Button>,
        <Button key="submit" onClick={onSave}>
          保存
        </Button>
      ]}
      className={styles.modal}
    >
      <Form layout="inline">
        ...
        <Form.Item label="角色">...</Form.Item>
      </Form>
    </Modal>
  )
}
export default Form.create()(NewPage) as any

为按钮添加点击事件toggleForm()

<Button type="primary" onClick={() => toggleForm(-1)}>创建账号</Button>

toggleForm()主要用来控制弹框的显示与隐藏。-1表示是‘新建’而不是‘修改’(但本页面不涉及修改)。每次触发将显示与隐层通过  [visible, setVisible] = useState(false)置反

const toggleForm = useCallback((v: any) => {
    setVisible(prevState => {
      return !prevState
    })
  }, [])

最后在主页面设置,通过visible控制显示。并传入参数。

        <CollectionCreateForm
          setVisible={setVisible}
          visible={visible}
          dispatch={dispatch}
          init={init}
          toggleForm={toggleForm}
        />
      )}

(2)点击‘新增’ 调接口实现

  const onSave = useCallback(() => {
    console.log('保存')
    validateFields((err: any, fieldsValue: any) => {
      if (err) return
      let res = {
        ...fieldsValue
      }
      if (fieldsValue.email == undefined) {
        fieldsValue.email = ''
      }
      console.log(fieldsValue)
      dispatch({
        type: 'usermanage/post',
        params: ['/api/manage/user/create', res]
      }).then((v: any) => {
        if (v.code === 200) {
          toggleForm()
          message.success(v.message)
          init()
        }
      })
    })
  }, [dispatch, init, toggleForm, validateFields])

6. 查看详情功能

(1)点击查看详出现弹框

这里所有的弹框实现都类比【新增】功能,写组件页面后,通过visible和toggle来控制其显示与隐藏。

        <DetailForm
          setDetVisible={setDetVisible}
          detailVisible={detailVisible}
          dispatch={dispatch}
          init={init}
          userId={userId}
          toggleDetail={toggleDetail}
          detailData={detailData}
        />
      )}

(2)显示详情信息

由于点击不同的数据展示不同详情,因此需要在点击事件中传入该行id

onClick={() => toggleDetail(value.id)}

在点击后,通过visible控制modal的显示,同时初始化页面详情信息。

    (index: any) => {
      dispatch({ type: 'usermanage/getDetail', params: [{ userId: index }] })
    },
    [dispatch]
  )
const toggleDetail = useCallback(
    (v: any) => {
      setUserId(v)
      editInit(v)
      console.log('显示详情')
      setDetVisible(detailState => {
        return !detailState
      })
    },
    [editInit]
  )

(3)图片展示的处理

使用自定义封装组件ImgUpload,其中diabled={true}表示不可上传,之展示图片。其中qualificationFileIds返回值需要满足格式url:""

  // var contentUrl = [
  //   {url:'http://39.101.69.143:9000/si..'},
  //   {url:'http://39.101.69.143:9000/simulati..'}
  // ]
                {/* {(detailData && detailData.qualificationFileIds) || ''} */}
                {getFieldDecorator('qualificationFileIds', {
                  initialValue: detailData.qualificationFileIds || []
                  // initialValue: contentUrl
                })(
                  <ImgUpload
                    accept={['png', 'jpg']}
                    remoteValue={{ type: 0 }}
                    tip="(请上传图片)"
                    disabled={true}
                  />
                )}
              </Form.Item>