标准列表管理页模板 (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>
硬件打印组件 (AcmePrinter)
1. 组件定位与运行模式
AcmePrinter 是 Acme.IBP 的统一物理打印和预览服务组件。它支持本地 PDF 直接渲染 (基于 PDF.js 预览容器) 和云端静默针式/标签打印 (对接本地打印代理) 双重运行模式。彻底摒弃以往使用浏览器原生 window.print() 出现的页眉页脚空白、字体模糊及打印布局变形等低级顽疾。
2. 何时使用 / 何时不要使用
- 财务报销凭证、发运面单、资产条码物理标签打印。
- 需要在页面内嵌入精美非标 PDF 格式合同并提供下载。
- 仓库 PDA 进行物料拣货单静默网络打印。
- 普通的屏幕报表及日志文本展示(应直接采用 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" }
});
AI 知识检索组件 (AcmeKnowledgeBase)
1. 组件定位与本地匹配机制
AcmeKnowledgeBase 是系统深度整合的本地常见问题问答检索与文档速查组件。通过绑定前端输入框与显示面板,组件会在用户输入时,高频检索挂载在全局 window.LOCAL_SOLUTIONS 数据源中的知识点关键词,实现毫秒级的纯前端模糊过滤匹配展示,并触发对应跳转或自定义回调。
2. JS 驱动初始化参数 (Options)
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>
商品与物料卡片 (ProductCard)
1. 视觉规格与物理设计约定
在供应链、SPU/SKU 配置仓储及大宗商品管理页面中,禁止使用粗糙表格平铺展现图片。必须统一使用 .product-card 专属设计卡片。卡片支持 0.98 倍物理压缩微缩点击反馈,配备高质感轻度悬浮投影(Box Shadow Transition)。
2. 卡片视觉规范与 HTML 结构
wwwroot/css/components/product-card.cssJS 归属路径:
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) {
// 事件响应...
}
});
移动弹性滚动 (AcmeMobileScroll)
1. 组件定位与运行机制
在移动 PDA、微信 H5 及仓储操作 WebApp 窄屏设备下,常规纵向拉伸表格一律禁止显示。由于手持 PDA 经常涉及高频库位扫描,我们需要在原生 Webview 内提供完美平滑、支持弹性摩擦和阻尼控制的轻量级滚动面板。AcmeMobileScroll 正是充当了这层物理滚动包装器 (iScroll 封装) 的角色。
2. 滚动机制与 DOM 结构约定
wwwroot/js/components/acme.mobileScroll.js调用入口:
AcmeMobileScroll.init(root) (接收 DOM root 元素或 document,不接收选择器字符串;函数返回 undefined)
AcmeMobileScroll 底层仅负责扫描带有 [data-mobile-iscroll="true"] 属性的 DOM 根容器并为其绑定标准 IScroll 弹性触控滚动。它自身不负责从网络接口异步拉取分页数据。
在初始化时,AcmeMobileScroll 会在底层自动扫描并在这个根容器下动态生成包装层 .mobile-iscroll-scroller,并自动把原有的所有子节点搬移进去。因此开发人员不需要手动在外层包裹单一子元素,直接平铺子条目列表即可。
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>