Skip to content

ConfigList 配置列表 ​

介绍 ​

通用列表容器:分页加载、筛选、详情与新增,接口数据内存缓存。

组合使用 config-list、config-form、config-detail、config-footer,覆盖列表、详情、保存等常见 CRUD 场景。

基础用法(#item) ​

无列表增删动画时,用 #item 即可。列表内部已做 v-for,插槽只负责单行内容;适合筛选、分页、跳转详情等常规场景。

html
<config-list
  ref="listRef"
  list-key="id"
  :api="fetchTaskList"
  :params="params"
  :immediate="true"
  :row-gap="12"
  @item-click="handleItemClick"
>
  <template #item="{ item, index, checked }">
    <view class="task-card">
      <view class="title">{{ item.title }}</view>
      <view class="desc">{{ item.content || '暂无描述' }}</view>
    </view>
  </template>
</config-list>

插槽参数:

参数说明
item当前行数据
index下标
checked当前行是否选中(配合 v-model:checked)

TIP

#item 与 #content 二选一。传入 #content 后不再渲染内置 #item 列表。

列表动画(#content) ​

需要增删动画时,不要往 #item 里塞 transition-group,改用 #content,在页面侧自行接入 TransitionGroup + TransitionCollapse。

为什么不内置 transition-group ​

微信小程序对「自定义组件内 v-for + 作用域插槽」支持不完整。若在列表组件内部用 #item 再包动画:

  • 作用域插槽会编译成 item-0、item-1… 按下标命名
  • 列表前插 / 删除时下标变化,再叠加离场占位,容易出现 More than one slot named "item-0"、空高度占位等异常
  • H5 往往正常,小程序复现,属于端能力差异,不是业务数据写错

因此:#item 只做静态行渲染;动画结构放到页面,让 v-for 落在页面节点上。

正确写法 ​

#content 提供数据源;页面维护本地 values,v-for 遍历 values(不要遍历 data):

html
<config-list
  ref="listRef"
  list-key="id"
  :api="fetchTaskList"
  :params="params"
  :immediate="true"
  :row-gap="12"
  :back-top="true"
>
  <template #content="{ data, getKey }">
    <vd-transition-group
      v-if="data.length"
      v-model="values"
      :data="data"
      :data-key="getKey"
      name="enter-right"
      :duration="{ enter: 400, leave: 600 }"
      :delay="{ enter: 100, leave: 100 }"
    >
      <vd-transition-collapse
        v-for="item in values"
        :key="getKey(item)"
        :item-key="getKey(item)"
        custom-class="task-card"
        content-class="task-content"
        @click="handleItemClick(item)"
      >
        <view class="task-head">
          <view class="title">{{ item.title }}</view>
          <!-- ... -->
        </view>
        <view class="task-desc">{{ item.content || '暂无描述' }}</view>
        <view class="task-footer" @click.stop>
          <!-- 删除等操作 -->
        </view>
      </vd-transition-collapse>
    </vd-transition-group>
  </template>
</config-list>
ts
const values = ref<any[]>([]);

// 筛选 / 重载前清空 values,避免旧离场项残留
function handleSearch() {
  values.value = [];
  listRef.value?.reloadData();
}

// 从详情返回时刷新已加载区间
onReshow(() => {
  listRef.value?.refreshData();
});

样式上建议:custom-class 放卡片外壳(背景、圆角、阴影),content-class 放内容区内边距,避免折叠测高把 padding 算乱。

#content 常用参数:

参数说明
data列表数据源(完整数据,不含离场占位)
total总数
checked已选中项
getKey按 list-key 取唯一标识
isChecked判断某项是否选中

注意事项 ​

  1. 有动画时用 #content;无动画时用 #item,二者不要混用。
  2. v-for 必须遍历 values:data 是完整数据源,values 才含离场项;遍历 data 时删除没有离场动画。
  3. item-key / :key 用稳定唯一标识(如 id),与 list-key / data-key 一致,不要用 index。
  4. 增删改走列表接口数据:reloadData / refreshData / refreshItem 更新 data;由 transition-group 同步 values。
  5. 筛选、强制重载前先 values = [],再 reloadData(),避免上一批离场项残留。
  6. 跨端:H5 上「#item + transition-group」偶发可用,小程序仍可能异常,动画请统一走 #content。

错误示范 ​

不要在 #item 里包 transition-group

html
<!-- ❌ 错误:组件内作用域插槽 + 动画,小程序增删易异常 -->
<config-list :api="fetchTaskList">
  <template #item="{ item }">
    <vd-transition-collapse :item-key="item.id">
      ...
    </vd-transition-collapse>
  </template>
</config-list>
html
<!-- ✅ 无动画:用 #item -->
<config-list :api="fetchTaskList">
  <template #item="{ item }">
    <view class="task-card">{{ item.title }}</view>
  </template>
</config-list>

<!-- ✅ 有动画:用 #content,在页面 v-for values -->

不要对 data 做 v-for

html
<!-- ❌ 错误:离场项只存在于 values -->
<vd-transition-group v-model="values" :data="data" data-key="id">
  <vd-transition-collapse
    v-for="item in data"
    :key="item.id"
    :item-key="item.id"
  />
</vd-transition-group>
html
<!-- ✅ 正确 -->
<vd-transition-collapse
  v-for="item in values"
  :key="item.id"
  :item-key="item.id"
/>

不要用 index 当 key

html
<!-- ❌ 错误:前插/删除后节点复用错乱 -->
<vd-transition-collapse
  v-for="(item, index) in values"
  :key="index"
  :item-key="index"
/>

筛选时不要忘记清空 values

ts
// ❌ 错误:只 reload,旧 values 可能残留离场项
function handleSearch() {
  listRef.value?.reloadData();
}

// ✅ 正确
function handleSearch() {
  values.value = [];
  listRef.value?.reloadData();
}

赣ICP备2025061025号-1