vxe-grid 全局自定义filter过滤器,支持字典过滤

news2024/9/28 19:26:17

一、vxe-table的全局筛选器filters的实现

官网例子:https://vxetable.cn/#/table/renderer/filter
进入之后:我们可以参照例子自行实现,也可以下载它的源码,进行调整

在这里插入图片描述
下载好后并解压,用vscode将解压后的文件打开。全局检索FilterInput,你工作中用的vxe-table多少版本的那就进多少版本的目录
在这里插入图片描述

点击进入,按照这个路劲找到对应文件夹
在这里插入图片描述
将filter相关的vue,复制到自己的项目中components文件夹下
在这里插入图片描述
将filter.tsx 到你的项目中去,常用的FilterInput和FilterContent组件,将其他的FilterExtend 和FilterComplex 相关的可以先注释掉。
在这里插入图片描述

打开filter.ts
在这里插入图片描述
这样就可以使用了,使用方式,按照官网的例子放到columns里即可,filters的结构不允许改变,他与filterRendes的组件是一一对应的
在这里插入图片描述
这里提供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
  }
})

二、自行优化filters实现,支持字典的转换

通过上面的方式,已经实现了过滤器,但是多次使用,衍生出一个问题,就是如果我的列是个字典列,并且字典值的转换用的是columns内的solt,如FilterContent组件,它将会渲染的时字典的value列表,并非用户看到的中文列表,用户怎么知道你过滤器的看到的1,2,3和你的数据时对应的,或者时FilterInput 你输入的必须时字典code,不能是中文,否则将无法筛选出数据。

解决问题1: 在数据库查询数据时,根据该列关联字典表,将中文给转换过来,
解决问题2:前端查询数据后,在给表格赋值时将字典数据转换回来,这样有些麻烦,
解决问题3:优化筛选组件,使其支持字典的筛选。由于项目中只用到了FilterInput和FilterContent这里只提供这两个组件的优化。
1. 先说下在哪里做了调整
filtes的结构上做了些调整,data里都增加了字典的数据, filterContent 参数移除sval参数,同样在filterContent 组件内input筛选也将被移除,只保留多选列表进行筛选
在这里插入图片描述
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
//   }
// })


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CRM系统中的营销自动化能解决什么问题

CRM客户管理系统营销自动化的范围远远超出了人们的认知。许多人认为它只是自动化完成重复和乏味的任务来减少营销人员的工作量。虽然这确实占了很大一部分&#xff0c;但它真正的价值在于提高潜客转化&#xff0c;增加业务收入。那么&#xff0c;什么是CRM系统营销自动化&#…

195、【动态规划】AcWing —— 91. 最短Hamilton路径(C++版本)

题目描述 原题链接&#xff1a;91. 最短Hamilton路径 解题思路 动态规划五步曲&#xff1a; &#xff08;1&#xff09;dp[i][j]含义&#xff1a; 到达点j并且状态为i时&#xff0c;具有的最短路径长度&#xff0c;其中状态j用状态压缩二进制的方式表示。j中从0-n-1位分别对…

【玩转c++】List讲解和模拟底层实现

本期主题&#xff1a;list的讲解和模拟实现博客主页&#xff1a;小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限&#xff0c;出现错误希望大家不吝赐1.list的介绍和使用1.1.list的介绍1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&…

Good Idea, 利用MySQL JSON特性优化千万级文库表

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 利用MySQL JSON特性优化千万级文库表 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f…

常见问题整理1

目录 偏差和方差 欠拟合underfitting 过拟合overfitting 梯度消失和梯度爆炸 归一化 偏差和方差 偏差&#xff1a;算法期望预测和真实预测之间的偏差程度。反应的是模型本身的拟合能力。 方差&#xff1a;度量了同等大小的训练集的变动导致学习性能的变化&#xff0c;刻画…

万字长文带你走进MySql优化(系统层面优化、软件层面优化、SQL层面优化)

文章目录系统层面优化采用分布式架构使用缓存使用搜索引擎软件层面优化调整 MySQL 参数配置定期清理无用数据创建索引创建索引普通索引唯一索引全文索引组合索引空间索引主键索引外键索引索引前缀适合创建索引的场景不适合创建索引的场景优化表结构分库分表SQL优化explain执行计…

Python3-File(文件) 方法

Python3 File(文件) 方法 open() 方法 Python open() 方法用于打开一个文件&#xff0c;并返回文件对象。 在对文件进行处理过程都需要使用到这个函数&#xff0c;如果该文件无法被打开&#xff0c;会抛出 OSError。 注意&#xff1a;使用 open() 方法一定要保证关闭文件对…

Nowcoder .链表分割

文章目录哨兵位节点哨兵位节点 链表分割 小于X 尾插到一个新链表 大于等于X 尾插到另一个链表 最后将两个链表链接起来 需要注意的细节&#xff1a;将第一个链表的尾与第二个链表的头相连接&#xff0c;再返回连接后的整个链表的头&#xff08;哨兵位头节点的下一个&#xff0…

ECharts 环形图组件封装

一、ECharts引入1.安装echarts包npm install echarts --save2.引入echarts这里就演示全局引入了&#xff0c;挂载到vue全局&#xff0c;后面使用时&#xff0c;直接使用 $echartsimport * as echarts from echarts Vue.prototype.$echarts echarts二、写echarts组件这里演示环…

【蓝桥杯嵌入式】PWM的设置,原理图解析与代码实现(第十一届省赛为例)——STM32

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都在这儿哦&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 - 蓝…

Lombok 常用注解

文章目录简介MAVEN 依赖常用的注解1. Data 注解 :2. Setter 注解&#xff1a;3.Getter 注解&#xff1a;4.Log4j or Slf4j 注解5.NoArgsConstructor注解&#xff1a;6.AllArgsConstructor注解&#xff1a;7.RequiredArgsConstructor注解:8.Builder注解&#xff1a;9.Cleanup注解…

全国青少年软件编程(Scratch)等级考试一级真题——2019.12

青少年软件编程&#xff08;Scratch&#xff09;等级考试试卷&#xff08;一级&#xff09;分数&#xff1a;100 题数&#xff1a;37一、单选题(共25题&#xff0c;每题2分&#xff0c;共50分)1.下列关于舞台的描述&#xff0c;不正确的是&#xff1f;&#xff08; &#xff09…

DMHS搭建DMDSC 2节点集群同步到单库

DMHS搭建DMDSC 2节点集群同步到单库环境介绍1 安装DMOCI1.1 关闭数据库实例服务1.2 将DMOCI 复制到源端与目的端的数据库bin目录1.3 对数据库bin 执行目录文件更改用户属组和权限2 启动源数据库服务并配置数据库实例参数2.1 使用DMCSSM启动集群实例2.2 DMDSC源其中一个节点执行…

为何英格兰与苏格兰同属英国,却争端不断?

英国作为世界上的强国其组成部分是由大不列颠岛上的英格兰、威尔士和苏格兰以及爱尔兰岛东北部的北爱尔兰以及一系列附属岛屿。英国作为西欧的一个岛国&#xff0c;地理位置十分优越&#xff0c;位于欧洲大陆西北面的不列颠群岛。英国作为西欧的一颗不容忽视的“明星”&#xf…

【Unity风格化草地】概述风格化草地的实现方法

写在前面 最近本专业开始多很多事情了&#xff0c;要开始建模写论文了&#xff08;不然研究生毕不了业&#xff09;&#xff0c;TA方面的学习进度更慢了&#xff0c;&#xff0c;so sad。 废话不多说&#xff0c;这篇文章其实是个小总结&#xff0c;毕竟学习新东西就是先要当…

序列索引序列切片

六个标准数据类型中是序列的有&#xff1a;字符串&#xff08;String&#xff09;、列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;。 通过索引和切片的方式可以访问序列中的元素。 一、序列索引 序列中的每一个元素都有一个属于自己的编号&#xff0c…

演唱会的火车票没了?Python实现12306查票以及zidong购票....

嗨害大家好&#xff01;我是小熊猫~ 不知道大家抢到演唱会的门票没有呢? 不管抢到没有&#xff0c;火车票也是很重要的哇 24小时抢票不间断的那种喔~ ~ ~ 不然可就要走路去了喔~ 准备工作 环境 Python 3.8Pycharm 插件 谷歌浏览器驱动 模块 需要安装的第三方模块&am…

1/4车、1/2车、整车悬架LQR仿真合集

目录 前言 1. 1/4悬架系统 1.1数学模型 1.2 LQR求解反馈阵阵(负反馈) 1.3仿真分析 2. 1/2悬架系统 2.1数学模型 2.2 LQR求解反馈阵阵(负反馈) 2.3仿真分析 3. 整车悬架系统 3.1数学模型 整车7自由度主动悬架数学模型 3.2 LQR求解反馈阵阵(负反馈) 3.3仿真分析 4…

MySQL(四)视图、存储过程、触发器

视图、存储过程、触发器视图检查选项视图的更新存储过程存储过程基本语法变量系统变量用户自定义变量局部变量if判断参数casewhile循环repeat循环loop循环cursor游标handler条件处理程序存储函数触发器视图 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据…

ERP原理与应用教程(作业2)

1. 简答题 ERP是一个高度集成的信息系统&#xff0c;从管理信息集成的角度来看&#xff0c;从MRP到MRPII到ERP&#xff0c;是企业管理信息继承的不断扩展和深化。 所有成为ERP的软件都把MRPII作为其生产和控制模块&#xff0c;以MRPII为基础的计划功能在整个供应链的业务处理…