【React入门实践】结合Ant-Design 从0带你手把手开发包含【列表】和【搜索栏】的简单【用户管理】页面

这个世界轻而易举、一蹴而就的事情很少很少。要说真正的有所成,只能是慢慢来。

上节回顾:【React入门实践】结合Ant-Design从0带你手把手开发【个人中心-信息修改】页面_Sabrina_cc的博客-CSDN博客

写在前面的话: 上节我们实现了简单的是form表单,如下,今天我们来学习最常见的表单组件的使用。一步步向更复杂的页面进发。

 本次页面核心知识点:(1)【search表单模块】搜索search、重置功能reset、日期选择器DatePicker、下拉框使用select;(2)【列表展示模块】时间规范化moment、tag标签的使用、省略款的Tooltip使用;


目录

1. 看页面需求

2. 分析接口文档

3. 参照UI编写页面代码

(1) 列表部分

(2)search查询栏部分

4.初始化页面 - 列表数据显示

5.列表数据按需渲染render

7. 查询功能实现


1. 看页面需求

 分析:页面分为两个核心部分:【搜索栏】【列表】

2. 分析接口文档

(1)留言列表接口-主要是在页面初始化时调用。pageNum pageSize为请求参数,非必须。响应参数在list中。

 (2)搜索接口 - 但搜索接口一般可以直接使用列表接口即可

 

 3. 参照UI编写页面代码

(1) 列表部分

使用封装的<StandardTable>组件(代码后附),data={listData}表示列表中绑定listData数据,注意dataIndex需要对照接口返回的参数编写

function Feedback({
  dispatch,
  feedback: { listData, detailData },
  loading
}: any) {
  const init = useCallback(() => {
    dispatch('feedback/init')
  }, [dispatch])

  useEffect(() => {
    init()
  }, [init])

  const columns = useMemo(
    () => [
      {
        title: '序号',
        dataIndex: '_id'
      },
      {
        title: '用户名',
        dataIndex: 'username'
      },
      {
        title: '反馈标题',
        dataIndex: 'title'
      },
      {
        title: '反馈内容',
        dataIndex: 'content',
      },
      {
        title: '提交时间',
        dataIndex: 'createdTime',
      },
      {
        title: '状态',
        dataIndex: 'status',
      },
      {
        title: '操作',
        render: (value: any) => (
          <>
            {value.status === 0 && (
              <a type="primary" onClick={() => toggleMessage(value.id)}>
                反馈留言
              </a>
            )}
            {value.status === 1 && (
              <a type="primary" onClick={() => toggleDetail(value.id)}>
                查看
              </a>
            )}
          </>
        )
      }
    ],
    [toggleDetail, toggleMessage]
  )

  return (
    <PageHeaderLayout className={'commonList'}>
      <Search init={init} dispatch={dispatch} />
      <Card
        className={styles.table}
        style={{ marginTop: 1 }}
        bordered={false}
        bodyStyle={{ padding: '8px 32px 32px 32px' }}
      >
        <StandardTable
          loading={loading}
          data={listData}
          columns={columns}
          onChange={handleStandardTableChange}
        />
      </Card>
    </PageHeaderLayout>
  )
}

(2)search查询栏部分

const Search: any = Form.create()(function({ form, init, dispatch }: any) {
  const { validateFields } = form
  const simpleForm = useMemo(() => {
    const { getFieldDecorator, getFieldValue } = form
    const { query } = getLocation()
   
    return (
      <Form
        layout="inline"
        style={{ display: 'flex' }}
        className={styles.commonForm}
      >
        <Form.Item label="">
          {getFieldDecorator('username', {
            initialValue: query.username
          })(<Input placeholder="用户名" />)}
        </Form.Item>
        <Form.Item label="">
          {getFieldDecorator('title', {
            initialValue: query.title
          })(<Input placeholder="标题" />)}
        </Form.Item>
        <Form.Item label="">
          {getFieldDecorator('submitTimeBegin', {
            initialValue: query.startDate || ''
          })(
            <DatePicker
              onChange={change1}
              disabledDate={disabledDate1}
              placeholder="请选择日期"
            />
          )}
        </Form.Item>
        <Form.Item label="">
          {getFieldDecorator('submitTimeEnd', {
            initialValue: query.startDate || ''
          })(
            <DatePicker
              onChange={change2}
              disabledDate={disabledDate2}
              placeholder="请选择日期"
            />
          )}
        </Form.Item>
        <Form.Item>
          {getFieldDecorator('status', {
            initialValue: query.status || ''
          })(
            <Select>
              <Option value={''}> 状态 </Option>
              {feedbackStatus.map((v: any) => (
                <Option key={v.key} value={v.key}>
                  {v.value}
                </Option>
              ))}
            </Select>
          )}
        </Form.Item>
        <Button className={styles.search} onClick={handleSearch} icon="search">
          查询
        </Button>
        <Button
          className={styles.reset}
          style={{ marginLeft: 10 }}
          onClick={handleFormReset}
          icon="undo"
        >
          重置
        </Button>
      </Form>
    )
  }, [form, handleFormReset, handleSearch, time1, time2])
  return (
    <Card bordered={false}>
      <div className={'commonForm'}>{simpleForm}</div>
    </Card>
  )
})

可以看到,在页面将search设置成组件,并在后面掉哦组件。至此已达到页面效果,但是还没有数据

 4.初始化页面 - 列表数据显示

在page页面的主函数中写init函数

const init = useCallback(() => {
    dispatch('feedback/init')
  }, [dispatch])

  useEffect(() => {
    init()
  }, [init])

 在model页面中写详细的接口调用信息

init: async (action, { dispatch }) => {
      const { query } = getLocation()
      const data = await dispatch({
        type: 'feedback/post',
        params: ['manage/feedback/page', { ...query }]
      })
      dispatch({
        type: 'feedback/setStore',
        params: [{ listData: listMOM1(data) }]
      })
    },

 其中我们使用到了ListMOM1组件来帮助页面数据回显,其主要功能是,添加页面的数据编号和实现分页功能,详细的封装代码后附。

其中type: 'feedback/setStore',表示把数据存储在缓存中,可以通过组件传递数据,因此在StandardTable中需要绑定data={listData},从而实现数据初始化显示。

// list数据处理中间件
export const listMOM1 = (resData: any) => {
  const {
    content,
    // total,
    totalCount,
    currPage: currPage = 1,
    pageSize: pageSize = 40,
    data,
    list
  } = resData
  const list1 = content || data || list
  //添加序号
  if (list) {
    list.forEach((element: any, index: number) => {
      element._id = index + 1 + (currPage - 1) * pageSize
      element._symbol = element._id
    })
  }
  //分页
  const pagination = {
    total: totalCount,
    pageSize: Number(pageSize) || 40,
    current: Number(currPage)
  }

  return {
    list,
    pagination: totalCount && { ...listData.pagination, ...pagination }
  }
}

5.列表数据按需渲染render

可以看到后台返回的数据如下,

 但是,(1)【标题和内容】数据过长,我们需要将其展示前5个字符后省略号(比如这样就。。。).(2)对于【日期】数据只需要显示到具体某天,不需要时分秒,(3)对于【状态】数据显示为0,1但是前端也需要整理为‘未回答’‘已回答’

(1)数据截取缩略显示,hover后显示全部---使用Tooltip组件

{
    title: '反馈内容',
        dataIndex: 'content',
        render: (text: any, record: any, index: any) => {
          if (record.content.length <= 5) {
            return record.content
          } else {
            return (
              <Tooltip title={record.content}>
                {record.content.substring(0, 5) + '...'}
              </Tooltip>
            )
          }
        }
      },

(2)日期截取同理

{
        title: '提交时间',
        dataIndex: 'createdTime',
        render: (text: any, record: any, index: any) => {
          return record.createdTime.substring(0, 10)
        }
      },

(3)对于接口返回的0,1,2状态数值数据需要分情况渲染

{
        title: '状态',
        dataIndex: 'status',
        render: (value: any) => (
          <>
            {value == 0 && <Tag color="red">未回答</Tag>}
            {value == 1 && <Tag color="green">已回答</Tag>}
          </>
        )
      },

6. 查询功能实现

const handleSearch = useCallback(() => {
    validateFields((err: any, data: any) => {
      // pushPath({
      //   query: data
      // })
      // init()
      const res = { ...data }
      if (data.username !== undefined) {
        res.username = data.username.replace(/\s*/g, '')
      }
      res.pageNum = 1
      dispatch({
        type: 'feedback/search',
        params: [res]
      })
    })
  }, [dispatch, validateFields])

对于按照日期的查询需要做特殊处理。

(1)结束时间需要晚于开始时间

因此DataPicker组件中加入disableddata。

<DatePicker
     onChange={change1}
     disabledDate={disabledDate1}
     placeholder="请选择日期"
/>
function disabledDate1(current: any) {
      return current && current > time2
    }
    function disabledDate2(current: any) {
      return current && current < time1
    }
    function change1(date: any, dateString: any) {
      settime1(date)
      settime11(dateString)
    }
    function change2(date: any, dateString: any) {
      settime2(date)
      settime21(dateString)
    }
    const dataValidate = (rule: any, value: any, callback: any) => {
      if (value && parseInt(value) > parseInt(getFieldValue('priceHigh'))) {
        callback('不能高于最高值')
      } else if (
        value &&
        parseInt(value) < parseInt(getFieldValue('priceLow'))
      ) {
        callback('不能低于最低值')
      } else {
        callback()
      }
    }

(2)提交时间格式处理

const [time11, settime11] = useState('')
const [time21, settime21] = useState('')
const [time1, settime1] = useState(moment().format('YYYY-MM-DD'))
const [time2, settime2] = useState(moment().format('YYYY-MM-DD'))

并在查询函数中添加time时间格式处理

const handleSearch = useCallback(() => {
    validateFields((err: any, data: any) => {
      const res = { ...data }
      if (data.username !== undefined) {
        res.username = data.username.replace(/\s*/g, '')
      }
      res.submitTimeBegin = time11
      res.submitTimeEnd = time21
      if (res.submitTimeEnd !== '') {
        res.submitTimeEnd = time21 + ' 23:59:59'
      }
      res.pageNum = 1
      dispatch({
        type: 'feedback/search',
        params: [res]
      })
    })
  }, [dispatch, time11, time21, validateFields])

对于重置函数中也需要添加相应的时间处理

const handleFormReset = useCallback(() => {
    clearPath()
    init()
    form.resetFields()
    // 重置查询时间
    settime11('')
    settime21('')
  }, [form, init])

至此,完成了本文需要的页面,页面效果如下

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用,在安装@ant-design/react-native时,不再需要手动链接(@ant-design/icons-react-native),而是可以使用自动链接功能。只需要使用命令`npm install @ant-design/icons-react-native --save-dev`来安装@ant-design/icons-react-native即可。此外,可以在`node_modules/@ant-design`文件夹下找到安装的文件。 根据引用,可以使用命令`react-native -v`来检查是否成功安装了react-native。 根据引用,示例代码展示了如何全局使用ant-design-mobile组件。官方文档中的示例是按需引入的,如果要全局使用,需要手动引入相应的组件文件。示例代码中引入了Button和InputItem组件,并在App组件中使用了这些组件。 所以,ant-design-mobile是一个基于React Native的UI组件库,可以通过自动链接或手动引入组件文件的方式来使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [react native 0.70版本使用ant-design-mobile-rn及icons字体图标库](https://blog.csdn.net/weixin_43233914/article/details/126849915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [react native 使用ant-design-mobile-rn的icon字体库](https://blog.csdn.net/weixin_43233914/article/details/119450451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白Rachel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值