Skip to content
vue3+elementplus选择弹出表格框demo
vue
<template>
  <el-main>
    <el-alert
      title="表格选择器演示 —— 数据写死,无请求"
      type="success"
      style="margin-bottom: 20px"
    />

    <!-- ====== 单选 ====== -->
    <el-card shadow="never" header="单选">
      <el-select
        ref="singleSelect"
        v-model="singleKey"
        placeholder="请选择(单选)"
        clearable
        :filter-method="filterMethod"
        style="width: 260px"
        @visible-change="visibleChange"
      >
        <template #empty>
          <div class="table-box">
            <el-table
              ref="singleTable"
              :data="tableData"
              height="245"
              highlight-current-row
              @row-click="rowClick"
            >
              <el-table-column type="index" width="45" />
              <el-table-column prop="id" label="ID" width="180" />
              <el-table-column prop="user" label="姓名" />
            </el-table>

            <div class="pagination-box">
              <el-pagination
                small
                background
                layout="prev, pager, next"
                :total="rawData.length"
                :page-size="pageSize"
                v-model:current-page="currentPage"
                @current-change="reload"
              />
            </div>
          </div>
        </template>
      </el-select>
    </el-card>

    <div style="height: 15px" />

    <!-- ====== 多选 ====== -->
    <el-card shadow="never" header="多选">
      <el-select
        ref="multiSelect"
        v-model="multiKeys"
        placeholder="请选择(多选)"
        multiple
        clearable
        collapse-tags
        collapse-tags-tooltip
        filterable
        :filter-method="filterMethod"
        style="width: 400px"
        @visible-change="visibleChange"
        @remove-tag="removeTag"
        @clear="clearMulti"
      >
        <template #empty>
          <div class="table-box">
            <el-form :inline="true" :model="formData">
              <el-form-item>
                <el-select v-model="formData.sex" placeholder="性别" clearable>
                  <el-option label="男" value="1" />
                  <el-option label="女" value="2" />
                </el-select>
              </el-form-item>
              <el-form-item>
                <el-date-picker
                  v-model="formData.date"
                  value-format="YYYY-MM-DD"
                  type="date"
                  placeholder="注册时间"
                />
              </el-form-item>
              <el-form-item>
                <el-button type="primary" @click="formSubmit">查询</el-button>
              </el-form-item>
            </el-form>
            <el-alert
              title="自定义 FORM 插槽:支持性别 / 注册时间筛选"
              type="info"
              style="margin-bottom: 8px"
            />

            <el-table
              ref="multiTable"
              :data="tableData"
              height="245"
              @select="select"
              @select-all="selectAll"
            >
              <el-table-column type="selection" width="45" />
              <el-table-column prop="id" label="ID" width="180" />
              <el-table-column prop="user" label="姓名" width="100" />
              <el-table-column prop="cip" label="最后请求IP" width="150" />
              <el-table-column prop="time" label="注册时间" />
            </el-table>

            <div class="pagination-box">
              <el-pagination
                small
                background
                layout="prev, pager, next"
                :total="rawData.length"
                :page-size="pageSize"
                v-model:current-page="currentPage"
                @current-change="reload"
              />
            </div>
          </div>
        </template>
      </el-select>
    </el-card>
  </el-main>
</template>

<script setup>
import { nextTick, ref, watch } from 'vue'

/* ------------------ 写死数据 ------------------ */
const rawData = ref([
  { id: '410000199512025445', user: '魏磊', sex: '1', time: '2023-01-15', cip: '192.168.1.101' },
  { id: '520000198407304275', user: '史平', sex: '1', time: '2023-02-20', cip: '192.168.1.102' },
  { id: '330000199003034567', user: '王丽', sex: '2', time: '2023-03-10', cip: '192.168.1.103' },
  { id: '110000198801012233', user: '李强', sex: '1', time: '2023-04-05', cip: '192.168.1.104' },
  { id: '440000199212126666', user: '赵敏', sex: '2', time: '2023-05-18', cip: '192.168.1.105' },
  { id: '310000198505059999', user: '张三', sex: '1', time: '2023-06-22', cip: '192.168.1.106' },
  { id: '220000199111114444', user: '李四', sex: '1', time: '2023-07-30', cip: '192.168.1.107' },
  { id: '150000198707077777', user: '周五', sex: '2', time: '2023-08-11', cip: '192.168.1.108' },
  { id: '640000199404048888', user: '吴六', sex: '1', time: '2023-09-03', cip: '192.168.1.109' },
  { id: '510000198909093333', user: '郑七', sex: '2', time: '2023-10-14', cip: '192.168.1.110' }
])

/* ------------------ 分页 / 搜索 ------------------ */
const pageSize = 5
const currentPage = ref(1)
const keyword = ref('')
const formData = ref({ sex: '', date: '' })

const tableData = ref([])
const reload = () => {
  let list = rawData.value.filter(item => {
    const kw = keyword.value
    if (kw && !item.user.includes(kw) && !item.id.includes(kw)) return false
    if (formData.value.sex && item.sex !== formData.value.sex) return false
    if (formData.value.date && item.time !== formData.value.date) return false
    return true
  })
  const start = (currentPage.value - 1) * pageSize
  const end = start + pageSize
  tableData.value = list.slice(start, end)
  nextTick(() => syncChecked())
}

const filterMethod = val => {
  keyword.value = val || ''
  currentPage.value = 1
  reload()
}
const formSubmit = () => {
  currentPage.value = 1
  reload()
}

/* ------------------ 单选 ------------------ */
const singleKey = ref('')
const singleSel = ref({})
const singleSelect = ref(null)

const rowClick = row => {
  singleKey.value = row.id
  singleSel.value = row
  nextTick(() => {
    singleSelect.value.selectedLabel = row.user
    console.log(singleSelect.value,'singleSelect.value')
    singleSelect.value.blur()
  })
}

/* ------------------ 多选 ------------------ */
const multiKeys = ref([])
const multiSel = ref([])
const multiTable = ref(null)

const select = (rows, row) => {
  const checked = rows.includes(row)
  if (checked) {
    if (!multiKeys.value.includes(row.id)) {
      multiKeys.value.push(row.id)
      multiSel.value.push(row)
    }
  } else {
    multiKeys.value = multiKeys.value.filter(k => k !== row.id)
    multiSel.value = multiSel.value.filter(r => r.id !== row.id)
  }
}

const selectAll = rows => {
  const checked = rows.length > 0
  if (checked) {
    rows.forEach(r => {
      if (!multiKeys.value.includes(r.id)) {
        multiKeys.value.push(r.id)
        multiSel.value.push(r)
      }
    })
  } else {
    const curPageIds = tableData.value.map(i => i.id)
    multiKeys.value = multiKeys.value.filter(k => !curPageIds.includes(k))
    multiSel.value = multiSel.value.filter(r => !curPageIds.includes(r.id))
  }
}

const removeTag = tag => {
  multiKeys.value = multiKeys.value.filter(k => k !== tag.id)
  multiSel.value = multiSel.value.filter(r => r.id !== tag.id)
  const row = tableData.value.find(r => r.id === tag.id)
  if (row) multiTable.value.toggleRowSelection(row, false)
}

const clearMulti = () => {
  multiKeys.value = []
  multiSel.value = []
}

const syncChecked = () => {
  if (!multiTable.value) return
  multiTable.value.clearSelection()
  multiSel.value.forEach(r => {
    const r2 = tableData.value.find(i => i.id === r.id)
    if (r2) multiTable.value.toggleRowSelection(r2, true)
  })
}

/* 统一 visibleChange */
const visibleChange = val => {
  if (val) reload()
  else {
    nextTick(() => {
      if (singleSel.value.user) singleSelect.value.selectedLabel = singleSel.value.user
    })
  }
}

/* 初始值 */
multiKeys.value = ['410000199512025445', '520000198407304275']
multiSel.value = [
  { id: '410000199512025445', user: '魏磊' },
  { id: '520000198407304275', user: '史平' }
]
singleKey.value = '520000198407304275'
singleSel.value = { id: '520000198407304275', user: '史平' }

watch(currentPage, reload)
</script>

<style scoped>
.table-box {
  padding: 12px;
  width: 700px;
}
.pagination-box {
  padding-top: 12px;
  text-align: right;
}
</style>