图书订阅管理系统——用户设计与整个项目总结
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
- 欢迎交流

浙公网安备 33010602011771号