项目名称:仿金山表单 默认运行端口号:5000 代码编写环境:TS + React18 + ReactRouter 6 + antd
# 下载依赖
$ npm i
# 运行
$ npm start
App 组件:
- 功能:存放路由表,将用户名、头像等全局信息在当前组件初始化。使用Spin将App组件包裹,实现loading状态的显示与隐藏
- 设计思路:存放路由表的初始位置
Login 组件:
- 功能:获取用户输入的用户名和密码,当用户登录成功后,跳转至首页表单列表页面,并更新全局管理的用户名和头像信息
- 设计思路:使用antd的Form组件进行表单验证
Register 组件:
- 功能:获取用户填写的用户名和密码,注册成功后跳转到登录页
- 设计思路:使用Form组件进行校验,将后端返回的信息进行显示,可能用户名已存在
User 组件:
- 功能:此页面可以进行修改用户名密码操作,使用antd的upload组件实现上传修改图像功能
- 设计思路:用户登录后将用户名,头像等存入localstorage中,更改了信息后对localstorage中的值进行修改
Home 组件:
- 功能:该组件是后续功能页面的父组件,包含公用的头部区,对全局管理的表单信息进行初始化
- 设计思路:将头部组件写在Home组件中进行复用,使用Outlet对其子路由进行嵌套
FormList 组件:
- 功能:对创建、收集的表单的展示,每个表单都可以进行标星、删除、查看等功能,根据表单的状态不同,点击表单跳转的页面也不一样。该组件还可以进行新建表单功能,跳转到新建页面
- 设计思路:使用antd的Table组件,配合分页器实现按需加载
Create 组件:
- 功能:新建表单页面,能根据题型动态创建表单结构,每个题型是一个静态组件。使用一个全局变量管理当前题目是否激活,点击当前题目时,就将该变量设为当前题目的下标,同时给App组件绑定点击事件,点击App时将该变量赋为-1,此时所有题目都未被激活。点击预览跳转到预览页面,此时表单还未创建,点击保存为草稿时,表单状态为草稿,不能填写,只能继续编辑。
- 设计思路:每个题型对应一个组件,当用户点击题型时,根据type值来判断用哪一个题型的组件,使用全局变量管理哪个题目处于激活状态,当创建表单或者预览表单前判断选择题选项是否填写完整,标题是否填写完整
FooterActions 组件:
- 功能:每个题目底部共有的操作栏。包含切换题型、赋值、删除、必填和设为常用题等
- 设计思路:每个题目底部都有操作栏,于是将他单独抽成一个组件进行复用,调用自定义hooks里的逻辑对表单进行修改
FormAction 组件:
- 功能:当表单创建完成后,该页面就可以显示表单的收集状况(Data),表单问题查看(Form),分享(Share)等功能,使用NavLink可以在这三个页面间来回跳转
- 设计思路:该组件作为表单创建完成后的显示页面,包含着三个页面,于是在该页面使用NavLink进行跳转到对应的子路由页面
Data 组件:
- 功能:展示表单填写结果,可以切换展示收集到的表单的不同数据,当表单还未收集到数据时展示空的图片和邀请填写链接,包含数据统计和单份详情两个功能页
- 设计思路:将通过表单id得到的结果汇总成表格或饼图、柱状图便于分析,或者单独展示每份提交数据
Form 组件:
- 功能:复用Review组件展示表单效果,底部有填写表单链接,可以跳转至填写表单页面
- 设计思路:这个页面跟预览页面很相似,于是复用了Review页面,当表单已经截止时,使用遮罩层将其遮盖
Share 组件:
- 功能:使用QRCode创建表单的二维码,可以复制链接或生成二维码分享给他人填写表单
InputFrom 组件:
- 功能:表单填写页面,主题结构复用Review组件,对用户输入的数据进行校验,对必填项进行检查,对输入字数进行限制,提交成功后跳转到Success页面
- 设计思路:该页面任何人都已访问,也符合用户填写表单时不需要登录的情况,当用户没有登录时访问该页面,头部不显示用户头像和用户名
Review 组件:
- 功能:在组件预览,查看表单问题时进行复用,展示创建的表单结构和处理表单提交时的逻辑
Success 组件:
- 功能:表单填写成功页面,显示成功状态和提交时间,用户可以选择再填一份
在路由较为复杂时,可以使用路由表统一管理
const routes = [
{
// 登录
path:'/login',
element:<Login/>
},
{
// 注册
path:'/register',
element:<Register/>
},
{
// 功能页主体
path:'/home',
element:<Home/>,
children:[
{
// 表单列表
path:'formList',
element:<FormList/>
},
{
// 创建表单
path:'createForm',
element:<Create/>
},
{
// 表单详情
path:'formAction',
element:<FormAction/>,
children:[
{
// 表单填写详情
path:'data',
element:<Data/>
},
{
// 表单问题查看
path:'form',
element:<Form/>
},
{
// 分享
path:'share',
element:<Share/>
}
]
},
{
// 提交成功
path:'success/:id',
element:<Success/>
},
{
// 表单填写
path:'inputForm/:id',
element:<InputForm/>
},
{
// 预览
path:'preview',
element:<Preview/>
},
{
// 重定向至表单列表页面
path:'',
element:<Navigate to='formList'></Navigate>
}
]
},
{
// 个人中心
path:'/user',
element:<User/>
},
{
// 重定向至登录页
path:'/',
element:<Navigate to='/login'></Navigate>
}
]
路由表
- 设计思路:考虑到项目较为复杂,页面切换较多,于是采用路由表统一管理。
页面加载时开启Loading状态
- 设计思路:当请求时间较长时,应使用loading状态。于是将Spin组件卸载App 组件内,在封装fetch的函数内更改Spin组件的状态,当发起请求时将Spin组件显示,当请求结束时Spin组件隐藏。
创建表单时点击题目实现 “激活状态”
- 设计思路:使用一个全局变量控制创建表单时哪一个题目处于激活状态,当用户点击除题目以外的App组件的空间时,将该变量设为-1,此时,没有题目的下标与之对应。所有题目都处于失活状态。当点击题目时,将这个全局管理的状态赋为题目的下标,此时题目处于激活状态。需要注意的是,当给App组件绑定点击事件时,他的子组件里的点击事件需要阻止冒泡,防止触发App组件的点击事件。
Fetch请求拦截
- 设计思路:在封装fetch的函数里得到返回值时,先判断后端是否返回了未登录的报错,如果是,则将页面跳转至登录页进行登录。如果未报未登录的错则继续执行
Echarts数据统计可视化
- 设计思路:当一份表单收集到很多份答卷时,将数据汇总进行统计是很有必要的。于是,在查看提交详情页面有两个选项,一个是所有数据的汇总显示,一个是单独每份的提交详情。使用了Echarts里的饼图和柱状图,将其单独封装成一个组件,使用时只需要往里面传入配置项即可
- 在给App组件添加点击事件时,若其子组件也有点击事件,此时则会触发冒泡,每次点击子组件的点击事件也会触发App组件的点击事件
- 解决方法:e.stopPropagation()阻止冒泡
- 在使用css module时如何修改antd深层次结构的样式
- 解决方法:使用 :global 包裹需要修改的antd组件的类名
- context管理的数据刷新之后就重置了
- 解决方法:将数据存入localstoreage中或者在组件每次挂载时重新请求数据
- 在进行项目初始构建时,需要搞清楚页面结构的层级关系,哪些地方是可以复用的。
- 当数据结构较为复杂时,需要将其拆分成多个子项,每个子项对应一个接口类型,然后组合成一个大的接口类型,而不是将所有的数据都包裹在一个对象里只定义一个接口类型,这样不利于后面的类型的复用和拓展。
- 不应过度使用路由,使用antd的tabs的标签页也可以实现局部页面的切换并且不需要创建新的组件和传递参数。