React 新框架的一些实践心得(关注业务的话,框架的设计封装思路)

发布于:2025-06-22 ⋅ 阅读:(14) ⋅ 点赞:(0)


新的框架的描述

基本的增删改查已经被限制住了,这里的逻辑大致分成了

  • index.tsx : 用于展示数据列表,搜索过滤等功能
  • form.tsx: 用于处理数据的新增和编辑
  • detail.tsx: 用于展示数据的详情

这样的好处就是,尽量的关注业务,一些重复的细节可以忽略掉


基础建设

已经把基础的页面布局,以及常用的按钮等封装到私有的 npm 仓库,前期需要熟悉一下如何使用

拿几个组件来举例子,这个 BasicPage 是我们封装的,我们只需传递后端资源的地址,就可以完成对资源的获取,分页等功能已经内置了

然后就是表格的列定义,这里面定义了多少列,页面上就会支持筛选多少列

// index.tsx
import type { ProColumns } from '@ant-design/pro-table/es/typing'
import type { PageRef } from '@/starter'
import { BasicPage, DeleteButton } from '@/starter'
import { createRoutePage } from '@/starter'
import { Button, Space } from 'antd'
import { useRef } from 'react'
import Form from './form'

export default createRoutePage({
    title: '商品属性',
    component: () => {
        const pageRef = useRef<PageRef>(null)
        const columns: ProColumns<Api.GoodsAttrs>[] = [
            { dataIndex: ['id'], title: 'ID', fixed: 'left', width: 60, hideInForm: true },
            { dataIndex: ['name'], title: '名称' },
            { dataIndex: ['sort'], title: '排序值', hideInSearch: true },
            { dataIndex: ['created_at'], title: '创建时间', hideInForm: true, hideInSearch: true },
            {
                dataIndex: ['action'],
                title: '操作',
                fixed: 'right',
                width: 120,
                hideInDescriptions: true,
                hideInForm: true,
                hideInSearch: true,
                align: 'center',
                render: (_, record, _index, action) => {
                    return (
                        <Space>
                            <Button
                                icon={<IconLucideSquarePen />}
                                type="text"
                                onClick={() => pageRef.current?.showEditForm(record.id)}
                            />

                            <DeleteButton
                                resource="goods-specs"
                                resourceName="商品规格"
                                id={record.id}
                                onSuccess={() => action?.reload()}
                            />
                        </Space>
                    )
                },
            },
        ]

        return (
            <BasicPage<Api.GoodsAttrs>
                resource="goods-attrs"
                resourceName="商品属性"
                columns={columns}
                ref={pageRef}
                Form={Form}
                basicDetailProps={{
                    variant: 'modal',
                    width: 400,
                    title: record => record.name,
                }}
                basicFormProps={{
                    variant: 'modal',
                }}
                basicTableProps={{ scroll: undefined, virtual: false, params: { sorters: [{ field: 'id', order: 'desc' }] } }}
            />
        )
    },
})

在这里插入图片描述

form.tsx 的栗子如下

import { ProFormDigit, ProFormSwitch, ProFormText, ProFormTextArea } from '@ant-design/pro-components'
import { ResourceTreeSelect } from '@kiyoung/starter'
import { Space, Tag } from 'antd'
import { memo } from 'react'

function form() {
    return (
        <>
            <ProFormText
                label="名称"
                name={['name']}
                rules={[{ required: true }]}
            />

            <ResourceTreeSelect
                label="分类"
                resource="goods-categories"
                name={['cate_id']}
            />

            <ProFormDigit label="排序值" name={['sort']} width={120} />
        </>
    )
}

export default memo(form)

form 中也是按照资源去获取 options,以及根据 name 来回显字段等等,都已经在上层组件中封装好了


前后端的沟通成本

听说后端是对表中字段的监控,从而实现各种各样的需求,从前端的角度理解的话,这样会少些很多接口

例如

如果我想改某个数据的状态从 未签收 => 已签收

  • 那么按照之前的经验,可能会有专门的接口用于处理这个逻辑

  • 但是现在,我可以通过 put 请求,通过修改状态,后端通过监控这个字段的状态,来实现这个逻辑

看一下这个按钮的实现

<EditButton
    icon={<IconLucideClipboardX />}
    resource="delivery-orders"
    resourceName="撤销发货"
    tooltip="撤销发货"
    type="text"
    id={record.id}
    params={{ status: 2 }}
    onSuccess={() => {
        window.$message?.success('撤销发货成功')
    }}
/>

这是一个修改状态的按钮,上面 resource 定义了请求那个资源, params 中定义了要改那个字段,当然这里的状态使用的硬编码,这是不对的,那么就讲一下枚举值的定义吧


枚举值的定义

以下代码是定义一个枚举值的栗子

import type { ConstantEnum } from '@/utils/common'
import { transformRecordToOption } from '@/utils/common'

/**
 * 接单状态
 */
export type ReceiveStatus = 0 | 1 | 2 | 3

export const ReceiveStatusUnreceived = 0
export const ReceiveStatusReceived = 1
export const ReceiveStatusPartiallyReceived = 2
export const ReceiveStatusError = 3

export const RECEIVE_STATUS: ConstantEnum<ReceiveStatus> = {
    [ReceiveStatusUnreceived]: '未接单',
    [ReceiveStatusReceived]: '已接单',
    [ReceiveStatusPartiallyReceived]: '部分接单',
    [ReceiveStatusError]: '异常',
}

export const RECEIVE_STATUS_OPTIONS = transformRecordToOption(RECEIVE_STATUS) // 第二个形参传递 tag[] 的颜色

其中有好处也有坏处

好处就是

  • 如果后端私自动了某一个枚举值,但是没通知你,那么就是后端的问题了
  • 结构直观明显,标签的枚举的话也会自动给你分配颜色
  • 一次定义,受益的很
  • ts 类型会检测问题

坏处

  • 麻烦,定义一次要敲很多代码,不过,可以给 ai 来帮你处理

上层组件 bug 的反馈和更新

因为现在这套脚手架,基建还不是很完善,会有一些 bug,还有新的组件需要封装等等因素,所以导致我们私有的 start 经常需要更新

所以经常需要以下操作

  1. 拉取 origin/main 分支合并到自己的分支上
  2. 删除本地依赖
  3. 重新安装依赖
  4. 启动开发服务器

当然这些操作可以用脚本来跑

#!/bin/bash

# 提示用户是否合并 origin/develop 分支
read -p "是否需要合并 origin/develop 分支?(Y/n): " ans

# 如果输入为空,默认 Y
ans=${ans:-Y}
ans=$(echo "$ans" | tr '[:upper:]' '[:lower:]') # 转小写

if [[ "$ans" == "y" || "$ans" == "yes" ]]; then
  echo "🚀 正在合并 origin/develop 分支..."
  git pull --no-rebase origin develop
  if [ $? -ne 0 ]; then
    echo "❌ 合并失败,请检查冲突后重试。"
    exit 1
  fi
else
  echo "⚠️ 跳过合并分支。"
fi

echo "🗑️ 正在删除 node_modules 文件夹..."
rm -rf node_modules

echo "📦 正在安装依赖 (pnpm i)..."
pnpm i

echo "🔧 启动开发环境 (pnpm run dev)..."
pnpm run dev


网站公告

今日签到

点亮在社区的每一天
去签到