Acme Harmony UI Design System
Components / Data Display & Operations

数据表格与批量操作

规范了以 ag-Grid 为基础的 acmeAgGrid 表格初始化、自适应视口监听、多选内存拦截,以及高度整合了右侧数据动作挂载点 dataOperationsContainer、操作按钮组 AcmeGridOperations 和 data-operations.css 样式包的数据操作体系。

组件源文件: wwwroot/js/components/acme.AgGrid.js & acme.gridOperations.js 样式文件: wwwroot/css/components/data-operations.css 最后更新: 2026-05-28
01 / Overview

组件定位与选用决策

在 Acme.IBP 后台系统中,大宗数据展现与批量流转是业务核心。为了维持视觉的高度统一并提供流畅的交互,我们设计了以 表格呈现 (acmeAgGrid) 为主体,数据动作管控 (AcmeGridOperations) 为核心的列表页体系。所有列表页的动作必须遵循“左业务、右数据”的布局铁律,且必须杜绝写死高度造成的“双重垂直滚动条”。

高度自适应硬性红线

任何列表页表格禁止写死固定高度 (如 style="height:500px")。必须使用 acmeAgGrid.enableResizeListener(selector) 启用全局高度调整,系统会自动扣除顶部检索区 .filter-section、操作区 .operetion-section 及底部分页条的高度,以动态拉伸撑满视口剩余空间。

02 / Component Spec

acmeAgGrid 核心表格组件

1. 组件定位与基本描述

基于企业级高能表格引擎 ag-Grid Enterprise 进行二次深度定制封装,内置了服务端自动分页机制、多选主键内存勾选集自动拦截、自适应动画窗口 resize、列检索过滤器等开箱即用能力,是 Acme.IBP 唯一的官方列表展示组件。

2. 何时使用 / 何时不要使用

何时使用 (Do)
  • 展示多列、多属性的结构化业务实体数据。
  • 需要支持前端/服务端高频字段过滤筛选、自定排序。
  • 包含多选批量流转审批、异步大批量 Excel 导入导出。
何时不用 (Don't)
  • 展示非结构化的纯文本、博客、日志流(应采用卡片或 Timeline 样式)。
  • 移动端数据列表(移动端必须采用 AcmeMobileScroll 无限滚动列表)。
  • 仅有 3-5 行的极简静态属性配置展示(使用普通 Bootstrap 简易 Table 即可)。

3. 资源归属与引用

JS 路径: wwwroot/js/components/acme.AgGrid.js
初始化入口: var myGrid = acmeAgGrid.init(options);
自适应绑定: acmeAgGrid.enableResizeListener('#MyGridContainer');

4. 核心参数配置表 (Options)

参数名 (Option) 类型 必填 默认值 详细功能与规范说明
div String - 容器选择器名,例如 '#MyGridContainer'。注意必须包含 ID 前缀 #
url String - 异步数据拉取 API 终结点。分页请求体和响应体默认符合 Furion 后端 IPagedList 规范。
primaryKey String 'id' 数据行主键字段。决定了多选操作中 getSelectedRowIds() 返回的唯一标示。
pageSize Number 20 分页大小,系统下拉支持 20, 50, 100, 200 可选。
isSelect Boolean false 是否在表格最左侧渲染复选框列,用于批量选取操作。
rowSelection String 'multiple' 行选中模式。'single' 为单选,'multiple' 为多选。
columnDefs Array - 列定义配置集合。具体子配置项如下表。

5. 列配置详细子属性 (columnDefs)

属性名 类型 可选值 功能说明与设计约定
headerName String - 表头文本标题。必须精准概括,超过 6 字应缩写或加 tooltip。
field String - 绑定数据的 JSON Key。驼峰或帕斯卡命名需与后端返回结构大小写完全一致。
width Number - 列宽度(像素)。重要列建议显式声明宽度,普通文本建议 120-160,状态 90,操作列 100。
sortable Boolean true / false 是否支持列排序。默认开启。非库表直连物理字段建议设置为 false
filter String/Boolean agTextColumnFilter / agNumberColumnFilter / false ag-Grid 列置顶浮动过滤器类型。若为 false 则关闭该列快捷过滤。
cellRenderer Function - 行单元格 HTML 自定义渲染器。入参 p(包含 p.value, p.data 完整行数据)。必须确保返回安全的转义 HTML 字符串。
valueFormatter Function - 值转义/格式化器。入参 p。常用于千分位数值处理、时间戳转 YYYY-MM-DD

6. 实例核心 API & 公开方法

调用 acmeAgGrid.init 后返回的实例对象,支持以下标准方法:

方法签名 返回值 用途描述与调用约束
refresh() void 保持当前查询参数和分页码,重新拉取并渲染表格数据。适用于局部静默刷新。
search(queryParams) void 传入过滤参数对象,重置页码为 1 并向后端发起带有新过滤条件的数据查询。
getSelectedRows() Array<Object> 获取当前选中的所有行数据数组。若未开启 isSelect 则返回空数组。
getSelectedRowIds() Array<String/Number> 快速获取所有勾选行的 primaryKey(默认 id)值集合。用于批量提交操作。

7. 后端返回数据结构契约

acmeAgGrid 默认底层数据拉取约定后端返回 JSON 必须遵循 Furion 的标准 IPagedList 响应包装结构:

{
  "statusCode": 200,
  "succeeded": true,
  "data": {
    "items": [
      { "id": "1001", "docNo": "DOC202605001", "amount": 12500.00, "status": 1, "statusText": "待审核" },
      { "id": "1002", "docNo": "DOC202605002", "amount": 8800.50, "status": 2, "statusText": "已通过" }
    ],
    "totalCount": 142,
    "pageIndex": 1,
    "pageSize": 20,
    "totalPages": 8,
    "hasPrevPages": false,
    "hasNextPages": true
  },
  "errors": null,
  "extras": null,
  "timestamp": 1779933560000
}
03 / Operations Component

批量操作与挂载容器 (DataOperations)

1. 组件构成与资源路径

数据操作区并非单一组件,而是由“两层定义、一套样式”协同构成的规范系统:

  • 挂载点容器 dataOperationsContainer: 列表页操作条 .operetion-section 右侧固定占位 ID 容器。所有系统动作默认在此渲染,禁止自写按钮。
  • 动作驱动器 AcmeGridOperations: 封装在 acme.gridOperations.js 中的 JS 控制器,动态渲染刷新、导入、导出、批量删除按钮并绑定对应行为。
  • 基础样式包 data-operations.css: 集中处理右侧按钮对齐、Dropdown 下拉组堆叠动画、异步导入拖拽上传框的专属样式。

2. 左右对称排布核心原则

为了消灭以往按钮散落、各列表页排版凌乱的问题,Acme.IBP 规定 .operetion-section 必须实行强力对称划分:

左侧区域 (class="left-buttons")
专放业务创建动作:如“新建单据”、“录入合同”、“批量指派人员”等。
右侧容器 (id="dataOperationsContainer")
专放通用数据动作:如刷新、大宗导入、导出 Excel、批量删除等。

3. AcmeGridOperations 初始化参数 (Options)

参数名 (Option) 类型 必填 功能说明与设计细节
containerId String 挂载的容器 ID,不带 #。固定为 'dataOperationsContainer'
grid Object 关联的 acmeAgGrid 实例,用于操作前读取勾选行、操作后静默刷新。
delete Object 批量删除动作。配置格式:{ api: '/api/...', permission: 'auth_key', onSuccess: fn }。按钮渲染为红色高危警示,执行前自动拉起二次弹窗。
exportList Array<Obj> 导出动作集合。如果有多项,系统会自动折叠为 Dropdown 下拉组。如果导出数据量预估超过 1000 条,必须在此对接 TaskCenter 走异步队列,防止网关超时挂起。
importList Array<Obj> 导入动作集合。配置项中必须配置 template 下载链接。点击导入按钮后,系统会自动弹窗包含直传 OSS 组件的上传框。

4. 异步任务与回调触发机制

当用户在 exportList 中点击触发异步导出时,若后端接口生成并返回任务标识 taskNoAcmeGridOperations 会自动执行:

  1. 调用统一的 Msg.Success 向用户通知“导出任务创建成功,任务编号为: [taskNo]”。
  2. 自动触发该导出配置项绑定的 onSuccess(taskNo) 回调函数。
  3. 开发人员可在 onSuccess 回调中,引导用户通过点击右上角的系统任务中心进行进度查看和文件物理下载,AcmeGridOperations 自身不内置任何后台定时轮询或长连接逻辑。

5. 常见交互实现错误

04 / Code Pattern

标准列表与数据操作全页模板 (RZ安全版)

以下是遵循全部红线与规范的完整物理列表页骨架代码。已对 Razor 冲突标签进行了彻底的字符实体安全转义,请在页面开发时直接放心复制:

<!-- 1. DOM 骨架规划 -->
<div class="page-archetype-list">
    <!-- 顶部过滤区 -->
    <div class="filter-section" id="myFilterSection">
        <form id="searchForm" class="form-row align-items-end">
            <div class="form-group col-md-3">
                <label>关键字检索</label>
                <input type="text" class="form-control" name="keyword" placeholder="订单号/供应商" />
            </div>
            <div class="form-group col-md-3">
                <label>订单状态</label>
                <select class="form-control" name="status" id="statusSelect">
                    <option value="">全部</option>
                    <option value="1">待处理</option>
                    <option value="2">已完成</option>
                </select>
            </div>
            <div class="form-group col-md-3">
                <button type="button" class="btn btn-primary" id="btnSearch">
                    <i class="fal fa-search mr-1"></i>查询
                </button>
                <button type="button" class="btn btn-secondary ml-2" id="btnReset">
                    重置
                </button>
            </div>
        </form>
    </div>

    <!-- 标准对称操作条区块 -->
    <div class="operetion-section">
        <!-- 左侧: 业务创建按钮 -->
        <div class="left-buttons">
            <button type="button" class="btn btn-success" id="btnAddOrder">
                <i class="fal fa-plus mr-1"></i>录入新订单
            </button>
        </div>
        <!-- 右侧: AcmeGridOperations 批量操作专属挂载点 -->
        <div class="right-buttons" id="dataOperationsContainer">
            <!-- 按钮结构由 JS 动态生成渲染,请勿在此堆积 HTML -->
        </div>
    </div>

    <!-- 自适应高度数据表格区 -->
    <div class="data-section">
        <div id="MyOrdersGrid" class="ag-theme-alpine" style="width: 100%;"></div>
    </div>
</div>

<script>
$(function () {
    // 2. 初始化核心表格
    var orderGrid = acmeAgGrid.init({
        div: '#MyOrdersGrid',
        url: '/api/star-pomelo/orders/page-list',
        primaryKey: 'id',
        isSelect: true,
        rowSelection: 'multiple',
        pageSize: 20,
        columnDefs: [
            { 
                headerName: '订单编号', 
                field: 'orderNo', 
                width: 150, 
                filter: 'agTextColumnFilter',
                cellRenderer: function(p) {
                    return '<a href="javascript:void(0)" class="grid-link" onclick="openDetail(\'' + p.data.id + '\')">' + p.value + '</a>';
                }
            },
            { 
                headerName: '销售额', 
                field: 'totalAmount', 
                width: 120, 
                filter: 'agNumberColumnFilter',
                valueFormatter: function(p) { 
                    return p.value ? '¥' + Number(p.value).toLocaleString() : '¥0'; 
                } 
            },
            { 
                headerName: '订单状态', 
                field: 'statusText', 
                width: 100,
                cellRenderer: function(p) {
                    var color = p.data.status === 2 ? '#10b981' : '#f59e0b';
                    return '<span class="ds-status-tag" style="background:' + color + '15; color:' + color + ';">' + p.value + '</span>';
                }
            }
        ]
    });

    // 启用表格高度视口自适应(核心红线)
    acmeAgGrid.enableResizeListener('#MyOrdersGrid');

    // 3. 关联批量操作动作
    AcmeGridOperations.init({
        containerId: 'dataOperationsContainer',
        grid: orderGrid,
        delete: {
            api: '/api/star-pomelo/orders/delete-batch',
            permission: 'order_delete_batch',
            onSuccess: function() {
                Msg.Success('操作提示', '选中订单批量删除成功');
            }
        },
        exportList: [
            {
                label: '导出当前页订单',
                api: '/api/star-pomelo/orders/export-current',
                fileName: '当前订单表',
                permission: 'order_export_simple'
            },
            {
                label: '导出大宗历史明细 (异步任务)',
                api: '/api/star-pomelo/orders/export-all-async',
                permission: 'order_export_async' // 注意:AcmeGridOperations 自身仅识别接口返回的 taskNo 并触发提示/回调;真正的飞行动画和 TaskCenter 挂接是由全局 Ajax 成功拦截器静默劫持处理
            }
        ],
        importList: [{
            label: '导入销售单据',
            api: '/api/star-pomelo/orders/import-excel',
            template: 'https://oss-scm.ihzxy.com/templates/order_import_template.xlsx',
            permission: 'order_import',
            onSuccess: function() {
                Msg.Success('导入完毕', '数据已更新并静默刷新表格');
            }
        }]
    });

    // 4. 查询与重置
    $('#btnSearch').click(function () {
        var params = $('#searchForm').serializeObject();
        orderGrid.search(params);
    });

    $('#btnReset').click(function () {
        $('#searchForm')[0].reset();
        orderGrid.search({});
    });
});

function openDetail(id) {
    CommonDrawer.open('/star-pomelo/orders/detail?id=' + id, '订单详情', 'lg');
}
</script>
05 / Quality Assurance

交付验收清单

# 自检标准项 标准达成指引(未通过者不予发布)
1 左右排版是否规范? 左侧放置“新增”、“指派”等业务动作按钮;右侧固定使用 dataOperationsContainer,不允许有单独的导出/导入/刷新按钮游离在容器外。
2 高度是否彻底自适应? 打开页面后,视口不可产生双重垂直滚动条。缩放浏览器窗口时,表格高度应平滑变化,表头与底部分页器不发生遮挡。
3 大文件导出是否异步? 大文件、全量导出选项必须对接 TaskCenter。严禁让浏览器在导出期间处于旋转加载、无限转圈的白屏状态。
4 空值处理是否优雅? 数值型字段没有值时应使用 valueFormatter 转义显示 0;字符串类型为空时显示 - 占位,严禁留白。
代码复制成功!已写入剪贴板