图书订阅管理系统——用户设计与整个项目总结
1.用户设计
用户设计主要是三个大方面、用户主页、用户订阅、用户个人信息(这个和管理员设计成一个了后面统一说)
2.图片1
- 用户主页、轮播图——书籍推荐,这里和管理员用户类似,管理员设置,并在数据库存档,用户这里刷新就会出现,第一次点击刷新后数据会进行缓存,以后就会采用缓存数据,组件逻辑设计如下:
- 建立状态用于存储从数据库获取的轮播图数据,因此,在设计组件的结构时,使用的是:如果状态不为空使用状态渲染;否则若缓存不为空,使用缓存渲染;否则显示暂无数据;
- 考虑到页面第一次加载会在还没有进行操作时进行渲染,因此要对本地缓存进行判空,如果为null,赋值【】,否则它是没有map属性的,数据数据渲染常用的就是map。看代码:
import React, { useState } from 'react' import { Carousel } from 'antd'; import '../../../../assets/iconfont.css' import './style.less' import HomeBooks from '../../../AdminLayout/AdminHome/Adminbody/HomeBooks' import { reqgetcrollChartData } from '../../../../api/index-ajax'; export default function UserHomeBody() { const [crollChart,setcrollChart] = useState([]); let current = localStorage.getItem('crollChart'); if(current===null) current=[]; else current = JSON.parse(localStorage.getItem('crollChart')); const contentStyle = { height: '160px', width: '300px', color: '#fff', margin: '0 auto', lineHeight: '160px', textAlign: 'center', background: '#364d79', }; const getcrollChartData = async()=>{ const response = await reqgetcrollChartData({}); console.log(response) setcrollChart(response.data.data); localStorage.setItem('crollChart',JSON.stringify(response.data.data)) } return ( <div> <div className="crollChartWrapper"> <div className="crollBooks"> <Carousel autoplay> { crollChart.length !== 0 ? crollChart.map((imgObj) => { return ( <div key={imgObj.id}> <img style={contentStyle} src={imgObj.bookImg} alt="加载失败或未设置轮播图书籍" /> </div> ) }) :current.length!==0 ? current.map((imgObj) => { return ( <div key={imgObj.id}> <img style={contentStyle} src={imgObj.bookImg} alt="加载失败或未设置轮播图书籍" /> </div> ) }) : <div>暂无数据</div> } </Carousel> </div> <div className='getcrollChartData'> <span onClick={()=>getcrollChartData()} className='iconfont icon-shuaxin'></span> </div> </div> <div className="bookList"> <HomeBooks state={current} /> </div> </div> ) }
3.图片2、3
书籍小车表示订阅的申请列表,2是列表,3是申请所需要的信息,在图2点击【去订阅】,跳转到图3,搜索书籍,点击进行选择,选择订阅开始时间和结束时间,点击按钮,就会产生订阅申请,点击【书籍小车】返回到订阅申请列表页,这里并非是在本地缓存中进行申请记录保存,而是只在数据库,点击刷新,就可以将数据进行渲染,用户也同时了解订阅的进度(三个状态:【申请中】【同意申请】【拒绝申请】)。本地缓存在当下一次页面跳转时,antd组件的选择功能失效了,因此去掉了。这个页面也添加了取消订阅用来让用户进行删除订阅申请。(突然想起管理员要同意申请的话,在现实里应该是取了书才进行数据库数量-1吧,这只是产生一个凭证)。
import React, { useState } from 'react' import IconToFirstPage from '../../../components/IconToFirstPage' import './style.less' import { Checkbox, Col, Row, Button } from 'antd'; import { NavLink } from 'react-router-dom'; import { reqDeleteSub, reqGetSubApplyList } from '../../../api/index-ajax'; export default function SubBook() { // 创建状态 const [subList, setsubList] = useState([]); const [deleteSub,setDeleteSub] = useState(); // let current = localStorage.getItem("subList"); // console.log(); // if(current===null) current=[]; // else current = JSON.parse(current) const onChange = (checkedValues) => { console.log('checked = ', checkedValues); setDeleteSub(checkedValues); // console.log(selectBooks); }; const toShift = async()=>{ try { const response = await reqGetSubApplyList({applyPerson:JSON.parse(localStorage.getItem('userInfo')).username}); console.log(response); if(response.data.status===1) { setsubList(response.data.data); alert(response.data.msg) // localStorage.setItem('subList',JSON.stringify(response.data.data)) }else{ alert(response.data.msg) setsubList(response.data.data); // localStorage.setItem('subList',JSON.stringify(response.data.data)) } } catch (error) { console.log("请求失败",error) } } const todelSub = async()=>{ try{ const response = await reqDeleteSub({values:deleteSub}); alert(response.data.msg); }catch(error){ console.log(error) } } return ( <div className='subBookWrapper'> <div className="subBookheader"> <IconToFirstPage /> <div className='subBook-title'><span>书籍订阅列表</span></div> <NavLink to='/user/searchResult'><div className="lastPage"><span>去订阅</span></div></NavLink> </div> <div className="subListWrapper"> <div className='subListContainer'> <div className="subListBodyTitle"> <div className="applyPerson"><span>申请人</span></div> <div className="applyBook"><span>书籍</span></div> <div className="applyBookTime"><span>申请时间</span></div> <div className="subStartTime"><span>订阅开始</span></div> <div className="subEndTime"><span>订阅结束</span></div> <div className="subStatus"><span>状态</span></div> </div> <div className="booksCheckbox"> <Checkbox.Group style={{ width: '100%', }} onChange={onChange} > { subList.length !== 0 ? subList.map((bookObj) => { return ( <Row className='row' key={bookObj._id}> <Col span={50}> <Checkbox value={bookObj}> <div className="applyListData"> <div className="applyPerson"><span>{bookObj.applyPerson}</span></div> <div className="applyBook"><span>{bookObj.applyBook}</span></div> <div className="applyBookTime"><span>{bookObj.applyBookTime}</span></div> <div className="subStartTime"><span>{bookObj.subStartTime}</span></div> <div className="subEndTime"><span>{bookObj.subEndTime}</span></div> <div className="subStatus"><span>{bookObj.subStatus}</span></div> </div> </Checkbox> </Col> </Row> ) }) // : // current.length !== 0 ? current.map((bookObj) => { // return ( // <Row className='row' key={bookObj._id}> // <Col span={50}> // <Checkbox value={bookObj}> // <div className="applyListData"> // <div className="applyPerson"><span>{bookObj.applyPerson}</span></div> // <div className="applyBook"><span>{bookObj.applyBook}</span></div> // <div className="applyBookTime"><span>{bookObj.applyBookTime}</span></div> // <div className="subStartTime"><span>{bookObj.subStartTime}</span></div> // <div className="subEndTime"><span>{bookObj.subEndTime}</span></div> // <div className="subStatus"><span>{bookObj.subStatus}</span></div> // </div> // </Checkbox> // </Col> // </Row> // ) // }) : <div>暂无书籍可供选择,请搜索书籍</div> } </Checkbox.Group> </div> </div> </div> <div className="subListFooter"> <Button type="primary" htmlType="submit" onClick={() => toShift()}>刷新数据</Button> <Button type="primary" htmlType="submit" onClick={() => todelSub()}>取消订阅</Button> </div> </div> ) }
最后的时个人信息管理:antd组件库里的form表单进行组合制造了一个用户个人信息表,根据提示,若是修改,就点击上面的选择点,进行信息填写,然后提交,重新登陆即可,
import React, { useState } from 'react' import IconToFirstPage from '../../../components/IconToFirstPage' import './style.less' import { Form, Input, Button, Checkbox, } from 'antd'; import { useNavigate } from 'react-router-dom'; import { reqChangSelfInfo, reqDeleteSelf } from '../../../api/index-ajax'; export default function SelfInfo() { let current={} if(localStorage.getItem("userInfo")!==undefined) current = JSON.parse(localStorage.getItem("userInfo")); console.log(current) const navigate = useNavigate(); const toDeleteSelf = async()=>{ try { const response = await reqDeleteSelf({username:current.username,identify:current.identify}); if(response.data.status===1) navigate('/register'); } catch (error) { console.log("请求失败",error) } } const toExit = ()=>{ navigate('/login'); } const FormDisabledDemo = () => { const [componentDisabled, setComponentDisabled] = useState(true); const onFormLayoutChange = ({ disabled }) => { setComponentDisabled(disabled); }; const onFinish = async(values) => { values.identify = current.identify; values.preUsername = current.username; console.log('Success:', values); try { const response =await reqChangSelfInfo({values:values}); console.log(response); if(response.data.status===1) navigate('/login') } catch (error) { console.log("请求出错",error) } }; const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; return ( <> <Checkbox checked={componentDisabled} onChange={(e) => setComponentDisabled(e.target.checked)} > 点击可进行修改个人信息,修改完点击提交 </Checkbox> <Form name="basic" labelCol={{ span: 8, }} wrapperCol={{ span: 16, }} initialValues={{ remember: true, }} layout="horizontal" onValuesChange={onFormLayoutChange} disabled={componentDisabled} onFinish={onFinish} onFinishFailed={onFinishFailed} autoComplete="off" > <Form.Item label="姓名" name="username" > <Input placeholder={current.username}/> </Form.Item> <Form.Item label="密码" name="password" > <Input.Password placeholder={current.password}/> </Form.Item> <Form.Item label="学工号" name="jobnum" > <Input.Password placeholder={current.jobnum}/> </Form.Item> <Form.Item label="身份" name="identify" > <Input disabled placeholder={current.identify}/> </Form.Item> <Form.Item label="联系方式" name="phone" > <Input.Password placeholder={current.phone}/> </Form.Item> <Form.Item label="电子邮件" name="email" > <Input.Password placeholder={current.email}/> </Form.Item> <Form.Item wrapperCol={{ offset: 0, span: 16, }} > <Button type="primary" htmlType="submit"> Submit </Button> </Form.Item> </Form> </> ); }; return ( <div> <div className="selfInfo-head"> <IconToFirstPage /> <div className='selfInfo-title'><span>个人信息管理</span></div> </div> <div className="selfInfo-body"> <FormDisabledDemo /> </div> <div className="selfInfo-footer"> <Button type="primary" htmlType="submit" onClick={()=>toDeleteSelf()}> 注销登录 </Button> <Button type="primary" htmlType="submit" onClick={()=>toExit()}> 退出登录 </Button> </div> </div> ) }
总体项目总结
图书订阅管理系统用时17号到今天29号,共计用时12天时间,从需求分析功能设计开始,我经历了如下几个阶段:
- 首先是后台服务器的搭建以及连接数据库的时候出现了很多问题,我都在之前做了总结,提出数据库单独放出来,解决使用时还没创建好的问题
- 其次是路由层级与页面组件的继承问题,这个问题不解决,我就只能写个登陆注册了,刚开始不懂这个过程,后来搜集资料、看视频,从别人的言语缝隙中搜索自己想要的信息,那就是页面的样式继承直接受路由层级控制。
- 最后就是页面信息传递、PubSub订阅发布、useEffect监听,都一度让我头疼,目前我知道的是,发布订阅需要是同一页面内的组件,一个组件订阅定一个消息时,他自己对于发布消息的组件来说不能刷新,一旦刷新就拿不到数据(在去除副作用的时候),另外,不是到为什么useEffect的第二参数,在监听的时候所谓的刷新到底指的什么,因为我在实际做的时候,不管第二参数是啥,他都在执行。
- 这个项目的目前只是针对375*667的尺寸做的,没有适配所有尺寸的屏,使用者可以通过谷歌的后台开发工具(F12)选择手机iphone6、7、8,进一步修改别的样式,就样子来说,表格做的有亿点丑,我做了两遍功能测试,一切正常。
- 最后,我将自己实践useEffect以及redux,项目就到这里了,代码我找时间上传到github,已上传https://github.com/qqqq123456qqqq/reactBookSubSys
- 欢迎交流