回顾
上一节已经贴出了项目列表页面的全部前端代码,由于笔者前端也没有深入学习,所以只能给大家大概讲解一下吧。还是按照之前的几个部分来说吧!
状态管理
image
设置了如下变量:
- data
存放当前项目数组 - pagination
项目分页 - visible
创建项目表单是否可见,默认是否 - users
由于我们需要选择项目管理员,所以需要一个userId => 用户信息的映射。当然这个接口我们暂时还没有实现。
方法编写
const fetchData = async (current=pagination.current, size=pagination.size) => { await process(async ()=> { const res = await listProject({page: current, size }); if (auth.response(res)) { setData(res.data) setPagination({...pagination, total: res.total}) } }); }
这个方法是获取项目列表的方法,current代表page,size代表分页大小,如果不传入则是pagination默认的值。
listProject其实就是包装的request方法,请求后端服务。
useEffect(async () => { await fetchData(); }, []) const onSearchProject = async projectName => { await process(async ()=> { const res = await listProject({page: 1, size: pagination.size, name: projectName}); if (auth.response(res)) { setData(res.data) setPagination({...pagination, current: 1, total: res.total}) } }); }
useEffect是react
新版本的特性,它支持2个参数,第一个是方法,第二个是变量数组,传入空数组的话,则每次这个组件开始渲染
的时候会调用且只调用一次方法。
我们如果正式使用的话,是会有多套测试环境的。如果我们对项目做了环境
的区分,那么就应该在切换环境的时候,获取不同环境的项目。
假设我们需要实现这种功能,那么我们先创建一个env
的变量,然后改写useEffect:
useEffect(async () => { await fetchData(); }, [env])
这样的话,每当env发生变化,这个方法就会自动执行一次。需要说明的是,这里只是介绍一下useEffect的使用方式,因为我们这边暂时还没有扩展到多套环境,所以此处我们选择[]即可
。
const onHandleCreate = async values => { const res = await insertProject(values); if (auth.response(res, true)) { // 创建成功后自动获取第一页的数据, 因为项目会按创建时间排序 await fetchData(1); } }
这边也如出一辙,创建完毕了之后如果接口未返回错误,则刷新项目列表页面,并且自动去第一页
。
const content = (item) => { return <div> {/* <p>负责人: {userMap[item.owner].name}</p> */} <p>简介: {item.description || '无'}</p> <p>更新时间: {item.updated_at}</p> </div> }; const opt = <Select placeholder="请选择项目负责人"> { Object.keys(users).map(id => <Option key={id} value={id}>{users[id].name}</Option>) } </Select> const fields = [ { name: 'projectName', label: '项目名称', required: true, message: "请输入项目名称", type: 'input', placeholder: "请输入项目名称", }, { name: 'owner', label: '项目负责人', required: true, component: opt, type: 'select', }, { name: 'description', label: '项目描述', required: false, message: "请输入项目描述", type: 'textarea', placeholder: "请输入项目描述", }, { name: 'private', label: '是否私有', required: true, message: "请选择项目是否私有", type: 'switch', valuePropName: "checked", }, ]
这里content是一个方法,会返回一个div的html结构,目的是为了展示项目的详情,比如负责人,项目描述等。
fields的话,是表单的字段,因为我针对antd的form进行了一点封装,编写了一套高阶组件。等于说是规划好了表单里面的表单组成,由input和select以及switch组件组成。
待会会讲这个组件!
最后看return里面的组件
return ( <PageContainer title={false}> <FormForModal width={600} title="添加项目" left={6} right={18} record={{}} visible={visible} onCancel={() => setVisible(false)} fields={fields} onFinish={onHandleCreate} /> <Row gutter={8} style={{marginBottom: 16}}> <Col span={18}> <Button type="primary" onClick={() => setVisible(true)}>创建项目 <Tooltip title="只有超级管理员可以创建项目"><QuestionCircleOutlined/></Tooltip> </Button> </Col> <Col span={6}> <Search onSearch={onSearchProject} style={{float: 'right'}} placeholder="请输入项目名称"/> </Col> </Row> <Spin spinning={false}> <Row gutter={16}> { data.length === 0 ? <Col span={24} style={{textAlign: 'center', marginBottom: 12}}> <Card><Empty description="暂无项目, 快点击『创建项目』创建一个吧!"/></Card> </Col> : data.map(item => <Col key={item.id} span={4} style={{marginBottom: 12}}> <Popover content={content(item)} placement="rightTop"> <Card hoverable bordered={false} style={{borderRadius: 16, textAlign: 'center'}} bodyStyle={{padding: 16}} onClick={() => { history.push(`/project/${item.id}`); }}> <Avatar style={{backgroundColor: '#87d068'}} size={64} >{item.name.slice(0, 2)}</Avatar> <p style={{ textAlign: 'center', fontWeight: 'bold', fontSize: 18, marginTop: 8 }}>{item.name}</p> </Card> </Popover> </Col> ) } </Row> </Spin> </PageContainer> ) }
PageContainer
是最外层,也就是咱们看到的这一块:
image
FormForModal
是一个对话框表单,默认是不显示的,只有点击创建
项目
才会显示。
然后用Row分了2行: 分别是 创建栏/搜索栏和项目展示栏
image
这边项目展示栏如果data.length === 0
就展示一个空状态,提示用户去添加项目,否则就把项目展示出来,每个项目占屏幕的1/6,因为Col总共有24份。
tips: 这里类似于bootstrap,一行共有24份,所以span={4}代表的是4/24,可参考: 官网介绍
image
其他的就是Card
(卡片)组件和Avatar
(头像)组件的互相嵌入,Popover
是悬浮窗口,如图:
image
编写获取用户列表的接口
修改app/dao/auth/UserDao.py
image
筛选出未被删除的用户即可。
修改app/controllers/auth/user.py
image
这里注意开启一下权限,但是不能控制太死,能让登录用户访问即可。
前端页面编写listUsers方法
image
看下效果
image
发现一个问题: 创建成功后,对话框没有关闭
所以我们需要在创建成果后,setVisible(false)
去关闭对话框。
image
还有一个地方就是之前注释掉的项目负责人,现在可以重新开启了
。
image
image
看下搜索效果
image
image
这就是本节的内容了,大部分是讲解代码为主,因为前端内容居多,所以可能有点懵逼。如果有不理解的地方,还请多多看看react
和es6
相关的教程。箭头函数,看着别怕,熟悉了就好。
下一节可能是具体项目的编辑页面了,感觉这一讲就讲了一个世纪,再后面就是用例那块了。