列表List
把一组同类信息按行排列,用于「设置项 / 订单 / 成员」这类需要扫读的场合
数据展示 规格 24 · 列表 List 6 端实现 触摸优先
<!-- ① 令牌:PC 令牌 + 移动端 --kole-m-* 合成单文件,引一次 -->
<link rel="stylesheet" href="kole-ui/mobile/tokens.css">
<!-- ② 本组件样式(全量则用 kole-ui/mobile/components/index.css) -->
<link rel="stylesheet" href="kole-ui/mobile/components/mobile-list.css">
<!-- ③ 结构照抄下方任一演示块(类名与 6 端实现一致) -->
演示
每个演示都是真实渲染:预览帧加载 frameworks-mobile/List.html?demo=<id>(只显示该演示块),代码是该演示块在演示页里的原文,可复制。全部演示同屏可看 演示页 ↗。
01 组件类型
标题 + 副标题 + 后缀位(值 + 箭头),行间 1px 分隔线且最后一条不画。
查看代码(演示页原文 · 20 行)
<section class="demo-block" data-demo="basic">
<p class="demo-label">基础列表(可点行整行热区 ≥56px,后缀位放值与箭头)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-basic">
<li class="kole-m-list__item" data-assert="list-basic-row-1">
<span class="kole-m-list__body">
<span class="kole-m-list__title">待付款</span>
<span class="kole-m-list__desc">共 2 笔,合计 ¥ 328.00</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">去支付</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">待收货</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">1 件</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">退款/售后</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">无</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
</ul>
</section>前缀放头像、图标或序号;它是装饰,点击等于点整行。
查看代码(演示页原文 · 21 行)
<section class="demo-block" data-demo="prefix">
<p class="demo-label">带前缀位(前缀是装饰,点击等于点整行)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-prefix">
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">王</span></span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">王小明</span>
<span class="kole-m-list__desc">产品设计部 · 交互设计师</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">在职</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">李</span></span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">李小红</span>
<span class="kole-m-list__desc">前端研发部 · 工程师</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">休假</span></span>
</li>
</ul>
</section>后缀位放标签这类非文字内容,与值文字共用一行。
查看代码(演示页原文 · 16 行)
<section class="demo-block" data-demo="custom">
<p class="demo-label">自定义后缀(后缀位放标签等非文字内容,与值文字共用一行)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-custom">
<li class="kole-m-list__item">
<span class="kole-m-list__body">
<span class="kole-m-list__title">订单 20260920-001</span>
<span class="kole-m-list__desc">顺丰标快 · 预计 09-22 送达</span>
</span>
<span class="kole-m-list__suffix"><span class="demo-tag">已发货</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">订单 20260919-114</span></span>
<span class="kole-m-list__suffix"><span class="demo-tag">待付款</span></span>
</li>
</ul>
</section>divider=inset 时线从行内容起点开始;divider=full 时通栏不缩进。
查看代码(演示页原文 · 21 行)
<section class="demo-block" data-demo="divider">
<p class="demo-label">分隔线两态(divider=inset 缩进到内容起点 / full 通栏)</p>
<ul class="kole-m-list kole-m-list--border kole-m-list--inset" data-assert="list-divider-inset">
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">A</span></span>
<span class="kole-m-list__body"><span class="kole-m-list__title">inset:线从内容起点开始</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">B</span></span>
<span class="kole-m-list__body"><span class="kole-m-list__title">缩进量 = 页面留白</span></span>
</li>
</ul>
<ul class="kole-m-list kole-m-list--border kole-m-list--full" data-assert="list-divider-full" style="margin-top: 12px;">
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">full:线通栏,不缩进</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">用于无明显前缀位的列表</span></span>
</li>
</ul>
</section>02 组件状态
size=compact 行高 44px,仍是触控最小边长,用于高密度筛选类列表。
查看代码(演示页原文 · 17 行)
<section class="demo-block" data-demo="compact">
<p class="demo-label">紧凑尺寸(size=compact:行高 44px,仍是触控最小边长)</p>
<ul class="kole-m-list kole-m-list--border kole-m-list--compact" data-assert="list-compact">
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">全部订单</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">128</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">待评价</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">6</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">已取消</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">2</span></span>
</li>
</ul>
</section>可点行是原生 button:键盘可达、按下整行背景变化。
查看代码(演示页原文 · 21 行)
<section class="demo-block" data-demo="clickable">
<p class="demo-label">可点行(整行是原生 button:键盘可达、按下整行背景变化)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-clickable">
<li>
<button class="kole-m-list__item" type="button" id="list-row-link"
data-behavior="click-sets-attr:#list-row-link|data-tapped|true">
<span class="kole-m-list__body">
<span class="kole-m-list__title">收货地址</span>
<span class="kole-m-list__desc">杭州市余杭区 · 王小明</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">修改</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</button>
</li>
<li>
<button class="kole-m-list__item" type="button">
<span class="kole-m-list__body"><span class="kole-m-list__title">发票信息</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</button>
</li>
</ul>
</section>状态 disabled:置灰且不响应点击,用于无权限项。
查看代码(演示页原文 · 20 行)
<section class="demo-block" data-demo="disabled">
<p class="demo-label">禁用行(置灰且不响应点击,用于无权限项)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-disabled">
<li>
<button class="kole-m-list__item is-disabled" type="button" disabled aria-disabled="true">
<span class="kole-m-list__body">
<span class="kole-m-list__title">企业认证</span>
<span class="kole-m-list__desc">需要管理员权限</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">暂无权限</span></span>
</button>
</li>
<li>
<button class="kole-m-list__item" type="button">
<span class="kole-m-list__body"><span class="kole-m-list__title">实名认证</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">已完成</span></span>
</button>
</li>
</ul>
</section>状态 empty:占位文案用 aria-live 播报,读屏能听到「暂无订单」。
查看代码(演示页原文 · 6 行)
<section class="demo-block" data-demo="empty">
<p class="demo-label">空列表(状态 empty:占位文案用 aria-live 播报)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-empty">
<li><p class="kole-m-list__empty" aria-live="polite">暂无订单,去逛逛吧</p></li>
</ul>
</section>API
props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs
Props
| 名称 | 类型 | 默认值 | 说明 | 必传 |
|---|---|---|---|---|
items | Array | [] | 数据驱动的行数组,每项含 title / desc / value / prefix / arrow / static / disabled(规格 §24.2) | N |
size | 'default' | 'compact' | 'default' | 变体 size:default 行高 56px / compact 44px(规格 §24.3) | N |
border | boolean | true | 变体 border:是否画行间 1px 分隔线(规格 §24.3) | N |
divider | 'inset' | 'full' | 'inset' | 变体 divider:分隔线缩进到内容起点 / 通栏(规格 §24.3) | N |
header | string | '' | 分组标题,位于列表之上(规格 §24.2 header) | N |
footer | string | '' | 分组脚注,用于补充说明(规格 §24.2 footer) | N |
emptyText | string | '' | 状态 empty:列表为空时的占位文案(规格 §24.4) | N |
「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。
事件
| 名称 | 参数 | 说明 |
|---|---|---|
select | (index) | 可点行被点击时触发,回传该行在 items 里的下标 |
插槽
| 名称 | 说明 |
|---|---|
default | 追加在数据驱动行之后的自定义行(规格 §24.2 item) |
empty | 状态 empty 的自定义占位内容,替代 emptyText(规格 §24.4) |
CSS 变量
组件级变量(在组件样式表里定义)。业务侧可在自己的作用域内覆盖,不必改组件源码。
| 名称 | 默认值 | 说明 |
|---|---|---|
--kole-m-list-row-height | 56px | default 行高(compact 44px) |
--kole-m-list-divider-inset | var(--kole-m-gutter) | 分隔线左端缩进(= 页面留白) |
--kole-m-list-row-height | 44px | 组件内部默认值,可在业务侧覆盖 |
何时使用
- 把一组同类信息按行排列,用于「设置项 / 订单 / 成员」这类需要扫读的场合
- 移动端一屏只有 6~8 行,每行信息层级必须收敛到「标题 + 可选的说明或值」
- 分组之间用标题与留白分隔,而不是靠边框
- 可点行整行都是热区,default 行高不小于 56px、compact 不小于 44px
- 列表滚动由宿主容器负责;本组件不接管滚动、不做虚拟列表
交互与触控
- 可点行整行都是热区,default 行高不小于 56px、compact 不小于 44px
- 按下反馈是整行背景变化,不是只有文字变色
- 前缀位不参与点击判定(点图标等于点整行),后缀位里的独立控件(开关、按钮)要阻止事件冒泡,避免一次点击触发两个动作
- 列表滚动由宿主容器负责;本组件不接管滚动、不做虚拟列表
无障碍
- 容器 role="list",纯展示行 role="listitem";可点行用原生 button(原生语义优先于 listitem)
- 分组标题用 aria-label 或可见文本,读屏在进入分组时能读到
- 空列表用 aria-live="polite" 播报占位文案
相似组件
从「该用哪一个」的角度区分;PC 端的对应实现见 PC 文档站。
| 组件 | 何时用它而不是本组件 |
|---|---|
| 单元格Cell | 单行信息用单元格(自带 arrow / value 语义);成组出现且需要分组标题与脚注时用列表 |
| 滑动单元格SwipeCell | 行内还需要左滑操作时用滑动单元格;只做扫读与点击用列表 |
| 头像Avatar | 行的前缀位放头像时用头像组件;列表只负责行的骨架 |
| 折叠面板Collapse | 行点击后要就地展开内容用折叠面板;跳转到新页面用列表的可点行 |
规格未定 / 禁止发明
| 类别 | 条目 |
|---|---|
| 禁止发明 | 虚拟滚动与无限加载的触发规则 |
| 禁止发明 | 行的拖拽排序与左滑操作(那是 SwipeCell 的职责) |
| 规格未定 | 单行最多几列(前缀 + 主区 + 后缀之外的排布) |
| 规格未定 | 分组标题是否吸顶 |
结构(anatomy)
| 字段 | 说明 |
|---|---|
list | 根元素,一个列表区块 |
header | 可选分组标题,位于列表之上 |
item | 列表项,一行承载「前缀 + 主区 + 后缀」 |
prefix | 可选前缀位(头像、图标、序号) |
body | 主区,标题 + 可选副标题,两行都单行省略 |
suffix | 可选后缀位(值文字、标签、箭头、开关) |
footer | 可选分组脚注,用于补充说明 |
变体维度与类名映射
类名映射由构建脚本从契约 variantClasses 生成,并被 verify:mobile-docs 逐条对照组件 CSS 校验(类/变量必须真实存在)。
| 维度 | 取值 | 对应类名 / 变量 |
|---|---|---|
border | true / false | true .kole-m-list--border false (由数据驱动,无专属类) |
size | default / compact | default (由数据驱动,无专属类) compact .kole-m-list--compact |
divider | inset / full | inset .kole-m-list--inset full .kole-m-list--full |
代表变体
| 变体 | 标签 |
|---|---|
border=true · size=default · divider=inset | 标准分组列表(默认:行高 56px、分隔线缩进) |
border=true · size=compact · divider=full | 紧凑高密度列表(行高 44px、通栏分隔线) |
border=false · size=default · divider=inset | 无分隔线列表(靠留白分组) |
用到的令牌
构建时从本组件样式表扫描得出。蓝色为移动端自有令牌,绿色为继承的 PC 令牌(改一处两端生效)。
6 端源码
同一组件的六份实现(生产环境的类名与结构一致,差异只在技术栈写法与单位)。点开查看,右侧可复制。
frameworks-mobile/List.css · 纯样式(CSS) · 167 行
/* Kole UI Mobile · List 样式 — 对齐移动端规格 §24
列表:一组同类信息按行排列。default 行高 56px、compact 44px;
行间 1px 分隔线用 ::after 画(inset 缩进到行内容起点,full 通栏),可点行整行热区。 */
.kole-m-list {
--kole-m-list-row-height: 56px; /* default 行高(compact 44px) */
--kole-m-list-divider-inset: var(--kole-m-gutter); /* 分隔线左端缩进(= 页面留白) */
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0;
list-style: none;
background: var(--kole-color-card-bg);
font-family: var(--kole-font-family);
color: var(--kole-color-text-body);
font-size: var(--kole-m-font-size-body);
line-height: 1.4;
}
/* 变体 size=compact:行高 44px(仍不小于触控最小边长),用于高密度列表 */
.kole-m-list--compact {
--kole-m-list-row-height: 44px;
font-size: var(--kole-m-font-size-label);
}
/* 变体 divider:inset 缩进到行内容起点 / full 通栏 —— 只在 border=true 时可见 */
.kole-m-list--inset { --kole-m-list-divider-inset: var(--kole-m-gutter); }
.kole-m-list--full { --kole-m-list-divider-inset: 0px; }
/* 分组标题与脚注:在卡片之外,靠留白区分分组 */
.kole-m-list__header,
.kole-m-list__footer {
margin: 0;
padding: var(--kole-space-12) var(--kole-m-gutter) var(--kole-space-8);
background: var(--kole-color-page-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-label);
}
.kole-m-list__footer {
padding-top: var(--kole-space-8);
padding-bottom: var(--kole-space-12);
}
/* 行:前缀位 + 主区 + 后缀位 */
.kole-m-list__item {
box-sizing: border-box;
display: flex;
align-items: center;
gap: var(--kole-space-12);
width: 100%;
min-height: var(--kole-m-list-row-height);
padding: var(--kole-space-12) var(--kole-m-gutter);
margin: 0;
border: 0;
background: var(--kole-color-card-bg);
color: var(--kole-color-text-body);
font-family: inherit;
font-size: inherit;
line-height: 1.4;
text-align: start;
}
/* 变体 border=true:行间 1px 分隔线。
线画在**列表子项**(li)的上沿而不是行的下沿 —— 行既可能是 li 本身,
也可能是 li 里的原生 button(可点行),挂在下沿会让两层 :last-child 都对不上。
绝对定位保证线画在行背景之上。 */
.kole-m-list--border > * { position: relative; }
.kole-m-list--border > * + *::after {
content: '';
position: absolute;
top: 0;
left: var(--kole-m-list-divider-inset);
right: 0;
height: 1px;
background: var(--kole-color-border);
}
/* 可点行:整行热区,按下反馈是整行背景变化(不是只有文字变色) */
button.kole-m-list__item {
cursor: pointer;
touch-action: manipulation;
}
button.kole-m-list__item:active { background: var(--kole-color-table-header-bg); }
/* 状态 disabled:置灰且不响应点击 */
.kole-m-list__item.is-disabled,
.kole-m-list__item:disabled {
color: var(--kole-color-text-disabled);
cursor: not-allowed;
}
.kole-m-list__item.is-disabled:active,
.kole-m-list__item:disabled:active { background: var(--kole-color-card-bg); }
/* 前缀位(头像 / 图标 / 序号):装饰性,点击等于点整行 */
.kole-m-list__prefix {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
color: var(--kole-color-text-secondary);
}
/* 主区:标题 + 可选副标题,两行都单行省略 */
.kole-m-list__body {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.kole-m-list__title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--kole-color-text-title);
}
.kole-m-list__desc {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
/* 后缀位:值文字 / 标签 / 箭头 */
.kole-m-list__suffix {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
gap: var(--kole-space-8);
max-width: 45%;
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
.kole-m-list__value {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.kole-m-list__arrow {
flex: 0 0 auto;
display: inline-flex;
color: var(--kole-color-text-placeholder);
}
/* 状态 empty:空列表占位(宿主用 aria-live 播报) */
.kole-m-list__empty {
margin: 0;
padding: var(--kole-space-40) var(--kole-m-gutter);
background: var(--kole-color-card-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-label);
text-align: center;
}
button.kole-m-list__item:focus-visible {
outline: 2px solid var(--kole-color-focus-ring);
outline-offset: -2px;
}
frameworks-mobile/List.html · H5 原生(无框架) · 208 行
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Kole UI Mobile · List(H5)</title>
<link rel="stylesheet" href="../.design_library/kole-ui-mobile/colors_and_type.css">
<link rel="stylesheet" href="List.css">
<style>
body { margin: 0; background: var(--kole-color-page-bg); font-family: var(--kole-font-family); color: var(--kole-color-text-body); }
.demo { max-width: 375px; margin: 0 auto; padding: var(--kole-m-gutter) 0; }
.demo-label { margin: 0; padding: var(--kole-space-12) var(--kole-m-gutter) var(--kole-space-8);
font-size: var(--kole-m-font-size-label); color: var(--kole-color-text-secondary); }
.demo-tag { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: 11px;
background: var(--kole-color-brand-bg); color: var(--kole-color-brand); font-size: var(--kole-m-font-size-caption); }
/* 前缀位占位块:代表宿主放进来的头像 / 图标(纯展示,不承载交互) */
.demo-dot { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
border-radius: 50%; background: var(--kole-color-brand-bg); color: var(--kole-color-brand);
font-size: var(--kole-m-font-size-label); }
.demo-block[hidden] { display: none; }
</style>
</head>
<body>
<div class="demo">
<section class="demo-block" data-demo="basic">
<p class="demo-label">基础列表(可点行整行热区 ≥56px,后缀位放值与箭头)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-basic">
<li class="kole-m-list__item" data-assert="list-basic-row-1">
<span class="kole-m-list__body">
<span class="kole-m-list__title">待付款</span>
<span class="kole-m-list__desc">共 2 笔,合计 ¥ 328.00</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">去支付</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">待收货</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">1 件</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">退款/售后</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">无</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
</ul>
</section>
<section class="demo-block" data-demo="prefix">
<p class="demo-label">带前缀位(前缀是装饰,点击等于点整行)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-prefix">
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">王</span></span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">王小明</span>
<span class="kole-m-list__desc">产品设计部 · 交互设计师</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">在职</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">李</span></span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">李小红</span>
<span class="kole-m-list__desc">前端研发部 · 工程师</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">休假</span></span>
</li>
</ul>
</section>
<section class="demo-block" data-demo="custom">
<p class="demo-label">自定义后缀(后缀位放标签等非文字内容,与值文字共用一行)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-custom">
<li class="kole-m-list__item">
<span class="kole-m-list__body">
<span class="kole-m-list__title">订单 20260920-001</span>
<span class="kole-m-list__desc">顺丰标快 · 预计 09-22 送达</span>
</span>
<span class="kole-m-list__suffix"><span class="demo-tag">已发货</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">订单 20260919-114</span></span>
<span class="kole-m-list__suffix"><span class="demo-tag">待付款</span></span>
</li>
</ul>
</section>
<section class="demo-block" data-demo="divider">
<p class="demo-label">分隔线两态(divider=inset 缩进到内容起点 / full 通栏)</p>
<ul class="kole-m-list kole-m-list--border kole-m-list--inset" data-assert="list-divider-inset">
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">A</span></span>
<span class="kole-m-list__body"><span class="kole-m-list__title">inset:线从内容起点开始</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__prefix"><span class="demo-dot">B</span></span>
<span class="kole-m-list__body"><span class="kole-m-list__title">缩进量 = 页面留白</span></span>
</li>
</ul>
<ul class="kole-m-list kole-m-list--border kole-m-list--full" data-assert="list-divider-full" style="margin-top: 12px;">
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">full:线通栏,不缩进</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">用于无明显前缀位的列表</span></span>
</li>
</ul>
</section>
<section class="demo-block" data-demo="compact">
<p class="demo-label">紧凑尺寸(size=compact:行高 44px,仍是触控最小边长)</p>
<ul class="kole-m-list kole-m-list--border kole-m-list--compact" data-assert="list-compact">
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">全部订单</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">128</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">待评价</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">6</span></span>
</li>
<li class="kole-m-list__item">
<span class="kole-m-list__body"><span class="kole-m-list__title">已取消</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">2</span></span>
</li>
</ul>
</section>
<section class="demo-block" data-demo="clickable">
<p class="demo-label">可点行(整行是原生 button:键盘可达、按下整行背景变化)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-clickable">
<li>
<button class="kole-m-list__item" type="button" id="list-row-link"
data-behavior="click-sets-attr:#list-row-link|data-tapped|true">
<span class="kole-m-list__body">
<span class="kole-m-list__title">收货地址</span>
<span class="kole-m-list__desc">杭州市余杭区 · 王小明</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">修改</span><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</button>
</li>
<li>
<button class="kole-m-list__item" type="button">
<span class="kole-m-list__body"><span class="kole-m-list__title">发票信息</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__arrow" aria-hidden="true">›</span></span>
</button>
</li>
</ul>
</section>
<section class="demo-block" data-demo="disabled">
<p class="demo-label">禁用行(置灰且不响应点击,用于无权限项)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-disabled">
<li>
<button class="kole-m-list__item is-disabled" type="button" disabled aria-disabled="true">
<span class="kole-m-list__body">
<span class="kole-m-list__title">企业认证</span>
<span class="kole-m-list__desc">需要管理员权限</span>
</span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">暂无权限</span></span>
</button>
</li>
<li>
<button class="kole-m-list__item" type="button">
<span class="kole-m-list__body"><span class="kole-m-list__title">实名认证</span></span>
<span class="kole-m-list__suffix"><span class="kole-m-list__value">已完成</span></span>
</button>
</li>
</ul>
</section>
<section class="demo-block" data-demo="empty">
<p class="demo-label">空列表(状态 empty:占位文案用 aria-live 播报)</p>
<ul class="kole-m-list kole-m-list--border" data-assert="list-empty">
<li><p class="kole-m-list__empty" aria-live="polite">暂无订单,去逛逛吧</p></li>
</ul>
</section>
</div>
<script>
/* 演示页脚本:可点行被点击后打个标记(真实业务里是路由跳转或拉取详情)。
事件委托到列表容器,避免给每一行都绑监听。 */
(function () {
var row = document.getElementById('list-row-link');
if (!row) return;
row.addEventListener('click', function () {
row.setAttribute('data-tapped', 'true');
});
})();
</script>
<script>
/* ?demo=<id> → 只显示该演示块(文档站按块预览用;无参数时全部显示,测试与回归走无参数路径) */
(function () {
var id = new URLSearchParams(location.search).get('demo');
if (!id) return;
var blocks = Array.prototype.slice.call(document.querySelectorAll('.demo-block'));
var hit = false;
blocks.forEach(function (b) {
var on = b.getAttribute('data-demo') === id;
if (on) hit = true;
b.hidden = !on;
});
if (!hit) { blocks.forEach(function (b) { b.hidden = false; }); return; }
document.body.classList.add('demo-single');
blocks.forEach(function (b) {
var label = b.querySelector('.demo-label');
if (label && !b.hidden) label.hidden = true;
});
})();
</script>
</body>
</html>
frameworks-mobile/List.jsx · React · 88 行
import React from 'react';
import './List.css';
/* 列表(移动端)— 规格 §24
一行 = 前缀位 + 主区 + 后缀位;可点行是原生 button(整行热区、键盘可达),
纯展示行用 div + role="listitem"(原生语义优先于 listitem)。
本端不接管滚动、不做虚拟列表(规格 §24.5);inline 控件要自己阻止冒泡,避免一次点击触发两个动作。 */
export default function List({
items = [],
size = 'default',
border = true,
divider = 'inset',
header = '',
footer = '',
emptyText = '',
onSelect,
children = null,
empty = null,
}) {
const cls =
'kole-m-list' +
(size === 'compact' ? ' kole-m-list--compact' : '') +
(border ? ' kole-m-list--border' : '') +
` kole-m-list--${divider}`;
const suffixOf = (it) => (
<>
{it.value ? <span className="kole-m-list__value">{it.value}</span> : null}
{it.arrow ? (
<span className="kole-m-list__arrow" aria-hidden="true">
›
</span>
) : null}
</>
);
const bodyOf = (it) => (
<>
{it.prefix ? <span className="kole-m-list__prefix">{it.prefix}</span> : null}
<span className="kole-m-list__body">
<span className="kole-m-list__title">{it.title}</span>
{it.desc ? <span className="kole-m-list__desc">{it.desc}</span> : null}
</span>
</>
);
if (!items.length) {
return (
<div className={cls} role="list" aria-live="polite">
{header ? <p className="kole-m-list__header">{header}</p> : null}
{empty || <p className="kole-m-list__empty">{emptyText || '暂无数据'}</p>}
{footer ? <p className="kole-m-list__footer">{footer}</p> : null}
</div>
);
}
return (
<div className={cls} role="list">
{header ? <p className="kole-m-list__header">{header}</p> : null}
{items.map((it, i) =>
it && it.static ? (
/* 纯展示行:div + role="listitem",不绑点击(规格 §24.6) */
<div key={it.key || i} className={'kole-m-list__item' + (it.disabled ? ' is-disabled' : '')} role="listitem" aria-disabled={it.disabled ? 'true' : undefined}>
{bodyOf(it)}
<span className="kole-m-list__suffix">{suffixOf(it)}</span>
</div>
) : (
<button
key={it.key || i}
className={'kole-m-list__item' + (it.disabled ? ' is-disabled' : '')}
type="button"
disabled={!!it.disabled}
onClick={(e) => {
if (it.disabled) return;
if (onSelect) onSelect(i, e);
}}
>
{bodyOf(it)}
<span className="kole-m-list__suffix">{suffixOf(it)}</span>
</button>
)
)}
{children}
{footer ? <p className="kole-m-list__footer">{footer}</p> : null}
</div>
);
}
frameworks-mobile/List.vue2.vue · Vue 2 · 89 行
<template>
<div :class="listClass" role="list" :aria-live="items.length ? null : 'polite'">
<p v-if="header" class="kole-m-list__header">{{ header }}</p>
<template v-if="items.length">
<template v-for="(it, i) in items">
<!-- 纯展示行:div + role="listitem",不绑点击(规格 §24.6) -->
<div
v-if="it && it.static"
:key="'static-' + i"
class="kole-m-list__item"
:class="{ 'is-disabled': it.disabled }"
role="listitem"
:aria-disabled="it.disabled ? 'true' : null"
>
<span v-if="it.prefix" class="kole-m-list__prefix">{{ it.prefix }}</span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">{{ it.title }}</span>
<span v-if="it.desc" class="kole-m-list__desc">{{ it.desc }}</span>
</span>
<span class="kole-m-list__suffix">
<span v-if="it.value" class="kole-m-list__value">{{ it.value }}</span>
<span v-if="it.arrow" class="kole-m-list__arrow" aria-hidden="true">›</span>
</span>
</div>
<button
v-else
:key="'item-' + i"
class="kole-m-list__item"
:class="{ 'is-disabled': it && it.disabled }"
type="button"
:disabled="!!(it && it.disabled)"
@click="onSelect(i)"
>
<span v-if="it && it.prefix" class="kole-m-list__prefix">{{ it.prefix }}</span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">{{ it && it.title }}</span>
<span v-if="it && it.desc" class="kole-m-list__desc">{{ it.desc }}</span>
</span>
<span class="kole-m-list__suffix">
<span v-if="it && it.value" class="kole-m-list__value">{{ it.value }}</span>
<span v-if="it && it.arrow" class="kole-m-list__arrow" aria-hidden="true">›</span>
</span>
</button>
</template>
<slot></slot>
</template>
<!-- 状态 empty:空列表占位 -->
<slot v-else name="empty">
<p class="kole-m-list__empty">{{ emptyText || '暂无数据' }}</p>
</slot>
<p v-if="footer" class="kole-m-list__footer">{{ footer }}</p>
</div>
</template>
<script>
export default {
name: 'KoleMList',
props: {
items: { type: Array, default: function () { return []; } },
size: { type: String, default: 'default' },
border: { type: Boolean, default: true },
divider: { type: String, default: 'inset' },
header: { type: String, default: '' },
footer: { type: String, default: '' },
emptyText: { type: String, default: '' }
},
computed: {
listClass: function () {
return [
this.size === 'compact' ? 'kole-m-list--compact' : '',
this.border ? 'kole-m-list--border' : '',
'kole-m-list--' + this.divider
].filter(Boolean);
}
},
methods: {
onSelect: function (i) {
this.$emit('select', i);
}
}
};
</script>
<style src="./List.css"></style>
frameworks-mobile/List.vue3.vue · Vue 3 · 83 行
<template>
<div :class="listClass" role="list" :aria-live="items.length ? null : 'polite'">
<p v-if="header" class="kole-m-list__header">{{ header }}</p>
<template v-if="items.length">
<template v-for="(it, i) in items" :key="it.key || i">
<!-- 纯展示行:div + role="listitem",不绑点击(规格 §24.6) -->
<div
v-if="it && it.static"
class="kole-m-list__item"
:class="{ 'is-disabled': it.disabled }"
role="listitem"
:aria-disabled="it.disabled ? 'true' : null"
>
<span v-if="it.prefix" class="kole-m-list__prefix">{{ it.prefix }}</span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">{{ it.title }}</span>
<span v-if="it.desc" class="kole-m-list__desc">{{ it.desc }}</span>
</span>
<span class="kole-m-list__suffix">
<span v-if="it.value" class="kole-m-list__value">{{ it.value }}</span>
<span v-if="it.arrow" class="kole-m-list__arrow" aria-hidden="true">›</span>
</span>
</div>
<button
v-else
class="kole-m-list__item"
:class="{ 'is-disabled': it && it.disabled }"
type="button"
:disabled="!!(it && it.disabled)"
@click="onSelect(i)"
>
<span v-if="it && it.prefix" class="kole-m-list__prefix">{{ it.prefix }}</span>
<span class="kole-m-list__body">
<span class="kole-m-list__title">{{ it && it.title }}</span>
<span v-if="it && it.desc" class="kole-m-list__desc">{{ it.desc }}</span>
</span>
<span class="kole-m-list__suffix">
<span v-if="it && it.value" class="kole-m-list__value">{{ it.value }}</span>
<span v-if="it && it.arrow" class="kole-m-list__arrow" aria-hidden="true">›</span>
</span>
</button>
</template>
<slot></slot>
</template>
<!-- 状态 empty:空列表占位 -->
<slot v-else name="empty">
<p class="kole-m-list__empty">{{ emptyText || '暂无数据' }}</p>
</slot>
<p v-if="footer" class="kole-m-list__footer">{{ footer }}</p>
</div>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
items: { type: Array, default: () => [] },
size: { type: String, default: 'default' },
border: { type: Boolean, default: true },
divider: { type: String, default: 'inset' },
header: { type: String, default: '' },
footer: { type: String, default: '' },
emptyText: { type: String, default: '' }
});
const emit = defineEmits(['select']);
const listClass = computed(() => [
props.size === 'compact' ? 'kole-m-list--compact' : '',
props.border ? 'kole-m-list--border' : '',
`kole-m-list--${props.divider}`
].filter(Boolean));
function onSelect(i) {
emit('select', i);
}
</script>
<style src="./List.css"></style>
frameworks-mobile/List.uniapp.vue · uni-app(跨端:小程序 / App / H5) · 206 行
<template>
<view class="kole-m-list" :class="listClass" role="list" :aria-live="items.length ? '' : 'polite'">
<text v-if="header" class="kole-m-list__header">{{ header }}</text>
<template v-if="items.length">
<view v-for="row in rows" :key="row.index" class="kole-m-list__wrap">
<view
v-if="row.item.static"
class="kole-m-list__item"
:class="{ 'is-disabled': row.item.disabled }"
role="listitem"
:aria-disabled="row.item.disabled ? 'true' : 'false'"
>
<text v-if="row.item.prefix" class="kole-m-list__prefix">{{ row.item.prefix }}</text>
<view class="kole-m-list__body">
<text class="kole-m-list__title">{{ row.item.title }}</text>
<text v-if="row.item.desc" class="kole-m-list__desc">{{ row.item.desc }}</text>
</view>
<view class="kole-m-list__suffix">
<text v-if="row.item.value" class="kole-m-list__value">{{ row.item.value }}</text>
<text v-if="row.item.arrow" class="kole-m-list__arrow">›</text>
</view>
</view>
<view
v-else
class="kole-m-list__item"
:class="{ 'is-disabled': row.item.disabled }"
:role="row.item.disabled ? '' : 'button'"
:aria-disabled="row.item.disabled ? 'true' : 'false'"
@tap="onSelect(row)"
>
<text v-if="row.item.prefix" class="kole-m-list__prefix">{{ row.item.prefix }}</text>
<view class="kole-m-list__body">
<text class="kole-m-list__title">{{ row.item.title }}</text>
<text v-if="row.item.desc" class="kole-m-list__desc">{{ row.item.desc }}</text>
</view>
<view class="kole-m-list__suffix">
<text v-if="row.item.value" class="kole-m-list__value">{{ row.item.value }}</text>
<text v-if="row.item.arrow" class="kole-m-list__arrow">›</text>
</view>
</view>
</view>
<slot></slot>
</template>
<!-- 状态 empty:空列表占位(宿主可换成自己的插槽内容) -->
<slot v-else name="empty">
<text class="kole-m-list__empty">{{ emptyText || '暂无数据' }}</text>
</slot>
<text v-if="footer" class="kole-m-list__footer">{{ footer }}</text>
</view>
</template>
<script setup>
/* uni-app 端 · 列表(移动端)— 规格 §24
跨端差异:小程序没有 ul/li/button 的列表语义,行一律用 view(标记 role="listitem" / "button"),
可点行用 @tap;分隔线画在包裹层 .kole-m-list__wrap 的上沿(与 H5 端同一策略 —— 行本身
既可能是纯展示层也可能是可点层,线挂在行下沿会在两种结构里对不上)。
尺寸用 rpx(88rpx = 375pt 下的 44px,故 default 行高 112rpx / compact 88rpx)。 */
import { computed } from 'vue';
const props = defineProps({
items: { type: Array, default: () => [] },
size: { type: String, default: 'default' },
border: { type: Boolean, default: true },
divider: { type: String, default: 'inset' },
header: { type: String, default: '' },
footer: { type: String, default: '' },
emptyText: { type: String, default: '' }
});
const emit = defineEmits(['select']);
const listClass = computed(() => [
props.size === 'compact' ? 'kole-m-list--compact' : '',
props.border ? 'kole-m-list--border' : '',
`kole-m-list--${props.divider}`
].filter(Boolean));
/* 行里带上原始下标:select 回传的是被点行在 items 里的下标 */
const rows = computed(() => (props.items || []).map((item, index) => ({ item: item || {}, index: index })));
function onSelect(row) {
if (!row || !row.item || row.item.disabled) return;
emit('select', row.index);
}
</script>
<style>
.kole-m-list {
--kole-m-list-row-height: 112rpx; /* default 行高(compact 88rpx) */
--kole-m-list-divider-inset: 32rpx; /* 分隔线左端缩进(= 页面留白) */
--kole-m-font-size-body: 32rpx;
--kole-m-font-size-label: 28rpx;
--kole-m-gutter: 32rpx;
box-sizing: border-box;
width: 100%;
background-color: var(--kole-color-card-bg);
color: var(--kole-color-text-body);
font-size: var(--kole-m-font-size-body);
}
.kole-m-list--compact {
--kole-m-list-row-height: 88rpx;
font-size: var(--kole-m-font-size-label);
}
.kole-m-list--inset { --kole-m-list-divider-inset: 32rpx; }
.kole-m-list--full { --kole-m-list-divider-inset: 0rpx; }
.kole-m-list__header,
.kole-m-list__footer {
display: block;
padding: 24rpx var(--kole-m-gutter) 16rpx;
background-color: var(--kole-color-page-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-label);
}
.kole-m-list__footer { padding-top: 16rpx; padding-bottom: 24rpx; }
.kole-m-list__wrap { position: relative; }
/* 变体 border=true:相邻行之间一条 1rpx 分隔线 */
.kole-m-list--border .kole-m-list__wrap + .kole-m-list__wrap::after {
content: '';
position: absolute;
top: 0;
left: var(--kole-m-list-divider-inset);
right: 0;
height: 1rpx;
background-color: var(--kole-color-border);
}
.kole-m-list__item {
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
min-height: var(--kole-m-list-row-height);
padding: 24rpx var(--kole-m-gutter);
background-color: var(--kole-color-card-bg);
color: var(--kole-color-text-body);
font-size: inherit;
}
/* 状态 active:按下反馈是整行背景变化(触屏不用 :hover) */
.kole-m-list__item:active { background-color: var(--kole-color-table-header-bg); }
.kole-m-list__item.is-disabled { color: var(--kole-color-text-disabled); }
.kole-m-list__prefix {
flex-shrink: 0;
margin-right: 24rpx;
color: var(--kole-color-text-secondary);
}
.kole-m-list__body { flex: 1; display: flex; flex-direction: column; }
.kole-m-list__title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--kole-color-text-title);
}
.kole-m-list__desc {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
.kole-m-list__suffix {
flex-shrink: 0;
display: flex;
align-items: center;
max-width: 45%;
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
.kole-m-list__value {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.kole-m-list__arrow {
flex-shrink: 0;
padding-left: 12rpx;
color: var(--kole-color-text-placeholder);
}
.kole-m-list__empty {
display: block;
padding: 80rpx var(--kole-m-gutter);
background-color: var(--kole-color-card-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-label);
text-align: center;
}
</style>
测试与回归
断言在真实的 375×640 设备帧里跑(引擎与 PC 侧共用 tests/_runtime.js,触控行为动词来自移动端 tests/mobile/_behaviors.js)。
断言 21 条 · 全部通过 报告 2026-09-20 16:50:33
node site/dev-server.js &
REG_BASE=http://127.0.0.1:3311 node tools/run-mobile-regression.mjs # 全量 5 个组件
npm run verify:mobile-docs # 本页内容完整性 + API 与源码一致性
设计契约
components/mobile-list.json(点击展开原始 JSON)
{
"schemaVersion": 1,
"sourceKind": "authored-spec",
"provenance": "authored-in-repo",
"specFile": "spec/移动端规格.md",
"specSection": "24 · 列表 List",
"confidence": "high",
"slug": "mobile-list",
"name": "列表 List",
"semanticTypeCandidates": [
"list",
"list-group",
"settings-list"
],
"variantDimensions": [
{
"name": "border",
"values": [
"true",
"false"
]
},
{
"name": "size",
"values": [
"default",
"compact"
]
},
{
"name": "divider",
"values": [
"inset",
"full"
]
}
],
"representativeVariants": [
{
"border": "true",
"size": "default",
"divider": "inset",
"label": "标准分组列表(默认:行高 56px、分隔线缩进)"
},
{
"border": "true",
"size": "compact",
"divider": "full",
"label": "紧凑高密度列表(行高 44px、通栏分隔线)"
},
{
"border": "false",
"size": "default",
"divider": "inset",
"label": "无分隔线列表(靠留白分组)"
}
],
"anatomy": {
"list": "根元素,一个列表区块",
"header": "可选分组标题,位于列表之上",
"item": "列表项,一行承载「前缀 + 主区 + 后缀」",
"prefix": "可选前缀位(头像、图标、序号)",
"body": "主区,标题 + 可选副标题,两行都单行省略",
"suffix": "可选后缀位(值文字、标签、箭头、开关)",
"footer": "可选分组脚注,用于补充说明"
},
"structurePatterns": {
"border": "true(行间 1px 分隔线)/ false(无分隔线,靠间距分组)",
"size": "default(行高 56px)/ compact(行高 44px)",
"divider": "inset(分隔线缩进到行内容起点)/ full(通栏分隔线)"
},
"usageHints": [
"把一组同类信息按行排列,用于「设置项 / 订单 / 成员」这类需要扫读的场合",
"移动端一屏只有 6~8 行,每行信息层级必须收敛到「标题 + 可选的说明或值」",
"分组之间用标题与留白分隔,而不是靠边框",
"可点行整行都是热区,default 行高不小于 56px、compact 不小于 44px",
"列表滚动由宿主容器负责;本组件不接管滚动、不做虚拟列表"
],
"doNotInvent": [
"虚拟滚动与无限加载的触发规则",
"行的拖拽排序与左滑操作(那是 SwipeCell 的职责)"
],
"unknowns": [
"单行最多几列(前缀 + 主区 + 后缀之外的排布)",
"分组标题是否吸顶"
],
"interaction": [
"可点行整行都是热区,default 行高不小于 56px、compact 不小于 44px",
"按下反馈是整行背景变化,不是只有文字变色",
"前缀位不参与点击判定(点图标等于点整行),后缀位里的独立控件(开关、按钮)要阻止事件冒泡,避免一次点击触发两个动作",
"列表滚动由宿主容器负责;本组件不接管滚动、不做虚拟列表"
],
"accessibility": [
"容器 role=\"list\",纯展示行 role=\"listitem\";可点行用原生 button(原生语义优先于 listitem)",
"分组标题用 aria-label 或可见文本,读屏在进入分组时能读到",
"空列表用 aria-live=\"polite\" 播报占位文案"
],
"api": {
"source": "implementation",
"note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs",
"requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。",
"props": [
{
"name": "items",
"type": "Array",
"default": "[]",
"desc": "数据驱动的行数组,每项含 title / desc / value / prefix / arrow / static / disabled(规格 §24.2)",
"required": false
},
{
"name": "size",
"type": "'default' | 'compact'",
"default": "'default'",
"desc": "变体 size:default 行高 56px / compact 44px(规格 §24.3)",
"required": false
},
{
"name": "border",
"type": "boolean",
"default": "true",
"desc": "变体 border:是否画行间 1px 分隔线(规格 §24.3)",
"required": false
},
{
"name": "divider",
"type": "'inset' | 'full'",
"default": "'inset'",
"desc": "变体 divider:分隔线缩进到内容起点 / 通栏(规格 §24.3)",
"required": false
},
{
"name": "header",
"type": "string",
"default": "''",
"desc": "分组标题,位于列表之上(规格 §24.2 header)",
"required": false
},
{
"name": "footer",
"type": "string",
"default": "''",
"desc": "分组脚注,用于补充说明(规格 §24.2 footer)",
"required": false
},
{
"name": "emptyText",
"type": "string",
"default": "''",
"desc": "状态 empty:列表为空时的占位文案(规格 §24.4)",
"required": false
}
],
"events": [
{
"name": "select",
"params": "(index)",
"desc": "可点行被点击时触发,回传该行在 items 里的下标"
}
],
"slots": [
{
"name": "default",
"desc": "追加在数据驱动行之后的自定义行(规格 §24.2 item)"
},
{
"name": "empty",
"desc": "状态 empty 的自定义占位内容,替代 emptyText(规格 §24.4)"
}
]
},
"variantClasses": {
"border": {
"true": [
".kole-m-list--border"
],
"false": []
},
"size": {
"default": [],
"compact": [
".kole-m-list--compact"
]
},
"divider": {
"inset": [
".kole-m-list--inset"
],
"full": [
".kole-m-list--full"
]
}
},
"demos": [
{
"id": "basic",
"group": "01 组件类型",
"title": "基础列表",
"desc": "标题 + 副标题 + 后缀位(值 + 箭头),行间 1px 分隔线且最后一条不画。",
"variant": "border=true / divider=inset"
},
{
"id": "prefix",
"group": "01 组件类型",
"title": "带前缀位",
"desc": "前缀放头像、图标或序号;它是装饰,点击等于点整行。",
"variant": "anatomy.prefix"
},
{
"id": "custom",
"group": "01 组件类型",
"title": "自定义后缀",
"desc": "后缀位放标签这类非文字内容,与值文字共用一行。",
"variant": "anatomy.suffix"
},
{
"id": "divider",
"group": "01 组件类型",
"title": "分隔线两态",
"desc": "divider=inset 时线从行内容起点开始;divider=full 时通栏不缩进。",
"variant": "divider=inset|full"
},
{
"id": "compact",
"group": "02 组件状态",
"title": "紧凑尺寸",
"desc": "size=compact 行高 44px,仍是触控最小边长,用于高密度筛选类列表。",
"variant": "size=compact"
},
{
"id": "clickable",
"group": "02 组件状态",
"title": "可点行",
"desc": "可点行是原生 button:键盘可达、按下整行背景变化。",
"variant": "events.select"
},
{
"id": "disabled",
"group": "02 组件状态",
"title": "禁用行",
"desc": "状态 disabled:置灰且不响应点击,用于无权限项。",
"variant": "状态 disabled"
},
{
"id": "empty",
"group": "02 组件状态",
"title": "空列表",
"desc": "状态 empty:占位文案用 aria-live 播报,读屏能听到「暂无订单」。",
"variant": "状态 empty"
}
],
"related": [
{
"slug": "cell",
"why": "单行信息用单元格(自带 arrow / value 语义);成组出现且需要分组标题与脚注时用列表"
},
{
"slug": "swipecell",
"why": "行内还需要左滑操作时用滑动单元格;只做扫读与点击用列表"
},
{
"slug": "mobile-avatar",
"why": "行的前缀位放头像时用头像组件;列表只负责行的骨架"
},
{
"slug": "mobile-collapse",
"why": "行点击后要就地展开内容用折叠面板;跳转到新页面用列表的可点行"
}
]
}