{ "schemaVersion": 1, "sourceKind": "authored-spec", "provenance": "authored-in-repo", "specFile": "spec/移动端规格.md", "specSection": "47 · 表格 Table", "confidence": "high", "slug": "mobile-table", "name": "表格 Table", "semanticTypeCandidates": [ "table", "datagrid", "list-grid" ], "variantDimensions": [ { "name": "mode", "values": [ "scroll", "card" ] }, { "name": "size", "values": [ "default", "compact" ] }, { "name": "stripe", "values": [ "false", "true" ] }, { "name": "bordered", "values": [ "false", "true" ] } ], "representativeVariants": [ { "mode": "scroll", "size": "default", "stripe": "false", "bordered": "true", "label": "横向滚动(列多时在组件容器里左右滑)" }, { "mode": "scroll", "size": "default", "stripe": "true", "bordered": "false", "label": "斑马纹(偶数行浅底,便于对齐行)" }, { "mode": "card", "size": "default", "stripe": "false", "bordered": "false", "label": "卡片模式(每行摊成键值对,小屏不横滚)" }, { "mode": "scroll", "size": "compact", "stripe": "false", "bordered": "true", "label": "紧凑(收紧内边距与字号,行高仍 ≥44px)" } ], "anatomy": { "table": "根元素,纵向排列「视口 + 空态」", "table__viewport": "横向滚动的唯一发生点(overflow-x: auto),页面整体不横向溢出", "table__inner": "原生 table,min-width 由 --kole-m-table-min-width 给出;超过容器即滚", "table__caption": "可选表格标题(说明这张表在讲什么)", "table__head": "表头,position: sticky 固定在滚动视口顶部", "table__head-cell": "表头单元格,写 scope=col 让读屏报出列名", "table__row": "数据行,行高不小于 44px", "table__cell": "单元格,可带 data-label(卡片模式的字段名来源)", "table__empty": "空态占位,role=status + aria-live=polite" }, "structurePatterns": { "mode": "scroll(横向滚动,列多时用)/ card(每行摊成键值对,小屏彻底不横滚)", "size": "default(常规行高)/ compact(收紧内边距与字号;行高仍不小于 44px)", "stripe": "false(纯白底)/ true(偶数行浅底,便于横向扫读时对齐行)", "bordered": "false(只有横线)/ true(单元格之间也有竖线)" }, "usageHints": [ "把多条同构记录按列并排,用于订单列表 / 库存明细 / 对账单这类需要逐行横向对比的场合", "移动端表格必须在组件自己的容器里横向滚动,绝不能让页面整体横向溢出,否则整页会跟着左右晃", "横向滚动发生在 table__viewport 上:根与视口都写 min-width: 0,否则内部 min-width 会把父级顶宽", "表头用 position: sticky 而不是 fixed —— fixed 会脱出滚动容器,横向滚动时表头不跟随", "长文本列默认单行省略,完整值放 title;数字列用等宽字右对齐,便于按位对比", "点整行即选中(同表互斥);行内的独立控件要阻止冒泡,避免一次点击触发两个动作" ], "doNotInvent": [ "虚拟滚动与无限加载的触发规则(由宿主容器负责;本组件不接管纵向滚动)", "服务端排序 / 筛选 / 分页(排序图标与请求都由宿主发起)", "列宽拖拽调整与列显隐设置(移动端没有这个操作面)", "冻结列(position: sticky 的横向版本;当前只固定表头的纵向位置)" ], "unknowns": [ "表头是否需要在纵向滚动时也吸顶(当前只在宿主给固定高度时生效)", "行选中是单选还是多选(当前单选;多选应交给 Checkbox 列)", "卡片模式下字段名的排列是「左标签右值」还是「上标签下值」" ], "interaction": [ "横向滚动发生在 table__viewport 上:根与视口都写 min-width: 0,否则内部 min-width 会把父级顶宽、页面整体横向溢出", "选项行高不小于 44px;触屏上横向滑动不被父级纵向滚动吞掉(touch-action: pan-x pan-y)", "表头用 position: sticky 而不是 fixed——fixed 会脱出滚动容器,横向滚动时表头不跟随", "长文本列默认单行省略,完整值放 title;数字列用等宽字右对齐,便于按位对比", "点整行即选中(同表互斥);行内的独立控件(按钮 / 链接)要阻止冒泡,避免一次点击触发两个动作", "行可点时同时提供键盘路径(聚焦后回车 / 空格),不把鼠标点击当成唯一入口" ], "accessibility": [ "用原生 table / thead / tbody / tr / th / td,表头写 scope=\"col\"(读屏能报出列名)", "空态用 role=\"status\" + aria-live=\"polite\" 播报占位文案", "禁用行写 aria-disabled=\"true\",读屏播报不可用", "可点行必须同时可聚焦:clickable=true 时行写 tabindex=\"0\" + role=\"row\" + aria-selected,聚焦后回车 / 空格即选中 —— 只有鼠标点击而没有键盘路径是缺陷,不是简化", "焦点环画在行内第一个单元格上(tr 上的 outline 在部分浏览器不渲染)", "mode=card 时字段名由 data-label 生成并参与读屏(键值对在视觉与语义上都成立)" ], "api": { "source": "implementation", "note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs", "requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。", "props": [ { "name": "columns", "type": "Array<{ title: string, key?: string, align?: 'start'|'center'|'end', number?: boolean }>", "default": "[]", "desc": "列定义(宿主提供;number 是数字列的便捷写法,等价于 align=end + 等宽字)(规格 §47.3)", "required": false }, { "name": "rows", "type": "Array", "default": "[]", "desc": "行数据,二维数组或对象数组;单元格支持 { text, align, ellipsis, number, title }(规格 §47.2)", "required": false }, { "name": "mode", "type": "'scroll' | 'card'", "default": "'scroll'", "desc": "变体 mode:scroll 横向滚动,card 每行摊成键值对(规格 §47.3)", "required": false }, { "name": "size", "type": "'default' | 'compact'", "default": "'default'", "desc": "变体 size:compact 收紧内边距与字号,行高仍不小于 44px(规格 §47.3)", "required": false }, { "name": "stripe", "type": "boolean", "default": "false", "desc": "变体 stripe:偶数行浅底,便于横向扫读时对齐行(规格 §47.3)", "required": false }, { "name": "bordered", "type": "boolean", "default": "false", "desc": "变体 bordered:单元格之间也画竖线(规格 §47.3)", "required": false }, { "name": "clickable", "type": "boolean", "default": "false", "desc": "状态 clickable:整行可点,按下反馈是整行背景变化(规格 §47.4)", "required": false }, { "name": "caption", "type": "string", "default": "''", "desc": "可选表格标题(说明这张表在讲什么)(规格 §47.2)", "required": false }, { "name": "emptyText", "type": "string", "default": "'暂无数据'", "desc": "状态 empty 的占位文案(规格 §47.4)", "required": false } ], "events": [ { "name": "select", "params": "index: number, row: any", "desc": "点整行时触发,回传行下标与该行数据(clickable=true 时才有意义)(规格 §47.5)" } ], "slots": [ { "name": "default", "desc": "自定义内容(追加在空态之后)" } ] }, "variantClasses": { "mode": { "scroll": [ ".kole-m-table--scroll" ], "card": [ ".kole-m-table--card" ] }, "size": { "default": [], "compact": [ ".kole-m-table--compact" ] }, "stripe": { "false": [], "true": [ ".kole-m-table--stripe" ] }, "bordered": { "false": [], "true": [ ".kole-m-table--bordered" ] } }, "demos": [ { "id": "scroll", "group": "01 组件类型", "title": "横向滚动", "desc": "列多时在表格自己的容器里左右滑,页面整体不横向溢出。", "variant": "mode=scroll" }, { "id": "clickable", "group": "02 组件状态", "title": "表头固定与行点击", "desc": "点整行选中该行;数字列等宽右对齐便于按位对比,斑马纹便于横向对齐行。", "variant": "状态 selected | clickable" }, { "id": "card", "group": "01 组件类型", "title": "卡片模式", "desc": "mode=card:小屏上把每行摊成键值对,彻底不需要横向滚动。", "variant": "mode=card" }, { "id": "empty", "group": "02 组件状态", "title": "空态", "desc": "没有数据时显示占位文案,列宽语义不变,不会把容器撑宽。", "variant": "状态 empty" }, { "id": "states", "group": "02 组件状态", "title": "禁用行与省略列", "desc": "禁用行置灰不响应;长文本列单行省略,完整值在 title 里。", "variant": "状态 disabled" } ], "related": [ { "slug": "mobile-list", "why": "一行只需「标题 + 值 + 箭头」时用列表,不需要列对齐;列对比才是表格的强项" }, { "slug": "cell", "why": "键值对数量很少(三四项)时用单元格纵向排列,比建一张两列表格更轻" }, { "slug": "mobile-collapse", "why": "需要展开某一行看明细时用折叠面板承载详情,表格本身不做行内展开" }, { "slug": "mobile-loading", "why": "表格数据加载中用它占位,不要在表体里自己画骨架屏" }, { "slug": "mobile-tag", "why": "状态列用小标签呈现(已发货 / 待付款)比纯文字更好扫读" } ] }