基于vue+element ui实现下拉表格选择组件

单选

基于vue+element ui实现下拉表格选择组件_第1张图片

多选

基于vue+element ui实现下拉表格选择组件_第2张图片

index.vue

<template>
  <el-select
    ref="select"
    v-model="defaultValue"
    :title="isNeedTitle ? getTitle() : null"
    :clearable="false"
    :multiple="multiple"
    :filterable="false"
    :placeholder="placeholder"
    :disabled="disabled"
    :value-key="defaultProps.value"
    :filter-method="filterMethod"
    @remove-tag="removeTag"
    @visible-change="visibleChange"
    @clear="clear"
  >
    <template #empty>
      <div
        class="sc-table-select__table"
        :style="{ width: tableWidth + 'px' }"
        v-loading="loading"
      >
        <div class="sc-table-select__header">
          <slot name="header" :form="formData" :submit="formSubmit"></slot>
        </div>
        <el-table
          ref="table"
          style="font-size: 16px"
          :data="tableData"
          :height="245"
          :highlight-current-row="!multiple"
          @row-click="rowClick"
          @select="select"
          @select-all="selectAll"
          reserve-selection
        >
          <el-table-column
            v-if="multiple"
            type="selection"
            width="45"
          ></el-table-column>
          <el-table-column v-else type="index" width="45">
            <template #default="scope"
              ><span>{{
                scope.$index + (currentPage - 1) * pageSize + 1
              }}</span></template
            >
          </el-table-column>
          <slot></slot>
        </el-table>
        <div class="sc-table-select__page">
          <el-pagination
            small
            background
            layout="prev, pager, next"
            :total="total"
            :page-size="pageSize"
            :current-page.sync="currentPage"
            @current-change="currentChange"
          ></el-pagination>
        </div>
      </div>
    </template>
  </el-select>
</template>

<script>
import config from './tableSelect'

export default {
  props: {
    value: { type: [String, Number, Array, Object], default: '' },
    // Not Request,all Table Data
    tabularData: { type: Array, default: () => [] },
    // 是否是需要请求
    isNeedRequest: { type: Boolean, default: false },
    // 数据value是否以逗号隔开
    isDataJoin: { type: Boolean, default: true },
    // 请求url
    requestUrl: { type: Function, default: Function },
    params: { type: Object, default: () => {} },
    placeholder: { type: String, default: '请选择' },
    multiple: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
    tableWidth: { type: Number, default: 400 },
    mode: { type: String, default: 'popover' },
    // 合并默认配置
    props: { type: Object, default: () => {} },
    // 合并默认配置
    isNeedTitle: { type: Boolean, default: false },
    // 是否需要数据过滤
    isNeedDataFilter: { type: Boolean, default: false },
    filterParam: { type: Object, default: () => {} }
  },
  data() {
    return {
      loading: false,
      keyword: null,
      defaultValue: [],
      tableData: [],
      pageSize: config.pageSize,
      total: 0,
      currentPage: 1,
      defaultProps: {
        label: config.props.label,
        value: config.props.value,
        page: config.request.page,
        pageSize: config.request.pageSize,
        keyword: config.request.keyword
      },
      formData: {},
      // Not Request,Need Table Data
      tabularMap: {}
    }
  },
  watch: {
    value: {
      handler() {
        this.$nextTick(() => {
          if (!this.isDataJoin) {
            if (this.multiple) {
              this.defaultValue = this.value || []
            } else {
              this.defaultValue = this.value
            }
          } else {
            // 多选
            if (this.multiple) {
              this.defaultValue = Array.isArray(this.value) ? this.value : this.value ? this.value.split(',') : []
              this.defaultValue = (this.defaultValue || []).map(item => {
                return { [this.defaultProps.value]: item }
              })
            } else {
              this.defaultValue = this.value ? { [this.defaultProps.value]: this.value } : ''
            }
          }
          this.findLabel()
        })
      },
      deep: true,
      immediate: true
    },
    tabularData: {
      handler(val) {
        if (this.isNeedRequest) return
        this.$nextTick(() => {
          val.forEach(item => {
            this.tabularMap[item[this.defaultProps.value]] = item[this.defaultProps.label]
          })
          this.findLabel()
        })
      },
      deep: true,
      immediate: true
    }
  },
  created() {
    // bug,在这不能使用nextTick
    this.defaultProps = Object.assign(this.defaultProps, this.props)
  },
  methods: {
    // 表格显示隐藏回调
    visibleChange(visible) {
      if (visible) {
        this.currentPage = 1
        this.keyword = null
        this.formData = {}
        this.getData()
      } else {
        this.findLabel()
      }
    },
    // 获取表格数据
    async getData() {
      if (this.isNeedRequest) {
        this.initTable()
      } else {
        this.initTabularData()
      }
    },
    initTabularData() {
      // this.tableData = deepClone(this.tabularData)
      this.total = this.tabularData.length
      this.tableData = this.tabularData.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize)
      if (Object.keys(this.formData).length > 0) {
        const input = this.formData[this.defaultProps.label]
        if (input) {
          const arr = this.tabularData.filter((item) => {
            return String(item[this.defaultProps.label]).toLowerCase().match(input)
          })
          this.total = arr.length
          this.tableData = arr.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize)
        }
      }
      // 表格默认赋值
      this.$nextTick(() => {
        if (this.multiple) {
          this.defaultValue.forEach(row => {
            const arr = this.tableData.filter(item => item[this.defaultProps.value] === row[this.defaultProps.value])
            if (arr.length > 0) {
              this.$refs.table.toggleRowSelection(arr[0], true)
            }
          })
        } else {
          const arr = this.tableData.filter(item => item[this.defaultProps.value] === this.defaultValue[this.defaultProps.value])
          this.$refs.table.setCurrentRow(arr[0])
        }
        this.$refs.table.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
      })
    },
    async initTable() {
      this.loading = true
      var reqData = {
        [this.defaultProps.page]: this.currentPage,
        [this.defaultProps.pageSize]: this.pageSize,
        [this.defaultProps.keyword]: this.keyword
      }
      Object.assign(reqData, this.params, this.formData)
      var res = await this.requestUrl(reqData)
      var parseData = config.parseData(res)
      this.tableData = parseData.list
      this.total = parseData.total
      this.loading = false
      // 表格默认赋值
      this.$nextTick(() => {
        if (this.multiple) {
          this.defaultValue.forEach((row) => {
            const arr = this.tableData.filter(item => item[this.defaultProps.value] === row[this.defaultProps.value])
            if (arr.length > 0) {
              this.$refs.table.toggleRowSelection(arr[0], true)
            }
          })
        } else {
          const arr = this.tableData.filter(item => item[this.defaultProps.value] === this.defaultValue[this.defaultProps.value])
          this.$refs.table.setCurrentRow(arr[0])
        }
        this.$refs.table.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
      })
    },
    // 插糟表单提交
    formSubmit() {
      this.currentPage = 1
      this.keyword = null
      this.getData()
    },
    // 分页刷新表格
    currentChange(val) {
      this.currentPage = val
      this.getData()
    },
    // 赋值
    findLabel() {
      this.$nextTick(() => {
        if (this.multiple) {
          this.$refs.select.selected.forEach((item) => {
            if (this.isNeedRequest) {
              item.currentLabel = item.value[this.defaultProps.label]
            } else {
              item.currentLabel = this.tabularMap[item.value[this.defaultProps.value]]
            }
          })
        } else {
          if (this.isNeedRequest) {
            this.$refs.select.selectedLabel = this.defaultValue[this.defaultProps.label] || ''
          } else {
            this.$refs.select.selectedLabel = this.tabularMap[this.defaultValue[this.defaultProps.value]] || ''
          }
        }
      })
    },
    // 表格勾选事件
    select(rows, row) {
      var isSelect = rows.length && rows.indexOf(row) !== -1 // tip:row属于rows里的数据,同一地址,所以可判断
      if (isSelect) {
        // console.log(row, 'row')
        this.defaultValue.push(row)
        if (this.isNeedDataFilter) {
          var flag = false
          if (+row[this.filterParam.key] == this.filterParam.value) {
            flag = true
          }
        }
        this.dataFilter(flag)
      } else {
        this.defaultValue.splice(this.defaultValue.findIndex(item => item[this.defaultProps.value] === row[this.defaultProps.value]), 1)
      }
      if (this.isDataJoin) {
        this.defaultValue = this.defaultValue.map(item => item[this.defaultProps.value]).join(',')
      }
      this.findLabel()
      this.$emit('input', this.defaultValue)
      this.$emit('change', this.defaultValue)
    },
    // 表格全选事件
    selectAll(rows) {
      var isAllSelect = rows.length > 0
      if (isAllSelect) {
        var flag = false
        rows.forEach(row => {
          var isHas = this.defaultValue.find(item => item[this.defaultProps.value] === row[this.defaultProps.value])
          if (!isHas) {
            this.defaultValue.push(row)
            if (this.isNeedDataFilter) {
              if (+row[this.filterParam.key] == this.filterParam.value) {
                flag = true
              }
            }
          }
        })
        this.dataFilter(flag)
      } else {
        this.tableData.forEach(row => {
          const index = this.defaultValue.findIndex(item => item[this.defaultProps.value] === row[this.defaultProps.value])
          if (index !== -1) {
            this.defaultValue.splice(index, 1)
          }
        })
      }
      if (this.isDataJoin) {
        this.defaultValue = this.defaultValue.map(item => item[this.defaultProps.value]).join(',')
      }
      this.findLabel()
      this.$emit('input', this.defaultValue)
      this.$emit('change', this.defaultValue)
    },
    async rowClick(row) {
      if (this.multiple) {
        // 处理多选点击行
        this.$refs.table.toggleRowSelection(row)
        const isSelect = this.defaultValue.filter(item => item.bizid === row.bizid).length !== 0
        let oldDefaultValue = [...this.defaultValue] || []
        if (isSelect) oldDefaultValue = this.defaultValue.filter(item => item.bizid !== row.bizid)
        if (!isSelect) oldDefaultValue.push(row)
        this.select(oldDefaultValue || [], row)
      } else {
        this.defaultValue = ''
        this.$emit('input', this.defaultValue)
        this.$emit('change', this.defaultValue)
        await this.$nextTick()
        this.defaultValue = row
        if (this.isDataJoin) {
          this.defaultValue = row[this.defaultProps.value]
        }
        this.$refs.select.blur()
        this.findLabel()
        this.$emit('input', this.defaultValue)
        this.$emit('change', this.defaultValue)
      }
    },
    // tags删除后回调
    removeTag(tag) {
      var row = this.findRowByKey(tag[this.defaultProps.value])
      this.$refs.table.toggleRowSelection(row, false)
      if (this.isDataJoin) {
        this.defaultValue = this.defaultValue.map(item => item[this.defaultProps.value]).join(',')
      }
      this.$emit('input', this.defaultValue)
      this.$emit('change', this.defaultValue)
    },
    // 清空后的回调
    clear() {
      this.$emit('input', this.defaultValue)
      this.$emit('change', this.defaultValue)
    },
    // 关键值查询表格数据行
    findRowByKey(value) {
      return this.tableData.find(item => item[this.defaultProps.value] === value)
    },
    filterMethod(keyword) {
      if (!keyword) {
        this.keyword = null
        return false
      }
      this.keyword = keyword
      this.getData()
    },
    // 触发select隐藏
    blur() {
      this.$refs.select.blur()
    },
    // 触发select显示
    focus() {
      this.$refs.select.focus()
    },
    async getTitle() {
      try {
        await this.$nextTick()
        const title = []
        if (!this.isNeedRequest) {
          const map = {}
          for (const item of this.tabularData) {
            map[item[this.defaultProps.value]] = item[this.defaultProps.label]
          }
          if (this.multiple) {
            for (const v of this.defaultValue || []) {
              title.push(map[v[[this.defaultProps.value]]])
            }
          } else {
            return map[this.defaultValue[this.defaultProps.value]] || ''
          }
        }
        return title.length > 0 ? title.join(',') : ''
      } catch (error) {
        return ''
      }
    },
    dataFilter(flag) {
      if (!this.isNeedRequest && this.multiple && this.isNeedDataFilter) {
        this.defaultValue = this.defaultValue.filter(v => {
          const tepArr = this.tabularData.filter(i => {
            if (flag) {
              return i[this.filterParam.key] != this.filterParam.value
            } else {
              return i[this.filterParam.key] == this.filterParam.value
            }
          })
          const tepVal = tepArr.find(t => t[this.defaultProps.value] === v[this.defaultProps.value])
          if (tepVal) {
            this.$refs.table.toggleRowSelection(tepVal, false)
            return false
          }
          return true
        })
      }
    }
  }
}
</script>

<style scoped>
.sc-table-select__table {
  padding: 12px;
}
.sc-table-select__page {
  padding-top: 12px;
}
</style>


tableSelect.js

// 表格选择器配置
export default {
  pageSize: 20,						// 表格每一页条数
  parseData: function(res) {
    return {
      data: res.data,
      list: res.data.data.list,		// 分析行数据字段结构
      total: res.data.data.total,		// 分析总数字段结构
      msg: res.data.message,			// 分析描述字段结构
      code: res.data.code				// 分析状态字段结构
    }
  },
  request: {
    page: 'page',					// 规定当前分页字段
    pageSize: 'pageSize',			// 规定一页条数字段
    keyword: 'keyword'				// 规定搜索字段
  },
  props: {
    label: 'label',					// 映射label显示字段
    value: 'value'					// 映射value值字段
  }
}

使用


<template>
<!-- 单选用法 -->
<YyTableSelect
  v-model="xxx"
  :props="props" 
  :tabularData="table"
  :table-width="600"
  @change="change()">
  <el-table-column prop="parameterName" label="参数名称"></el-table-column>
  <el-table-column prop="parameterScore" label="分值" width="180"></el-table-column>
</YyTableSelect>
<!-- 多选用法 -->
  <YyTableSelect
    v-model="value"
    :apiObj="apiObj"
    :table-width="700"
    multiple
    :props="props"
    @change="change"
  >
    <template #header="{ form, submit }">
      <el-form :inline="true" :model="form">
        <el-form-item>
          <el-select
            v-model="form.sex"
            placeholder="性别"
            clearable
            :teleported="false"
          >
            <el-option label="男" value="1"></el-option>
            <el-option label="女" value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-date-picker
            v-model="form.date"
            value-format="YYYY-MM-DD"
            type="date"
            placeholder="时间"
            :teleported="false"
          ></el-date-picker>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="submit">查询</el-button>
        </el-form-item>
      </el-form>
    </template>
    <el-table-column prop="id" label="ID" width="180"></el-table-column>
  </YyTableSelect>
</template>

<script>
import YyTableSelect from '@/views/components/YyTableSelect'
export default {
  components: {
    YyTableSelect
  },
  data() {
    return {
      params: {
        name: ''
      },
      value: [
        {
          id: '1',
          user: 'xx'
        },
        {
          id: '2',
          user: 'xx'
        }
      ],
      props: {
        label: 'label',
        value: 'value',
        keyword: 'keyword'
      }
    }
  },
  computed: {},
  mounted() {},
  methods: {
    // 值变化
    change(val) {
      console.log(val)
    }
  }
}
</script>

<style scoped></style>

根据https://lolicode.gitee.io/scui-doc/demo/#/dashboard里的组件修改

你可能感兴趣的:(vue,vue.js,elementui)