SelectMultiple 组件:多选弹出层的实现与使用
组件功能
SelectMultiple 组件是一个用于选择多个数据项的弹出层组件。它允许用户从一个列表中选择多个项,并可以将这些项移动到另一个列表中。用户可以通过搜索来筛选左侧的选项,并且可以将左侧的所有选项一次性移动到右侧。右侧列表显示已选中的项,用户可以选择清空这些项或确认选择。
组件效果

请求数据格式

组件相关代码
<template>
<div>
<div class="mask" v-show="visible"></div>
<el-popover placement="bottom" width="500" trigger="manual" style="padding: 0" v-model="visible"
overlay="false">
<el-row>
<el-col :span="12">
<div style="border-right: 1px solid #DCDFE6;">
<el-card class="box-card" shadow="never">
<div slot="header" class="clearfix">
<span>字段</span>
</div>
<el-input size="small"
style="margin-top: 2px;width: 95%"
prefix-icon="el-icon-search"
v-model="filterText"
clearable
placeholder="请输入查询内容"/>
<div class="items" v-loading="loading">
<template v-for="o in leftData">
<el-tooltip class="item" effect="dark" :content="o.label" placement="top" :open-delay="300">
<div v-if="!filterText || o.label.includes(filterText)"
@click="leftClick(o)"
:key="o.id"
:class="rightData.findIndex(i => i.id === o.id) > -1?'text textChecked':'text'">
{{ o.label.length > 14 ? o.label.substring(0, 14) + '...' : o.label }}
</div>
</el-tooltip>
</template>
</div>
</el-card>
<el-button size="mini" style="width: 95%;margin-bottom: 2px"
@click="leftClickAll">添加左侧全部字段值
</el-button>
</div>
</el-col>
<el-col :span="12">
<div>
<el-card class="box-card" shadow="never" style="border-bottom: 1px solid #DCDFE6;">
<div slot="header" class="clearfix">
<span>已添加({{ this.rightData.length }})</span>
</div>
<div class="items" style="height: 530px">
<template v-for="o in rightData">
<el-tooltip class="item" effect="dark" :content="o.label" placement="top" :open-delay="300">
<div :key="o.id" class="text">
{{ o.label.length > 14 ? o.label.substring(0, 14) + '...' : o.label }}
</div>
</el-tooltip>
</template>
</div>
</el-card>
<el-button type="text" size="mini" icon="el-icon-delete" style="float: right;margin:3px 8px 0"
@click="rightClear">清空
</el-button>
</div>
</el-col>
</el-row>
<div style="text-align: right;border-top: 1px solid #DCDFE6;padding: 10px 0 0">
<el-button size="mini" @click="visible = false">取消</el-button>
<el-button type="primary" size="mini" @click="handleConfirm">确定</el-button>
</div>
<el-input slot="reference" placeholder="请选择"
:value="checkedName.length > 10 ? checkedName.substring(0, 10) + '...' : checkedName"
:readonly="visible"
clearable
@clear="rightClear"
@focus="visible = !visible">
</el-input>
</el-popover>
</div>
</template>
<script>
import request from "@/utils/request";
/**
* *****使用方法*****
*
* 在父组件中添加引用
*
* <select-multiple data-url="/monitor/operlog/list" :props="{ id: 'operId', label: 'title' }" v-model="ids"/>
*/
export default {
name: 'SelectMultiple',
props: {
dataUrl: {
type: String,
default: null
},
props: {
type: Object,
default: () => {
return {
id: 'id',
label: 'label'
}
}
}
},
data() {
return {
visible: false,
leftData: [],
rightData: [],
filterText: null,
checkedName: '',
loading: false,
};
},
watch: {
visible(val) {
if (val && this.dataUrl) {
this.getLeftData();
}
},
},
methods: {
getLeftData() {
this.loading = true;
request({url: this.dataUrl, method: 'get', params: {pageNum: 1, pageSize: 1000}})
.then(response => {
const list = response.data;
this.leftData = this.transformData(list, this.props);
this.loading = false;
})
.catch(error => {
console.error('Failed to fetch data:', error);
this.loading = false;
});
},
handleConfirm() {
this.checkedName = this.rightData.map(item => item.label).join(',');
const ids = this.rightData.map(item => item.id);
this.$emit('input', ids)
this.visible = false
},
transformData(data, fieldName) {
if (!Array.isArray(data)) {
console.error('Invalid data passed to transformData');
return [];
}
return data.map(item => ({
id: item[fieldName.id],
label: '测试数据'+item[fieldName.id],
}));
},
leftClick(item) {
if (!item || typeof item !== 'object') {
console.error('Invalid item passed to leftClick');
return;
}
//存在删除,不存在添加
const existingItemIndex = this.rightData.findIndex(i => i.id === item.id);
if (existingItemIndex > -1) {
this.rightData.splice(existingItemIndex, 1);
} else {
this.rightData.push(item);
}
},
leftClickAll() {
if (!Array.isArray(this.leftData)) {
console.error('leftData is not an array');
return;
}
this.rightData = [...this.leftData];
},
rightClear() {
this.rightData = [];
this.checkedName = '';
this.$emit('input', [])
}
}
}
</script>
<style scoped>
.mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.1);
z-index: 2000;
}
.text {
color: #606266;
font-size: 14px;
padding: 5px 0 5px 10px;
}
.text:hover {
background-color: #f6f9ff;
cursor: pointer;
}
.textChecked {
background-color: #f6f9ff;
color: #409eff;
font-weight: 700;
}
.items {
height: 500px;
overflow-y: auto;
}
.box-card {
border: 0;
border-radius: 0;
> .el-card__header {
padding: 5px 10px;
min-height: auto;
}
> .el-card__body {
padding: 0 1px 0 0;
}
}
</style>
主要功能特点
- 数据加载: 当弹出层显示时,自动从指定 URL 加载数据。
- 数据过滤: 用户可以在左侧列表中输入搜索文本,以过滤显示的数据项。
- 数据移动: 用户可以点击左侧的数据项将其移动到右侧,或者点击“添加左侧全部字段值”按钮将所有左侧数据项一次性移动到右侧。
- 数据清除: 用户可以点击“清空”按钮来清除右侧列表中的所有数据项。
- 确认与取消: 用户可以选择“确定”来提交当前的选择结果,或者选择“取消”来关闭弹出层而不保存任何更改。
使用方法
引入组件
首先确保已经在项目中安装并配置了 Element UI 和 Vue.js。然后,在父组件中引入 SelectMultiple 组件。
// 在 Vue 文件中引入 SelectMultiple 组件
import SelectMultiple from './SelectMultiple.vue';
export default {
components: {SelectMultiple,}, data() {
return {
selectedIds: [], // 用于存储选中的数据项 ID
};
},
};
添加组件引用
在父组件的模板中添加 SelectMultiple 组件引用,并传递必要的属性和绑定 v-model。
<template>
<div> <!-- 添加 SelectMultiple 组件 -->
<select-multiple data-url="/select/list" :props="{ id: 'operId', label: 'title' }" v-model="selectedIds"/>
</div>
</template>
data-url: 数据源 URL,用于获取左侧列表的数据。props: 对象,指定数据项的 ID 和标签字段。v-model: 绑定的模型数据,通常用于存储选中的数据项 ID 数组。
示例代码
下面是一个完整的示例代码,展示了如何在 Vue 组件中使用 SelectMultiple 组件。
<template>
<div> <!-- 添加 SelectMultiple 组件 -->
<select-multiple data-url="/monitor/operlog/list" :props="{ id: 'operId', label: 'title' }" v-model="selectedIds"/>
</div>
</template>
<script>
import SelectMultiple from './SelectMultiple.vue';
export default {
components: {SelectMultiple,}, data() {
return {
selectedIds: [], // 用于存储选中的数据项 ID
};
},
};
<script>
技术栈
- Vue.js: 用于构建组件逻辑和模板。
- Element UI: 用于 UI 组件,如
el-popover、el-input等。 - axios 或其他 HTTP 请求库: 用于发起 HTTP 请求获取数据。
注意事项
- 确保
data-url指向正确的 API 端点,以避免数据加载失败。 - 如果数据量较大,可能需要考虑优化加载过程。
- 确保
props对象中的id和label字段与实际数据结构相匹配。
7537






