n-list 列表
组装普通纵向列表:数据行、首尾内容、原生下拉刷新和触底事件。标准行复用 n-cell;网络请求、分页与加载状态由调用方管理,底部提示可直接组合 n-loadmore。
平台兼容性
Web 与微信小程序已完成示例及复制页面运行验证;Android 与 iOS 模拟器字节码目标已编译通过。鸿蒙因本机缺少工具链尚未编译;各 App 设备运行及真机下拉触感尚未验证,不将编译通过视为设备验收通过。
如何调用
<n-list title="账号设置" :list="rows" footer="选择项目查看详情" @click="onRowClick" />
import { ref } from "vue";
import { type NListClick } from "@/uni_modules/new-ui/components/n-list/props";
const rows = ref<UTSJSONObject[]>([
{ id: 1, title: "账号与安全", value: "已保护", arrow: true },
{ id: 2, title: "暂不可用", disabled: true }
]);
function onRowClick(detail: NListClick): void {
uni.showToast({ title: `选择第 ${detail.index + 1} 行`, icon: "none" });
}
height 默认 0,内容自然撑高。正数开启内部纵向滚动;virtual 默认开启,此时 itemHeight 为固定行高,组件只挂载可见行和缓冲行。多行文字、图片自然高度或需要保留屏外未提交输入时关闭 virtual。窗口渲染会卸载屏外行,业务编辑状态应绑定数据。
item 插槽接管完整行,仍由组件派发行点击;内部按钮等独立操作使用 click.stop 防止冒泡。Web 标准行支持键盘激活;提供 item 后,行作为列表项容器,插槽内的主操作和独立操作应各用可聚焦按钮承载。默认插槽追加自由内容,不派发数据行 click。数据源不被组件修改。
下面是可独立运行的刷新与分页页面。计时器模拟请求,正式使用时替换为业务接口,并保留并发守卫和过期结果检查。
<template>
<n-page>
<n-list ref="listRef" :list="rows" :height="300" refresher-enabled @refresherrefresh="onRefresh" @scrolltolower="loadMore">
<template #bottom><n-loadmore :status="loadStatus" @loadmore="loadMore" /></template>
</n-list>
</n-page>
</template>
<script setup lang="uts">
import { ref } from "vue";
import { type NLoadmoreStatus } from "@/uni_modules/new-ui/components/n-loadmore/props";
const listRef = ref<NListComponentPublicInstance | null>(null);
const rows = ref<UTSJSONObject[]>([{ id: 1, title: "第一条记录" }]);
const loadStatus = ref<NLoadmoreStatus>("loadmore");
let timer = -1;
let revision = 0;
let pending = false;
function requestRows(refresh: boolean): void {
if (pending) return;
pending = true;
loadStatus.value = "loading";
const request = ++revision;
timer = setTimeout((): void => {
timer = -1;
if (request != revision) return;
const start = refresh ? 0 : rows.value.length;
const page: UTSJSONObject[] = [];
for (let i = 0; i < 20; i++) page.push({ id: start + i + 1, title: `列表记录 ${start + i + 1}` });
rows.value = refresh ? page : rows.value.concat(page);
pending = false;
loadStatus.value = "loadmore";
listRef.value?.stopRefresh();
}, 600);
}
function loadMore(): void { requestRows(false); }
function onRefresh(): void {
if (timer != -1) clearTimeout(timer);
timer = -1;
revision++;
pending = false;
requestRows(true);
}
onUnload((): void => { revision++; if (timer != -1) clearTimeout(timer); });
</script>
接口失败应保留原数据并给出明确反馈,同时释放 pending、恢复 loadStatus,并调用 stopRefresh()。示例的一键复制包含当前数据、配置、插槽、模拟失败及请求清理。
header/footer 位于滚动容器外,替换标题与说明;top/bottom 位于滚动内容内。刷新插槽由原生滚动控件承载,组件不通过手写拖动模拟刷新。自定义刷新节点显式宽高,并放在滚动容器最后一个子元素。
数据标识
nodeKey 默认 id,字符串与数字分别识别;缺少标识时按对象身份保持当前挂载行。重复的显式标识会触发 error 并停止数据行渲染,修正后恢复。需要在不可变数据更新或重新请求后继续复用行时,应提供稳定的唯一标识。
scrollIntoView 可直接填写业务标识,例如 :scroll-into-view="1000" 定位数字 id=1000 的屏外行,无需访问实例内部数据。目标数据稍后到达或顶部插槽高度变化时会重新定位。数字仅匹配数字标识;字符串先匹配组件生成的行 ID,再匹配字符串标识,最后匹配数字标识的文本表示。getScrollTarget() 可将当前生成行 ID 还原为原类型的业务标识,适合复制配置和保存定位状态;没有业务标识的行应先补充稳定 nodeKey,再跨实例保存定位。
定制范围
主题令牌优先;customClass 必须定义于组件自己的样式表。根样式通过 customStyle,内部节点分别通过 titleStyle/titleTextStyle、footerStyle/footerTextStyle、bodyStyle、itemStyle。
窗口模式下 bodyStyle/itemStyle 中影响几何的尺寸、内外边距、边框宽度、定位和 transform 声明被移除,圆角和边框颜色保留;固定高度与占位统一由 itemHeight 管理。文字和复杂行布局由 item 插槽定制。非窗口模式不限制上述样式。整组外边距使用 customStyle,标题背景、留白、颜色和字号使用对应 Style,避免同义属性。
API
属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| list | 数据源;标准项读取 title/label/value/arrow/disabled,复杂内容使用 item 插槽 | UTSJSONObject[] | [] |
| nodeKey | 唯一标识字段;字符串与数字区分,缺省使用对象身份,重复标识触发 error 并停止数据项渲染 | string | id |
| height | 滚动视口高度(px);0 随内容撑高,正数开启内部纵向滚动;外层标题与说明不计入高度 | number | 0 |
| virtual | 限高时仅渲染可见行及缓冲行;行高由 itemHeight 统一管理,复杂不等高内容请关闭 | boolean | true |
| itemHeight | 窗口渲染的固定行高(px),须为不小于 40 的有限数;非窗口模式不限制行高 | number | 50 |
| bufferSize | 可见区域上下各预渲染的行数,须为非负整数 | number | 15 |
| scrollTop | 目标纵向滚动位置(px),须为有限非负数;不随手动滚动回写 | number | 0 |
| scrollIntoView | 定位到业务 nodeKey 或子节点 ID;数字只匹配数字标识,字符串依次匹配生成行 ID、字符串标识、数字标识的文本表示 | string / number | |
| scrollWithAnimation | 属性定位和 scrollToTop 是否使用原生滚动动画 | boolean | false |
| showScrollbar | 显示原生纵向滚动条 | boolean | false |
| upperThreshold | 距顶部多少 px 触发 scrolltoupper,须为有限非负数 | number | 50 |
| lowerThreshold | 距底部多少 px 触发 scrolltolower,须为有限非负数 | number | 50 |
| refresherEnabled | 限高时开启原生下拉刷新;请求完成后通过实例 stopRefresh() 结束 | boolean | false |
| refresherThreshold | 下拉刷新触发距离(px),须为有限正数 | number | 50 |
| refresherBackground | 刷新区域背景色;空 = 透明 | string | |
| refresherDefaultText | 默认下拉文字;空 = 内置语言包 | string | |
| refresherPullingText | 超过刷新阈值时的文字;空 = 内置语言包 | string | |
| refresherRefreshingText | 请求中的刷新文字;空 = 内置语言包 | string | |
| showBackTop | 限高列表滚动超过阈值时显示返回顶部按钮,复用 n-back-top | boolean | false |
| title | 列表标题;header 插槽提供时替代文字 | string | |
| footer | 列表末尾说明;footer 插槽提供时替代文字 | string | |
| border | 数据项之间显示分隔线;最后一项不绘制 | boolean | true |
| topBorder | 内容区域的顶部边线 | boolean | true |
| bottomBorder | 内容区域的底部边线 | boolean | false |
| borderColor | 边线颜色;空 = 主题边框色 | string | |
| topLeft | 顶部边线左侧内缩(px),须为有限非负数 | number | 0 |
| topRight | 顶部边线右侧内缩(px),须为有限非负数 | number | 0 |
| bottomLeft | 底部边线左侧内缩(px),须为有限非负数 | number | 0 |
| bottomRight | 底部边线右侧内缩(px),须为有限非负数 | number | 0 |
| titleStyle | 标题容器样式,可调整背景和内边距 | string | |
| titleTextStyle | 标题文字样式,可调整颜色、字号和字重 | string | |
| footerStyle | 末尾说明容器样式,可调整内边距 | string | |
| footerTextStyle | 末尾说明文字样式,可调整颜色与字号 | string | |
| bodyStyle | 内容容器样式;窗口几何由组件管理,不接受 height/min-height/max-height/padding/border 等改变尺寸的声明 | string | |
| itemStyle | 数据行容器样式;窗口模式不接受改变固定行高的尺寸、内外边距或边框声明 | string | |
| customClass | 根节点额外类,须在组件自己的样式表中定义 | string | |
| customStyle | 根节点行内样式;内部滚动视口高度只由 height 管理 | string |
事件
| 事件名 | 说明 |
|---|---|
| click | 点击数据行时触发,参数 {item,index};禁用行不触发,插槽内独立操作可用 click.stop |
| scroll | 内部纵向滚动时触发,参数为原生 UniScrollEvent |
| scrollend | 内部滚动结束时触发,参数为原生 UniScrollEvent |
| scrolltoupper | 内部滚动进入顶部阈值时触发,参数为原生 UniScrollToUpperEvent |
| scrolltolower | 内部滚动进入底部阈值时触发,参数为原生 UniScrollToLowerEvent,可衔接加载更多 |
| refresherpulling | 下拉时触发,参数为原生 UniRefresherEvent |
| refresherrefresh | 下拉达到阈值并释放时触发;请求完成须调用 stopRefresh,同次刷新不重复触发 |
| refresherrestore | 下拉刷新控件复位时触发,参数为原生 UniRefresherEvent |
| refresherabort | 下拉刷新中止时触发,参数为原生 UniRefresherEvent |
| error | 属性或数据标识非法时触发,参数 {prop,message};修正后自动恢复 |
插槽
| 插槽名 | 说明 |
|---|---|
| header | 替换 title;位于滚动视口上方,常驻并自然占位 |
| footer | 替换 footer 说明;位于滚动视口下方,常驻并自然占位 |
| top | 滚动内容前置区;实际高度参与可见窗口计算 |
| item | 数据行;参数 item、index(源下标)、last、id(本组件生成的行 ID) |
| default | 在数据行后追加自由内容;自行组装 n-cell 或其他内容,不产生逐行 click |
| bottom | 滚动内容末尾;适合 n-loadmore 等加载提示 |
| empty | 数据为空且未提供默认插槽时的内容,默认复用 n-empty |
| refresher | 原生刷新区;参数 status(default/pulling/refreshing)、text |
方法
| 方法名 | 说明 |
|---|---|
| stopRefresh() | 请求结束后调用,停止下拉刷新;不会修改数据源 |
| scrollToTop() | 返回内部滚动视口顶部;是否平滑由 scrollWithAnimation 决定 |
| getScrollTarget() | 返回当前目标对应的 string/number 业务标识;无业务标识时保留原生节点目标,适合复制配置及保存定位状态 |
样式令牌
| CSS 变量 | 默认值 | 作用 |
|---|---|---|
| --n-list-padding-x | 16px | 首尾与分隔线的水平内缩 |
| --n-list-font-size | 14px | 标题、说明与刷新文字字号 |
| --n-list-title-padding-top | 16px | 标题上留白 |
| --n-list-title-padding-bottom | 10px | 标题下留白 |
| --n-list-footer-padding-top | 10px | 末尾说明上留白 |
| --n-list-footer-padding-bottom | 0px | 末尾说明下留白 |
| --n-list-disabled-opacity | 0.5 | 自定义数据行禁用透明度;标准行使用 n-cell 的令牌 |
| --n-list-pressed-background | rgba(0,0,0,0.08) | 自定义数据行按压底色 |
实现说明
| 能力 | 常见做法 | 本组件做法与取舍 |
|---|---|---|
| 标准列表行 | 容器再实现一套行布局与箭头 | 复用 n-cell;图片、图标、折叠和侧滑通过 item 组合现有组件 |
| 普通容器 | 标题、末尾说明、上下边线及独立内缩 | 保留全部布局能力;字号、颜色、背景及留白收敛到已有 Style 定制通道 |
| 长列表 | 固定行高、缓冲窗口与上下占位 | 复用共享窗口工具,支持关闭窗口以使用自然高度;顶部内容按真实高度测量,不依赖业务填写预留高度 |
| 下拉刷新 | 原生滚动组件手势与业务回调 | 透传完整刷新事件、阈值和背景,三态文案跟随语言;stopRefresh 显式结束,不加入固定延时或模拟触感 |
| 触底加载 | 列表容器内置请求或独立加载提示 | 容器仅派发事件;n-loadmore 显示状态,调用方管理网络、并发与重试 |
| 返回顶部 | 在列表内再实现一个浮动按钮 | showBackTop 复用 n-back-top,直接调用统一的回顶函数 |
| 索引与分组 | 通用滚动列表同时包含索引、分组与勾选 | 由 n-index-list 承担完整职责,避免两个组件维护同一组公共 API;普通纵向列表使用本组件,横向浏览使用 n-scroll-list |
| 实例调用 | 字符串分发或不明确的实例类型 | defineExpose 暴露方法,使用编译器生成的明确实例类型直接调用 |