vue3+elementplus的下拉弹出层自定义数据示例代码(含阻止关闭弹窗示例代码)
仅仅表格展示
html
<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-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>带搜索项和内部下拉选项筛选(单选)
html
<!-- ProductSelect.vue -->
<template>
<!-- 外层仅做占位,真实内容在 #empty 插槽里 -->
<el-select
ref="singleSelect"
v-model="singleKey"
placeholder="请选择产品(单选)"
clearable
style="width: 260px"
>
<template #empty>
<div class="table-box">
<!-- 搜索区 -->
<div class="sc-table-select__header">
<el-select
v-model="firstValue"
placeholder="主分类"
size="small"
clearable
style="width: 160px; margin-right: 12px"
@change="onFirstPick"
:teleported="false"
>
<el-option
v-for="item in productCategories"
:key="item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
<el-select
v-model="secondValue"
placeholder="子项"
size="small"
clearable
style="width: 160px; margin-right: 12px"
:teleported="false"
>
<el-option
v-for="child in secondOptions"
:key="child.name"
:label="child.name"
:value="child.name"
/>
</el-select>
<el-input
v-model="keyword"
size="small"
clearable
style="width: 180px; margin-right: 12px"
placeholder="ID 或 产品名称"
/>
<el-button size="small" type="primary" @click="doSearch">查询</el-button>
<el-button size="small" @click="doReset">重置</el-button>
</div>
<!-- 表格 -->
<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="160" />
<el-table-column prop="main" label="主分类" width="120" />
<el-table-column prop="sub" label="次分类" width="120" />
<el-table-column prop="name" label="产品名称" />
</el-table>
<!-- 分页 -->
<div class="pagination-box">
<el-pagination
small
background
layout="prev, pager, next"
:total="filterTotal"
:page-size="pageSize"
v-model:current-page="currentPage"
@current-change="reload"
/>
</div>
</div>
</template>
</el-select>
</template>
<script setup>
import { ref, reactive, computed, nextTick } from 'vue'
/* ------------------ 1. 原始数据 ------------------ */
const productCategories = reactive([
{ name: '积分礼品', children: [{ name: '赠品' }, { name: '杯' }, { name: '返利商城' }] },
{ name: '试用装及广促品', children: [{ name: '广促品' }, { name: '试用装' }] },
{ name: '礼包', children: [{ name: '礼包' }] }
])
// 4 列完整数据
const allList = reactive([
{ id: 'P001', main: '积分礼品', sub: '返利商城', name: '10元京东卡' },
{ id: 'P002', main: '礼包', sub: '礼包', name: '新春福袋' },
{ id: 'P003', main: '试用装及广促品', sub: '试用装', name: '小棕瓶7ml' },
{ id: 'P004', main: '积分礼品', sub: '杯', name: '星巴克联名杯' },
{ id: 'P005', main: '积分礼品', sub: '赠品', name: '定制马克杯' },
{ id: 'P006', main: '试用装及广促品', sub: '广促品', name: '品牌手提袋' },
{ id: 'P007', main: '礼包', sub: '礼包', name: '中秋礼包' },
{ id: 'P008', main: '积分礼品', sub: '返利商城', name: '50元天猫卡' },
{ id: 'P009', main: '试用装及广促品', sub: '试用装', name: '粉底液5ml' },
{ id: 'P010', main: '积分礼品', sub: '杯', name: '保温杯套装' },
{ id: 'P011', main: '礼包', sub: '礼包', name: '双11狂欢礼包' },
{ id: 'P012', main: '积分礼品', sub: '赠品', name: '数据线' },
{ id: 'P013', main: '试用装及广促品', sub: '广促品', name: '广告扇' },
{ id: 'P014', main: '积分礼品', sub: '返利商城', name: '100元苏宁卡' },
{ id: 'P015', main: '礼包', sub: '礼包', name: '圣诞礼包' },
{ id: 'P016', main: '试用装及广促品', sub: '试用装', name: '面膜单片' },
{ id: 'P017', main: '积分礼品', sub: '杯', name: '陶瓷杯' },
{ id: 'P018', main: '积分礼品', sub: '赠品', name: '鼠标垫' },
{ id: 'P019', main: '试用装及广促品', sub: '广促品', name: '宣传册' },
{ id: 'P020', main: '礼包', sub: '礼包', name: '会员专属礼包' },
{ id: 'P021', main: '积分礼品', sub: '返利商城', name: '20元话费' },
{ id: 'P022', main: '试用装及广促品', sub: '试用装', name: '洗发水50ml' },
{ id: 'P023', main: '积分礼品', sub: '杯', name: '玻璃杯' },
{ id: 'P024', main: '礼包', sub: '礼包', name: '新人礼包' },
{ id: 'P025', main: '积分礼品', sub: '赠品', name: '钥匙扣' },
{ id: 'P026', main: '试用装及广促品', sub: '广促品', name: '海报' },
{ id: 'P027', main: '积分礼品', sub: '返利商城', name: '30元饿了么红包' },
{ id: 'P028', main: '礼包', sub: '礼包', name: '端午礼包' },
{ id: 'P029', main: '试用装及广促品', sub: '试用装', name: '口红小样' },
{ id: 'P030', main: '积分礼品', sub: '杯', name: '塑料杯' },
{ id: 'P031', main: '积分礼品', sub: '赠品', name: '支架' },
{ id: 'P032', main: '试用装及广促品', sub: '广促品', name: '易拉宝' },
{ id: 'P033', main: '礼包', sub: '礼包', name: '情侣礼包' },
{ id: 'P034', main: '积分礼品', sub: '返利商城', name: '500元携程卡' },
{ id: 'P035', main: '试用装及广促品', sub: '试用装', name: '护肤乳15ml' },
{ id: 'P036', main: '积分礼品', sub: '杯', name: '不锈钢杯' },
{ id: 'P037', main: '礼包', sub: '礼包', name: '生日礼包' },
{ id: 'P038', main: '积分礼品', sub: '赠品', name: '卡包' },
{ id: 'P039', main: '试用装及广促品', sub: '广促品', name: '展架' },
{ id: 'P040', main: '积分礼品', sub: '返利商城', name: '5元支付宝红包' },
{ id: 'P041', main: '礼包', sub: '礼包', name: '开学礼包' },
{ id: 'P042', main: '试用装及广促品', sub: '试用装', name: '香水2ml' },
{ id: 'P043', main: '积分礼品', sub: '杯', name: '保温杯' },
{ id: 'P044', main: '积分礼品', sub: '赠品', name: '绕线器' },
{ id: 'P045', main: '试用装及广促品', sub: '广促品', name: '桌牌' },
{ id: 'P046', main: '礼包', sub: '礼包', name: '旅行礼包' },
{ id: 'P047', main: '积分礼品', sub: '返利商城', name: '200元加油卡' },
{ id: 'P048', main: '试用装及广促品', sub: '试用装', name: '洁面乳20ml' },
{ id: 'P049', main: '积分礼品', sub: '杯', name: '磨砂杯' },
{ id: 'P050', main: '礼包', sub: '礼包', name: '养生礼包' },
{ id: 'P051', main: '积分礼品', sub: '赠品', name: '指甲刀' },
{ id: 'P052', main: '试用装及广促品', sub: '广促品', name: '门型展架' },
{ id: 'P053', main: '积分礼品', sub: '返利商城', name: '15元美团红包' },
{ id: 'P054', main: '礼包', sub: '礼包', name: '夏日礼包' },
{ id: 'P055', main: '试用装及广促品', sub: '试用装', name: '防晒5ml' },
{ id: 'P056', main: '积分礼品', sub: '杯', name: '折叠杯' },
{ id: 'P057', main: '积分礼品', sub: '赠品', name: '开瓶器' },
{ id: 'P058', main: '试用装及广促品', sub: '广促品', name: '旗帜' },
{ id: 'P059', main: '礼包', sub: '礼包', name: '儿童礼包' },
{ id: 'P060', main: '积分礼品', sub: '返利商城', name: '80元滴滴券' },
{ id: 'P061', main: '试用装及广促品', sub: '试用装', name: '精华3ml' },
{ id: 'P062', main: '积分礼品', sub: '杯', name: '咖啡杯' },
{ id: 'P063', main: '礼包', sub: '礼包', name: '健身礼包' },
{ id: 'P064', main: '积分礼品', sub: '赠品', name: '购物袋' },
{ id: 'P065', main: '试用装及广促品', sub: '广促品', name: '横幅' },
{ id: 'P066', main: '积分礼品', sub: '返利商城', name: '99元网易严选卡' },
{ id: 'P067', main: '礼包', sub: '礼包', name: '厨房礼包' },
{ id: 'P068', main: '试用装及广促品', sub: '试用装', name: '眼霜3g' },
{ id: 'P069', main: '积分礼品', sub: '杯', name: '运动水杯' },
{ id: 'P070', main: '积分礼品', sub: '赠品', name: '冰箱贴' },
{ id: 'P071', main: '试用装及广促品', sub: '广促品', name: '灯箱' },
{ id: 'P072', main: '礼包', sub: '礼包', name: '宠物礼包' },
{ id: 'P073', main: '积分礼品', sub: '返利商城', name: '66元喜茶券' },
{ id: 'P074', main: '试用装及广促品', sub: '试用装', name: 'BB霜8ml' },
{ id: 'P075', main: '积分礼品', sub: '杯', name: '啤酒杯' },
{ id: 'P076', main: '礼包', sub: '礼包', name: '女神礼包' },
{ id: 'P077', main: '积分礼品', sub: '赠品', name: '手机支架' },
{ id: 'P078', main: '试用装及广促品', sub: '广促品', name: '吊旗' },
{ id: 'P079', main: '积分礼品', sub: '返利商城', name: '150元盒马卡' },
{ id: 'P080', main: '礼包', sub: '礼包', name: '电竞礼包' },
{ id: 'P081', main: '试用装及广促品', sub: '试用装', name: '卸妆水30ml' },
{ id: 'P082', main: '积分礼品', sub: '杯', name: '威士忌杯' },
{ id: 'P083', main: '积分礼品', sub: '赠品', name: '魔方' },
{ id: 'P084', main: '试用装及广促品', sub: '广促品', name: '手举牌' },
{ id: 'P085', main: '礼包', sub: '礼包', name: '程序员礼包' },
{ id: 'P086', main: '积分礼品', sub: '返利商城', name: '88元星巴克卡' },
{ id: 'P087', main: '试用装及广促品', sub: '试用装', name: '护发素20ml' },
{ id: 'P088', main: '积分礼品', sub: '杯', name: '搪瓷杯' },
{ id: 'P089', main: '礼包', sub: '礼包', name: '美白礼包' },
{ id: 'P090', main: '积分礼品', sub: '赠品', name: '挖耳勺' },
{ id: 'P091', main: '试用装及广促品', sub: '广促品', name: '背景板' },
{ id: 'P092', main: '积分礼品', sub: '返利商城', name: '300元叮咚卡' },
{ id: 'P093', main: '礼包', sub: '礼包', name: '懒人礼包' },
{ id: 'P094', main: '试用装及广促品', sub: '试用装', name: '精油1ml' },
{ id: 'P095', main: '积分礼品', sub: '杯', name: '果汁杯' },
{ id: 'P096', main: '积分礼品', sub: '赠品', name: '开快递刀' },
{ id: 'P097', main: '试用装及广促品', sub: '广促品', name: '注水旗' },
{ id: 'P098', main: '礼包', sub: '礼包', name: '露营礼包' },
{ id: 'P099', main: '积分礼品', sub: '返利商城', name: '66元瑞幸券' },
{ id: 'P100', main: '试用装及广促品', sub: '试用装', name: '素颜霜10ml' },
{ id: 'P101', main: '积分礼品', sub: '杯', name: '保温杯Pro' },
{ id: 'P102', main: '礼包', sub: '礼包', name: '火锅礼包' },
{ id: 'P103', main: '积分礼品', sub: '赠品', name: '鞋拔子' },
{ id: 'P104', main: '试用装及广促品', sub: '广促品', name: 'X展架' },
{ id: 'P105', main: '积分礼品', sub: '返利商城', name: '20元腾讯视频月卡' },
{ id: 'P106', main: '礼包', sub: '礼包', name: '学霸礼包' },
{ id: 'P107', main: '试用装及广促品', sub: '试用装', name: '隔离霜8ml' },
{ id: 'P108', main: '积分礼品', sub: '杯', name: '啤酒杯套装' },
{ id: 'P109', main: '积分礼品', sub: '赠品', name: '牙签筒' },
{ id: 'P110', main: '试用装及广促品', sub: '广促品', name: '门贴' },
{ id: 'P111', main: '礼包', sub: '礼包', name: '单身礼包' },
{ id: 'P112', main: '积分礼品', sub: '返利商城', name: '99元得到课程券' },
{ id: 'P113', main: '试用装及广促品', sub: '试用装', name: '唇釉1.5ml' },
{ id: 'P114', main: '积分礼品', sub: '杯', name: '牛奶杯' },
{ id: 'P115', main: '礼包', sub: '礼包', name: '减脂礼包' },
{ id: 'P116', main: '积分礼品', sub: '赠品', name: '指甲锉' },
{ id: 'P117', main: '试用装及广促品', sub: '广促品', name: '拉网展架' },
{ id: 'P118', main: '积分礼品', sub: '返利商城', name: '166元猫眼电影券' },
{ id: 'P119', main: '礼包', sub: '礼包', name: '熬夜党礼包' },
{ id: 'P120', main: '试用装及广促品', sub: '试用装', name: '睫毛膏4ml' }
])
/* ------------------ 2. 搜索 / 分页 ------------------ */
const pageSize = 5
const currentPage = ref(1)
const firstValue = ref('')
const secondValue = ref('')
const keyword = ref('')
const secondOptions = computed(() => {
if (!firstValue.value) return []
const hit = productCategories.find(i => i.name === firstValue.value)
return hit ? hit.children : []
})
// 过滤结果
const filterList = computed(() => {
let arr = allList
if (firstValue.value) arr = arr.filter(v => v.main === firstValue.value)
if (secondValue.value) arr = arr.filter(v => v.sub === secondValue.value)
if (keyword.value) {
const k = keyword.value.trim().toLowerCase()
arr = arr.filter(v => v.id.toLowerCase().includes(k) || v.name.toLowerCase().includes(k))
}
return arr
})
const filterTotal = computed(() => filterList.value.length)
const tableData = ref([])
const reload = () => {
const start = (currentPage.value - 1) * pageSize
const end = start + pageSize
tableData.value = filterList.value.slice(start, end)
}
reload()
const doSearch = () => {
currentPage.value = 1
reload()
}
const doReset = () => {
firstValue.value = ''
secondValue.value = ''
keyword.value = ''
currentPage.value = 1
reload()
}
const onFirstPick = () => {
secondValue.value = ''
nextTick(() => doSearch())
}
/* ------------------ 3. 单选回填 ------------------ */
const singleKey = ref('')
const singleSelect = ref(null)
const rowClick = row => {
singleKey.value = row.id
singleSelect.value?.blur()
}
</script>
<style scoped>
.table-box {
padding: 12px;
width: 700px;
}
.pagination-box {
padding-top: 12px;
text-align: right;
}
.sc-table-select__header {
display: flex;
align-items: center;
padding: 6px 0;
}
</style>