我们在上一章中为列表组件增加了快速查询的功能,本章我们在列表依附的页面容器(PageContainer)中增加一些内容,在展现页面容器常用能力的同时讨论一下数据管理模块中更贴近用户需求的设计。

PageContainer的详细文档见https://procomponents.ant.design/components/page-container

11.1 关闭面包屑功能

页面路径(Breadcrumb或Breadcrumb Trail/Navigation)又称面包屑导航,作为辅助和补充的导航方式(Secondary Navigation Scheme),它能让用户知道在网站或应用中所处的位置并能方便地回到原先的地点。面包屑这个词来自《糖果屋》这个童话故事——故事中汉赛尔与葛丽特企图依靠洒下的面包屑找到回家的路。

面包屑导航在页面顶部水平出现,它们提供给用户返回之前任何一个页面的链接(这些链接也是能到达当前页面的路径),在层级架构中通常是这个页面的父级页面。页面路径提供给用户回溯到网站首页或入口页面的一条路径,通常是以大于号(>)出现,尽管一些设计是其他的符号(如»)。它们绝大部分看起来就像这样:首页>分类页>次级分类页 或者 首页>>分类页>>次级分类页。

默认的情况下,Ant Desing Pro的页面布局和页面容器会互相配合,在容器的顶端显示面包屑导航。如会员列表页现在是下面这个样子
image.png
对于我们常见的应用系统来说,左侧的子系统和功能模块菜单已经有足够的导航作用,这个面包屑导航不仅用处不大,还占用了宝贵的页面空间,我们可以用如下的方法关闭它。

  1. - <PageContainer>
  2. + <PageContainer
  3. + header={{
  4. + breadcrumb: {},
  5. + }}
  6. + >

在上面的代码中,通过设置breadcrumb为空对象来关闭面包屑导航。接下来我们设置两个不同的标题属性

  1. <PageContainer
  2. + title='低优先级的标题'
  3. header={{
  4. + title: '管理会员信息',
  5. breadcrumb: {},
  6. }}
  7. >

如果没有任何一个title属性,PageContainer会把菜单的名称作为标题。上面的代码显示出两种定义页面标题的方法和他们的优先级:当定义了header.title之后,在title中定义的标题会被替代。

11.2 在页面头部的显示数据

通常情况下,用户进入管理数据的模块,打开某个单位,他首先想看的可能汇总的分析情况而不是数据列表,为此我们在页面的头部实现这个功能。

11.2.1 在容器头部定义分区

在第10章,我们把一个完整的查询表单放在了PageContainer组件的content属性,现在我们放一个栅格容器进去

  1. content={
  2. <>
  3. <Row gutter={16}>
  4. <Col span={1} style={{backgroundColor: '#8a8a8a'}}>靠边</Col>
  5. <Col span={4} style={{backgroundColor: '#a8a8a8'}}>分区1</Col>
  6. <Col span={4} style={{backgroundColor: '#989898'}}>分区2</Col>
  7. <Col span={4} style={{backgroundColor: '#a8a8a8'}}>分区3</Col>
  8. <Col span={4} style={{backgroundColor: '#989898'}}>分区4</Col>
  9. <Col span={24-4*4-1} style={{backgroundColor: '#888888'}}>后面的不要了</Col>
  10. </Row>
  11. </>
  12. }

这个栅格容器的宽度是根据当前浏览器窗口的宽度自适应的,下面是在不同宽度下它的表现
image.png
image.png
更详细的说明请点击查看Ant Desing Grid 24 栅格系统

11.2.2 用Statistic组件显示统计数据

现在我们在栅格里面写下有意义的统计数据。这里使用了Antd Desingn的Statistic组件,实际的项目中你也可以自己设计想要的格式。

  1. content={
  2. <>
  3. <Row gutter={16}>
  4. <Col span={8}><Statistic title="会员总数" value={112893} /></Col>
  5. <Col span={8}><Statistic title="男女比例" value={48.3278} formatter={ (value ) => {
  6. const leftValue = Math.floor((value as number)*10) / 10;
  7. return `${leftValue}% / ${100 - leftValue}%`
  8. }}/></Col>
  9. <Col span={8}><Statistic
  10. title="采集完成率"
  11. value={87.123}
  12. precision={2}
  13. suffix="%"
  14. prefix={<ArrowUpOutlined/>}
  15. valueStyle={{ color: '#3f8600' }}
  16. />
  17. </Col>
  18. </Row>
  19. <Descriptions style={{ marginTop: 8, marginBottom: -16 }}>
  20. <Descriptions.Item label="当前单位">朗思云网科技股份有限公司工会委员会</Descriptions.Item>
  21. <Descriptions.Item label="地址">北京市朝阳区北四环东路世奥国际中心A1015</Descriptions.Item>
  22. </Descriptions>
  23. </>
  24. }

下面是页面看到的工资
image.png
如果想给这个组件加上边框,可以直接定义它的风格,也可以用Card组件包一下

  1. <Col span={8}><Card><Statistic title="会员总数" value={112893} /></Card></Col>

image.png

11.2.3 页面加载时自动读取数据

现在我们让这些统计数据“动”起来。首先是在页面加载的时候自动读数据。

首先是在Mock中模拟统计数据(这里我们做了简化,只是产生随机数据,不考虑不同请求之间应该递增的逻辑):

  1. function getStatisticData(req: Request, res: Response) {
  2. return res.json({
  3. success: true,
  4. data: {
  5. total: Math.floor(Math.random()*1000000),
  6. male: Math.floor(Math.random()*1000)/10,
  7. finished: Math.floor(Math.random()*1000)/10,
  8. company: '朗思云网科技股份有限公司工会委员会',
  9. address: '北京市朝阳区北四环东路世奥国际中心A座',
  10. },
  11. })
  12. }
  13. export default {
  14. 'GET /api/member/statistic': getStatisticData,

引用request

  1. import { request } from 'umi';

先定义三个React Hookk(我们假设到这里你已经能够熟练使用React Hook,否则的话需要从头去学习一下)

  1. const [headerState, toggleHeaderState] = useState(false)
  2. const [statisticData, setStatisticData] = useState({
  3. total: 0,
  4. male: 50.0,
  5. finished: 0,
  6. company: '',
  7. address: '',
  8. })
  9. useEffect( ()=> {
  10. //这里偷懒简化了定义,没有使用service角色的函数,
  11. //直接把网络请求的代码放到了这里
  12. //因为后端的这个服务只在这里使用,这么做也不算违规
  13. //另外请注意这里的关于异步的操作的写法
  14. const fetchData = async () => {
  15. try {
  16. const result = await request('/api/member/statistic', {
  17. method: 'GET',
  18. })
  19. setStatisticData(result.data);
  20. } catch (e) {
  21. console.log(e)
  22. }
  23. }
  24. fetchData()
  25. },[headerState])

然后把数据展示部分改成和读取的数据关联

  1. content={
  2. <>
  3. <Row gutter={16}>
  4. <Col span={8}><Statistic title="会员总数" value={statisticData?.total} /></Col>
  5. <Col span={8}><Statistic title="男女比例" value={statisticData?.male} formatter={ (value ) => {
  6. const leftValue = Math.floor((value as number)*10) / 10;
  7. return `${leftValue}% / ${Math.floor((100 - leftValue)*10)/10}%`
  8. }}/></Col>
  9. <Col span={8}><Statistic
  10. title="采集完成率"
  11. value={statisticData?.finished}
  12. precision={2}
  13. suffix="%"
  14. prefix={<ArrowUpOutlined/>}
  15. valueStyle={{ color: '#3f8600' }}
  16. />
  17. </Col>
  18. </Row>
  19. <Descriptions style={{ marginTop: 8, marginBottom: -16 }}>
  20. <Descriptions.Item label="当前单位">{statisticData?.company}</Descriptions.Item>
  21. <Descriptions.Item label="地址">{statisticData?.address}</Descriptions.Item>
  22. </Descriptions>
  23. </>
  24. }

现在每次刷新页面都会去服务器请求新的统计数据。

11.2.4 设计一个手动刷新链接

依赖手动刷新整个页面来更新数据,这不是一个友好的数据,现在我们设计一个手动刷新链接,点击它的时候只会做局部的刷新。具体方法是给ProTable增加一个属性

  1. extra={[
  2. <a
  3. key="reload-header"
  4. title="刷新统计数据"
  5. onClick={() => toggleHeaderState(!headerState)}
  6. ><ReloadOutlined /></a>
  7. ]}

现在点击页面头部右上角的图标就可以获取新的统计数据。这里估计你也明白了我们为什么要在上面的代码定义headerState这个Hook了。

image.png

11.3 页面容器的其它能力

本节我们仅讨论页面容易的其它能力,不做具体的设计实现。

11.3.1 更多的按钮和菜单

在我们设计的手动刷新链接前面通过下面的代码放置几个按钮和下拉菜单

  1. extra={[
  2. <Button key="3" onClick={()=>message.info('你点击了操作1',1)}>操作</Button>,
  3. <Button key="2" onClick={()=>message.info('你点击了操作2',1)}>操作</Button>,
  4. <Button key="1" onClick={()=>message.info('你点击了操作3',1)}type="primary">
  5. 主操作
  6. </Button>,
  7. <Dropdown
  8. key="dropdown"
  9. trigger={['click']}
  10. overlay={
  11. <Menu>
  12. <Menu.Item key="1" onClick={()=>message.info('你点击了第1项',1)}>下拉菜单</Menu.Item>
  13. <Menu.Item key="2" onClick={()=>message.info('你点击了第2项',1)}>下拉菜单2</Menu.Item>
  14. <Menu.Item key="3" onClick={()=>message.info('你点击了第3项',1)}>下拉菜单3</Menu.Item>
  15. </Menu>
  16. }
  17. >
  18. <Button key="4" style={{ padding: '0 8px' }}>
  19. <EllipsisOutlined />
  20. </Button>
  21. </Dropdown>,
  22. <a
  23. key="reload-header"
  24. title="刷新统计数据"
  25. onClick={() => toggleHeaderState(!headerState)}
  26. ><ReloadOutlined /></a>
  27. ]}

现在页面变成了如下的模样
image.png

11.3.2 头部分页卡片

给ProTable增加下面的属性

  1. tabList={[
  2. {
  3. tab: '基本信息',
  4. key: 'base',
  5. },
  6. {
  7. tab: '详细信息',
  8. key: 'info',
  9. },
  10. ]}
  11. onTabChange={(activeKey) =>message.info(`你切换到了${activeKey}`)}

可以得到一组好看的分页卡片,在功能设计需要的时候可以使用
image.png

版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。