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

Nuxt中如何调用对象内过滤函数并实现多条件筛选?

Nuxt筛选菜单:修复函数调用错误+实现多条件筛选

问题描述

在Nuxt项目头部实现筛选下拉菜单,用户选择条件后触发过滤函数时出现"... is not a function"错误;同时需要实现多条件同时筛选的功能。

相关代码示例

筛选配置对象

const filters = [
  {
    section: 'Call Types',
    slug: 'call-types',
    type: 'multiselect',
    options: [
      {
        label: 'Inbound',
        slug: 'inbound',
        value: true,
        func: (data) =>
          data.items.filter((item) => {
            return item.className === 'ib'
          }),
      },
      {
        label: 'Outbound',
        slug: 'outbound',
        value: true,
        func: (data) =>
          data.items.filter((item) => {
            return item.className === 'ob'
          }),
      },
    ],
  },
  {
    section: 'Data Types',
    slug: 'data-types',
    type: 'multiselect',
    options: [
      {
        label: 'Sentiment',
        slug: 'sentiment',
        value: false,
        func: (data) =>
          data.items.filter((item) => {
            return item.d1 === 1
          }),
      },
      {
        label: 'Capture Fails',
        slug: 'capture-fails',
        value: false,
        func: (data) =>
          data.items.filter((item) => {
            return item.d2 > 0.5
          }),
      },
      {
        label: 'Audio Notes',
        slug: 'audio-notes',
        value: false,
        func: (data) =>
          data.items.filter((item) => {
            return item.d1 === 1 && item.d2 > 0.5
          }),
      },
    ],
  },
  {
    section: 'Others',
    slug: 'others',
    type: 'singleselect',
    options: [
      {
        label: 'Show All Agents',
        slug: 'show-all-agents',
        value: true,
        func: (data) =>
          data.items.filter((item) => {
            return item
          }),
      },
    ],
  },
]

exports.filters = filters

Timeline组件代码

<template>
  <vis-timeline
    :groups="filteredData.groups"
    :items="filteredData.items"
  />
</template>

<script>
import { mapActions, mapState } from 'vuex'
import VisTimeline from '~/components/UI/VisTimeline.vue'
export default {
  components: { VisTimeline },
  computed: {
    ...mapState({
      callTimeline: (state) => state.agents.callTimeline,
      timelineLoading: (state) => state.agents.loading,
      filterOption: (state) => state.agents.filterOption,
    }),
    filteredData() {
      var filtered = Object.assign(
        {},
        JSON.parse(JSON.stringify(this.callTimeline))
      )
      return this.filterItems(filtered)
    },
  },
  methods: {
    filterItems(data) {
      if (this.filterOption) {
        this.filterOption.forEach((filter) => {
          filter.options.some((option) => {
            if (option.value) {
              return option.func(data)
            }
          })
        })
      }
      return data
    }
  }
}
</script>

Header组件代码

<template>
  <a-layout-header style="background: #fff; padding: 0">
    <a-dropdown
      :trigger="['click']"
      :visible="visible"
      @visibleChange="handleVisible"
    >
      <template #overlay>
        <a-menu>
          <a-menu-item-group
            v-for="filter in filters"
            :key="filter.slug"
            :title="filter.section"
          >
            <a-menu-item :key="filter.slug">
              <a-checkbox
                v-for="option in filter.options"
                :key="option.slug"
                :name="option.slug"
                :checked="option.value"
                @change="filterChanges"
              >
                {{ option.label }}
              </a-checkbox>
            </a-menu-item>
          </a-menu-item-group>

          <a-menu-divider />
          <a-menu-item key="buttons"
            ><div class="flex justify-between">
              <a-button @click="visible = false" type="danger" ghost
                >Cancel</a-button
              >
              <a-button @click="applyFilters" type="primary">Filter</a-button>
            </div></a-menu-item
          >
        </a-menu>
      </template>
      <div class="mb-1">
        <a-button type="primary">
          <div class="flex flex-row justify-center space-x-2 items-center">
            <a-icon type="filter" />
            <span>Filters</span>
            <a-icon type="down" />
          </div>
        </a-button>
      </div>
    </a-dropdown>
  </a-layout-header>
</template>
<script>
import { mapActions} from 'vuex'
import { filters } from '~/utils/Filters'
export default {
  name: 'DBHeader',
  data() {
    return {
      visible: false,
      filters: filters,
    }
  },
  methods: {
    ...mapActions({
      setFilterOption: 'agents/setFilterOption',
    }),
    handleVisible(flag) {
      this.visible = flag
    },
    filterChanges(e) {
      this.filters.forEach((filter) => {
        filter.options.some((option) => {
          if (option.slug === e.target.name) {
            option.value = e.target.checked
          }
        })
      })
    },
    applyFilters() {
      var filters = Object.assign([], JSON.parse(JSON.stringify(this.filters)))
      this.setFilterOption(filters)
      this.visible = false
    },
  },
}
</script>

解决方案

1. 修复"is not a function"错误

问题根源:JSON.stringify无法序列化函数类型数据,你在applyFilters中把整个filters对象序列化后存入Vuex,导致filterOption里的func字段丢失,变成undefined。

修改步骤:

步骤1:修改Header组件的applyFilters方法

只存储筛选状态(slug和value),不保存函数:

applyFilters() {
  // 收集选中的筛选状态,剔除函数
  const selectedFilters = this.filters.map(filter => ({
    slug: filter.slug,
    options: filter.options.map(option => ({
      slug: option.slug,
      value: option.value
    }))
  }))
  this.setFilterOption(selectedFilters)
  this.visible = false
}

步骤2:修改Timeline组件的过滤逻辑

导入原始filters配置,关联对应的过滤函数:

<script>
import { mapActions, mapState } from 'vuex'
import VisTimeline from '~/components/UI/VisTimeline.vue'
// 导入原始筛选配置
import { filters as originalFilters } from '~/utils/Filters'
export default {
  components: { VisTimeline },
  computed: {
    ...mapState({
      callTimeline: (state) => state.agents.callTimeline,
      timelineLoading: (state) => state.agents.loading,
      filterOption: (state) => state.agents.filterOption,
    }),
    filteredData() {
      var filtered = Object.assign(
        {},
        JSON.parse(JSON.stringify(this.callTimeline))
      )
      return this.filterItems(filtered)
    },
  },
  methods: {
    filterItems(data) {
      if (!this.filterOption || !this.filterOption.length) return data

      let filteredItems = [...data.items]
      // 遍历选中的筛选组
      this.filterOption.forEach(filterState => {
        // 匹配原始配置中的筛选组
        const originalFilter = originalFilters.find(f => f.slug === filterState.slug)
        if (!originalFilter) return

        // 收集当前组内选中的过滤函数
        const activeFuncs = []
        filterState.options.forEach(optionState => {
          if (optionState.value) {
            const originalOption = originalFilter.options.find(o => o.slug === optionState.slug)
            if (originalOption?.func) {
              activeFuncs.push(originalOption.func)
            }
          }
        })

        // 叠加执行过滤函数
        activeFuncs.forEach(func => {
          filteredItems = func({ items: filteredItems }).items
        })
      })

      data.items = filteredItems
      return data
    }
  }
}
</script>

2. 实现多条件组合筛选

如果需要支持不同筛选组的逻辑(比如组内多选是OR还是AND),可以给筛选配置添加logic字段:

步骤1:更新筛选配置对象

const filters = [
  {
    section: 'Call Types',
    slug: 'call-types',
    type: 'multiselect',
    logic: 'or', // 组内多选:满足任意一个即可
    options: [/* ... */]
  },
  {
    section: 'Data Types',
    slug: 'data-types',
    type: 'multiselect',
    logic: 'and', // 组内多选:必须满足所有选中项
    options: [/* ... */]
  },
  {
    section: 'Others',
    slug: 'others',
    type: 'singleselect',
    logic: 'single',
    options: [/* ... */]
  },
]

步骤2:修改Timeline组件的filterItems方法

适配不同逻辑的筛选:

filterItems(data) {
  if (!this.filterOption || !this.filterOption.length) return data

  let filteredItems = [...data.items]

  this.filterOption.forEach(filterState => {
    const originalFilter = originalFilters.find(f => f.slug === filterState.slug)
    if (!originalFilter) return

    // 将过滤函数转换为单item的判断函数,方便逻辑组合
    const itemCheckers = []
    filterState.options.forEach(optionState => {
      if (optionState.value) {
        const originalOption = originalFilter.options.find(o => o.slug === optionState.slug)
        if (originalOption?.func) {
          // 判断单个item是否符合当前筛选条件
          const checker = item => originalOption.func({ items: [item] }).items.length > 0
          itemCheckers.push(checker)
        }
      }
    })

    if (itemCheckers.length === 0) return

    // 根据组内逻辑过滤
    switch (originalFilter.logic) {
      case 'or':
        filteredItems = filteredItems.filter(item => itemCheckers.some(check => check(item)))
        break
      case 'and':
        filteredItems = filteredItems.filter(item => itemCheckers.every(check => check(item)))
        break
      case 'single':
        // 单选逻辑,直接取第一个生效的过滤结果
        const firstChecker = itemCheckers[0]
        filteredItems = filteredItems.filter(firstChecker)
        break
    }
  })

  data.items = filteredItems
  return data
}

内容的提问来源于stack exchange,提问作者Halil ibrahim yüce

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:48:22