一、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
// }
// })