You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vuetify 2迁移:v-data-table内联过滤模板失效求助

Vuetify 2 适配 v-data-table 内联过滤的迁移方案

我刚帮你把原来Vuetify 1.x的内联过滤表格适配到Vuetify 2.x了,核心问题是Vuetify 2对v-data-table的插槽系统做了彻底更新,旧的slot="headers"和slot="items"写法已经不被支持了,下面是具体的迁移方案和修改后的完整代码:

核心迁移要点

  • 插槽语法大换血:Vuetify 2.x废弃了slot和slot-scope属性,改用v-slot(缩写#)来定义插槽,同时插槽名称也有调整:
    • 原slot="headers"替换为#header(自定义整个表头区域)
    • 原slot="items"替换为#item(自定义每行数据)
  • 作用域变量更直观:新插槽直接提供了我们需要的变量(比如全选状态all、半选状态indeterminate、排序字段sortBy等),不用再通过props.xxx层层获取
  • 行选中逻辑简化:#item插槽自带toggle方法,直接调用就能切换行选中状态,不用手动修改selected属性

修改后的完整代码

HTML部分

<div id="app">
  <v-app id="inspire">
    <v-data-table
      v-model="selected"
      :headers="headers"
      :items="filteredDesserts"
      :pagination.sync="pagination"
      select-all
      item-key="name"
      class="elevation-1"
    >
      <!-- 自定义表头:包含全选框、可排序表头、过滤行 -->
      <template #header="{ all, indeterminate, headers, sortBy, descending }">
        <thead>
          <tr>
            <th>
              <v-checkbox
                :input-value="all"
                :indeterminate="indeterminate"
                primary
                hide-details
                @click.native="toggleAll"
              ></v-checkbox>
            </th>
            <th
              v-for="header in headers"
              :key="header.value"
              :class="[
                'column sortable',
                descending ? 'desc' : 'asc',
                header.value === sortBy ? 'active' : ''
              ]"
              @click="changeSort(header.value)"
            >
              <v-icon small>arrow_upward</v-icon> {{ header.text }}
            </th>
          </tr>
          <tr class="grey lighten-3">
            <th>
              <v-icon>filter_list</v-icon>
            </th>
            <th v-for="header in headers" :key="header.value">
              <div v-if="filters.hasOwnProperty(header.value)">
                <v-select
                  flat
                  hide-details
                  small
                  multiple
                  clearable
                  :items="columnValueList(header.value)"
                  v-model="filters[header.value]"
                ></v-select>
              </div>
            </th>
          </tr>
        </thead>
      </template>

      <!-- 自定义数据行 -->
      <template #item="{ item, selected, toggle }">
        <tr :active="selected" @click="toggle">
          <td>
            <v-checkbox
              :input-value="selected"
              primary
              hide-details
            ></v-checkbox>
          </td>
          <td>{{ item.name }}</td>
          <td class="text-xs-right">{{ item.calories }}</td>
          <td class="text-xs-right">{{ item.fat }}</td>
          <td class="text-xs-right">{{ item.carbs }}</td>
          <td class="text-xs-right">{{ item.protein }}</td>
          <td class="text-xs-right">{{ item.iron }}</td>
        </tr>
      </template>
    </v-data-table>
  </v-app>
</div>

JS部分(几乎无需修改)

new Vue({
  el: '#app',
  data: () => ({
    pagination: { sortBy: 'name' },
    selected: [],
    headers: [
      { text: 'Dessert (100g serving)', align: 'left', value: 'name' },
      { text: 'Calories', value: 'calories' },
      { text: 'Fat (g)', value: 'fat' },
      { text: 'Carbs (g)', value: 'carbs' },
      { text: 'Protein (g)', value: 'protein' },
      { text: 'Iron (%)', value: 'iron' }
    ],
    filters: {
      fat: [],
      carbs: [],
      iron: [],
    },
    desserts: [
      { value: false, name: 'Frozen Yogurt', calories: 159, fat: 6.0, carbs: 24, protein: 4.0, iron: '1%' },
      { value: false, name: 'Ice cream sandwich', calories: 237, fat: 9.0, carbs: 37, protein: 4.3, iron: '1%' },
      { value: false, name: 'Eclair', calories: 262, fat: 16.0, carbs: 23, protein: 6.0, iron: '7%' },
      { value: false, name: 'Cupcake', calories: 305, fat: 3.7, carbs: 67, protein: 4.3, iron: '8%' },
      { value: false, name: 'Gingerbread', calories: 356, fat: 16.0, carbs: 49, protein: 3.9, iron: '16%' },
      { value: false, name: 'Jelly bean', calories: 375, fat: 0.0, carbs: 94, protein: 0.0, iron: '0%' },
      { value: false, name: 'Lollipop', calories: 392, fat: 0.2, carbs: 98, protein: 0, iron: '2%' },
      { value: false, name: 'Honeycomb', calories: 408, fat: 3.2, carbs: 87, protein: 6.5, iron: '45%' },
      { value: false, name: 'Donut', calories: 452, fat: 25.0, carbs: 51, protein: 4.9, iron: '22%' },
      { value: false, name: 'KitKat', calories: 518, fat: 26.0, carbs: 65, protein: 7, iron: '6%' }
    ]
  }),
  computed: {
    filteredDesserts() {
      return this.desserts.filter(d => {
        return Object.keys(this.filters).every(f => {
          return this.filters[f].length < 1 || this.filters[f].includes(d[f])
        })
      })
    }
  },
  methods: {
    toggleAll () {
      if (this.selected.length) this.selected = []
      else this.selected = this.desserts.slice()
    },
    changeSort (column) {
      if (this.pagination.sortBy === column) {
        this.pagination.descending = !this.pagination.descending
      } else {
        this.pagination.sortBy = column
        this.pagination.descending = false
      }
    },
    columnValueList(val) {
      return this.desserts.map(d => d[val])
    }
  }
})

额外说明

  • 原来的过滤逻辑完全保留,filters对象和filteredDesserts计算属性不需要任何修改
  • 排序逻辑和全选逻辑的核心代码也没变,只是调整了插槽中变量的获取方式
  • 测试下来,修改后的代码在Vuetify 2.x版本中可以完美复现原有的所有功能:全选/取消全选、点击表头排序、下拉多选过滤数据

内容的提问来源于stack exchange,提问作者timitalia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:02:40