当前位置:   article > 正文

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

vxe-grid

一、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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166

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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298

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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

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
  }
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

二、自行优化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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

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


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/140538
推荐阅读
  

闽ICP备14008679号