从 0 到 1 构建 React + TypeScript 车辆租赁后台管理系统

从 0 到 1 构建 React + TypeScript 车辆租赁后台管理系统

在企业级后台管理系统开发中,React 结合 TypeScript 已是主流技术栈 ------TypeScript 的类型校验能大幅降低大型项目的维护成本,React 的组件化特性则适配后台系统复杂的 UI 与交互场景。本文以车辆租赁后台管理系统为例,拆解从技术选型、架构设计到核心功能实现的全流程,为前端开发者提供可落地的实战思路。

一、项目背景与技术栈选型

1. 业务场景特点

车辆租赁后台需覆盖「车辆管理、订单调配、客户管理、财务结算、操作日志」等核心模块,具有以下特点:

  • 数据交互频繁(如实时查询车辆状态、订单流转);
  • 表单 / 表格操作密集(如批量派发任务、筛选车辆信息);
  • 权限分级严格(不同角色可见 / 可操作的模块不同);
  • 需兼容多端(PC 端为主,部分功能适配平板)。

2. 核心技术栈

技术 / 框架 选型理由
React 18 组件化开发、Hooks 简化状态管理、Concurrent Mode 提升交互流畅度
TypeScript 5.x 强类型约束避免类型错误、提升代码可读性、便于团队协作
Umi 4 开箱即用的路由 / 构建 / 权限方案,适配中后台开发的工程化需求
Ant Design Pro 提供 ProTable/ProForm/Descriptions 等中后台高频组件,减少重复造轮子
ahooks 封装常用 Hooks(如 useRequest、useSelections),简化异步请求 / 状态管理
axios 统一封装请求拦截、响应处理,适配后端接口规范

二、项目架构设计

遵循「高内聚、低耦合」原则,采用分层式架构,目录结构如下:

plaintext

scss 复制代码
src/
├── api/            // 接口请求封装(按模块拆分)
│   ├── car.ts      // 车辆管理接口
│   ├── order.ts    // 订单调配接口
│   └── user.ts     // 用户/权限接口
├── components/     // 通用业务组件(如OperationRecord、PreviewFiles)
├── pages/          // 页面级组件(按业务模块拆分)
│   ├── CarManage/  // 车辆管理
│   ├── OrderAlloc/ // 订单调配(核心模块)
│   └── System/     // 系统设置
├── utils/          // 工具函数(如字典映射、时间格式化)
├── typings/        // TS类型声明(接口返回值、组件Props)
└── access.ts       // 权限控制配置

关键设计原则

  1. 类型先行:所有接口返回值、组件 Props、全局状态均定义 TS 类型,例如订单调配模块的核心类型:

typescript

运行

typescript 复制代码
// typings/order.d.ts
/** 订单基础信息 */
export interface AllocOrder {
  orderNo: string; // 工单编号
  orderStatus: 'toFormulate' | 'toDistribute' | 'completed'; // 订单状态
  carNo: string; // 车牌
  vin: string; // 车架号
  allocateInfo: {
    batchNo: string;
    deliveryType: string; // 交付类型
    outContactsName: string; // 调出联系人
  };
}

/** 订单列表请求参数 */
export interface AllocOrderParams {
  orderSource: string;
  batchNo?: string;
  pageSize: number;
  current: number;
}
  1. 复用性优先:抽离通用业务组件(如操作记录组件 OperationRecord),避免重复代码:

tsx

typescript 复制代码
// components/OperationRecordSection/index.tsx
import React, { useEffect, useRef } from 'react';
import type { ActionType, ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { operationListPage } from '@/api/order';

interface OperationRecordSectionProps {
  formValues: {
    orderNo?: string;
    [key: string]: any;
  };
}

const OperationRecordSection: React.FC<OperationRecordSectionProps> = ({ formValues }) => {
  const actionRef = useRef<ActionType>();

  // 监听工单编号变化,自动刷新操作记录
  useEffect(() => {
    if (formValues?.orderNo) {
      actionRef.current?.reload();
    }
  }, [formValues?.orderNo]);

  const columns: ProColumns[] = [
    { title: '操作节点', dataIndex: 'orderNode' },
    { title: '操作人', dataIndex: 'operationName' },
    { title: '操作时间', dataIndex: 'createdDate' },
    { title: '操作详情', dataIndex: 'operatorDetail' },
  ];

  return (
    <ProTable
      search={false}
      actionRef={actionRef}
      request={async (params) => {
        if (!formValues?.orderNo) return { data: [], total: 0 };
        return operationListPage({ ...params, orderNo: formValues.orderNo });
      }}
      columns={columns}
      pagination={{ defaultPageSize: 5 }}
    />
  );
};

export default OperationRecordSection;

三、核心功能实现

1. 订单调配模块(核心场景)

订单调配是车辆租赁后台的核心,需实现「方案制定、任务派发、详情查看」等功能,且需结合状态控制权限与交互。

(1)带状态控制的表格渲染

使用 ProTable 实现订单列表,根据订单状态(toFormulate/toDistribute)动态展示操作按钮,且仅允许勾选「方案制定」状态的行:

tsx

typescript 复制代码
// pages/OrderAlloc/index.tsx
import React, { useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { Button, message } from 'antd';
import { getAllocationList } from '@/api/order';
import Func from '@/utils/Func';

const OrderAlloc = () => {
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
  const [selectedRows, setSelectedRows] = useState<AllocOrder[]>([]);

  // 表格列配置
  const columns = [
    {
      title: '工单编号',
      dataIndex: 'orderNo',
      key: 'orderNo',
    },
    {
      title: '订单状态',
      dataIndex: 'orderStatus',
      valueEnum: Func.dictEnum(dictMap, 'AllocOrderStatus'), // 字典映射
    },
    {
      title: '车牌',
      dataIndex: 'carNo',
    },
    {
      title: '操作',
      dataIndex: 'option',
      valueType: 'option',
      render: (_, record) => (
        <>
          {/* 仅「方案制定」状态显示「方案制定」按钮 */}
          {record.orderStatus === 'toFormulate' && (
            <a onClick={() => handleOpenFormulateModal(record)}>方案制定</a>
          )}
          {/* 仅「待派发」状态显示「派发任务」按钮 */}
          {record.orderStatus === 'toDistribute' && (
            <a onClick={() => handleOpenDispatchModal(record)}>派发任务</a>
          )}
          <a onClick={() => handleOpenDetail(record)}>详情</a>
        </>
      ),
    },
  ];

  return (
    <PageHeaderWrapper>
      <ProTable
        rowKey="orderNo"
        rowSelection={{
          selectedRowKeys,
          onChange: (keys, rows) => {
            // 仅保留「方案制定」状态的选中行
            const validRows = rows.filter(row => row.orderStatus === 'toFormulate');
            setSelectedRowKeys(validRows.map(row => row.orderNo));
            setSelectedRows(validRows);
            if (rows.length > validRows.length) {
              message.warning('仅"方案制定"状态的订单可勾选');
            }
          },
          // 禁用非「方案制定」状态的行勾选
          getCheckboxProps: (record) => ({
            disabled: record.orderStatus !== 'toFormulate',
          }),
        }}
        request={(params) => getAllocationList({ ...params, orderSource: 'ALLOCATE' })}
        columns={columns}
        toolBarRender={() => [
          <Button
            type="primary"
            disabled={selectedRows.length === 0}
            onClick={() => handleBatchFormulate()}
          >
            批量方案制定
          </Button>,
        ]}
      />
    </PageHeaderWrapper>
  );
};

export default OrderAlloc;
(2)详情弹窗实现

通过 Modal 封装详情弹窗,整合 ProTable、Descriptions 组件展示订单多维度信息,并复用操作记录组件:

tsx

javascript 复制代码
// pages/OrderAlloc/components/DeploymentDetail.tsx
import React, { useEffect, useState } from 'react';
import { Modal, Descriptions, Collapse } from 'antd';
import ProTable from '@ant-design/pro-table';
import OperationRecordSection from '@/components/OperationRecordSection';
import { allocatePlanDetail } from '@/api/order';
import Func from '@/utils/Func';

const DeploymentDetail = ({ visible, onCancel, formValues }) => {
  const [orderDetail, setOrderDetail] = useState<AllocOrder | null>(null);
  const [taskList, setTaskList] = useState<AllocOrder[]>([]);

  // 获取订单详情
  useEffect(() => {
    if (visible && formValues?.batchNo) {
      allocatePlanDetail({ batchNo: formValues.batchNo }).then(res => {
        setOrderDetail(res.data);
        setTaskList(res.data.taskList || []);
      });
    }
  }, [visible, formValues]);

  // 工单信息表格列
  const workColumns = [
    { title: '工单编号', dataIndex: 'orderNo' },
    { title: '车型', dataIndex: 'modelName', render: (_, r) => r.carInfo?.modelName || '-' },
    { title: '交付类型', dataIndex: 'deliveryType', render: (_, r) => Func.dictLabel(dictMap, 'DeliveryType', r.allocateInfo?.deliveryType) || '-' },
  ];

  return (
    <Modal
      open={visible}
      onCancel={onCancel}
      width="80%"
      footer={null}
      destroyOnClose
    >
      <Collapse defaultActiveKey={['1', '2', '3']}>
        <Collapse.Panel header="工单信息" key="1">
          <ProTable dataSource={taskList} columns={workColumns} pagination={false} />
        </Collapse.Panel>
        <Collapse.Panel header="调配信息" key="2">
          <Descriptions column={3}>
            <Descriptions.Item label="交付类型">
              {Func.dictLabel(dictMap, 'DeliveryType', orderDetail?.allocateInfo?.deliveryType) || '-'}
            </Descriptions.Item>
            <Descriptions.Item label="备注">{orderDetail?.remark || '-'}</Descriptions.Item>
          </Descriptions>
        </Collapse.Panel>
        <Collapse.Panel header="操作记录" key="3">
          <OperationRecordSection formValues={{ ...formValues, orderNo: orderDetail?.orderNo }} />
        </Collapse.Panel>
      </Collapse>
    </Modal>
  );
};

export default DeploymentDetail;

2. 字典映射封装

车辆租赁系统包含大量枚举值(如交付类型、订单状态),封装通用字典工具函数,统一处理类型映射:

typescript

运行

typescript 复制代码
// utils/Func.ts
/**
 * 字典转下拉/表格枚举格式
 * @param dictMap 全局字典对象
 * @param dictKey 字典key
 * @returns 枚举对象
 */
export const dictEnum = (dictMap: any, dictKey: string) => {
  if (!dictMap?.[dictKey]) return {};
  return dictMap[dictKey].reduce((acc: any, item: any) => {
    acc[item.value] = item.label;
    return acc;
  }, {});
};

/**
 * 根据值获取字典标签
 * @param dictMap 全局字典对象
 * @param dictKey 字典key
 * @param value 字典值
 * @returns 字典标签
 */
export const dictLabel = (dictMap: any, dictKey: string, value: any) => {
  if (!dictMap?.[dictKey] || !value) return '-';
  const item = dictMap[dictKey].find((item: any) => item.value === value);
  return item?.label || '-';
};

四、项目优化要点

1. 性能优化

  • 数据缓存:使用 ahooks 的 useRequest 配置 cacheKey,缓存高频查询的订单列表,避免重复请求;
  • 组件懒加载:通过 Umi 的 dynamicImport 实现页面级懒加载,减少首屏加载体积;
  • 表格虚拟滚动:ProTable 开启 scroll={{y: 600}} + virtual={true},优化大数据量表格渲染。

2. 体验优化

  • 状态联动:勾选行时实时过滤无效行并给出提示,避免用户误操作;
  • 兜底显示:所有字典映射、嵌套数据访问均添加兜底(|| '-'),避免页面空白;
  • 加载状态:异步请求时添加骨架屏 / 加载动画,提升交互体验。

3. 可维护性优化

  • 统一请求封装:拦截请求 / 响应,统一处理 token、错误提示;
  • 注释规范:关键业务逻辑、工具函数添加 TSDoc 注释;
  • 分支管理:按「功能分支 - 测试分支 - 主分支」流程开发,避免代码冲突。

五、总结

React + TypeScript 构建车辆租赁后台管理系统,核心在于「类型约束降低风险、组件复用提升效率、业务逻辑分层解耦」。本文从架构设计到核心功能实现,覆盖了中后台开发的高频场景:表格状态控制、字典映射、通用组件封装、权限控制等。

实际开发中,需结合业务场景灵活调整 ------ 例如车辆租赁的「车辆定位、合同管理」等扩展模块,可复用本文的架构思路,通过 TypeScript 定义类型、Pro 组件快速实现 UI,最终打造高性能、易维护的企业级后台系统。

相关推荐
How_doyou_do6 小时前
pnpm优化理念 - 幻影依赖、monorepo - 升级npm
前端
雨落秋垣6 小时前
在前端把图片自动转换为 WebP 格式
前端
羽沢316 小时前
一些css属性学习
前端·css·学习
二狗哈7 小时前
Cesium快速入门22:fabric自定义着色器
运维·开发语言·前端·webgl·fabric·cesium·着色器
计算衎7 小时前
FastAPI后端和VUE前端的数据交互原理详解
前端·vue.js·fastapi
黑岚樱梦7 小时前
Linux系统编程
java·开发语言·前端
xrl20127 小时前
ruoyi-vue2前端集成DMN规则引擎
前端·规则引擎·工作流·dmn
转转技术团队7 小时前
前端工程化实践:打包工具的选择与思考
前端·javascript·webpack
前端郭德纲7 小时前
React 19.2 已发布,现已上线 npm!
前端·react.js·npm