【前端2】单据编辑 -订单单据主子表,万能模板行,自定义添加行

【前端2】单据编辑 -订单单据主子表,万能模板行,自定义添加行

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

前一篇,我发发了 《【前端1】单据编辑 -EasyUI/Vue/React/Bootstrap 主流框架实现订单单据主子表显示和编辑比较,哪个你最易入门?点击查看

本篇开始,只发效果图和源码!

先预览

这个示例,不仅实现你新增行时,按你想要的格式做模板,而且点保存时,组织了Json数据,自己连接后端就是一个真实的应用。我已在AI中作了训练,直接用自然语言,它就按UserBaoDataGrid前端渲染引擎的要求,帮我生成了。

**以下是AI对话,你们用AI编程是如何做的?**欢迎探讨分享,反正我不会让AI直接生成C#、Java具体语言,因为这个前端界面,也只是为了验证UserBaoDataGrid前端引擎以及给大家分享前端示例。其实我让AI帮我生成一个描述配置,我去解析,最后我的解析程序也是可直接动态生成前端,实现前端0编码,后端0编码,不需要编译,对AI说完话它生成的东西平台就直接解析运行,解决AI编程还要编译、调试bug、部署、运行这最后一公里的麻烦。

一、标准Html Table,不改变习惯

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

**第一步:我们只按标准table,加一个,组件自动循环 **

**第二步:我们只加Json字段名,组件就替换 **

**第三步:新增行时,这行就是模板,删除行就是当前所在行。 **

**第四步:相编辑,就加input文本框、下拉选择框等等,完全自由 **

html 复制代码
<button onclick="detailGrid.appendByTemplateRow();" >➕ 新增明细</button>
<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">
            <!-- ★★★ 明细表字段全部变成 input ★★★ -->
            <td><input type="text" value="<templatefield>OrderID</templatefield>" </td>
            <td><input type="text" value="<templatefield>ProductID</templatefield>"  /></td>
            <td><input type="number" value="<templatefield>Qty</templatefield>"  /></td>
            <td><input type="number" step="0.01" value="<templatefield>Price</templatefield>" /></td>
            <td><input type="number" step="0.01" value="<templatefield>Amount</templatefield>" " /></td>
            <td><input type="text" value="<templatefield>Memo</templatefield>" /></td>
            <td>
                <button onclick="detailGrid.removeRow(this)">移除</button>
            </td>
        </tr>
    </tbody>
</table>

二、3行JavaScript代码,搞定数据加载

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

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

因而实现了如下效果:

三、新增行时自定义模板

有没有方法?需求特殊,想在添加表格行时,按特定的格式定义模板,增加行就是想要的效果?

原生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" />

    <style>
        .row-editing {
            background: #f0f8ff !important;
            border-left: 4px solid #2b6cff !important;
        }

        .row-saved {
            background: #fafffe !important;
            border-left: 4px solid #1f9a6e !important;
        }

        .auto-id-label {
            color: #999;
            font-size: 13px;
            font-weight: 400;
        }

        .btn-save-row {
            padding: 2px 12px;
            background: #1f9a6e;
            color: #fff;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }

            .btn-save-row:hover {
                background: #16825b;
            }

        .btn-cancel-row {
            padding: 2px 12px;
            background: #dc3545;
            color: #fff;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }

            .btn-cancel-row:hover {
                background: #b02a37;
            }

        .btn-edit-row {
            padding: 2px 12px;
            background: #2b6cff;
            color: #fff;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }

            .btn-edit-row:hover {
                background: #1a56db;
            }
    </style>

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

        // ★★★ 数据存储 ★★★
        var masterDataList = [];
        var detailDataList = [];

        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() {
            masterDataList = [];
            detailDataList = [];

            masterGrid.datagrid({
                url: "/demo/data/13orders_relation_with_customers.json",
                queryParams: { pageSize: 100 },
                success: function (data) {
                    if (data && data.rows) {
                        data.rows = data.rows.filter(function (row) { return row.id == mOrderId; });
                        data.total = data.rows.length;
                        // 将加载的数据存入 masterDataList
                        if (data.rows.length > 0) {
                            masterDataList.push(JSON.parse(JSON.stringify(data.rows[0])));
                        }
                    }
                }
            });

            detailGrid.datagrid({
                url: "/demo/data/13order_details_relation.json",
                queryParams: { pageSize: 100 },
                success: function (data) {
                    if (data && data.rows) {
                        data.rows = data.rows.filter(function (row) { return row.OrderID == mOrderId; });
                        data.total = data.rows.length;
                        // 将加载的数据存入 detailDataList
                        data.rows.forEach(function (row) {
                            detailDataList.push(JSON.parse(JSON.stringify(row)));
                        });
                    }
                }
            });
        }

        function reloadAll() { doFilter(); }

        // ============================================================
        // ★★★ 自定义新增主表行 ★★★
        // ============================================================
        function addMasterRow() {
            var customHtml = '' +
                '<div class="webmis-templatepackrow row-editing" style="cursor:default;display:block;padding:0;margin-bottom:8px;border-radius:6px;">' +
                '   <div class="webmis-card" style="padding:12px 16px;">' +
                '       <div class="webmis-bill-row">' +
                '           <span class="webmis-bill-label">订单编号</span>' +
                '           <span class="webmis-bill-value auto-id-label">自动编号</span>' +
                '       </div>' +
                '       <div class="webmis-bill-row">' +
                '           <span class="webmis-bill-label">客户名称</span>' +
                '           <span class="webmis-bill-value">' +
                '               <input type="text" class="fld-customerName" placeholder="请输入客户名称" style="width:100%;padding:4px 8px;border:1px solid #2b6cff;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" class="fld-total_amount" placeholder="0.00" style="width:100%;padding:4px 8px;border:1px solid #2b6cff;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" class="fld-order_date" style="width:100%;padding:4px 8px;border:1px solid #2b6cff;border-radius:4px;" />' +
                '           </span>' +
                '       </div>' +
                '       <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px;padding-top:8px;border-top:1px solid #e8ecf1;">' +
                '           <button class="btn-save-row" onclick="saveMasterRow(this)">💾 保存</button>' +
                '           <button class="btn-cancel-row" onclick="$(this).closest(\'.webmis-templatepackrow\').remove()">✕ 取消</button>' +
                '       </div>' +
                '   </div>' +
                '</div>';

            // 追加到主表容器
            $('#masterGrid .webmis-templatepack').append(customHtml);
        }

        // ★★★ 保存主表行 ★★★
        function saveMasterRow(btn) {
            var $row = $(btn).closest('.webmis-templatepackrow');
            var order_id = $row.find('.auto-id-label').text().trim();
            var customerName = $row.find('.fld-customerName').val();
            var total_amount = $row.find('.fld-total_amount').val();
            var order_date = $row.find('.fld-order_date').val();

            if (!customerName) { alert('请输入客户名称!'); return; }

            var data = {
                order_id: order_id,
                customerName: customerName,
                total_amount: total_amount || '0',
                order_date: order_date || new Date().toISOString().slice(0, 16)
            };

            // 存储数据
            masterDataList.push(data);

            // 行变为已保存样式
            $row.removeClass('row-editing').addClass('row-saved');
            $row.find('input').css('border-color', '#d0d8e4').prop('readonly', true);
            $row.find('.btn-save-row, .btn-cancel-row').remove();
            $row.find('.webmis-card').append(
                '<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px;padding-top:8px;border-top:1px solid #e8ecf1;">' +
                '   <span style="color:#1f9a6e;font-size:13px;">✅ 已保存</span>' +
                '   <button class="btn-edit-row" onclick="editMasterRow(this)">✎ 编辑</button>' +
                '</div>'
            );

            alert('✅ 主表行已保存!\n\n' + JSON.stringify(data, null, 2));
        }

        // ★★★ 编辑主表行 ★★★
        function editMasterRow(btn) {
            var $row = $(btn).closest('.webmis-templatepackrow');
            $row.removeClass('row-saved').addClass('row-editing');
            $row.find('input').prop('readonly', false).css('border-color', '#2b6cff');
            $row.find('.btn-edit-row, span:contains("已保存")').closest('div').remove();
            $row.find('.webmis-card').append(
                '<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px;padding-top:8px;border-top:1px solid #e8ecf1;">' +
                '   <button class="btn-save-row" onclick="saveMasterRow(this)">💾 保存</button>' +
                '   <button class="btn-cancel-row" onclick="$(this).closest(\'.webmis-templatepackrow\').remove()">✕ 取消</button>' +
                '</div>'
            );
        }

        // ============================================================
        // ★★★ 自定义新增明细行 ★★★
        // ============================================================
        function addDetailRow() {
            var customHtml = '' +
                '<tr class="row-editing">' +
                '   <td><span class="auto-id-label">自动编号</span></td>' +
                '   <td><input type="text" class="fld-ProductID" placeholder="商品编号" style="width:100%;padding:4px 6px;border:1px solid #2b6cff;border-radius:3px;" /></td>' +
                '   <td><input type="number" class="fld-Qty" placeholder="数量" style="width:100%;padding:4px 6px;border:1px solid #2b6cff;border-radius:3px;" /></td>' +
                '   <td><input type="number" step="0.01" class="fld-Price" placeholder="单价" style="width:100%;padding:4px 6px;border:1px solid #2b6cff;border-radius:3px;" /></td>' +
                '   <td><input type="number" step="0.01" class="fld-Amount" placeholder="金额" style="width:100%;padding:4px 6px;border:1px solid #2b6cff;border-radius:3px;" /></td>' +
                '   <td><input type="text" class="fld-Memo" placeholder="备注" style="width:100%;padding:4px 6px;border:1px solid #2b6cff;border-radius:3px;" /></td>' +
                '   <td>' +
                '       <button class="btn-save-row" onclick="saveDetailRow(this)">💾 保存</button>' +
                '       <button class="btn-cancel-row" onclick="$(this).closest(\'tr\').remove()">✕ 取消</button>' +
                '   </td>' +
                '</tr>';

            $('#detailGrid tbody').append(customHtml);
        }

        // ★★★ 保存明细行 ★★★
        function saveDetailRow(btn) {
            var $row = $(btn).closest('tr');
            var id = $row.find('.auto-id-label').text().trim();
            var ProductID = $row.find('.fld-ProductID').val();
            var Qty = $row.find('.fld-Qty').val();
            var Price = $row.find('.fld-Price').val();
            var Amount = $row.find('.fld-Amount').val();
            var Memo = $row.find('.fld-Memo').val();

            if (!ProductID) { alert('请输入商品编号!'); return; }

            var data = {
                id: id,
                OrderID: parseInt(mOrderId) || 0,
                ProductID: ProductID,
                Qty: parseInt(Qty) || 0,
                Price: parseFloat(Price) || 0,
                Amount: parseFloat(Amount) || 0,
                Memo: Memo || ''
            };

            // 存储数据
            detailDataList.push(data);

            // 行变为已保存样式
            $row.removeClass('row-editing').addClass('row-saved');
            $row.find('input').css('border-color', '#d0d8e4').prop('readonly', true);
            $row.find('.btn-save-row, .btn-cancel-row').remove();
            $row.find('td:last').html(
                '<span style="color:#1f9a6e;font-size:13px;">✅ 已保存</span>' +
                ' <button class="btn-edit-row" onclick="editDetailRow(this)">✎ 编辑</button>'
            );

            alert('✅ 明细行已保存!\n\n' + JSON.stringify(data, null, 2));
        }

        // ★★★ 编辑明细行 ★★★
        function editDetailRow(btn) {
            var $row = $(btn).closest('tr');
            $row.removeClass('row-saved').addClass('row-editing');
            $row.find('input').prop('readonly', false).css('border-color', '#2b6cff');
            $row.find('.btn-edit-row, span:contains("已保存")').remove();
            $row.find('td:last').html(
                '<button class="btn-save-row" onclick="saveDetailRow(this)">💾 保存</button>' +
                ' <button class="btn-cancel-row" onclick="$(this).closest(\'tr\').remove()">✕ 取消</button>'
            );
        }

        // ============================================================
        // ★★★ 全局保存:弹出所有数据 ★★★
        // ============================================================
        function saveAllData() {
            // 收集当前所有已保存的行(从存储列表中取)
            var masterJson = masterDataList.length > 0 ? masterDataList[0] : {};
            var detailJson = detailDataList;

            // 检查是否有正在编辑的行未保存(提示用户)
            if ($('#masterGrid .row-editing').length > 0) {
                if (!confirm('有主表行正在编辑未保存,确定要提交吗?')) return;
            }
            if ($('#detailGrid .row-editing').length > 0) {
                if (!confirm('有明细行正在编辑未保存,确定要提交吗?')) return;
            }

            var result = {
                master: masterJson,
                details: detailJson
            };

            alert('📦 保存数据\n\n' +
                '【主表】\n' + JSON.stringify(result.master, null, 2) + '\n\n' +
                '【明细表】共 ' + result.details.length + ' 条\n' + JSON.stringify(result.details, null, 2)
            );

            console.log('保存数据:', result);
        }
    </script>

</head>
<body>
    <div class="webmis-bill-container">
        <div class="webmis-bill-title">📋 订单详情(高级版)</div>
        <div class="webmis-bill-subtitle">
            自定义新增行 · 自动编号 · 保存/取消 · 当前订单 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>
            <button class="webmis-bill-btn" style="background:#2b6cff;color:#fff;" onclick="addMasterRow();">📝 新增主表</button>
            <button class="webmis-bill-btn" style="background:#1f9a6e;color:#fff;font-weight:bold;" onclick="saveAllData();">💾 保存</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;margin-bottom:8px;">
                        <div class="webmis-card" style="padding:12px 16px;">
                            <div class="webmis-bill-row">
                                <span class="webmis-bill-label">订单编号</span>
                                <span class="webmis-bill-value"><templatefield>order_id</templatefield></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;" readonly />
                                </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;" readonly />
                                </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;" readonly />
                                </span>
                            </div>
                            <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px;padding-top:8px;border-top:1px solid #e8ecf1;">
                                <span style="color:#1f9a6e;font-size:13px;">✅ 已加载</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:#2b6cff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;">➕ 新增明细</button>
        </div>

        <table id="detailGrid" class="webmis-bill-detail">
            <thead>
                <tr>
                    <th width="10%">明细ID</th>
                    <th width="20%">商品编号</th>
                    <th width="10%">数量</th>
                    <th width="15%">单价</th>
                    <th width="15%">金额</th>
                    <th width="20%">备注</th>
                    <th width="10%">操作</th>
                </tr>
            </thead>
            <tbody>
                <tr templaterow="true" data-src="rows">
                    <td><input type="text" value="<templatefield>id</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" readonly /></td>
                    <td><input type="text" value="<templatefield>ProductID</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" readonly /></td>
                    <td><input type="number" value="<templatefield>Qty</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" readonly /></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;" readonly /></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;" readonly /></td>
                    <td><input type="text" value="<templatefield>Memo</templatefield>" style="width:100%;padding:4px 6px;border:1px solid #d0d8e4;border-radius:3px;" readonly /></td>
                    <td><span style="color:#1f9a6e;font-size:13px;">✅ 已加载</span></td>
                </tr>
            </tbody>
        </table>
    </div>

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

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

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

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

相关推荐
qq_452396231 小时前
第六篇:《CI/CD 流水线:从前端构建到自动化部署》
前端·ci/cd·自动化
咏方舟【长江支流】1 小时前
【前端1】单据编辑 -EasyUI/Vue/React/Bootstrap 主流框架实现订单单据主子表显示和编辑比较,哪个你最易入门?
前端·vue.js·easyui·咏方舟-长江支流·userbaodatagrid
m0_579146651 小时前
Element UI 表格合并单元格完全指南:从原理到实战
前端·vue.js·elementui
sibylyue1 小时前
# Web端流媒体JS播放器开源库
前端·javascript·开源
码视野1 小时前
基于 Vue3 + Element Plus 的【基于物联网与深度学习的智慧工业园区能耗监测与光储充一体化微电网协同调度系统】设计与实现(附完整源码与PRD)
前端·人工智能·深度学习·物联网·vue3
铁皮饭盒1 小时前
网页端, 5.5mb谷歌模型, 识别躯干, 视频都不卡
前端·javascript·后端
雪芽蓝域zzs2 小时前
Prettier (代码格式化)配置详解
前端·vue.js
晴天162 小时前
Gradio 与 Streamlit:Python 快速构建 Web 应用的双子星-Day28
开发语言·前端·python
码哥DFS2 小时前
LRU缓存
前端·javascript·缓存