【前端1】单据编辑 -EasyUI/Vue/React/Bootstrap 主流框架实现订单单据主子表显示和编辑比较,哪个你最易入门?

【前端1】单据编辑 -EasyUI/Vue/React/Bootstrap 主流框架实现订单单据主子表显示和编辑比较,哪个你最易入门?

作者:长江支流 日期:2026-08-25

你开发一个订单管理系统,老板说:

"订单列表里,每一行都要显示该订单包含的所有商品明细,而且要能直接编辑、新增行、删除行。"

你心想:"就一个主子表单据而已,不难吧?"

结果你打开项目,发现这个"看起来很简单"的需求,比你想的要复杂得多:

  • EasyUI DataGrid:要配置 editor、formatter,还要手动管编辑状态
  • Vue + Element:要写 template、v-for、响应式数据、方法函数
  • React + Ant Design:要写 JSX、useState、handleChange、map 渲染
  • Bootstrap Table:要写 formatter、手动拼 HTML 字符串

纯HTML实现方式,5行JS代码解决

加载表格数据,其实5行核心代码,实例化插件、加载数据到网格/Table/DIV/任意元素,然后就可增加删除(模板)行。

javascript 复制代码
//Step1 实例化插件
 var detailGrid = new UserBaoDataGrid("detailGrid");

function doFilter() {
		//Step2 加载数据到Table/DIV等任意元素
    detailGrid.datagrid({
        url: "/demo/data/13order_details_relation.json"
    });
}

// ★★★ 新增明细行(模板方式) ★★★
function addDetailRow() {
	  //Step3 增加行,可以指定参数按自己的定制模板增加
    detailGrid.appendByTemplateRow();	
}

// ★★★ 删除明细行 ★★★
function deleteDetailRow(el) {
    if (confirm('确定要删除该明细记录吗?')) {
    		//Step3 删除行
        detailGrid.removeRow(el);
    }
}

完整单据代码如下,会Html人都会。

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>订单详情(可编辑 · 简单版)-- 长江支流·咏方舟</title>

    <script type="text/javascript" src="/lib/jquery/dist/jquery.min.js"></script>
    <script src="/js/UserBaoDataGrid.js"></script>
    <link href="/css/GoldReportTemplatePack.css" rel="stylesheet" />

    <script type="text/javascript">
        var masterGrid = new UserBaoDataGrid("masterGrid");
        var detailGrid = new UserBaoDataGrid("detailGrid");

        function getUrlParameter(name) {
            var urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(name);
        }

        var mOrderId = getUrlParameter('orderId');
        if (!mOrderId) mOrderId = '1';

        $(document).ready(function () {
            doFilter();
        });

        function doFilter() {
            masterGrid.datagrid({
                url: "/demo/data/13orders_relation_with_customers.json",
                queryParams: { pageSize: 100 },
                success: function (data) {
                    //alert("主表加载");
                    //这个是离线json演示才加if,在线根本不用,因为在线的肯定直接返回当前订单的明细
                    if (data && data.rows) {
                        data.rows = data.rows.filter(function (row) { return row.id == mOrderId; });
                        data.total = data.rows.length;
                    }
                }
            });

            detailGrid.datagrid({
                url: "/demo/data/13order_details_relation.json",
                queryParams: { pageSize: 100 },
                success: function (data) {
                    //这个是离线json演示才加if,在线根本不用,因为在线的肯定直接返回当前订单的明细
                    if (data && data.rows) {
                        data.rows = data.rows.filter(function (row) { return row.OrderID == mOrderId; });
                        data.total = data.rows.length;
                    }
                }
            });
        }

        function reloadAll() { doFilter(); }

        // ★★★ 新增明细行(模板方式) ★★★
        function addDetailRow() {
            detailGrid.appendByTemplateRow();
        }

        // ★★★ 删除明细行 ★★★
        function deleteDetailRow(el) {
            if (confirm('确定要删除该明细记录吗?')) {
                detailGrid.removeRow(el);
            }
        }
    </script>

</head>
<body>
    <div class="webmis-bill-container">
        <div class="webmis-bill-title">📋 订单详情(可编辑)</div>
        <div class="webmis-bill-subtitle">
            主表 + 明细,全部可编辑 &nbsp;·&nbsp; 当前订单 ID:<span class="webmis-bill-badge" id="orderIdDisplay">1</span>
        </div>

        <div class="webmis-bill-btn-group">
            <button class="webmis-bill-btn webmis-bill-btn-primary" onclick="doFilter();">🔍 查询</button>
            <button class="webmis-bill-btn webmis-bill-btn-success" onclick="reloadAll();">🔄 重新加载</button>
        </div>

        <!-- ============================================================ -->
        <!-- 主表(可编辑) -->
        <!-- ============================================================ -->
        <div class="webmis-bill-master">
            <div id="masterGrid">
                <div class="webmis-templatepack" data-src="rows">
                    <div class="webmis-templatepackrow" templaterow="true" style="cursor:default;display:block;padding:0;border-bottom:1px solid #e8ecf1;margin-bottom:8px;">
                        <div class="webmis-card">
                            <!-- ★★★ 主表字段全部变成 input ★★★ -->
                            <div class="webmis-bill-row">
                                <span class="webmis-bill-label">订单编号</span>
                                <span class="webmis-bill-value">
                                    <input type="text" value="<templatefield>order_id</templatefield>" style="width:100%;padding:4px 8px;border:1px solid #d0d8e4;border-radius:4px;" />
                                </span>
                            </div>
                            <div class="webmis-bill-row">
                                <span class="webmis-bill-label">客户名称</span>
                                <span class="webmis-bill-value">
                                    <input type="text" value="<templatefield>customerName</templatefield>" style="width:100%;padding:4px 8px;border:1px solid #d0d8e4;border-radius:4px;" />
                                </span>
                            </div>
                            <div class="webmis-bill-row">
                                <span class="webmis-bill-label">总金额</span>
                                <span class="webmis-bill-value">
                                    <input type="number" step="0.01" value="<templatefield>total_amount</templatefield>" style="width:100%;padding:4px 8px;border:1px solid #d0d8e4;border-radius:4px;" />
                                </span>
                            </div>
                            <div class="webmis-bill-row">
                                <span class="webmis-bill-label">下单日期</span>
                                <span class="webmis-bill-value">
                                    <input type="datetime-local" value="<templatefield>order_date</templatefield>" style="width:100%;padding:4px 8px;border:1px solid #d0d8e4;border-radius:4px;" />
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="webmis-bill-separator"></div>

        <!-- ============================================================ -->
        <!-- 明细表(可编辑) -->
        <!-- ============================================================ -->
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
            <div style="font-weight:600;font-size:14px;color:#1a2a3a;">📦 商品明细</div>
            <button onclick="addDetailRow();" style="padding:4px 16px;background:#1f9a6e;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;">➕ 新增明细</button>
        </div>

        <table id="detailGrid" class="webmis-bill-detail">
            <thead>
                <tr>
                    <th>订单ID</th>
                    <th>商品编号</th>
                    <th>数量</th>
                    <th>单价</th>
                    <th>金额</th>
                    <th>备注</th>
                    <th width="15%">操作</th>
                </tr>
            </thead>
            <tbody>
                <tr templaterow="true" data-src="rows">
                    <!-- ★★★ 明细表字段全部变成 input ★★★ -->
                    <td><input type="text" value="<templatefield>OrderID</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td><input type="text" value="<templatefield>ProductID</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td><input type="number" value="<templatefield>Qty</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td><input type="number" step="0.01" value="<templatefield>Price</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td><input type="number" step="0.01" value="<templatefield>Amount</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td><input type="text" value="<templatefield>Memo</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" /></td>
                    <td>
                        <button onclick="deleteDetailRow(this)" style="padding:2px 12px;background:#dc3545;color:#fff;border:none;border-radius:3px;cursor:pointer;font-size:12px;">移除</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>

    <script>
        $(document).ready(function () {
            $('#orderIdDisplay').text(mOrderId);
        });
    </script>

</body>
</html>

一个"主子表单据",四种主流方案要写多少行?

EasyUI DataGrid(约 60 行)

javascript 复制代码
$('#masterGrid').datagrid({
    url: '/api/order',
    columns: [[
        { field: 'orderNo', title: '订单编号' },
        { field: 'customer', title: '客户名称', editor: 'text' },
        { field: 'total', title: '总金额', editor: 'numberbox' }
    ]],
    onDblClickRow: function(index) {
        $('#masterGrid').datagrid('beginEdit', index);
    }
});

// 明细表
$('#detailGrid').datagrid({
    url: '/api/order_details',
    columns: [[
        { field: 'product', title: '商品', editor: 'text' },
        { field: 'qty', title: '数量', editor: 'numberbox' },
        { field: 'price', title: '单价', editor: 'numberbox' }
    ]],
    onDblClickRow: function(index) {
        $('#detailGrid').datagrid('beginEdit', index);
    }
});

// 新增行、删除行还要单独写
function addRow() {
    $('#detailGrid').datagrid('appendRow', {});
}
function deleteRow() {
    var row = $('#detailGrid').datagrid('getSelected');
    var index = $('#detailGrid').datagrid('getRowIndex', row);
    $('#detailGrid').datagrid('deleteRow', index);
}

Vue + Element(约 50 行)

vue 复制代码
<template>
    <el-table :data="masterData">
        <el-table-column prop="orderNo" label="订单编号" />
        <el-table-column prop="customer" label="客户名称">
            <template #default="{ row }">
                <el-input v-model="row.customer" />
            </template>
        </el-table-column>
    </el-table>
    <el-table :data="detailData">
        <el-table-column prop="product" label="商品">
            <template #default="{ row }">
                <el-input v-model="row.product" />
            </template>
        </el-table-column>
    </el-table>
</template>

<script>
data() {
    return {
        masterData: [],
        detailData: []
    };
},
methods: {
    loadData() {
        axios.get('/api/order').then(res => { this.masterData = res.data; });
        axios.get('/api/order_details').then(res => { this.detailData = res.data; });
    },
    addRow() { this.detailData.push({}); },
    deleteRow(index) { this.detailData.splice(index, 1); }
}
</script>

React + Ant Design(约 70 行)

jsx 复制代码
const columns = [
    { title: '订单编号', dataIndex: 'orderNo' },
    { title: '客户名称', dataIndex: 'customer',
      render: (text, record) => <Input value={text} onChange={...} /> }
];

const detailColumns = [
    { title: '商品', dataIndex: 'product',
      render: (text, record) => <Input value={text} onChange={...} /> }
];

function OrderPage() {
    const [master, setMaster] = useState([]);
    const [details, setDetails] = useState([]);
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        setLoading(true);
        axios.get('/api/order').then(res => setMaster(res.data));
        axios.get('/api/order_details').then(res => setDetails(res.data));
    }, []);

    const addRow = () => setDetails([...details, {}]);
    const deleteRow = (index) => setDetails(details.filter((_, i) => i !== index));

    return (
        <div>
            <Table dataSource={master} columns={columns} loading={loading} />
            <Table dataSource={details} columns={detailColumns} />
            <Button onClick={addRow}>新增明细</Button>
        </div>
    );
}

Bootstrap Table(约 80 行)

javascript 复制代码
$('#masterGrid').bootstrapTable({
    url: '/api/order',
    columns: [
        { field: 'orderNo', title: '订单编号' },
        { field: 'customer', title: '客户名称' },
        { field: 'total', title: '总金额' }
    ]
});

$('#detailGrid').bootstrapTable({
    url: '/api/order_details',
    columns: [
        { field: 'product', title: '商品' },
        { field: 'qty', title: '数量' },
        { field: 'price', title: '单价' }
    ],
    // 编辑需要额外配置
});

数据对比

方案 主表+明细 + 增删改 需要学什么 代码量
EasyUI DataGrid 配置 columns + editor + 手动管状态 DataGrid API、editor 配置 ~60 行
Vue + Element 写 template + data + methods v-for、响应式、axios ~50 行
React + Ant Design 写 JSX + useState + useEffect Hooks、JSX、axios ~70 行
Bootstrap Table 配置 + 手动拼 HTML formatter、DOM 操作 ~80 行
UserBaoDataGrid引擎 3 行模板 + 1 行 JS 会写 HTML 即可 ~4 行

UserBaoDataGrid引擎:3 行模板 + 1 行 JS

主表模板(1 行)

html 复制代码
<div class="webmis-templatepackrow" templaterow="true">
    <input value="<templatefield>customer</templatefield>">
    <input value="<templatefield>total</templatefield>">
</div>

明细表模板(1 行)

html 复制代码
<tr templaterow="true">
    <td><input value="<templatefield>product</templatefield>"></td>
    <td><input value="<templatefield>qty</templatefield>"></td>
    <td><button onclick="detailGrid.removeRow(this)">删除</button></td>
</tr>

JS 调用(1 行)

javascript 复制代码
var masterGrid = new UserBaoDataGrid("masterGrid");
var detailGrid = new UserBaoDataGrid("detailGrid");

function doFilter() {
    masterGrid.datagrid({ url: '/api/order' });
    detailGrid.datagrid({ url: '/api/order_details' });
}

function addDetailRow() {
    detailGrid.appendByTemplateRow();  // 新增一行
}

为什么差这么多?

主流框架 UserBaoDataGrid引擎
核心问题 如何管理状态、如何渲染列表 数据填到哪里
你需要做什么 学 API、配 columns、管 state 会写 HTML 就行
新增一行 操作数组 / 调用 API appendByTemplateRow()
删除一行 找索引 / splice removeRow(this)
谁适配谁 你适配框架 引擎适配你的数据

一个真实场景,覆盖 18 个行业

这种"主表 + 明细"的编辑场景,不是你一个人的痛点:

行业 主表 明细
电商/零售 订单列表 商品明细(名称、数量、单价)
教育培训 学生信息 选修课程列表
考试系统 考生信息 各科成绩
企业OA 审批流程 审批节点列表
项目管理 项目列表 任务列表
生产制造 工单列表 加工工序列表
人力资源 员工信息 岗位变动记录
客户管理 客户类型 该类型下的客户列表
财务系统 报销单 费用明细
医疗系统 患者档案 就诊记录
合同管理 合同列表 变更记录
教学管理 课程表 上课班级列表
物流系统 运单 运输节点
餐饮系统 桌台订单 菜品明细
建筑/工程 工程项目 施工阶段
行政/办公 会议室预约 参会人员名单
IT/运维 配置项 属性列表
通知公告 公告列表 抄送人员列表

结论

EasyUI、Vue、React、Bootstrap,能用几行JS完成主子表单据的显示和编辑?

答案是:至少 50-80 行,还要学一堆 API。

UserBaoDataGrid引擎呢?

3 行模板 + 1 行 JS,不用学任何框架 API。

这就是差距。


UserBaoDataGrid引擎,解决进销存ERP表单、单据、报表,不必要重资产

让组件迁就你的数据,而不是你迁就组件。

本文由 csdn 长江支流-咏方舟 原创,欢迎转载,源码免费索取! 📩 需要UserBaoDataGrid前端引擎完整源码的朋友,欢迎私信或评论区留言,转载和粉丝免费!

相关推荐
m0_5791466540 分钟前
Element UI 表格合并单元格完全指南:从原理到实战
前端·vue.js·elementui
sibylyue44 分钟前
# Web端流媒体JS播放器开源库
前端·javascript·开源
码视野1 小时前
基于 Vue3 + Element Plus 的【基于物联网与深度学习的智慧工业园区能耗监测与光储充一体化微电网协同调度系统】设计与实现(附完整源码与PRD)
前端·人工智能·深度学习·物联网·vue3
铁皮饭盒1 小时前
网页端, 5.5mb谷歌模型, 识别躯干, 视频都不卡
前端·javascript·后端
雪芽蓝域zzs1 小时前
Prettier (代码格式化)配置详解
前端·vue.js
晴天161 小时前
Gradio 与 Streamlit:Python 快速构建 Web 应用的双子星-Day28
开发语言·前端·python
码哥DFS1 小时前
LRU缓存
前端·javascript·缓存
mldong1 小时前
一条命令,十分钟:jeeflow 工作流应用的六语言一键部署
前端·后端·架构
运维全栈笔记5 小时前
Vue + Spring Boot 前后端分离项目部署笔记(若依 RuoYi-Vue 3.9.2)
运维·服务器·vue.js·spring boot·笔记·开源·开源软件