vxe-grid グローバル カスタム フィルタ フィルタ、辞書フィルタリングをサポート

1. vxe-table グローバル フィルター フィルターの実装

公式サイトの例: https://vxetable.cn/#/table/renderer/filter
入力後: サンプルを参考に自分で実装することも、ソースコードをダウンロードして調整することもできます

ここに画像の説明を挿入
ダウンロードして解凍したら、vscode を使用して解凍されたファイルを開きます。FilterInput、作業で使用する vxe テーブルのバージョンの数をグローバルに取得し、バージョンのディレクトリを入力します。
ここに画像の説明を挿入

クリックして入力し、このパスに従って対応するフォルダーを見つけます
ここに画像の説明を挿入
。フィルター関連の vue を自分のプロジェクトのコンポーネント フォルダーにコピーし、
ここに画像の説明を挿入
filter.tsx をプロジェクトに追加します。一般的に使用される FilterInput および FilterContent コンポーネント、その他の FilterExtend および FilterComplex を使用できます。最初にコメントアウトしました。
ここに画像の説明を挿入

filter.ts を開いて
ここに画像の説明を挿入
使用できるようにします 使用方法は公式サイトの例に従ってカラムに配置します フィルターの構造は変更できません フィルターレンデスのコンポーネントに 1 つずつ対応しますv4 の関連ソースコードは次のとおりです
ここに画像の説明を挿入

FilterInput.vue

<template>
  <div class="my-filter-input">
    <vxe-input type="text" v-model="demo1.option.data" placeholder="支持回车筛选" @keyup="keyupEvent" @input="changeOptionEvent"></vxe-input>
  </div>
</template>

<script lang="ts">
import {
      
       defineComponent, PropType, reactive } from 'vue'
import {
      
       VxeInputEvents, VxeGlobalRendererHandles } from 'vxe-table'

export default defineComponent({
      
      
  name: 'FilterInput',
  props: {
      
      
    params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>
  },
  setup (props) {
      
      
    const demo1 = reactive({
      
      
      option: null as any
    })

    const load = () => {
      
      
      const {
      
       params } = props
      if (params) {
      
      
        const {
      
       column } = params
        const option = column.filters[0]
        demo1.option = option
      }
    }

    const changeOptionEvent = () => {
      
      
      const {
      
       params } = props
      const {
      
       option } = demo1
      if (params && option) {
      
      
        const {
      
       $panel } = params
        const checked = !!option.data
        $panel.changeOption(null, checked, option)
      }
    }

    const keyupEvent: VxeInputEvents.Keyup = ({
       
        $event }) => {
      
      
      const {
      
       params } = props
      if (params) {
      
      
        const {
      
       $panel } = params
        if ($event.keyCode === 13) {
      
      
          $panel.confirmFilter($event)
        }
      }
    }

    load()

    return {
      
      
      demo1,
      changeOptionEvent,
      keyupEvent
    }
  }
})
</script>

<style scoped>
.my-filter-input {
      
      
  padding: 10px;
}
</style>

FilterContent.vue

<template>
  <div class="my-filter-content">
    <div class="my-fc-search">
      <div class="my-fc-search-top">
        <vxe-input v-model="demo1.option.data.sVal" placeholder="搜索"></vxe-input>
      </div>
      <div class="my-fc-search-content">
        <template v-if="demo1.valList.length">
          <ul class="my-fc-search-list my-fc-search-list-head">
            <li class="my-fc-search-item">
              <vxe-checkbox v-model="demo1.isAll" @change="changeAllEvent">全选</vxe-checkbox>
            </li>
          </ul>
          <ul class="my-fc-search-list my-fc-search-list-body">
            <li class="my-fc-search-item" v-for="(item, sIndex) in demo1.valList" :key="sIndex">
              <vxe-checkbox v-model="item.checked">{
    
    {
    
     item.value }}</vxe-checkbox>
            </li>
          </ul>
        </template>
        <template v-else>
          <div class="my-fc-search-empty">无匹配项</div>
        </template>
      </div>
    </div>
    <div class="my-fc-footer">
      <vxe-button status="primary" @click="confirmFilterEvent">确认</vxe-button>
      <vxe-button @click="resetFilterEvent">重置</vxe-button>
    </div>
  </div>
</template>

<script lang="ts">
import {
    
     defineComponent, PropType, reactive } from 'vue'
import {
    
     VxeGlobalRendererHandles } from 'vxe-table'
import XEUtils from 'xe-utils'

export default defineComponent({
    
    
  name: 'FilterContent',
  props: {
    
    
    params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>
  },
  setup (props) {
    
    
    interface ColValItem {
    
    
      checked: boolean;
      value: string;
    }

    const demo1 = reactive({
    
    
      isAll: false,
      option: null as any,
      colValList: [] as ColValItem[],
      valList: [] as ColValItem[]
    })

    const load = () => {
    
    
      const {
    
     params } = props
      if (params) {
    
    
        const {
    
     $table, column } = params
        const {
    
     fullData } = $table.getTableData()
        const option = column.filters[0]
        const {
    
     vals } = option.data
        const colValList = Object.keys(XEUtils.groupBy(fullData, column.field)).map((val) => {
    
    
          return {
    
    
            checked: vals.includes(val),
            value: val
          } as ColValItem
        })
        demo1.option = option
        demo1.colValList = colValList
        demo1.valList = colValList
      }
    }

    const searchEvent = () => {
    
    
      const {
    
     option, colValList } = demo1
      if (option) {
    
    
        demo1.valList = option.data.sVal ? colValList.filter((item) => item.value.indexOf(option.data.sVal) > -1) : colValList
      }
    }

    const changeAllEvent = () => {
    
    
      const {
    
     isAll } = demo1
      demo1.valList.forEach((item) => {
    
    
        item.checked = isAll
      })
    }

    const confirmFilterEvent = () => {
    
    
      const {
    
     params } = props
      const {
    
     option, valList } = demo1
      if (params && option) {
    
    
        const {
    
     data } = option
        const {
    
     $panel } = params
        data.vals = valList.filter((item) => item.checked).map((item) => item.value)
        $panel.changeOption(null, true, option)
        $panel.confirmFilter()
      }
    }

    const resetFilterEvent = () => {
    
    
      const {
    
     params } = props
      if (params) {
    
    
        const {
    
     $panel } = params
        $panel.resetFilter()
      }
    }

    load()

    return {
    
    
      demo1,
      searchEvent,
      changeAllEvent,
      confirmFilterEvent,
      resetFilterEvent
    }
  }
})
</script>

<style>
.my-filter-content {
    
    
  padding: 10px;
  user-select: none;
}
.my-filter-content .my-fc-search .my-fc-search-top {
    
    
  position: relative;
  padding: 5px 0;
}
.my-filter-content .my-fc-search .my-fc-search-top > input {
    
    
  border: 1px solid #ABABAB;
  padding: 0 20px 0 2px;
  width: 200px;
  height: 22px;
  line-height: 22px;
}
.my-filter-content .my-fc-search .my-fc-search-content {
    
    
  padding: 2px 10px;
}
.my-filter-content .my-fc-search-empty {
    
    
  text-align: center;
  padding: 20px 0;
}
.my-filter-content .my-fc-search-list {
    
    
  margin: 0;
  padding: 0;
  list-style: none;
}
.my-filter-content .my-fc-search-list-body {
    
    
  overflow: auto;
  height: 120px;
}
.my-filter-content .my-fc-search-list .my-fc-search-item {
    
    
  padding: 2px 0;
  display: block;
}
.my-filter-content .my-fc-footer {
    
    
  text-align: right;
  padding-top: 10px;
}
.my-filter-content .my-fc-footer button {
    
    
  padding: 0 15px;
  margin-left: 15px;
}
</style>

FilterExtend.vue

<template>
  <div class="my-filter-excel">
    <div class="my-fe-top">
      <ul class="my-fe-menu-group">
        <li class="my-fe-menu-link">
          <span>升序</span>
        </li>
        <li class="my-fe-menu-link">
          <span>倒序</span>
        </li>
      </ul>
      <ul class="my-fe-menu-group">
        <li class="my-fe-menu-link" @click="resetFilterEvent">
          <span>清除筛选</span>
        </li>
        <li class="my-fe-menu-link">
          <i class="vxe-icon-question-circle-fill my-fe-menu-link-left-icon"></i>
          <span>筛选条件</span>
          <i class="vxe-icon-question-circle-fill my-fe-menu-link-right-icon"></i>
          <div class="my-fe-menu-child-list">
            <ul class="my-fe-child-menu-group-list" v-for="(cList, gIndex) in demo1.caseGroups" :key="gIndex">
              <li v-for="(cItem, cIndex) in cList" :key="cIndex" class="my-fe-child-menu-item" @click="childMenuClickEvent(cItem)">
                <span>{
   
   { cItem.label }}</span>
              </li>
            </ul>
          </div>
        </li>
      </ul>
    </div>
    <div class="my-fe-search">
      <div class="my-fe-search-top">
        <input v-model="demo1.option.data.sVal" placeholder="搜索"/>
        <i class="vxe-icon-question-circle-fill my-fe-search-icon"></i>
      </div>
      <ul class="my-fe-search-list">
        <li class="my-fe-search-item" @click="sAllEvent">
          <i class="vxe-icon-question-circle-fill my-fe-search-item-icon"></i>
          <span>(全选)</span>
        </li>
        <li class="my-fe-search-item" v-for="(val, sIndex) in searchList" :key="sIndex" @click="sItemEvent(val)">
          <i :class="[demo1.option.data.vals.indexOf(val) === -1 ? 'vxe-icon-question-circle-fill my-fe-search-item-icon' : 'vxe-icon-question-circle-fill my-fe-search-item-icon']"></i>
          <span>{
   
   { val }}</span>
        </li>
      </ul>
    </div>
    <div class="my-fe-footer">
      <vxe-button status="primary" @click="confirmFilterEvent">确认</vxe-button>
      <vxe-button @click="resetFilterEvent">重置</vxe-button>
    </div>
  </div>
</template>

<script lang="ts">
import {
      
       defineComponent, PropType, reactive, computed } from 'vue'
import {
      
       VXETable, VxeGlobalRendererHandles } from 'vxe-table'
import XEUtils from 'xe-utils'

export default defineComponent({
      
      
  name: 'FilterExtend',
  props: {
      
      
    params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>
  },
  setup (props) {
      
      
    interface CaseItem {
      
      
      label: string;
      value: string;
    }

    const demo1 = reactive({
      
      
      option: null as any,
      colValList: [] as string[],
      caseGroups: [
        [
          {
      
       value: 'equal', label: '等于' },
          {
      
       value: 'ne', label: '不等于' }
        ],
        [
          {
      
       value: 'greater', label: '大于' },
          {
      
       value: 'ge', label: '大于或等于' },
          {
      
       value: 'less', label: '小于' },
          {
      
       value: 'le', label: '小于或等于' }
        ]
      ] as CaseItem[][]
    })

    const searchList = computed(() => {
      
      
      const {
      
       option, colValList } = demo1
      if (option) {
      
      
        return option.data.sVal ? colValList.filter(val => val.indexOf(option.data.sVal) > -1) : colValList
      }
      return []
    })

    const load = () => {
      
      
      const {
      
       params } = props
      if (params) {
      
      
        const {
      
       $table, column } = params
        const {
      
       fullData } = $table.getTableData()
        const option = column.filters[0]
        const colValList = Object.keys(XEUtils.groupBy(fullData, column.field))
        demo1.option = option
        demo1.colValList = colValList
      }
    }

    const sAllEvent = () => {
      
      
      const {
      
       option } = demo1
      if (option) {
      
      
        const {
      
       data } = option
        if (data.vals.length > 0) {
      
      
          data.vals = []
        } else {
      
      
          data.vals = demo1.colValList
        }
      }
    }

    const sItemEvent = (val: string) => {
      
      
      const {
      
       option } = demo1
      if (option) {
      
      
        const {
      
       data } = option
        const vIndex = data.vals.indexOf(val)
        if (vIndex === -1) {
      
      
          data.vals.push(val)
        } else {
      
      
          data.vals.splice(vIndex, 1)
        }
      }
    }

    const confirmFilterEvent = () => {
      
      
      const {
      
       params } = props
      const {
      
       option } = demo1
      if (params && option) {
      
      
        const {
      
       data } = option
        const {
      
       $panel } = params
        data.f1 = ''
        data.f2 = ''
        $panel.changeOption(null, true, option)
        $panel.confirmFilter()
      }
    }

    const resetFilterEvent = () => {
      
      
      const {
      
       params } = props
      if (params) {
      
      
        const {
      
       $panel } = params
        $panel.resetFilter()
      }
    }

    const childMenuClickEvent = (cItem: CaseItem) => {
      
      
      VXETable.modal.alert({
      
       content: cItem.label })
    }

    load()

    return {
      
      
      demo1,
      searchList,
      sAllEvent,
      sItemEvent,
      confirmFilterEvent,
      resetFilterEvent,
      childMenuClickEvent
    }
  }
})
</script>

<style>
.my-filter-excel {
      
      
  user-select: none;
}
.my-filter-excel .my-fe-top .my-fe-menu-group {
      
      
  position: relative;
  margin: 0;
  padding: 0;
}
.my-filter-excel .my-fe-top .my-fe-menu-group:after {
      
      
  content: "";
  position: absolute;
  width: 190px;
  right: 0;
  bottom: 0;
  border-bottom: 1px solid #E2E4E7;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link {
      
      
  position: relative;
  padding: 4px 20px 4px 30px;
  cursor: pointer;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link:hover {
      
      
  background-color: #C5C5C5;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link-left-icon {
      
      
  position: absolute;
  left: 10px;
  top: 6px;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link-right-icon {
      
      
  position: absolute;
  right: 10px;
  top: 6px;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link:hover .my-fe-menu-child-list {
      
      
  display: block;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link .my-fe-menu-child-list {
      
      
  display: none;
  position: absolute;
  top: 0;
  right: -120px;
  width: 120px;
  background-color: #fff;
  border: 1px solid #DADCE0;
  box-shadow: 3px 3px 4px -2px rgba(0, 0, 0, 0.6);
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link .my-fe-child-menu-group-list {
      
      
  position: relative;
  padding: 0;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link .my-fe-child-menu-group-list:after {
      
      
  content: "";
  position: absolute;
  width: 90px;
  right: 0;
  bottom: 0;
  border-bottom: 1px solid #E2E4E7;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link .my-fe-child-menu-group-list > .my-fe-child-menu-item {
      
      
  position: relative;
  padding: 4px 20px 4px 30px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.my-filter-excel .my-fe-top .my-fe-menu-group .my-fe-menu-link .my-fe-child-menu-group-list > .my-fe-child-menu-item:hover {
      
      
  background-color: #C5C5C5;
}
.my-filter-excel .my-fe-search {
      
      
  padding: 0 10px 0 30px;
}
.my-filter-excel .my-fe-search .my-fe-search-top {
      
      
  position: relative;
  padding: 5px 0;
}
.my-filter-excel .my-fe-search .my-fe-search-top > input {
      
      
  border: 1px solid #ABABAB;
  padding: 0 20px 0 2px;
  width: 200px;
  height: 22px;
  line-height: 22px;
}
.my-filter-excel .my-fe-search .my-fe-search-top > .my-fe-search-icon {
      
      
  position: absolute;
  right: 5px;
  top: 10px;
}
.my-filter-excel .my-fe-search .my-fe-search-list {
      
      
  margin: 0;
  border: 1px solid #E2E4E7;
  padding: 2px 10px;
  overflow: auto;
  height: 140px;
}
.my-filter-excel .my-fe-search .my-fe-search-list .my-fe-search-item {
      
      
  cursor: pointer;
  padding: 2px 0;
}
.my-filter-excel .my-fe-search .my-fe-search-list .my-fe-search-item .my-fe-search-item-icon {
      
      
  width: 16px;
}
.my-filter-excel .my-fe-footer {
      
      
  text-align: right;
  padding: 10px 10px 10px 0;
}
.my-fe-popup .my-fe-popup-filter {
      
      
  padding-left: 30px;
}
.my-fe-popup .my-fe-popup-filter > .my-fe-popup-filter-select {
      
      
  width: 120px;
}
.my-fe-popup .my-fe-popup-filter > .my-fe-popup-filter-input {
      
      
  margin-left: 15px;
  width: 380px;
}
.my-fe-popup .my-fe-popup-describe {
      
      
  padding: 20px 0 10px 0;
}
.my-fe-popup .my-fe-popup-concat {
      
      
  padding-left: 50px;
}
.my-fe-popup .my-fe-popup-footer {
      
      
  text-align: right;
}
</style>

FilterComplex.vue

<template>
  <div class="my-filter-complex">
    <div class="my-fc-type">
      <vxe-radio v-model="demo1.option.data.type" name="fType" label="has">包含</vxe-radio>
      <vxe-radio v-model="demo1.option.data.type" name="fType" label="eq">等于</vxe-radio>
    </div>
    <div class="my-fc-name">
      <vxe-input v-model="demo1.option.data.name" type="text" placeholder="请输入名称" @input="changeOptionEvent()"></vxe-input>
    </div>
    <div class="my-fc-footer">
      <vxe-button status="primary" @click="confirmEvent">确认</vxe-button>
      <vxe-button @click="resetEvent">重置</vxe-button>
    </div>
  </div>
</template>

<script lang="ts">
import {
    
     defineComponent, PropType, reactive } from 'vue'
import {
    
     VxeGlobalRendererHandles } from 'vxe-table'

export default defineComponent({
    
    
  name: 'FilterComplex',
  props: {
    
    
    params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>
  },
  setup (props) {
    
    
    const demo1 = reactive({
    
    
      option: null as any
    })

    const load = () => {
    
    
      const {
    
     params } = props
      if (params) {
    
    
        const {
    
     column } = params
        const option = column.filters[0]
        demo1.option = option
      }
    }

    const changeOptionEvent = () => {
    
    
      const {
    
     params } = props
      const {
    
     option } = demo1
      if (params && option) {
    
    
        const {
    
     $panel } = params
        const checked = !!option.data.name
        $panel.changeOption(null, checked, option)
      }
    }

    const confirmEvent = () => {
    
    
      const {
    
     params } = props
      if (params) {
    
    
        const {
    
     $panel } = params
        $panel.confirmFilter()
      }
    }

    const resetEvent = () => {
    
    
      const {
    
     params } = props
      if (params) {
    
    
        const {
    
     $panel } = params
        $panel.resetFilter()
      }
    }

    load()

    return {
    
    
      demo1,
      changeOptionEvent,
      confirmEvent,
      resetEvent
    }
  }
})
</script>

<style scoped>
.my-filter-complex {
    
    
  width: 260px;
  padding: 5px 15px 10px 15px;
}
.my-filter-complex .my-fc-type {
    
    
  padding: 8px 0;
}
.my-filter-complex .my-fc-footer {
    
    
  text-align: center;
}
</style>

filter.tsx

import {
    
     VXETable } from 'vxe-table'
import FilterInput from './components/FilterInput.vue'
import FilterContent from './components/FilterContent.vue'
import FilterComplex from './components/FilterComplex.vue'
import FilterExtend from './components/FilterExtend.vue'

// 创建一个简单的输入框筛选
VXETable.renderer.add('FilterInput', {
    
    
  // 筛选模板
  renderFilter (renderOpts, params) {
    
    
    return <FilterInput params={
    
     params }></FilterInput>
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params
    options.forEach((option) => {
    
    
      option.data = ''
    })
  },
  // 重置筛选复原方法(当未点击确认时,该选项将被恢复为默认值)
  filterRecoverMethod ({
    
     option }) {
    
    
    option.data = ''
  },
  // 筛选方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params
    const {
    
     data } = option
    const cellValue = row[column.field]
    if (cellValue) {
    
    
      return cellValue.indexOf(data) > -1
    }
    return false
  }
})

// 创建一个条件的渲染器
VXETable.renderer.add('FilterComplex', {
    
    
  // 不显示底部按钮,使用自定义的按钮
  showFilterFooter: false,
  // 筛选模板
  renderFilter (renderOpts, params) {
    
    
    return <FilterComplex params={
    
     params }></FilterComplex>
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params
    options.forEach((option) => {
    
    
      option.data = {
    
     type: 'has', name: '' }
    })
  },
  // 筛选数据方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params
    const cellValue = row[column.field]
    const {
    
     name } = option.data
    if (cellValue) {
    
    
      return cellValue.indexOf(name) > -1
    }
    return false
  }
})

// 创建一个支持列内容的筛选
VXETable.renderer.add('FilterContent', {
    
    
  // 不显示底部按钮,使用自定义的按钮
  showFilterFooter: false,
  // 筛选模板
  renderFilter (renderOpts, params) {
    
    
    return <FilterContent params={
    
     params }></FilterContent>
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params
    options.forEach((option) => {
    
    
      option.data = {
    
     vals: [], sVal: '' }
    })
  },
  // 筛选数据方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params
    const {
    
     vals } = option.data
    const cellValue = row[column.field]
    return vals.includes(cellValue)
  }
})

// 创建一个复杂的筛选器
VXETable.renderer.add('FilterExtend', {
    
    
  // 不显示底部按钮,使用自定义的按钮
  showFilterFooter: false,
  // 筛选模板
  renderFilter (renderOpts, params) {
    
    
    return <FilterExtend params={
    
     params }></FilterExtend>
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params
    options.forEach((option) => {
    
    
      option.data = {
    
     vals: [], sVal: '', fMenu: '', f1Type: '', f1Val: '', fMode: 'and', f2Type: '', f2Val: '' }
    })
  },
  // 筛选数据方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params
    const cellValue = row[column.field]
    const {
    
     vals, f1Type, f1Val } = option.data
    if (cellValue) {
    
    
      if (f1Type) {
    
    
        return cellValue.indexOf(f1Val) > -1
      } else if (vals.length) {
    
    
        // 通过指定值筛选
        return vals.includes(cellValue)
      }
    }
    return false
  }
})

2. フィルタの実装を独自に最適化し、辞書の変換をサポート

上記の方法でフィルターは実装されましたが、何度も使用されているため、問題が発生します。つまり、列がディクショナリ列であり、ディクショナリ値の変換に列内のソルトが使用されている場合です。 FilterContent コンポーネントなどでは、レンダリング時に、辞書の値リストがユーザーに表示される中国語のリストではありません。ユーザーは、フィルターで表示される 1、2、および 3 がデータに対応していることをどのようにして知るのでしょうか。入力する辞書コードは FilterInput である必要があります。中国語は使用できません。そうでない場合は、データをフィルターで除外できません。

問題 1 を解決する: データベース内のデータをクエリするとき、列の関連付けられた辞書テーブルに従って中国語を中国語に変換する 問題 2 を解決する: フロントエンドでデータをクエリした後、値を割り当てるときに辞書データを逆変換
する問題 3の解決
: 辞書フィルタリングをサポートするようにフィルタリング コンポーネントを最適化します。プロジェクトでは FilterInput と FilterContent のみが使用されるため、ここではこれら 2 つのコンポーネントの最適化のみを提供します。
1. フィルターの構造を調整する場所について説明します
。フィルターの構造にいくつかの調整が加えられています。データに辞書のデータが追加されています。filterContent パラメーターは sval パラメーターを削除します。同様に、入力filterContent コンポーネントのフィルターも削除され、
ここに画像の説明を挿入
FilterInputをフィルターする複数選択リストのみが削除されます。
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

「FilterContent.vue」はFilterInputとは異なり、パネルを開く必要がある場合にフィルター一覧が表示されます。
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

2. 調整されたコード
FilterContent.vue

<template>
  <div class="my-filter-content">
    <div class="my-fc-search">
      <div class="my-fc-search-top">
        <!-- <vxe-input
          v-model="demo1.option.data.sVal"
          placeholder="搜索"
        ></vxe-input> -->
      </div>
      <div class="my-fc-search-content">
        <template v-if="demo1.valList.length">
          <ul class="my-fc-search-list my-fc-search-list-head">
            <li class="my-fc-search-item">
              <vxe-checkbox v-model="demo1.isAll" @change="changeAllEvent"
                >全选</vxe-checkbox
              >
            </li>
          </ul>
          <ul class="my-fc-search-list my-fc-search-list-body">
            <li
              v-for="(item, sIndex) in demo1.valList"
              :key="sIndex"
              class="my-fc-search-item"
            >
              <vxe-checkbox v-model="item.checked">{
   
   {
                item.label
              }}</vxe-checkbox>
            </li>
          </ul>
        </template>
        <template v-else>
          <div class="my-fc-search-empty">无匹配项</div>
        </template>
      </div>
    </div>
    <div class="my-fc-footer">
      <vxe-button status="primary" @click="confirmFilterEvent">确认</vxe-button>
      <vxe-button @click="resetFilterEvent">重置</vxe-button>
    </div>
  </div>
</template>

<script lang="ts">
  import {
      
       defineComponent, PropType, reactive } from 'vue';
  import {
      
       VxeGlobalRendererHandles } from 'vxe-table';
  import XEUtils from 'xe-utils';

  export default defineComponent({
      
      
    name: 'FilterContent',
    props: {
      
      
      params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>,
    },
    setup(props) {
      
      
      interface ColValItem {
      
      
        checked: boolean;
        value: string;
        label: string;
      }

      const demo1 = reactive({
      
      
        isAll: false,
        option: null as any,
        colValList: [] as ColValItem[],
        valList: [] as ColValItem[],
      });

      const load = () => {
      
      
        const {
      
       params } = props;
        if (params) {
      
      
          const {
      
       $table, column } = params;
          const {
      
       fullData } = $table.getTableData();
          const option: any = column.filters[0];
          const {
      
       vals } = option.data;
          /*
           * 重置数据,解决bug:可能由于动态渲染columns,导致过滤面板勾选后点击确认按钮,过滤后无法更新表格数据,
           * 经过尝试,点击重置按钮,在进行过滤,可正常过滤,所以这里重置下data ;问题得以解决
           */
          option.data = {
      
      
            vals: option.data.vals,
            // sVal: option.data.sVal,
            dict: option.data.dict ? option.data.dict : undefined,
          };

          if (option.data.dict && option.data.dict.length > 0) {
      
      
            const colValList = Object.keys(
              XEUtils.groupBy(fullData, column.field)
            ).map((val) => {
      
      
              const value = option.data.dict.filter(
                (item) => String(item.value) === String(val)
              );
              let label = val;
              if (value.length > 0) {
      
      
                label = value[0]
                  .label;
              }
              return {
      
      
                checked: vals.includes(val),
                value: val,
                label,
              } as ColValItem;
            });
            demo1.option = option;
            demo1.colValList = colValList;
            demo1.valList = colValList;
          } else {
      
      
            const colValList = Object.keys(
              XEUtils.groupBy(fullData, column.field)
            ).map((val) => {
      
      
              return {
      
      
                checked: vals.includes(val),
                value: val,
                label: val,
              } as ColValItem;
            });
            demo1.option = option;
            demo1.colValList = colValList;
            demo1.valList = colValList;
          }
        }
      };

      // const searchEvent = () => {
      
      
      //   const { option, colValList } = demo1;
      //   if (option) {
      
      
      //     demo1.valList = option.data.sVal
      //       ? colValList.filter(
      //           (item) => item.value.indexOf(option.data.sVal) > -1
      //         )
      //       : colValList;
      //   }
      // };

      const changeAllEvent = () => {
      
      
        const {
      
       isAll } = demo1;
        demo1.valList.forEach((item) => {
      
      
          item.checked = isAll;
        });
      };

      const confirmFilterEvent = () => {
      
      
        const {
      
       params } = props;
        const {
      
       option, valList } = demo1;
        if (params && option) {
      
      
          const {
      
       data } = option;
          const {
      
       $panel } = params;
          data.vals = valList
            .filter((item) => item.checked)
            .map((item) => item.value);
          $panel.changeOption(null, true, option);
          $panel.confirmFilter();
        }
      };

      const resetFilterEvent = () => {
      
      
        const {
      
       params } = props;
        if (params) {
      
      
          const {
      
       $panel } = params;
          $panel.resetFilter();
        }
      };

      load();

      return {
      
      
        demo1,
        // searchEvent,
        changeAllEvent,
        confirmFilterEvent,
        resetFilterEvent,
      };
    },
  });
</script>

<style>
  .my-filter-content {
      
      
    padding: 10px;
    user-select: none;
  }
  .my-filter-content .my-fc-search .my-fc-search-top {
      
      
    position: relative;
    padding: 5px 0;
  }
  .my-filter-content .my-fc-search .my-fc-search-top > input {
      
      
    border: 1px solid #ababab;
    padding: 0 20px 0 2px;
    width: 200px;
    height: 22px;
    line-height: 22px;
  }
  .my-filter-content .my-fc-search .my-fc-search-content {
      
      
    padding: 2px 10px;
  }
  .my-filter-content .my-fc-search-empty {
      
      
    text-align: center;
    padding: 20px 0;
  }
  .my-filter-content .my-fc-search-list {
      
      
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .my-filter-content .my-fc-search-list-body {
      
      
    overflow: auto;
    height: 120px;
  }
  .my-filter-content .my-fc-search-list .my-fc-search-item {
      
      
    padding: 2px 0;
    display: block;
  }
  .my-filter-content .my-fc-footer {
      
      
    text-align: right;
    padding-top: 10px;
  }
  .my-filter-content .my-fc-footer button {
      
      
    padding: 0 15px;
    margin-left: 15px;
  }
</style>

FilterInput.vue

<template>
  <div class="my-filter-input">
    <vxe-input
      v-model="demo1.option.data.sVal"
      type="text"
      placeholder="支持回车筛选"
      @keyup="keyupEvent"
      @input="changeOptionEvent"
    ></vxe-input>
  </div>
</template>

<script lang="ts">
  import {
      
       defineComponent, PropType, reactive } from 'vue';
  import {
      
       VxeInputEvents, VxeGlobalRendererHandles } from 'vxe-table';

  export default defineComponent({
      
      
    name: 'FilterInput',
    props: {
      
      
      params: Object as PropType<VxeGlobalRendererHandles.RenderFilterParams>,
    },
    setup(props) {
      
      
      const demo1 = reactive({
      
      
        option: null as any,
      });

      const load = () => {
      
      
        const {
      
       params } = props;
        if (params) {
      
      
          const {
      
       column } = params;
          const option = column.filters[0];
          option.data = {
      
      
            sVal: option.data.sVal,
            dict: option.data.dict ? option.data.dict : undefined,
          };
          demo1.option = option;
        }
      };

      const changeOptionEvent = () => {
      
      
        const {
      
       params } = props;
        const {
      
       option } = demo1;
        if (params && option) {
      
      
          const {
      
       $panel } = params;
          const checked = !!option.data.sVal;
          $panel.changeOption(null, checked, option);
        }
      };

      const keyupEvent: VxeInputEvents.Keyup = ({
       
        $event }) => {
      
      
        const {
      
       params } = props;
        if (params) {
      
      
          const {
      
       $panel } = params;
          if ($event.keyCode === 13) {
      
      
            $panel.confirmFilter($event);
          }
        }
      };

      load();

      return {
      
      
        demo1,
        changeOptionEvent,
        keyupEvent,
      };
    },
  });
</script>

<style scoped>
  .my-filter-input {
      
      
    padding: 10px;
  }
</style>

filter.tsx

import {
    
     VXETable } from 'vxe-table';
import FilterInput from '@/components/Filter/FilterInput.vue';
import FilterContent from '@/components/Filter/FilterContent.vue';
// import FilterComplex from './components/FilterComplex.vue'
// import FilterExtend from './components/FilterExtend.vue'

// 创建一个简单的输入框筛选
VXETable.renderer.add('FilterInput', {
    
    
  // 筛选模板
  renderFilter: (renderOpts, params) => {
    
    
    return [<FilterInput params={
    
     params }></FilterInput>];
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params;
    options.forEach((option) => {
    
    
      option.data = {
    
    
        sVal: '',
        dict: option.data.dict ? option.data.dict : undefined,
      };
    });
  },
  // 重置筛选复原方法(当未点击确认时,该选项将被恢复为默认值)
  filterRecoverMethod ({
    
     option }) {
    
    
    option.data = {
    
    
      sVal: '',
      dict: option.data.dict ? option.data.dict : undefined,
    };
  },
  // 筛选方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params;
    const {
    
     data } = option;
    if(data.dict && data.dict.length > 0) {
    
    
      const cellValue = row[column.field];
      const dictItem = data.dict.filter((item)=>item.label.indexOf(data.sVal) > -1).map((item) => item.value);
      if(dictItem.length > 0){
    
    
        return dictItem.indexOf(cellValue) > -1;
      }
    } else {
    
    
      const cellValue = row[column.field];
      if (cellValue) {
    
    
        return cellValue.indexOf(data.sVal) > -1;
      }
    }
    return false;
  },
});

// // 创建一个条件的渲染器
// VXETable.renderer.add('FilterComplex', {
    
    
//   // 不显示底部按钮,使用自定义的按钮
//   showFilterFooter: false,
//   // 筛选模板
//   renderFilter (renderOpts, params) {
    
    
//     return <FilterComplex params={ params }></FilterComplex>
//   },
//   // 重置数据方法
//   filterResetMethod (params) {
    
    
//     const { options } = params
//     options.forEach((option) => {
    
    
//       option.data = { type: 'has', name: '' }
//     })
//   },
//   // 筛选数据方法
//   filterMethod (params) {
    
    
//     const { option, row, column } = params
//     const cellValue = row[column.field]
//     const { name } = option.data
//     if (cellValue) {
    
    
//       return cellValue.indexOf(name) > -1
//     }
//     return false
//   }
// })

// 创建一个支持列内容的筛选
VXETable.renderer.add('FilterContent', {
    
    
  // 不显示底部按钮,使用自定义的按钮
  showFilterFooter: false,
  // 筛选模板
  renderFilter (renderOpts, params) {
    
    
    return [<FilterContent params={
    
     params }></FilterContent>];
  },
  // 重置数据方法
  filterResetMethod (params) {
    
    
    const {
    
     options } = params;
    options.forEach((option) => {
    
    
      option.data = {
    
     vals: [],dict: option.data.dict ? option.data.dict : undefined};
    });
  },
  // 筛选数据方法
  filterMethod (params) {
    
    
    const {
    
     option, row, column } = params;
    const {
    
     vals } = option.data;
    const cellValue = row[column.field];
    console.log(cellValue,vals.includes(cellValue))
    return vals.includes(cellValue);
  },
});

// // 创建一个复杂的筛选器
// VXETable.renderer.add('FilterExtend', {
    
    
//   // 不显示底部按钮,使用自定义的按钮
//   showFilterFooter: false,
//   // 筛选模板
//   renderFilter (renderOpts, params) {
    
    
//     return <FilterExtend params={ params }></FilterExtend>
//   },
//   // 重置数据方法
//   filterResetMethod (params) {
    
    
//     const { options } = params
//     options.forEach((option) => {
    
    
//       option.data = { vals: [], sVal: '', fMenu: '', f1Type: '', f1Val: '', fMode: 'and', f2Type: '', f2Val: '' }
//     })
//   },
//   // 筛选数据方法
//   filterMethod (params) {
    
    
//     const { option, row, column } = params
//     const cellValue = row[column.field]
//     const { vals, f1Type, f1Val } = option.data
//     if (cellValue) {
    
    
//       if (f1Type) {
    
    
//         return cellValue.indexOf(f1Val) > -1
//       } else if (vals.length) {
    
    
//         // 通过指定值筛选
//         return vals.includes(cellValue)
//       }
//     }
//     return false
//   }
// })


おすすめ

転載: blog.csdn.net/weixin_43865196/article/details/129416996