ProTable 控件介绍
ProTable 是页面设计器中的通用远程表格控件,适合直接调用 HTTP API,并在页面中配置列、查询工具栏、分页和行操作。
阅读本手册后,你可以完成以下配置:
- 使用 Custom URL 对接 GET 或 POST 分页接口
- 通过 Query Context 将 Input 等控件的当前值传给接口
- 使用 Search 按钮发起一次从第一页开始的新查询
- 在翻页时继续携带当前查询条件
- 配置响应数据路径、列、行唯一键和显示样式
- 按本文的 Java 示例实现一个可供 ProTable 使用的后端接口
本文使用 Spark 自带的商品查询示例。接口提供 23 条模拟商品数据,并支持通过 keyword 对商品编码和名称做模糊查询。
1. ProTable 的适用场景
1.1 优先使用 ProTable 的情况
以下场景适合使用 ProTable:
- 页面需要直接请求自定义后端接口
- 数据来自第三方系统或聚合服务,不是单一 Business Object
- 接口已经有自己的分页、筛选、排序或权限逻辑
- 按钮调用接口后,需要通过
setTableData将结果数组写入表格
1.2 与 DataList、TaskList 的区别
| 控件 | 主要数据 | 适用场景 |
|---|---|---|
| ProTable | Custom URL 或 Model Data Source | 通用 HTTP API、聚合数据、定制分页协议 |
| DataList | Business Object | DQL、模型字段、对象动作和模型数据权限 |
| TaskList | Spark Tasks | 任务 Filter、任务状态、办理和任务上下文 |
ProTable 也支持 Model Data Source,其模型绑定方式与 DataList 类似。围绕 Business Object、DQL 和模型筛选的配置,请阅读 DataList 控件介绍。本文重点介绍 ProTable 独有的 Custom URL 用法。
2. 本文示例
示例接口:
POST https://spark.hiforce.ai/spark/api/sample/protable/products
示例页面已经配置为:
- 数据源:Custom URL
- 请求方式:POST
- 默认分页:10 条/页
- Left Toolbar:一个
Key WordInput 和一个SearchButton - Query Context:把 Input 的值映射为请求字段
keyword - Search 交互:调用当前 ProTable 的
reloadTable - 列:
code、name、category、price、stock
可以在登录 Spark 并具有该应用权限后打开本文对应的运行态页面验证。
3. Custom URL 分页契约
3.1 POST 请求
ProTable 加载第一页时,会把分页参数和 Query Context 参数合并为 JSON 请求体:
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
current | number | 当前页码,从 1 开始 |
pageSize | number | 每页记录数 |
keyword | string | 本文通过 Query Context 增加的业务查询参数 |
current 和 pageSize 由 ProTable 自动维护,不需要在 Query Context 中重复定义。
3.2 GET 请求
选择 GET 时,同一组参数会转换为 URL Query String:
/spark/api/sample/protable/products?current=1&pageSize=10&keyword=Graphite
GET 请求会忽略 undefined、null 和空字符串参数。数组或对象参数会被序列化为 JSON 字符串,后端需要按 JSON 解析。
3.3 默认响应结构
本文示例返回 Spark 标准 ApiResult,分页数据位于 data 中:
{
"success": true,
"code": 200,
"message": "商品数据加载成功。",
"data": {
"total": 1,
"current": 1,
"pageSize": 10,
"requestContext": {
"environment": "DEV",
"renderMode": "RUNTIME"
},
"records": [
{
"id": 4,
"code": "DSP-2002",
"name": "Graphite Monitor 32",
"category": "DISPLAY",
"status": "ACTIVE",
"price": 699.00,
"stock": 21,
"updatedAt": "2026-07-25T16:40:00Z"
}
]
}
}
因此数据源路径配置为:
Rows Path: data.records
Total Path: data.total
Current Page Path: data.current
Page Size Path: data.pageSize
路径使用点号读取嵌套属性。接口结构不同时,应填写真实路径。例如,记录数组位于 result.items 时,Rows Path 应填写 result.items。
分页接口必须返回过滤后的总记录数,而不是当前页数组长度。否则 ProTable 无法计算正确的总页数。
3.4 页面请求上下文 Header
ProTable 使用 Custom URL 发出真实 HTTP 请求时,会自动携带两个标准 Header。后端可以据此选择测试数据、限制设计器访问,或记录调用来源:
| Header | 枚举值 | 说明 |
|---|---|---|
X-Spark-Page-Environment | DEV、TEST、PROD | 当前页面环境;设计器中的数据预览使用 DEV |
X-Spark-Page-Render-Mode | DESIGN_PREVIEW、RUNTIME | Page Designer、设计器打开的 Preview 和真实记录预览使用 DESIGN_PREVIEW;普通运行页使用 RUNTIME |
环境和渲染场景是两个独立维度。例如,开发环境运行页发送 DEV + RUNTIME,设计器内加载真实记录发送 DEV + DESIGN_PREVIEW。这些值是稳定枚举,后端不应依赖页面 URL 或 Referer 猜测调用场景。
这是所有 Page Designer 和 Page Runtime 后端请求的统一规范,不局限于 ProTable 或 Custom URL。Model Data Source、Data Form、Task 控件、上传、行操作和 Send Request 等通道也会携带相同 Header。GET 和 POST 的行为相同;如果 Custom URL 指向跨域服务,服务端 CORS 配置必须在 Access-Control-Allow-Headers 中允许这两个 Header。
4. 添加并配置 ProTable
4.1 添加控件
- 打开目标页面并进入 Page Designer。
- 在左侧组件面板展开 Data 分组。
- 将 ProTable 拖入页面中的可接收容器。
- 选中 ProTable,建议设置一个稳定、易识别的控件名称,例如
productTable。
稳定的名称便于在 Button 的 Component API 中选择正确目标。
4.2 配置 Custom URL
在 ProTable 的 Data Source 中选择 Custom URL,按本文示例填写:
| 属性 | 值 |
|---|---|
| URL | https://spark.hiforce.ai/spark/api/sample/protable/products |
| Request Method | POST |
| Rows Path | data.records |
| Total Path | data.total |
| Current Page Path | data.current |
| Page Size Path | data.pageSize |
URL 支持三种形式:
https://api.example.com/products
connection://erp/products
/products # 同时选择 Connection Key
- 同域 Spark API 可以使用完整 URL 或站内相对地址。
- 外部服务建议先在应用设置中配置 External Connection,再使用 Connection Key。
- 密钥、Token 和密码应维护在连接配置中,不要写入 URL 或 Query Context。
- 接口仍受登录态、数据权限和后端鉴权约束;浏览器直接访问受保护接口时可能返回
401。
配置 Response Fields
Model Data Source 可以直接读取 Business Object 字段元数据,Custom URL 则需要在 Data Source 中维护 Response Fields。每一项包含:
| 属性 | 说明 |
|---|---|
| Field Path | 相对于单条记录的字段路径,例如 name 或 owner.name |
| Label | Columns 等字段选择器中显示的名称 |
| Data Type | TEXT、NUMBER、BOOLEAN、DATE、DATETIME、URL、OBJECT 或 ARRAY |
可以点击加号手工添加,也可以点击 Test & Import 请求最多 20 条记录并合并字段。导入操作遵循以下规则:
- 只在用户明确点击时请求后台,打开对话框不会自动请求。
- 使用当前 URL、Method、Query Context 和 Rows Path。
- 嵌套对象转换为点路径;数组只登记数组本身。
- 保留现有字段的 Label、Data Type 和顺序,只追加新路径。
- 不保存样例值;空响应或失败不会覆盖现有配置。
Test & Import 使用 DEV + DESIGN_PREVIEW 页面请求上下文。数据源对话框中的修改只有点击 Save 后才进入页面配置,Cancel 会丢弃当前草稿。
4.3 配置 Columns
保存 Data Source 后,在 Columns 中按顺序添加:
| Label | Data Field |
|---|---|
| Code | code |
| Name | name |
| Category | category |
| Price | price |
| Stock | stock |
Data Field 下拉列表会显示已保存的 Response Fields;Model Data Source 则显示 Business Object 字段。选择字段时会同步使用字段 Label 作为列标题。确实需要动态字段时仍可输入自定义点路径。例如接口记录中存在 owner.name,列字段可以使用:
owner.name
列宽应根据内容设置。编码列可以较窄,名称列应预留更多空间。不要依赖超长文本自动撑开整个表格。
4.4 配置分页和行唯一键
建议配置:
Pagination / Page Size: 10
Row Unique Key: id
Row Unique Key 对应的字段必须稳定且唯一。不要使用名称、状态等可能重复或变化的字段。
运行态可切换 10、20、50 条/页。页码或每页数量变化后,ProTable 会重新请求接口,并保留当前 Query Context 查询条件。
5. 在 Left Toolbar 中添加查询控件
选中 ProTable,在 Left Toolbar 中添加 Input:
Label: Key Word
Name: productKeywordInput
Allow Clear: true
本文实际页面中的控件内部 ID 为 field_bfd1e2b2。在自己的页面中不需要复制这个 ID,只要在 FX 中选择刚添加的 Input 即可。
Allow Clear 开启后,Input 有内容时会显示清除图标。点击清除图标只会清空控件值;需要再次点击 Search,才会使用空条件重新请求表格。
工具栏中的常见布局是:
- Left Toolbar:Input、Date Picker、Select、Radio 等查询条件和 Search
- Toolbar:Create、Import、Export 等业务动作
如果希望查询条件和 Search 保持在同一水平线上,可以像本文示例一样把 Search 也放入 Left Toolbar。
6. 使用 Query Context 绑定 keyword
Query Context 用于把页面运行时值映射成 HTTP 请求参数。本文需要得到以下映射:
Input 当前值 → productKeyword 变量 → keyword 请求字段
6.1 添加变量
- 编辑 ProTable 的 Data Source。
- 在 Query Context 中点击 FX。
- 点击 Add Variable。
- Variable Name 填写
productKeyword。 - Source 选择 Component Value。
- Component 选择刚添加的
Key WordInput。
6.2 添加请求参数
在 Query Context 的参数区域添加:
Parameter: keyword
Value: [[productKeyword]]
运行时会在每次请求前重新读取 Input 的当前值。用户没有输入内容时,keyword 为空;本文后端会忽略空白关键字,因此返回全部数据。
6.3 常用变量来源
| 来源 | 适用场景 |
|---|---|
| Component Value | Input、Select、Radio、Date Picker、Uploader 等控件当前值 |
| URL Parameter | 浏览器地址中的查询参数 |
| Page Context | 页面上下文或前置交互写入的值 |
| Current Row | 行操作中的当前记录字段 |
| Static Value | 固定字符串、数字或布尔值 |
Target Value 可以只引用一个变量,也可以组合多个变量:
[[productKeyword]]
prefix-[[productKeyword]]
当 Target Value 完全等于 [[变量名]] 时,会尽量保留变量的原始类型。数组、对象、数字或布尔参数不必先转成字符串。
7. 配置 Search 按钮
在 Left Toolbar 中添加 Button:
Text: Search
Button Type: Primary
然后配置点击交互:
- 选中 Search Button,打开 Interaction editor。
- 新增 Component API 动作。
- Target Component 选择当前 ProTable,例如
productTable。 - Function 选择
reloadTable。 - 保存交互配置。
reloadTable 表示一次新的查询,它会:
- 把当前页码重置为
1。 - 重新解析 Query Context 中的控件值。
- 使用新的
current、pageSize和keyword请求接口。
这个行为解决了一个常见问题:用户原本在第 2 页,输入一个只有 1 条匹配结果的关键字后,如果仍发送 current: 2,接口会正确返回空数组。Search 使用 reloadTable 后会发送 current: 1,因此能够找到结果。
普通翻页不是新搜索。用户点击下一页时,ProTable 只修改 current,并继续携带当前 keyword。因此行为是:
点击 Search:重置到第 1 页 + 保留最新查询条件
点击分页:切换页码 + 保留当前查询条件
8. Java 后端示例
Spark 仓库中的完整实现位于:
spark-sample/src/main/java/hiforce/spark/sample/protable/
spark-sample 已由 spark-console 引入,因此接口会随 Spark Console 一起发布。下面列出实现 ProTable Custom URL 所需的核心代码。
8.1 请求 DTO
package hiforce.spark.sample.protable.model;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
public class ProTableProductQueryRequest {
private Integer current = 1;
private Integer pageSize = 10;
private String keyword;
private String category;
private String status;
private BigDecimal minPrice;
private BigDecimal maxPrice;
}
除了本文使用的 keyword,示例还预留了分类、状态和价格区间。仓库完整实现还包含通用条件 conditions 和排序字段 sortBy。
8.2 行 DTO 与分页结果
package hiforce.spark.sample.protable.model;
import hiforce.spark.base.model.SparkPageRequestContext;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
@Getter
@AllArgsConstructor
public class ProTableProductDTO {
private final Long id;
private final String code;
private final String name;
private final String category;
private final String status;
private final BigDecimal price;
private final Integer stock;
private final Instant updatedAt;
}
@Getter
public class ProTablePageResult<T> {
private final long total;
private final int current;
private final int pageSize;
private final List<T> records;
private final SparkPageRequestContext requestContext;
public ProTablePageResult(long total, int current, int pageSize,
List<T> records, SparkPageRequestContext requestContext) {
this.total = total;
this.current = current;
this.pageSize = pageSize;
this.records = records;
this.requestContext = requestContext;
}
}
实际工程中两个 public 类应分别放在同名 Java 文件中。这里合并展示是为了说明请求和响应契约。
8.3 Controller
package hiforce.spark.sample.protable.web;
import hiforce.spark.base.model.result.ApiResult;
import hiforce.spark.base.web.SparkApiResultHelper;
import hiforce.spark.base.web.SparkPageRequestHeaders;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import hiforce.spark.sample.protable.service.ProTableProductService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Locale;
@RestController
@RequestMapping("/spark/api/sample/protable/products")
public class ProTableProductController {
private final ProTableProductService productService;
public ProTableProductController(ProTableProductService productService) {
this.productService = productService;
}
@PostMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> query(
@RequestBody(required = false) ProTableProductQueryRequest request,
@RequestHeader(value = "Accept-Language", required = false) String language,
@RequestHeader(value = SparkPageRequestHeaders.ENVIRONMENT, required = false) String environment,
@RequestHeader(value = SparkPageRequestHeaders.RENDER_MODE, required = false) String renderMode) {
Locale locale = SparkApiResultHelper.resolveLocale(language);
return SparkApiResultHelper.success(
locale,
productService.query(
request,
SparkPageRequestHeaders.resolve(environment, renderMode)),
"SPARK-SAMPLE-PROTABLE-0001");
}
}
Controller 返回 DTO 和分页结果,并统一包装为 ApiResult。SparkPageRequestHeaders.resolve(...) 将 Header 规范化为共享的 SparkPageRequestContext;缺失或非法环境值回退为 DEV,缺失或非法渲染场景回退为 RUNTIME。面向用户的成功文案通过 Lattice Message Code SPARK-SAMPLE-PROTABLE-0001 国际化,不在 Controller 中写死。
8.4 查询、模糊匹配和分页
package hiforce.spark.sample.protable.service;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class ProTableProductService {
private static final int MAX_PAGE_SIZE = 100;
private static final List<ProTableProductDTO> PRODUCTS = List.of(
product(1, "SPK-1001", "SparkBook Air", "COMPUTING", "ACTIVE", "999.00", 32,
"2026-07-27T14:20:00Z"),
product(2, "SPK-1002", "SparkBook Pro", "COMPUTING", "ACTIVE", "1599.00", 18,
"2026-07-28T09:10:00Z"),
product(3, "DSP-2001", "Aurora Monitor 27", "DISPLAY", "ACTIVE", "499.00", 45,
"2026-07-26T08:30:00Z"),
product(4, "DSP-2002", "Graphite Monitor 32", "DISPLAY", "ACTIVE", "699.00", 21,
"2026-07-25T16:40:00Z"),
product(5, "ACC-3001", "Pulse Mechanical Keyboard", "ACCESSORY", "ACTIVE", "129.00", 86,
"2026-07-24T11:00:00Z"),
product(6, "ACC-3002", "Flow Wireless Mouse", "ACCESSORY", "ACTIVE", "79.00", 120,
"2026-07-23T13:15:00Z"),
product(7, "ACC-3003", "Runtime Dock", "ACCESSORY", "ACTIVE", "219.00", 38,
"2026-07-22T09:45:00Z"),
product(8, "STO-4001", "Core SSD 1TB", "STORAGE", "ACTIVE", "149.00", 64,
"2026-07-21T15:20:00Z"),
product(9, "STO-4002", "Core SSD 2TB", "STORAGE", "ACTIVE", "269.00", 41,
"2026-07-20T10:05:00Z"),
product(10, "NET-5001", "Lattice Router", "NETWORK", "ACTIVE", "189.00", 27,
"2026-07-19T17:25:00Z"),
product(11, "NET-5002", "Mesh Access Point", "NETWORK", "ACTIVE", "139.00", 54,
"2026-07-18T12:35:00Z"),
product(12, "CMP-6001", "Build Station", "COMPUTING", "DRAFT", "2399.00", 7,
"2026-07-17T08:50:00Z"),
product(13, "DSP-2003", "Silk Portable Display", "DISPLAY", "ACTIVE", "329.00", 33,
"2026-07-16T14:10:00Z"),
product(14, "ACC-3004", "Spark AI Headset", "ACCESSORY", "ACTIVE", "199.00", 49,
"2026-07-15T10:45:00Z"),
product(15, "ACC-3005", "Console Camera", "ACCESSORY", "ACTIVE", "159.00", 72,
"2026-07-14T09:35:00Z"),
product(16, "STO-4003", "Archive Drive 8TB", "STORAGE", "ACTIVE", "289.00", 25,
"2026-07-13T18:20:00Z"),
product(17, "NET-5003", "Secure Gateway", "NETWORK", "DRAFT", "799.00", 5,
"2026-07-12T07:30:00Z"),
product(18, "CMP-6002", "Runtime Server", "COMPUTING", "ACTIVE", "3499.00", 4,
"2026-07-11T16:00:00Z"),
product(19, "ACC-3006", "USB-C Travel Hub", "ACCESSORY", "DISCONTINUED", "59.00", 0,
"2026-07-10T11:40:00Z"),
product(20, "DSP-2004", "Aurora Display Mini", "DISPLAY", "DRAFT", "279.00", 12,
"2026-07-09T13:05:00Z"),
product(21, "PRN-7001", "Flow Label Printer", "OFFICE", "ACTIVE", "249.00", 28,
"2026-07-08T08:25:00Z"),
product(22, "AUD-8001", "Aurora Conference Speaker", "AUDIO", "ACTIVE", "349.00", 16,
"2026-07-07T15:35:00Z"),
product(23, "PWR-9001", "Core Power Station", "POWER", "DRAFT", "899.00", 9,
"2026-07-06T10:15:00Z"));
public ProTablePageResult<ProTableProductDTO> query(ProTableProductQueryRequest request) {
ProTableProductQueryRequest query = request == null
? new ProTableProductQueryRequest()
: request;
int current = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1);
int pageSize = Math.min(
Math.max(query.getPageSize() == null ? 10 : query.getPageSize(), 1),
MAX_PAGE_SIZE);
String keyword = StringUtils.trimToNull(query.getKeyword());
List<ProTableProductDTO> filtered = PRODUCTS.stream()
.filter(product -> keyword == null
|| StringUtils.containsIgnoreCase(product.getCode(), keyword)
|| StringUtils.containsIgnoreCase(product.getName(), keyword))
.sorted(Comparator.comparing(ProTableProductDTO::getCode))
.toList();
long requestedOffset = (long) (current - 1) * pageSize;
int fromIndex = (int) Math.min(requestedOffset, filtered.size());
int toIndex = Math.min(fromIndex + pageSize, filtered.size());
return new ProTablePageResult<>(
filtered.size(),
current,
pageSize,
new ArrayList<>(filtered.subList(fromIndex, toIndex)));
}
private static ProTableProductDTO product(long id, String code, String name,
String category, String status, String price,
int stock, String updatedAt) {
return new ProTableProductDTO(
id, code, name, category, status,
new BigDecimal(price), stock, Instant.parse(updatedAt));
}
}
示例中的关键逻辑有三点:
StringUtils.trimToNull把空白关键字转为null,表示不增加关键字过滤。containsIgnoreCase同时匹配商品编码和名称,所以输入Graphite可以命中Graphite Monitor 32。- 先过滤并计算
total,再按current和pageSize截取当前页。
为突出本文的 keyword 查询流程,上面的 Service 省略了仓库完整实现中的分类、状态、价格、通用条件和多字段排序逻辑;23 条商品数据和分页行为与实际示例一致。
8.5 GET 接口扩展
需要同时支持 GET 时,可以把查询参数装配为同一个 Request DTO,然后复用 POST 查询逻辑:
@GetMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> queryByGet(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String keyword,
@RequestHeader(value = "Accept-Language", required = false) String language) {
ProTableProductQueryRequest request = new ProTableProductQueryRequest();
request.setCurrent(current);
request.setPageSize(pageSize);
request.setKeyword(keyword);
return query(request, language);
}
本文实际接口还支持以下可选字段:
| 参数 | 说明 |
|---|---|
category | 分类精确匹配,忽略大小写 |
status | 状态精确匹配,忽略大小写 |
minPrice / maxPrice | 价格区间 |
conditions | 通用条件数组 |
sortBy | 字段到 asc / desc 的有序映射 |
GET 方式传递 conditions 或 sortBy 时,它们是 URL 编码后的 JSON 字符串;POST 方式则直接使用 JSON 数组或对象。
9. 进阶配置
9.1 多个查询条件
可以在 Left Toolbar 中继续添加 Category Select、Status Radio 或 Price Input,并在 Query Context 中分别映射:
{
"keyword": "[[productKeyword]]",
"category": "[[productCategory]]",
"status": "[[productStatus]]",
"minPrice": "[[minimumPrice]]",
"maxPrice": "[[maximumPrice]]"
}
空值是否表示“不筛选”由后端接口定义。建议后端对可选字符串使用 trimToNull,对可选数字使用 null,不要用特殊字符串模拟空值。
9.2 Row Actions
在 Row Actions 中可以添加查看、编辑、删除等操作。行操作执行时会提供当前记录上下文,可以通过 FX 读取当前行的 id、code 等字段。
修改成功后,调用当前 ProTable 的 reloadTable,重新查询服务端数据。危险操作应增加确认步骤,并由后端完成权限校验和业务校验。
9.3 setTableData
当按钮先调用另一个接口,并已经得到完整记录数组时,可以调用:
setTableData(data)
它会用传入数组替换当前本地表格数据,并把页码重置为 1。setTableData 不会自动持久化数据,也不会从响应外层自动提取 data.records;传入值必须就是记录数组。
标准远程分页查询应优先使用 reloadTable,让查询条件、总数和分页继续由后端统一处理。
9.4 显示属性
| 属性 | 说明 |
|---|---|
| Enable Row Number | 在第一列显示跨页连续的行序号 |
| Table Size | 设置 large、middle 或 small 密度 |
| Show Header | 是否显示列标题行 |
| Bordered | 是否显示表格边框 |
| Row Unique Key | 每一行的唯一标识字段,默认 id |
10. 验证本文示例
保存页面后,在 Preview 和 Runtime 中依次验证:
- 不输入关键字,首次加载应显示第 1 页,共 23 条记录。
- 切换到第 2 页,表格应显示下一页数据。
- 在第 2 页输入
Graphite并点击 Search。 - 请求应使用
current: 1,结果应只有DSP-2002 / Graphite Monitor 32。 - 清除 Input,点击 Search,应重新显示全部 23 条记录,并回到第 1 页。
- 输入一个能匹配多页数据的关键字,点击 Search 后再翻页,后续请求应继续携带该关键字。
可以在浏览器开发者工具的 Network 中检查 POST Request Payload:
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
确认响应中的 data.total、data.current、data.pageSize 和 data.records 与请求一致。
11. 常见问题
11.1 在第 2 页输入 Graphite 后查询不到
Search 按钮可能配置成了普通请求,而没有调用 ProTable 的 reloadTable。请检查:
Interaction: Component API
Target Component: 当前 ProTable
Function: reloadTable
reloadTable 会把页码重置为 1;普通翻页仍会保留查询条件。
11.2 输入关键字后数据没有变化
依次检查:
- Query Context 是否存在
keyword参数。 productKeyword变量来源是否为正确 Input 的 Component Value。- Target Value 是否为
[[productKeyword]]。 - 后端接收字段是否也叫
keyword。 - Search 是否调用当前 ProTable 的
reloadTable。
11.3 Input 没有显示清除图标
确认 Input 的 Allow Clear 已开启,并且运行时 Input 中已有文本。清除图标不会在空输入框中显示。清空后仍需点击 Search 才会刷新表格。
11.4 接口有数据但表格为空
检查 Rows Path。本文响应的记录数组位于:
data.records
如果自定义了错误路径,ProTable 不会猜测另一个自定义路径,最终会显示空数组。
11.5 分页总数或页码不正确
检查:
- Total Path 是否指向过滤后的总记录数
- Current Page Path 是否指向服务端返回的当前页
- Page Size Path 是否指向服务端实际采用的每页数量
- 后端页码是否从 1 开始
11.6 接口返回 401、403 或 CORS 错误
401:登录态无效,或接口需要认证。403:当前用户没有接口或数据权限。- CORS:外部服务没有允许当前 Spark 域名跨域访问。
外部系统优先通过 External Connection 或受控后端代理接入,不要在页面配置中暴露长期凭证。
11.7 GET 的 conditions 无法解析
GET 会把对象和数组序列化为 JSON 字符串。后端应先 URL 解码,再使用 JSON Parser 解析,不要按逗号手工拆分复杂对象。POST 接口没有这个额外转换,复杂条件优先使用 POST。
11.8 Custom URL 的 Data Field 没有候选项
打开 Data Source 并保存 Response Fields。可以手工添加,也可以使用 Test & Import。Response Fields 属于数据源契约,加载一条表格预览数据不会临时改变字段目录。
12. 配置检查清单
- ProTable 已设置稳定且唯一的控件名称
- Data Source 已选择 Custom URL
- Response Fields 已手工配置或通过 Test & Import 导入并保存
- URL、Request Method 和鉴权方式正确
- Rows、Total、Current Page、Page Size 路径与响应一致
- Columns 的 Data Field 与记录字段一致
- Row Unique Key 稳定且唯一
- Query Context 参数名与后端 DTO 字段一致
- FX 变量指向正确的 Component Value
- Search 按钮调用当前 ProTable 的
reloadTable - 新搜索会回到第 1 页
- 普通翻页会保留当前搜索条件
- 空关键字、有效关键字和无匹配关键字均已验证
- Preview 与 Runtime 均已验证工具栏、表格和分页
完成以上配置后,ProTable 就可以作为自定义 HTTP API 的稳定列表入口,并正确协同查询控件、服务端分页和页面交互。