这个世界轻而易举、一蹴而就的事情很少很少。要说真正的有所成,只能是慢慢来。
上节回顾:【React入门实践】结合Ant-Design从0带你手把手开发【个人中心-信息修改】页面_Sabrina_cc的博客-CSDN博客
写在前面的话: 上节我们实现了简单的是form表单,如下,今天我们来学习最常见的表单组件的使用。一步步向更复杂的页面进发。
本次页面核心知识点:(1)【search表单模块】搜索search、重置功能reset、日期选择器DatePicker、下拉框使用select;(2)【列表展示模块】时间规范化moment、tag标签的使用、省略款的Tooltip使用;
目录
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])
至此,完成了本文需要的页面,页面效果如下