跳到主要内容

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 的区别​

控件主要数据适用场景
ProTableCustom URL 或 Model Data Source通用 HTTP API、聚合数据、定制分页协议
DataListBusiness ObjectDQL、模型字段、对象动作和模型数据权限
TaskListSpark 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 Word Input 和一个 Search Button
  • 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"
}

字段说明:

字段类型说明
currentnumber当前页码,从 1 开始
pageSizenumber每页记录数
keywordstring本文通过 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-EnvironmentDEV、TEST、PROD当前页面环境;设计器中的数据预览使用 DEV
X-Spark-Page-Render-ModeDESIGN_PREVIEW、RUNTIMEPage 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 添加控件​

  1. 打开目标页面并进入 Page Designer。
  2. 在左侧组件面板展开 Data 分组。
  3. 将 ProTable 拖入页面中的可接收容器。
  4. 选中 ProTable,建议设置一个稳定、易识别的控件名称,例如 productTable。

稳定的名称便于在 Button 的 Component API 中选择正确目标。

4.2 配置 Custom URL​

在 ProTable 的 Data Source 中选择 Custom URL,按本文示例填写:

属性值
URLhttps://spark.hiforce.ai/spark/api/sample/protable/products
Request MethodPOST
Rows Pathdata.records
Total Pathdata.total
Current Page Pathdata.current
Page Size Pathdata.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
LabelColumns 等字段选择器中显示的名称
Data TypeTEXT、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 中按顺序添加:

LabelData Field
Codecode
Namename
Categorycategory
Priceprice
Stockstock

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 添加变量​

  1. 编辑 ProTable 的 Data Source。
  2. 在 Query Context 中点击 FX。
  3. 点击 Add Variable。
  4. Variable Name 填写 productKeyword。
  5. Source 选择 Component Value。
  6. Component 选择刚添加的 Key Word Input。

6.2 添加请求参数​

在 Query Context 的参数区域添加:

Parameter: keyword
Value: [[productKeyword]]

运行时会在每次请求前重新读取 Input 的当前值。用户没有输入内容时,keyword 为空;本文后端会忽略空白关键字,因此返回全部数据。

6.3 常用变量来源​

来源适用场景
Component ValueInput、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

然后配置点击交互:

  1. 选中 Search Button,打开 Interaction editor。
  2. 新增 Component API 动作。
  3. Target Component 选择当前 ProTable,例如 productTable。
  4. Function 选择 reloadTable。
  5. 保存交互配置。

reloadTable 表示一次新的查询,它会:

  1. 把当前页码重置为 1。
  2. 重新解析 Query Context 中的控件值。
  3. 使用新的 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));
}
}

示例中的关键逻辑有三点:

  1. StringUtils.trimToNull 把空白关键字转为 null,表示不增加关键字过滤。
  2. containsIgnoreCase 同时匹配商品编码和名称,所以输入 Graphite 可以命中 Graphite Monitor 32。
  3. 先过滤并计算 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. 不输入关键字,首次加载应显示第 1 页,共 23 条记录。
  2. 切换到第 2 页,表格应显示下一页数据。
  3. 在第 2 页输入 Graphite 并点击 Search。
  4. 请求应使用 current: 1,结果应只有 DSP-2002 / Graphite Monitor 32。
  5. 清除 Input,点击 Search,应重新显示全部 23 条记录,并回到第 1 页。
  6. 输入一个能匹配多页数据的关键字,点击 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 输入关键字后数据没有变化​

依次检查:

  1. Query Context 是否存在 keyword 参数。
  2. productKeyword 变量来源是否为正确 Input 的 Component Value。
  3. Target Value 是否为 [[productKeyword]]。
  4. 后端接收字段是否也叫 keyword。
  5. 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 的稳定列表入口,并正确协同查询控件、服务端分页和页面交互。