vue-element-ui实现人员选择器
1.演示:
人员单选
人员多选
2.代码:
封装单人选择器组件:

<!-- 主题党日-人员选择器(单人选择器) --> <template> <div> <el-input v-model="perSelector.name" :readonly="true" :disabled="editVisible" placeholder="请选择人员" @focus="openPerSelector"><i slot="suffix" class="el-icon-s-operation" style="color: #c0c4cc;margin-right: 10px;" /></el-input> <el-dialog :title="title" :visible.sync="open" :before-close="handleCancel" width="70%" :append-to-body="true"> <div> <el-row> <el-col :span="6"> <el-input v-model="orgQuery.name" placeholder="请输入组织名称" clearable :validate-event="false" class="input-with-select perSelector-select-input-icon" size="mini" @input="orgFilter" @keyup.enter.native="orgFilter"> <el-button slot="append" icon="el-icon-search" @click="orgFilter" /> </el-input> <el-tree ref="tree" v-loading="orgLoading" class="el-table-high" :data="orgTree" :props="defaultProps" :default-expand-all="true" :expand-on-click-node="false" :draggable="false" :highlight-current="true" :filter-node-method="filterNode" :current-node-key="currentKey" node-key="id" @node-click="handleNodeClick" /> </el-col> <el-col :span="13"> <el-row> <el-col :span="24" align="right"> <el-input v-model="tableQuery.name" placeholder="请输入姓名" size="mini" class="filter-item perSelector-select-input-icon" clearable :validate-event="false" @input="queryDataSelect" @keyup.enter.native="queryDataSelect" > <i slot="suffix" style="cursor: pointer" class="el-input__icon el-icon-search" @click="queryDataSelect" /> </el-input> </el-col> </el-row> <el-row> <el-table ref="multipleTable" v-loading="listLoading" highlight-current-row class="el-table-high" :data="tableData" style="width: 100%" size="mini" row-key="id" :header-cell-style="headerCellStyle" @current-change="handleSelect" > <el-table-column type="index" width="50" label="序号" align="center" /> <el-table-column align="center" prop="name" label="人员姓名" /> <el-table-column align="center" prop="phone" label="手机号码" /> </el-table> <pagination v-show="tableQuery.total > 0" v-loading="paginationLoading" element-loading-spinner="el-icon-loading" class="perSelector-paginationIcon" :total="tableQuery.total" :page.sync="tableQuery.pageNo" :limit.sync="tableQuery.pageSize" @pagination="queryData" /> </el-row> </el-col> <el-col :span="5"> <el-row> <el-col :span="12"> <div class="selectText">已选人员</div> </el-col> <el-col :span="12" align="right"> <el-button type="text" size="mini" class="delAll" @click="delAll">清空</el-button> </el-col> </el-row> <el-row> <ul class="el-table-high"> <li v-show="!nameDisplay" style="list-style-type: none;"> <el-row> <div class="selectLi"> <el-col :span="8"> <div>{{ currentRow.name }}</div> </el-col> <el-col :span="16"> <div>{{ currentRow.phone }}</div> </el-col> </div> </el-row> </li> </ul> </el-row> </el-col> </el-row> </div> <div align="right" class="tree_btn"> <el-button type="primary" size="mini" @click="save">确 认</el-button> <el-button size="mini" @click="handleCancel">取 消</el-button> </div> </el-dialog> </div> </template> <script> import api from '@/api/hrOrganization' import { getPartyMemberList } from '@/api/party/database/partyMembers' import Pagination from '@/components/Pagination' import { mapGetters } from 'vuex' export default { name: 'PerSelectorThemeDay', components: { Pagination }, props: { // 已选人员 perSelector: { type: Object, default: () => {} }, // 是否可编辑 editVisible: { type: Boolean, default: false } }, data() { return { headerCellStyle: { backgroundColor: '#FAFAFA' }, orgLoading: true, listLoading: true, paginationLoading: true, title: '选择人员', // 组织查询参数 orgQuery: {}, // 组织树配置选项 defaultProps: { id: 'id', children: 'children', label: 'name' }, orgTree: [], // 组织树当前选中的节点 currentKey: '', // 人员列表查询参数 tableQuery: { name: null, orgId: null, pageNo: 1, pageSize: 10, total: 0 }, // 人员列表 tableData: [], // 已选人员 currentRow: {}, // 是否显示弹出层 open: false, nameDisplay: true } }, watch: { perSelector(newVal, oldVal) { if (newVal === null) { this.$emit('handleConfirm', {}) } else { this.nameDisplay = false this.perSelector = newVal } } }, computed: { ...mapGetters(['orgId']) }, created() {}, methods: { // 打开人员窗口加载组织人员数据 openPerSelector() { // 每次打开清空组织/人员搜索数据 this.orgQuery = {} this.tableQuery.name = null // 每次打开窗口显示第一页 this.tableQuery.pageNo = 1 this.initOrgTree() // 初始化已选人员列表 this.currentRow = this.perSelector this.open = true }, initOrgTree() { this.orgLoading = true this.listLoading = true this.paginationLoading = true api.currentTree().then(res => { this.orgTree = res.data this.currentKey = res.data[0].id this.$nextTick(() => { this.$refs.tree.setCurrentKey(this.currentKey) }) this.tableQuery.orgId = res.data[0].id this.queryData() this.orgLoading = false }) }, // 对树节点进行筛选时执行的方法 filterNode(value, data) { if (!value) return true return data.name.indexOf(value) !== -1 }, // 树节点被点击时的回调 handleNodeClick(data) { this.tableQuery.orgId = data.id this.queryDataSelect() }, // 组织查询按钮 orgFilter() { this.$nextTick(() => { this.$refs.tree.filter(this.orgQuery.name) }) }, // 查询人员列表 queryData() { this.tableQuery.unfinished = false this.getTableList() }, queryDataSelect() { this.tableQuery.pageNo = 1 this.getTableList() }, getTableList() { this.listLoading = true this.paginationLoading = true getPartyMemberList(this.tableQuery).then(res => { this.tableData = res.data.data this.tableQuery.total = Number(res.data.total) this.$nextTick(() => { this.seletListPer() }) this.listLoading = false this.paginationLoading = false }) }, // 根据已选人员列表设置人员列表选中状态(初始化加载只有首页人员选中) seletListPer() { this.$nextTick(() => { for (var i = 0; i < this.tableData.length; i++) { var per = this.tableData[i] if (this.currentRow.userId === per.userId) { this.$refs.multipleTable.setCurrentRow(per) } } }) }, // 人员列表单选(手动勾选数据行时会触发该事件) handleSelect(val) { this.nameDisplay = false this.currentRow = val }, // 取消 handleCancel() { this.$emit('handleCancel', false) // 清空人员列表选中状态 this.$nextTick(() => { var per = { name: undefined } this.$refs.multipleTable.setCurrentRow(per) if (this.perSelector.name === undefined) { this.nameDisplay = true } }) this.open = false }, // 清空已选员工 delAll() { this.nameDisplay = true // 清空人员列表选中状态 this.$nextTick(() => { var per = { name: undefined } this.$refs.multipleTable.setCurrentRow(per) }) }, save() { // if (this.currentRow.name !== undefined) { this.$emit('handleConfirm', this.currentRow) this.handleCancel() // } else { // this.$message({ // showClose: true, // message: '请选择人员', // type: 'warning' // }) // } } } } </script> <style lang="scss" scoped> .divTinymce { border: 1px solid #e4e7ed; border-radius: 5px; background-color: #f5f7fa; color: #c0c4cc; } .divTinymceIn { padding-right: 15px; padding-left: 15px; background-color: #f5f7fa; color: #c0c4cc; } .divTinymceShow { border: 1px solid #e4e7ed; border-radius: 5px; } .divTinymceInShow { padding-right: 15px; padding-left: 15px; } .selectText { text-align: center; height: 28px; line-height: 28px; font-size: 12px; } .selectLi { height: 28px; line-height: 28px; font-size: 12px; } .el-table-high { height: 54.9vh; overflow: auto; } </style> <style lang="scss"> //不显示搜索input输入验证icon图标 .perSelector-select-input-icon .el-input__validateIcon { width: 0% !important; } .perSelector-select-input-icon .el-input__validateIcon:before{ content: "" !important; } //不显示分页组件input输入验证icon图标 .perSelector-paginationIcon .el-input__validateIcon { width: 0% !important; } .perSelector-paginationIcon .el-input__validateIcon:before{ content: "" !important; } </style>
封装多人选择器组件:

<!-- 主题党日-人员选择器(多人选择器) --> <template> <div> <div v-if="editVisible" class="divTinymce"> <div class="divTinymceIn"> <el-tag v-for="perSelector in perSelectorList" :key="perSelector.userId" size="mini" type="info"> {{ perSelector.name }} </el-tag> <div v-show="!perSelectorList.length>0"> <el-row> <el-col :span="20"> <span style="color: #c0c4cc;">请选择人员</span> </el-col> <el-col :span="4" align="right"> <i class="el-icon-s-operation" style="color: #c0c4cc;" /> </el-col> </el-row> </div> </div> </div> <div v-else class="divTinymceShow" @click="openPerSelector"> <div class="divTinymceInShow"> <el-tag v-for="perSelector in perSelectorList" :key="perSelector.userId" size="mini" type="info"> {{ perSelector.name }} </el-tag> <div v-show="!perSelectorList.length>0"> <el-row> <el-col :span="20"> <span style="color: #c0c4cc;">请选择人员</span> </el-col> <el-col :span="4" align="right"> <i class="el-icon-s-operation" style="color: #c0c4cc;" /> </el-col> </el-row> </div> </div> </div> <el-dialog :title="title" :visible.sync="open" :before-close="handleCancel" width="70%" :append-to-body="true"> <div> <el-row> <el-col :span="6"> <el-input v-model="orgQuery.name" placeholder="请输入组织名称" clearable :validate-event="false" class="input-with-select" size="mini" @input="orgFilter" @keyup.enter.native="orgFilter"> <el-button slot="append" icon="el-icon-search" @click="orgFilter" /> </el-input> <el-tree ref="tree" v-loading="orgLoading" class="el-table-high" :data="orgTree" :props="defaultProps" :default-expand-all="true" :expand-on-click-node="false" :draggable="false" :highlight-current="true" :filter-node-method="filterNode" :current-node-key="currentKey" node-key="id" @node-click="handleNodeClick" /> </el-col> <el-col :span="13"> <el-row> <el-col :span="24" align="right"> <el-input v-model="tableQuery.name" placeholder="请输入姓名" size="mini" class="filter-item" clearable :validate-event="false" @input="queryDataSelect" @keyup.enter.native="queryDataSelect" > <i slot="suffix" style="cursor: pointer" class="el-input__icon el-icon-search" @click="queryDataSelect" /> </el-input> </el-col> </el-row> <el-row> <el-table ref="multipleTable" v-loading="listLoading" class="el-table-high" :data="tableData" style="width: 100%" size="mini" row-key="id" :header-cell-style="headerCellStyle" @selection-change="handleSelect" @row-click="rowClick" > <el-table-column align="center" type="selection" width="50px" :reserve-selection="true" /> <el-table-column align="center" prop="name" label="人员姓名" /> <el-table-column align="center" prop="phone" label="手机号码" /> </el-table> <pagination v-show="tableQuery.total > 0" v-loading="paginationLoading" element-loading-spinner="el-icon-loading" :total="tableQuery.total" :page.sync="tableQuery.pageNo" :limit.sync="tableQuery.pageSize" @pagination="queryData" /> </el-row> </el-col> <el-col :span="5"> <el-row> <el-col :span="12"> <div class="selectText">已选人员(<span style="color: #0080FF;">{{ selectList.length }}</span>)</div> </el-col> <el-col :span="12" align="right"> <el-button type="text" size="mini" class="delAll" @click="delAll">清空</el-button> </el-col> </el-row> <el-row> <ul class="el-table-high"> <li v-for="(item, index) in selectList" :key="index" style="list-style-type: none;"> <el-row> <div class="selectLi"> <el-col :span="8"> <div>{{ item.name }}</div> </el-col> <el-col :span="12"> <div>{{ item.phone }}</div> </el-col> <el-col :span="4" align="right"> <!-- <el-button type="text" size="small" @click="handleDelSelect(item)"><i class="el-icon-delete" /> --> <!-- </el-button> --> </el-col> </div> </el-row> </li> </ul> </el-row> </el-col> </el-row> </div> <div align="right" class="tree_btn"> <el-button type="primary" size="mini" @click="save">确 认</el-button> <el-button size="mini" @click="handleCancel">取 消</el-button> </div> </el-dialog> </div> </template> <script> import api from '@/api/hrOrganization' import { getPartyMemberList } from '@/api/party/database/partyMembers' import Pagination from '@/components/Pagination' import { mapGetters } from 'vuex' export default { name: 'PerSelectorsThemeDay', components: { Pagination }, props: { // 已选人员数组 perSelectorList: { type: Array, default: () => [] }, // 是否可编辑 editVisible: { type: Boolean, default: false } }, data() { return { headerCellStyle: { backgroundColor: '#FAFAFA' }, orgLoading: true, listLoading: true, paginationLoading: true, title: '选择人员', // 已选员工列表 selectList: [], // 组织查询参数 orgQuery: {}, // 组织树配置选项 defaultProps: { id: 'id', children: 'children', label: 'name' }, orgTree: [], // 组织树当前选中的节点 currentKey: '', // 人员列表查询参数 tableQuery: { name: null, orgId: null, pageNo: 1, pageSize: 10, total: 0 }, // 人员列表 tableData: [], // 是否显示弹出层 open: false } }, watch: { perSelectorList(newVal, oldVal) { this.perSelectorList = newVal } }, computed: { ...mapGetters(['orgId']) }, created() {}, methods: { // 打开人员窗口加载组织人员数据 openPerSelector() { // 每次打开清空组织/人员搜索数据 this.orgQuery = {} this.tableQuery.name = null // 每次打开窗口显示第一页 this.tableQuery.pageNo = 1 this.initOrgTree() // 初始化已选人员列表,根据已选人员列表设置人员列表选中状态(初始化加载) this.$nextTick(() => { for (var j = 0; j < this.perSelectorList.length; j++) { this.$refs.multipleTable.toggleRowSelection(this.perSelectorList[j], true) } }) this.open = true }, initOrgTree() { this.orgLoading = true this.listLoading = true this.paginationLoading = true api.currentTree().then(res => { this.orgTree = res.data this.currentKey = res.data[0].id this.$nextTick(() => { this.$refs.tree.setCurrentKey(this.currentKey) }) this.tableQuery.orgId = res.data[0].id this.queryData() this.orgLoading = false }) }, // 对树节点进行筛选时执行的方法 filterNode(value, data) { if (!value) return true return data.name.indexOf(value) !== -1 }, // 树节点被点击时的回调 handleNodeClick(data) { this.tableQuery.orgId = data.id this.queryDataSelect() }, // 组织查询按钮 orgFilter() { this.$nextTick(() => { this.$refs.tree.filter(this.orgQuery.name) }) }, // 查询人员列表 queryData() { this.tableQuery.unfinished = false this.getTableList() }, queryDataSelect() { this.tableQuery.pageNo = 1 this.getTableList() }, getTableList() { this.listLoading = true this.paginationLoading = true getPartyMemberList(this.tableQuery).then(res => { this.tableData = res.data.data this.tableQuery.total = Number(res.data.total) this.listLoading = false this.paginationLoading = false }) }, // 人员列表多选(手动勾选数据行时会触发该事件) handleSelect(val) { this.selectList = val }, // 人员列表行点击事件 rowClick(val) { this.$nextTick(() => { this.$refs.multipleTable.toggleRowSelection(val) }) }, // 选择员工内的删除 handleDelSelect(data) { this.$nextTick(() => { this.$refs.multipleTable.toggleRowSelection(data, true) this.$refs.multipleTable.toggleRowSelection(data, false) }) }, // 取消 handleCancel() { this.$emit('handleCancel', false) this.selectList = [] // 清空人员列表选中状态 this.$nextTick(() => { this.$refs.multipleTable.clearSelection() }) this.open = false }, // 清空已选员工 delAll() { this.selectList = [] // 清空人员列表选中状态 this.$nextTick(() => { this.$refs.multipleTable.clearSelection() }) }, save() { // if (this.selectList.length) { this.$emit('handleConfirm', this.selectList) this.handleCancel() // } else { // this.$message({ // showClose: true, // message: '请选择人员', // type: 'warning' // }) // } } } } </script> <style lang="scss" scoped> .divTinymce { border: 1px solid #e4e7ed; border-radius: 5px; background-color: #f5f7fa; color: #c0c4cc; } .divTinymceIn { padding-right: 15px; padding-left: 15px; background-color: #f5f7fa; color: #c0c4cc; } .divTinymceShow { border: 1px solid #e4e7ed; border-radius: 5px; } .divTinymceInShow { padding-right: 15px; padding-left: 15px; } .selectText { text-align: center; height: 28px; line-height: 28px; font-size: 12px; } .selectLi { height: 28px; line-height: 28px; font-size: 12px; } .el-table-high { height: 56.1vh; overflow: auto; } </style>
封装Pagination分页组件:

<template> <div :class="{'hidden':hidden}" class="pagination-container"> <el-pagination :background="background" :current-page.sync="currentPage" :page-size.sync="pageSize" :layout="layout" :page-sizes="pageSizes" :total="total" v-bind="$attrs" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </template> <script> import { scrollTo } from '@/utils/scroll-to' export default { name: 'Pagination', props: { total: { required: true, type: Number }, page: { type: Number, default: 1 }, limit: { type: Number, default: 10 }, pageSizes: { type: Array, default() { return [10, 20, 50] } }, layout: { type: String, default: 'total, sizes, prev, pager, next, jumper' }, background: { type: Boolean, default: true }, autoScroll: { type: Boolean, default: true }, hidden: { type: Boolean, default: false } }, computed: { currentPage: { get() { return this.page }, set(val) { this.$emit('update:page', val) } }, pageSize: { get() { return this.limit }, set(val) { this.$emit('update:limit', val) } } }, methods: { handleSizeChange(val) { this.$emit('pagination', { page: this.currentPage, limit: val }) if (this.autoScroll) { scrollTo(0, 800) } }, handleCurrentChange(val) { this.$emit('pagination', { page: val, limit: this.pageSize }) if (this.autoScroll) { scrollTo(0, 800) } } } } </script> <style scoped> .pagination-container { background: #fff; padding: 5px; } .pagination-container.hidden { display: none; } </style>
替换组件内的获取组织树接口和获取人员接口
3.使用方法
import perSelectorThemeDay from '@/components/perSelectorThemeDay' export default { components: { perSelectorThemeDay, }, data() { return { form: { content: null, }, } }, }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?