表格数据审阅
Table、筛选、批量动作和 iPhone fallback。
演示 Table 多列展示与 Picker 在列表模式之间的切换,共用同一数据源。
#预期效果
运行后会出现可筛选的数据审核表,支持选择行、查看摘要,并在窄屏上退化为列表体验。
#适合场景
- iPad 或横屏下需要多列查看的数据审核页。
- 手机上需要保留列表模式作为窄屏降级。
#页面结构
| 区域 | 结构 | 作用 |
|---|---|---|
| 模式切换 | Picker + segmented | 在列表和表格之间切换。 |
| 列表模式 | List + ForEach + NavigationLink | 手机上查看详情。 |
| 表格模式 | Table | iPad 上快速扫描多列数据。 |
#完整示例
已复制
import appui
state = appui.State(
mode="列表",
selected="",
rows=[
{"name": "TextField", "area": "输入", "status": "OK", "owner": "Form"},
{"name": "Slider", "area": "控制", "status": "OK", "owner": "Controls"},
{"name": "VideoPlayer", "area": "媒体", "status": "观察", "owner": "Media"},
{"name": "Table", "area": "数据", "status": "iPad 优先", "owner": "Data"},
],
)
columns = [
{"title": "组件", "key": "name"},
{"title": "区域", "key": "area"},
{"title": "状态", "key": "status"},
]
def set_mode(value):
state.mode = value
def select_row(row):
state.selected = f"{row.get('name', '')} / {row.get('status', '')}"
def row_detail(row):
return appui.Form(
[
appui.Section(
[
appui.LabeledContent("组件", value=row["name"]),
appui.LabeledContent("区域", value=row["area"]),
appui.LabeledContent("状态", value=row["status"]),
appui.LabeledContent("负责人", value=row["owner"]),
],
header="详情",
)
]
).navigation_title(row["name"])
def row_key(row):
return row["name"]
def row_link(row):
return appui.NavigationLink(
destination=row_detail(row),
label=appui.HStack(
[
appui.VStack(
[
appui.Text(row["name"]).font("headline"),
appui.Text(row["area"]).font("caption").foreground_color("secondaryLabel"),
],
alignment="leading",
),
appui.Spacer(),
appui.Text(row["status"]).font("caption").foreground_color("systemBlue"),
]
),
)
def list_mode():
return appui.List(
[
appui.Section("组件", [
appui.ForEach(
state.rows,
row_builder=row_link,
key=row_key,
)
])
]
)
def table_mode():
return appui.VStack(
[
appui.Table(data=state.rows, columns=columns, on_select=select_row).frame(height=260),
appui.Text(state.selected or "点选表格行查看选择结果")
.font("caption")
.foreground_color("secondaryLabel"),
],
spacing=12,
).padding()
def body():
content = table_mode() if state.mode == "表格" else list_mode()
return appui.NavigationStack(
appui.VStack(
[
appui.Picker("显示方式", selection=state.mode, options=["列表", "表格"], on_change=set_mode)
.picker_style("segmented")
.padding(horizontal=16),
content,
],
spacing=8,
).navigation_title("数据审阅")
)
appui.run(body, state=state, presentation="sheet")
#关键技巧
Table的columns使用{"title", "key"}字典列表;data为字典列表。- 手机窄屏可保留「列表」模式;
on_select接收选中行字典。 - 表格与列表共用纯数据行,不把视图对象放进
state.rows。
#失败路径
- 表格选中后把结果写入
state.selected,否则用户不知道点选是否生效。 - 如果业务数据为空,列表模式应显示
ContentUnavailableView,表格模式应显示说明文本。