1. 前言
最近项目中遇到一个需求:制作一个组件,左侧类似一个禁用的input输入框,右侧有一个选择按钮,点击后打开一个vue穿梭框,可以进行人员的选择,选择完成后,关闭弹框,选择人员的名单显示在input中。因为项目中多处用到,因此选择将其注册成全局组件。
2. 制作选择组件(select-input)
效果图:
Input.vue:
<template>
<div>
<div class="select-input">
<span v-for="(item, index) in list" :key="index">{
{
index === list.length - 1 ? item[field] : item[field] + '、'
}}</span>
</div>
<a-button type="link">选择</a-button>
</div>
</template>
<script>
export default {
props: {
// 数组
list: {
type: Array,
default: () => {
return [
{
name: '张三',
no: 'P001',
tel: '13876787675',
department: '工程部'
}
]
}
},
// 显示的数组字段
field: {
type: String,
default: 'name'
}
},
data() {
return {
}
},
created() {
}
}
</script>
<style lang="less" scoped>
.select-input {
display: inline-block;
width: calc(100% - 70px);
border: 1px solid #ccc;
padding: 6px 11px;
border-radius: 4px;
height: 36px;
}
</style>
上面的input输入框,实际上只是个div设置了边框,并不具备真实的输入功能。
右侧的选择按钮作为备用,先放着,用于点击显示vue穿梭框使用。
为了演示效果,props.list设置了初始值,实际使用时应该设置为空数组[ ]。
index.js:
import Input from './Input.vue'
const SelectInput = {
// install 为Vue实例上的一个方法
install: function(Vue) {
// 注册全局组件,此时MyBreadcrumb为使用的组件名称
Vue.component('SelectInput', Input)
}
}
// 导出组件
export default SelectInput
在main.js中全局挂载该组件:
import SelectInput from './components/select-input'
Vue.use(SelectInput)
之后在其他页面中通过标签<select-input></select-input>
即可使用。
<template>
<a-row>
<a-col :span="5">
<select-input></select-input>
</a-col>
</a-row>
</template>
<script>
export default {
data() {
return {
}
},
created() {
}
}
</script>
<style lang="less" scoped></style>
2. 制作vue穿梭框组件(table-transfer)
效果图:
一共两个表格,上面一个表格带有查询条件,是通过接口查询获取的所有人员数据;下面一个表格是已经选中的人员表格,通过中间的两个按钮,可以实现选中人员的增加和删除。
Transfer.vue:
<template>
<!-- 人员选择弹框 -->
<a-modal
:title="title"
:visible="modalVisible"
@ok="comfirm()"
@cancel="cancel()"
:width="1400"
cancelText="取消"
okText="选择"
>
<!-- 查询条件区域 -->
<a-form class="query-con" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row type="flex">
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="姓名">
<a-input v-model="query.name"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="员工编号">
<a-input v-model="query.no"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="联系电话">
<a-input v-model="query.tel"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="部门">
<a-select
default-value="0"
style="width: 120px"
@change="handleChange"
>
<a-select-option
v-for="(item, index) in department"
:key="index"
:value="item.value"
>
{
{
item.text }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="4" class="query-btn">
<a-button type="primary">查询</a-button>
<a-button>重置</a-button>
</a-col>
</a-row>
</a-form>
<!-- 查询表格区域 -->
<a-table
:row-selection="rowSelection"
:columns="columns"
:data-source="list"
:rowKey="rowKey"
:pagination="pagination"
@change="changePage"
>
</a-table>
<!-- 穿梭按钮区域 -->
<a-row class="query-btn" type="flex" justify="center">
<a-button @click="addSelectedRow()" type="primary">
<a-icon type="down" />
</a-button>
<a-button @click="deleteSelectedRow()" type="primary">
<a-icon type="up" />
</a-button>
</a-row>
<!-- 选中表格区域 -->
<a-table
:row-selection="rowSelection2"
:columns="columns"
:data-source="selectedList"
:rowKey="rowKey"
>
</a-table>
</a-modal>
</template>
<script>
import _ from 'lodash'
export default {
props: {
// 弹框标题
title: {
type: String,
default: '选择人员'
},
// 表格支持两种选择模式,复选框checkbox || 单选框radio
type: {
type: String,
default: 'checkbox'
},
// 选中表格列表
selectedList: {
type: Array,
default: () => {
// return []
return [
{
name: '张三',
no: 'P001',
tel: '13876787675',
department: '工程部'
}
]
}
},
// 列表唯一标识rowKey
rowKey: {
type: String,
default: 'no'
}
},
data() {
return {
labelCol: {
span: 8
},
wrapperCol: {
span: 16
},
// 弹框是否可见
modalVisible: true,
query: {
name: '',
no: '',
tel: ''
},
selectedRowKeys: [],
selectedRows: [],
selectedRowKeys2: [],
selectedRows2: [],
department: [
{
value: '0',
text: '工程部'
},
{
value: '1',
text: '运维部'
},
{
value: '2',
text: '财务部'
},
{
value: '3',
text: '人事部'
}
],
// 待办列表表格列
columns: [
{
title: '序号',
dataIndex: 'index',
key: 'index',
align: 'center',
width: 100,
customRender: (text, record, index) => `${
index + 1}`
},
{
title: '姓名',
dataIndex: 'name'
},
{
title: '员工编号',
dataIndex: 'no'
},
{
title: '联系电话',
dataIndex: 'tel'
},
{
title: '部门',
dataIndex: 'department'
}
],
list: [
{
name: '张三',
no: 'P001',
tel: '13876787675',
department: '工程部',
disabled: true
},
{
name: '李四',
no: 'P002',
tel: '13876787675',
department: '工程部'
},
{
name: '王五',
no: 'P003',
tel: '13876787675',
department: '工程部'
}
],
// 表格分页器配置
pagination: {
defaultCurrent: 1,
defaultPageSize: 5,
pageSizeOptions: ['2', '5', '10', '20', '50'],
showSizeChanger: true,
showQuickJumper: true,
showTotal: total => {
return `共 ${
total} 条`
},
total: 0
}
}
},
created() {
},
methods: {
// 显示选择人员弹框
show() {
this.modalVisible = true
},
// 点击选择按钮,抛出选中行
comfirm() {
// this.modalVisible = false
this.$emit('comfirm', this.selectedList)
},
cancel() {
this.modalVisible = false
},
handleChange(value) {
console.log(`selected ${
value}`)
},
changePage(pagination, filters, sorter, {
currentDataSource }) {
},
// 将查询表格选中的行添加到选中表格中
addSelectedRow() {
for (let i = 0; i < this.selectedRows.length; i++) {
// 先判断选中表格中是否已经有该行,如果有跳出本次循环
let flag = false
for (let j = 0; j < this.selectedList.length; j++) {
if (
this.selectedRows[i][this.rowKey] ===
this.selectedList[j][this.rowKey]
) {
flag = true
}
}
if (flag) {
continue
}
const selectedRow = _.cloneDeep(this.selectedRows[i])
console.log('hahah', selectedRow)
// 如果是单选模式,需要先删除第一个元素,因为是prop元素,所以不能直接赋值
if (this.type === 'radio') {
this.selectedList.splice(0, 1)
}
this.selectedList.push(selectedRow)
}
},
// 将选中表格的选中行删除
deleteSelectedRow() {
for (let i = 0; i < this.selectedRows2.length; i++) {
for (let j = 0; j < this.selectedList.length; j++) {
if (
this.selectedRows2[i][this.rowKey] ===
this.selectedList[j][this.rowKey]
) {
this.selectedList.splice(j, 1)
break
}
}
}
}
},
computed: {
// 查询表格选择框设置
rowSelection() {
return {
type: this.type,
// 选中变化事件
onChange: (selectedRowKeys, selectedRows) => {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
}
}
},
// 选中表格选择框设置
rowSelection2() {
return {
type: this.type,
onChange: (selectedRowKeys, selectedRows) => {
this.selectedRowKeys2 = selectedRowKeys
this.selectedRows2 = selectedRows
}
}
}
}
}
</script>
<style lang="less" scoped>
.query-con {
.ant-form-item .ant-form-item-control-wrapper {
display: block;
box-sizing: border-box;
width: 66.66666667%;
}
.ant-select {
width: 100% !important;
}
.query-btn {
display: flex;
margin-top: 4px;
.ant-btn {
margin-left: 10px;
}
}
}
</style>
因为这个穿梭框组件是整个项目通用的人员选择弹框,所以我直接将查询条件和表格数据直接写进了组件中。为了更加方便理解,同学们在看这段代码时,可以先忽略或者注释查询条件区域的代码。
如果需要更加通用的表格穿梭框组件,可以将其中的columns和list抽离出来作为props的属性。
index.js:
import Transfer from './Transfer.vue'
const TableTransfer = {
// install 为Vue实例上的一个方法
install: function(Vue) {
// 注册全局组件,此时MyBreadcrumb为使用的组件名称
Vue.component('TableTransfer', Transfer)
}
}
// 导出组件
export default TableTransfer
在main.js中全局挂载该组件:
import TableTransfer from './components/table-transfer'
Vue.use(TableTransfer)
之后在其他页面中通过标签<table-transfer></table-transfer>
即可使用。
<template>
<a-row>
<!-- <a-col :span="5">
<select-input></select-input>
</a-col> -->
<table-transfer></table-transfer>
</a-row>
</template>
<script>
export default {
data() {
return {
}
},
created() {
}
}
</script>
<style lang="less" scoped></style>
4. 将选择组件和穿梭框组件结合使用
4.1 点击选择组件按钮,弹出穿梭框
(1)在select-input组件中添加点击事件,并且暴露出去
(2)在页面中通过ref引用穿梭框的显示方法
4.2 将select-input组件中的数据显示在弹框中的已选表格中
4.2.1 全局引入lodash
在终端通过npm i lodash
下载lodash
在main.js中全局引入:
import _ from 'lodash'
Vue.prototype._ = _
4.2.2 初始化input-select组件list数据和穿梭框的selectList(已选中的人员)
点击选择按钮:
4.3 将重新选择后的表格数据显示在select-input组件中
重新选择人员后,点击确认:
4.3 单选模式
在使用穿梭框时,只需要设置 type 为 radio 即可切换成单选模式
5. 完整代码:
5.1 select-input 组件代码:
<template>
<div class="select-input-con">
<div class="select-input">
<span v-for="(item, index) in list" :key="index">{
{
index === list.length - 1 ? item[field] : item[field] + '、'
}}</span>
</div>
<a-button @click="showSelectModal()" type="link">选择</a-button>
</div>
</template>
<script>
export default {
props: {
// 数组
list: {
type: Array,
default: () => {
return []
}
},
// 显示的数组字段
field: {
type: String,
default: 'name'
}
},
data() {
return {
}
},
created() {
},
methods: {
showSelectModal() {
this.$emit('showSelectModal')
}
}
}
</script>
<style lang="less" scoped>
.select-input-con {
display: flex;
align-items: center;
.select-input {
display: inline-block;
width: calc(100% - 70px);
border: 1px solid #ccc;
padding: 6px 11px;
border-radius: 4px;
height: 36px;
}
}
</style>
5.2 table-transfer 组件代码:
<template>
<!-- 人员选择弹框 -->
<a-modal
:title="title"
:visible="modalVisible"
@ok="comfirm()"
@cancel="cancel()"
:width="1400"
cancelText="取消"
okText="选择"
>
<!-- 查询条件区域 -->
<a-form class="query-con" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row type="flex">
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="姓名">
<a-input v-model="query.name"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="员工编号">
<a-input v-model="query.no"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="联系电话">
<a-input v-model="query.tel"></a-input>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item :wrapper-col="wrapperCol" label="部门">
<a-select
default-value="0"
style="width: 120px"
@change="handleChange"
>
<a-select-option
v-for="(item, index) in department"
:key="index"
:value="item.value"
>
{
{
item.text }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="4" class="query-btn">
<a-button type="primary">查询</a-button>
<a-button>重置</a-button>
</a-col>
</a-row>
</a-form>
<!-- 查询表格区域 -->
<a-table
:row-selection="rowSelection"
:columns="columns"
:data-source="list"
:rowKey="rowKey"
:pagination="pagination"
@change="changePage"
>
</a-table>
<!-- 穿梭按钮区域 -->
<a-row class="query-btn" type="flex" justify="center">
<a-button @click="addSelectedRow()" type="primary">
<a-icon type="down" />
</a-button>
<a-button @click="deleteSelectedRow()" type="primary">
<a-icon type="up" />
</a-button>
</a-row>
<!-- 选中表格区域 -->
<a-table
:row-selection="rowSelection2"
:columns="columns"
:data-source="selectedList"
:rowKey="rowKey"
>
</a-table>
</a-modal>
</template>
<script>
import _ from 'lodash'
export default {
props: {
// 弹框标题
title: {
type: String,
default: '选择人员'
},
// 表格支持两种选择模式,复选框checkbox || 单选框radio
type: {
type: String,
default: 'checkbox'
},
// 选中表格列表
selectedList: {
type: Array,
default: () => {
return []
}
},
// 列表唯一标识rowKey
rowKey: {
type: String,
default: 'no'
}
},
data() {
return {
labelCol: {
span: 8
},
wrapperCol: {
span: 16
},
// 弹框是否可见
modalVisible: false,
query: {
name: '',
no: '',
tel: ''
},
selectedRowKeys: [],
selectedRows: [],
selectedRowKeys2: [],
selectedRows2: [],
department: [
{
value: '0',
text: '工程部'
},
{
value: '1',
text: '运维部'
},
{
value: '2',
text: '财务部'
},
{
value: '3',
text: '人事部'
}
],
// 待办列表表格列
columns: [
{
title: '序号',
dataIndex: 'index',
key: 'index',
align: 'center',
width: 100,
customRender: (text, record, index) => `${
index + 1}`
},
{
title: '姓名',
dataIndex: 'name'
},
{
title: '员工编号',
dataIndex: 'no'
},
{
title: '联系电话',
dataIndex: 'tel'
},
{
title: '部门',
dataIndex: 'department'
}
],
list: [
{
name: '张三',
no: 'P001',
tel: '13876787675',
department: '工程部',
disabled: true
},
{
name: '李四',
no: 'P002',
tel: '13876787675',
department: '工程部'
},
{
name: '王五',
no: 'P003',
tel: '13876787675',
department: '工程部'
}
],
// 表格分页器配置
pagination: {
defaultCurrent: 1,
defaultPageSize: 5,
pageSizeOptions: ['2', '5', '10', '20', '50'],
showSizeChanger: true,
showQuickJumper: true,
showTotal: total => {
return `共 ${
total} 条`
},
total: 0
}
}
},
created() {
},
methods: {
// 显示选择人员弹框
show() {
this.modalVisible = true
},
// 点击选择按钮,抛出选中行
comfirm() {
// this.modalVisible = false
this.$emit('comfirm', this.selectedList)
},
cancel() {
this.modalVisible = false
},
handleChange(value) {
console.log(`selected ${
value}`)
},
changePage(pagination, filters, sorter, {
currentDataSource }) {
},
// 将查询表格选中的行添加到选中表格中
addSelectedRow() {
for (let i = 0; i < this.selectedRows.length; i++) {
// 先判断选中表格中是否已经有该行,如果有跳出本次循环
let flag = false
for (let j = 0; j < this.selectedList.length; j++) {
if (
this.selectedRows[i][this.rowKey] ===
this.selectedList[j][this.rowKey]
) {
flag = true
}
}
if (flag) {
continue
}
const selectedRow = _.cloneDeep(this.selectedRows[i])
console.log('hahah', selectedRow)
// 如果是单选模式,需要先删除第一个元素,因为是prop元素,所以不能直接赋值
if (this.type === 'radio') {
this.selectedList.splice(0, 1)
}
this.selectedList.push(selectedRow)
}
},
// 将选中表格的选中行删除
deleteSelectedRow() {
for (let i = 0; i < this.selectedRows2.length; i++) {
for (let j = 0; j < this.selectedList.length; j++) {
if (
this.selectedRows2[i][this.rowKey] ===
this.selectedList[j][this.rowKey]
) {
this.selectedList.splice(j, 1)
break
}
}
}
}
},
computed: {
// 查询表格选择框设置
rowSelection() {
return {
type: this.type,
// 选中变化事件
onChange: (selectedRowKeys, selectedRows) => {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
}
}
},
// 选中表格选择框设置
rowSelection2() {
return {
type: this.type,
onChange: (selectedRowKeys, selectedRows) => {
this.selectedRowKeys2 = selectedRowKeys
this.selectedRows2 = selectedRows
}
}
}
}
}
</script>
<style lang="less" scoped>
.query-con {
.ant-form-item .ant-form-item-control-wrapper {
display: block;
box-sizing: border-box;
width: 66.66666667%;
}
.ant-select {
width: 100% !important;
}
.query-btn {
display: flex;
margin-top: 4px;
.ant-btn {
margin-left: 10px;
}
}
}
</style>
5.3 结合select-input和table-transfer 完整代码:
<template>
<a-row>
<a-col :span="5">
<select-input
:list="list"
@showSelectModal="showSelectModal"
></select-input>
</a-col>
<table-transfer
:selectedList="selectedList"
@comfirm="comfirm"
type="radio"
ref="staffmodal"
></table-transfer>
</a-row>
</template>
<script>
export default {
data() {
return {
list: [],
selectedList: []
}
},
created() {
},
methods: {
showSelectModal() {
// 深拷贝给已选择人员
this.selectedList = this._.cloneDeep(this.list)
this.$refs.staffmodal.show()
},
comfirm(selectList) {
this.list = selectList
this.selectedList = []
this.$refs.staffmodal.cancel()
}
}
}
</script>
<style lang="less" scoped></style>