React CRUD 项目

Flying
2022-08-24 / 0 评论 / 141 阅读 / 正在检测是否收录...

本项目使用 React + TypeScript + antd + axios + JSON Server 开发的简单 CRUD Web 应用。麻雀虽小,五脏俱全。感兴趣的话,可以访问该项目的 Github查看。

这是最终的效果图:

vue-crud.jpg

应用特性

本开源项目虽小但也有不少特性。

  1. 支持 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 来复用。

  1. 支持基本的搜索、分页、排序

可以按搜索姓名、手机号、收货日期、区域来搜索发货。分页、排序这些基本上都是 antd 自带组件的功能,开箱即用。

antd 的 Table 表格组件可以设置 dataSource 数据源,完全可以使用 JSON 数据动态渲染表格,前台没必要定义 Column 了,这点比 Element 表格组件灵活。
  1. 中国市区级联、自定义组件 / 数据访问 / 表单验证

考虑到代码复用,我们对一些 antd 组件进行了二次封装。比如 BaseInput.tsxBaseSelect.tsxBaseDatePicker.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 ''
}
  1. 响应式布局

适当使用 CSS3 Media Query,响应式布局能适配桌面和多数移动设备。

@media screen and (max-width: 767px) {
  .el-dialog {
    width: 92% !important;
  }
}
  1. 单元测试

出于学习 testing library 初衷,本项目的单元测试写的比较系统。网上的 React 教程多数时使用 test-utilsEnzyme 来测试组件,库依赖性太强,没法在 Vue 项目复用。感兴趣的同学可以访问 Testing Library 学习指南 了解更多。

运行项目

  1. 安装依赖
npm install
  1. 编译前运行 json serve
npm run mock
  1. 编译和热重载用于开发
npm run serve
  1. 单元测试
npm run test:unit

相关项目

1

评论 (0)

取消