Acme Harmony UI Design System
Scaffolding & Core Components

页面范式与业务核心组件

提供了 5 大列表/工作台标准页面范式(ListPage、ListModalPage 等)的完整物理骨架,以及 AcmePrinter 物理打印、AcmeKnowledgeBase 知识检索、ProductCard 商品卡片、全局 SPU 检索抽屉及移动端滚动加载的完备 API 指导手册。

开发者指令: 统一从组件百科中查找标准调用,禁止自行堆砌 最后更新: 2026-05-28
01 / Scaffolding Tpl

标准列表管理页模板 (ListPage)

适用于常规 CRUD 单据或信息列表,包含顶部三列查询过滤面板、左右对称工具动作区以及自适应表格:


@{
    ViewBag.Title = "单据管理";
    Layout = "_MainLayout"; // 使用全局系统父布局
}

<!-- 统一列表范式主容器 -->
<div class="page-archetype-list" data-ui-archetype="ListPage">
    
    <!-- 1. 顶层过滤搜索区 -->
    <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="docType" id="docTypeSelect">
                    <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>

    <!-- 2. 中间操作区 (左右对齐) -->
    <div class="operetion-section">
        <div class="left-buttons">
            <button type="button" class="btn btn-success" id="btnAdd">
                <i class="fal fa-plus mr-1"></i>新建单据
            </button>
        </div>
        <div class="right-buttons" id="dataOperationsContainer">
            <!-- 由 AcmeGridOperations 批量操作组件渲染 -->
        </div>
    </div>

    <!-- 3. 数据网格区 (自适应撑满) -->
    <div class="data-section">
        <div id="myGridContainer" class="ag-theme-alpine" style="width: 100%;"></div>
    </div>
</div>
02 / Hardware Integration

硬件打印组件 (AcmePrinter)

1. 组件定位与运行模式

AcmePrinter 是 Acme.IBP 的统一物理打印和预览服务组件。它支持本地 PDF 直接渲染 (基于 PDF.js 预览容器)云端静默针式/标签打印 (对接本地打印代理) 双重运行模式。彻底摒弃以往使用浏览器原生 window.print() 出现的页眉页脚空白、字体模糊及打印布局变形等低级顽疾。

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

何时使用 (Do)
  • 财务报销凭证、发运面单、资产条码物理标签打印。
  • 需要在页面内嵌入精美非标 PDF 格式合同并提供下载。
  • 仓库 PDA 进行物料拣货单静默网络打印。
何时不用 (Don't)
  • 普通的屏幕报表及日志文本展示(应直接采用 ag-Grid 表格)。
  • 纯移动端无打印机连接场景。

3. JS API 参数配置表 (Options)

参数名 类型 默认值 用途功能说明
templateId String 必填 后台注册的物理打印模板 ID。定义了页宽、字体及边距约定。
data Object/Array - 用于动态插值的 JSON 数据。需与打印模板中的属性命名占位完全吻合。
silent Boolean false 是否启用静默打印。若为 true,直接提交给指定物理打印机而不再拉起本地驱动确认弹框。
printerName String "" 指定物理打印机全称。若空则直接调用系统当前默认打印设备。

4. 打印机方法初始化及示例

打印控制句柄统一使用 AcmePrinter.init(options) 作为入口:

// 1. 物理面单静默网络打印 (action: "print")
AcmePrinter.init({
    action: "print",
    templateId: "tpl_scm_waybill",
    silent: true,
    data: {
        receiverName: "张三",
        receiverPhone: "13800138000",
        address: "浙江省杭州市西湖区求是路 15 号",
        weightKg: 2.45
    },
    onSuccess: function() {
        Msg.Success("打印通知", "面单已成功推送至物理打印机");
    }
});

// 2. 局部 DOM 容器嵌入 PDF 预览 (action: "preview")
AcmePrinter.init({
    action: "preview",
    templateId: "tpl_finance_receipt",
    container: "#pdfViewerContainer",
    data: { id: "REC_2026_092" }
});
03 / AI & Knowledge

AI 知识检索组件 (AcmeKnowledgeBase)

1. 组件定位与本地匹配机制

AcmeKnowledgeBase 是系统深度整合的本地常见问题问答检索与文档速查组件。通过绑定前端输入框与显示面板,组件会在用户输入时,高频检索挂载在全局 window.LOCAL_SOLUTIONS 数据源中的知识点关键词,实现毫秒级的纯前端模糊过滤匹配展示,并触发对应跳转或自定义回调。

2. JS 驱动初始化参数 (Options)

JS 归属路径: wwwroot/js/components/acme.knowledgeBase.js
调用入口: AcmeKnowledgeBase.init(inputSelector, containerSelector, options)
参数名 类型 必填 详细功能与校验约定
solutions Array 本地匹配的数据集合。默认使用 window.LOCAL_SOLUTIONS。格式为 [{ keywords: ["A", "B"], title: "标题", url: "/docs" }]
onSelected Function 用户点击模糊匹配出的知识条目时触发的确认回调函数(参数为当前被选中的 solution 对象)。
emptyText String 没有匹配到任何知识项时的缺省占位提示词,默认 "暂无匹配的知识指引"

3. 标准接入示例

<!-- 1. Razor 视图层 DOM 规划 -->
<div class="form-group col-md-12">
    <label>关联业务操作规范指引 (本地知识匹配支持)</label>
    <input type="text" id="financeKbInput" class="form-control" placeholder="输入关键字模糊匹配业务流程..." />
    <div id="financeKbContainer" class="kb-results-panel mt-2"></div>
</div>

<script>
$(function() {
    // 2. 页面内注册初始化本地匹配知识库
    AcmeKnowledgeBase.init("#financeKbInput", "#financeKbContainer", {
        solutions: window.LOCAL_SOLUTIONS || [
            { keywords: ["差旅", "报销", "机票"], title: "集团差旅管理及飞机舱位审批标准说明", url: "/docs/finance/travel-rule" },
            { keywords: ["请假", "年假", "调休"], title: "员工考勤与年假折算标准规范手册", url: "/docs/hr/attendance-standard" }
        ],
        onSelected: function(solution) {
            console.log("用户点击选中了匹配方案: ", solution);
            // 典型动作:跳转到知识文档页面
            window.open(solution.url);
        }
    });
});
</script>
04 / Visual Grid Unit

商品与物料卡片 (ProductCard)

1. 视觉规格与物理设计约定

在供应链、SPU/SKU 配置仓储及大宗商品管理页面中,禁止使用粗糙表格平铺展现图片。必须统一使用 .product-card 专属设计卡片。卡片支持 0.98 倍物理压缩微缩点击反馈,配备高质感轻度悬浮投影(Box Shadow Transition)。

2. 卡片视觉规范与 HTML 结构

CSS 归属路径: wwwroot/css/components/product-card.css
JS 归属路径: wwwroot/js/components/product-card.js
调用方法: ProductCard.init(element, options)ProductCard.create(data, options)

在 HTML 结构中,.ep-product-card 是最外层卡片容器,鼠标滑过时会自动触发投影渐变和轻度向上浮动效果;.ep-product-card__image-wrapper 内部的图片在 hover 时会自动以 1.05 倍微幅放大,展现极高的触感质感。

<!-- 静态 HTML 布局规范 -->
<div class="ep-card ep-product-card ep-card--hover" data-sku="SPU10029">
    <div class="ep-product-card__image-wrapper" style="position: relative; padding-bottom: 100%; overflow: hidden; border-radius: 4px; background: #f8f9fa; margin-bottom: 8px;">
        <a href="/images/product-large.jpg" class="lightGalleryHref">
            <img data-src="/images/product.jpg" 
                 src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
                 alt="Moji Coffee Product"
                 style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity 0.3s;"
                 class="lazyload ep-product-card__img">
        </a>
        <div class="ep-badge ep-badge-danger" style="position: absolute; top: 8px; left: 8px; padding: 2px 6px; border-radius: 4px; color: white; background: var(--danger-color); font-size: 10px;">新品</div>
    </div>
    <div class="ep-product-card__content">
        <h3 class="ep-text-base ep-text-bold ep-mb-1" style="height: 2.4em; overflow: hidden; line-height: 1.2;">Moji 焦糖拿铁咖啡大杯</h3>
        <div class="ep-d-flex ep-justify-between ep-align-center ep-mb-2">
            <span class="ep-text-xs ep-text-muted">SKU: SPU10029</span>
            <span class="ep-text-xs ep-text-muted">库存: 99 件</span>
        </div>
        <div class="ep-d-flex ep-justify-between ep-align-center ep-mt-2">
            <div class="ep-product-price">
                <span class="ep-text-lg ep-text-bold ep-text-danger">¥28.00</span>
                <span class="ep-text-xs ep-text-muted" style="text-decoration: line-through;">¥32.00</span>
            </div>
            <button class="ep-btn ep-btn-sm ep-btn-primary ep-product-card__action">
                <i class="fas fa-cart-plus"></i>
            </button>
        </div>
    </div>
</div>

3. 动态 JS 实例化与渲染示例

对于通过 API 异步获取的商品列表,建议使用 ProductCard.create(data, options) 动态生成 DOM 节点并完成初始化:

// 1. 动态生成商品卡片并追加到容器中
var cardElement = ProductCard.create({
    sku: "SPU10029",
    title: "Moji 焦糖拿铁咖啡大杯",
    image: "/images/moji-coffee.jpg",
    badge: "热销",
    badgeType: "danger",
    stock: 99,
    price: 28.00,
    originalPrice: 32.00
}, {
    onAddToCart: function(cardData) {
        console.log("加入购物车商品 SKU:", cardData.sku);
        // 执行购物车逻辑...
    },
    onImageError: function(imgElement) {
        console.warn("图片加载失败,降级为默认占位图");
    }
});

$("#productContainer").append(cardElement);

// 2. 对于已存在的静态 HTML 卡片,也可以直接调用 init 方法初始化事件监听
ProductCard.init(document.getElementById("existingCard"), {
    onAddToCart: function(cardData) {
        // 事件响应...
    }
});
05 / Mobile & Touch

移动弹性滚动 (AcmeMobileScroll)

1. 组件定位与运行机制

在移动 PDA、微信 H5 及仓储操作 WebApp 窄屏设备下,常规纵向拉伸表格一律禁止显示。由于手持 PDA 经常涉及高频库位扫描,我们需要在原生 Webview 内提供完美平滑、支持弹性摩擦和阻尼控制的轻量级滚动面板。AcmeMobileScroll 正是充当了这层物理滚动包装器 (iScroll 封装) 的角色。

2. 滚动机制与 DOM 结构约定

JS 归属路径: wwwroot/js/components/acme.mobileScroll.js
调用入口: AcmeMobileScroll.init(root) (接收 DOM root 元素或 document,不接收选择器字符串;函数返回 undefined)

AcmeMobileScroll 底层仅负责扫描带有 [data-mobile-iscroll="true"] 属性的 DOM 根容器并为其绑定标准 IScroll 弹性触控滚动。它自身不负责从网络接口异步拉取分页数据。

3. 移动端列表滚动包装示例

使用带有 data-mobile-iscroll="true" 属性的根容器包裹条目,在 JS 中直接调用 AcmeMobileScroll.init(document) 即可。当有异步加载等高度变化时,使用 AcmeMobileScroll.refreshAll() 重新计算滚动高度。

<!-- 1. 移动端 HTML 布局结构 -->
<div id="myMobileScrollContainer" data-mobile-iscroll="true" class="mobile-scroll-wrapper" style="height: 100vh; overflow: hidden; position: relative;">
    <ul class="list-unstyled p-0 m-0" id="myPdaTaskQueue">
        <li class="mobile-task-card p-3 border-bottom" onclick="startPick('T101')">
            <div class="d-flex justify-content-between">
                <strong class="text-dark">PDA 待提货单 1001</strong>
                <span class="text-success">待处理</span>
            </div>
            <div class="small text-muted mt-1">货位号: A-04 | 数量: 15 件</div>
        </li>
        <li class="mobile-task-card p-3 border-bottom" onclick="startPick('T102')">
            <div class="d-flex justify-content-between">
                <strong class="text-dark">PDA 待提货单 1002</strong>
                <span class="text-success">待处理</span>
            </div>
            <div class="small text-muted mt-1">货位号: B-12 | 数量: 8 件</div>
        </li>
    </ul>
</div>

<script>
$(function() {
    // 2. 初始化整个页面范围内的移动端弹性滚动 wrapper (传入 DOM document)
    AcmeMobileScroll.init(document);
    
    // 3. 当页面有异步数据动态追加或清空时,必须调用静态方法 refreshAll 重新计算高度,否则会导致滑动受阻
    // 注意:不能通过 init 的返回值调用 .refresh() (因为它返回 undefined)
    setTimeout(function() {
        AcmeMobileScroll.refreshAll();
    }, 300);
});
</script>
代码复制成功!已写入剪贴板