本项目使用 React + TypeScript + antd + axios + JSON Server 开发的简单 CRUD Web 应用。麻雀虽小,五脏俱全。感兴趣的话,可以访问该项目的 Github查看。
这是最终的效果图:
应用特性
本开源项目虽小但也有不少特性。
- 支持 CRUD
没有像 Shop 系列项目 使用后台 API,取而代之的是 JSON Server。JSON Server 是以一个在本地运行,可以存储 json 数据的 server,最关键的是,它能支持 CRUD 操作,打破了 Node.js + Express + MongoDB 这些后台技术壁垒,与后台协商好数据结构后,前端就可以随性所欲的模拟服务端接口数据,放飞自己了?。
CRUD 代码集中在 App.tsx
中。比如:
fetchData(params) {
this.loading = true
getReceiptList(params).then((data) => {
this.loading = false
this.tableData = data.list
this.total = data.total
})
.catch(() => {
this.loading = false
})
},
const toAdd = () => {
setAdding(true)
setDialogVisible(true)
// reset form
const value = { ...form }
Object.keys(value).forEach((item) => {
const key = item as FormKey
value[key] = undefined
})
setForm(value)
}
const toEdit = (row: Receipt) => {
setAdding(false)
setDialogVisible(true)
setForm(row)
}
const toDelete = (id: number) => {
confirm({
title: '确定删除 ?',
icon: <ExclamationCircleOutlined />,
onOk() {
return delReceipt(id).then(() => {
...
}).catch(err => {
console.log(err)
})
},
onCancel() {
console.log('canceled')
}
})
}
const doAdd = (data: Receipt) => {
setSaveLoading(true)
addReceipt(data).then(() => {
setSaveLoading(false)
refresh()
}).catch(() => {
setSaveLoading(false)
})
}
const doEdit = (data: Receipt) => {
setSaveLoading(true)
editReceipt(data).then(() => {
setSaveLoading(false)
// not call fetchData
setTableData(
tableData.map((item) =>
item.id === data.id ? { ...item, ...data } : item
)
)
}).catch(() => {
console.log('update falied')
setSaveLoading(false)
})
}
与 Vue.js 版相比代码会多一些,毕竟 React 的状态对象要求不可变,Vue.js 中是可以直接修改的。
对于中台系统来说,很多表单模块都有这种类似的 CRUD,收货表单和发货表单完全可以复用这部分逻辑。对于 React 应用,可以使用自定义 Hook 来复用。
- 支持基本的搜索、分页、排序
可以按搜索姓名、手机号、收货日期、区域来搜索发货。分页、排序这些基本上都是 antd 自带组件的功能,开箱即用。
antd 的Table
表格组件可以设置dataSource
数据源,完全可以使用 JSON 数据动态渲染表格,前台没必要定义 Column 了,这点比 Element 表格组件灵活。
- 中国市区级联、自定义组件 / 数据访问 / 表单验证
考虑到代码复用,我们对一些 antd 组件进行了二次封装。比如 BaseInput.tsx
、BaseSelect.tsx
、BaseDatePicker.tsx
。大家都知道,React 组件时严格遵守单向数据流的,因此这些自定义组件没办法想 Vue.js 组件一样可以双向绑定。
antd 的 Pagination
分页组件可以独立使用也可以与表格组件集成使用,这里简单处理就没有自定义分页组件了。
不像 Element,antd 的 Cascader
级联选择组件没有 getCheckedNodes
方法获取选中的节点,但我们可以“曲线救国”。一般说来,区号数据应该有三个:省级区号、市级区号、区级区号,我们这里只保存了区号 area,因为其他级别的区号是可以推算出来的。比如深圳市南山区的区号为 440305,将区号的最后两位都改为 0
,那么 440300
就是深圳市的区号;将区号的最后四位都改为 `0,那么
440000` 就是广东省的区号。
_value = [
Math.floor(value / 10000) * 10000,
Math.floor(value / 100) * 100,
value
]
经过处理一个区级区号就变脸成区号数组,可以赋值给级联选择组件的 value
参数了。
省市区数据保存在 area.js
中,其实时一个数组结构的数据模块,查询时的选择组件和表单操作时的级联选择组件都会用到它。根据区号读取相应的名称,只要遍历数组就行,不用来回查询数据库。如下代码所示:
export function getAreaNameByCode(list, value) {
if(Array.isArray(list)) {
for (const item of list) {
if (item.value === value) {
return item.label
} else if (item.children?.length > 0) {
return getAreaNameByCode(item.children, value)
}
}
}
return ''
}
- 响应式布局
适当使用 CSS3 Media Query,响应式布局能适配桌面和多数移动设备。
@media screen and (max-width: 767px) {
.el-dialog {
width: 92% !important;
}
}
- 单元测试
出于学习 testing library 初衷,本项目的单元测试写的比较系统。网上的 React 教程多数时使用 test-utils 或 Enzyme 来测试组件,库依赖性太强,没法在 Vue 项目复用。感兴趣的同学可以访问 Testing Library 学习指南 了解更多。
运行项目
- 安装依赖
npm install
- 编译前运行 json serve
npm run mock
- 编译和热重载用于开发
npm run serve
- 单元测试
npm run test:unit
评论 (0)