场景
要实现的效果如下
这里用到了ElemrntUI的Transfer穿梭框控件
官方示例代码
<template> <p style="text-align: center; margin: 0 0 20px">使用 render-content 自定义数据项</p> <div style="text-align: center"> <el-transfer style="text-align: left; display: inline-block" v-model="value" filterable :left-default-checked="[2, 3]" :right-default-checked="[1]" :render-content="renderFunc" :titles="['Source', 'Target']" :button-texts="['到左边', '到右边']" :format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }" @change="handleChange" :data="data"> <el-button class="transfer-footer" slot="left-footer" size="small">操作</el-button> <el-button class="transfer-footer" slot="right-footer" size="small">操作</el-button> </el-transfer> </div> <p style="text-align: center; margin: 50px 0 20px">使用 scoped-slot 自定义数据项</p> <div style="text-align: center"> <el-transfer style="text-align: left; display: inline-block" v-model="value4" filterable :left-default-checked="[2, 3]" :right-default-checked="[1]" :titles="['Source', 'Target']" :button-texts="['到左边', '到右边']" :format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }" @change="handleChange" :data="data"> <span slot-scope="{ option }">{{ option.key }} - {{ option.label }}</span> <el-button class="transfer-footer" slot="left-footer" size="small">操作</el-button> <el-button class="transfer-footer" slot="right-footer" size="small">操作</el-button> </el-transfer> </div></template><style> .transfer-footer { margin-left: 20px; padding: 6px 5px; }</style><script> export default { data() { const generateData = _ => { const data = []; for (let i = 1; i <= 15; i++) { data.push({ key: i, label: `备选项 ${ i }`, disabled: i % 4 === 0 }); } return data; }; return { data: generateData(), value: [1], value4: [1], renderFunc(h, option) { return <span>{ option.key } - { option.label }</span>; } }; }, methods: { handleChange(value, direction, movedKeys) { console.log(value, direction, movedKeys); } } };</script>注:
关注公众号 霸道的程序猿 获取编程相关电子书、教程推送与免费下载。
实现
Transfer 的数据通过 data 属性传入。
数据需要是一个对象数组,每个对象有以下属性:key 为数据的唯一性标识,label 为显示文本,disabled 表示该项数据是否禁止转移。
目标列表中的数据项会同步到绑定至 v-model 的变量,值为数据项的 key 所组成的数组。
当然,如果希望在初始状态时目标列表不为空,可以像本例一样为 v-model 绑定的变量赋予一个初始值。
就是说赋值需要构建一个对象数组,或者说叫键值对数组,每个对象有如下属性:
key为键,即唯一标识
label为值,即显示文本
比如上面的实现效果,在新增班次组时可以从左边所有的班次中移动到右边成为这个班次组所包含的班次,每个班次有班次编号和班次名称这个两个属性。
其中班次编号作为key,班次名称作为label即显示文本。
首先在页面添加一个transfer控件
<el-transfer style="text-align: left; display: inline-block" v-model="form.bhbcvalue" filterable :filter-method="filterMethod" filter-placeholder="请输入名称" :titles="['所有班次', '包含班次']" :button-texts="['到左边', '到右边']" :format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }" :data="bcalldata" ></el-transfer>上面使用了v-model进行数据的双向绑定,其中form.bhbcvalue是一个对象的数组属性
// 表单参数 form: { bczbh: undefined, bczmc: undefined, bhbcvalue: [], },还使用了:filter-method= "filterMethod"
自定义搜索逻辑,其 接收一个方法,当搜索关键字变化时,会将当前的关键字和每个数据项传给该方法。
若方法返回 true,则会在搜索结果中显示对应的数据项。
比如这里的自定义搜索逻辑就是
filterMethod(query, item) { return item.label.indexOf(query) > -1; },其中query就是关键字,item就是每个数据项,我们关键字搜索的是根据其显示的文本搜索,所以取的是item的label属性。
然后在这些个label数组中查找关键字的索引,如果有则会返回大于等于0的索引,如果没有,则会返回-1。
所以这里的判断条件是>-1,当结果为true时会显示对应的数据项。
上面还使用了format列表顶部勾选状态文案
:format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }"它是一个对象有两个属性,选中的和没选中的或者是总共的
格式示例
{ noChecked: '${checked}/${total}', hasChecked: '${checked}/${total}' }怎样给Transfer赋值?
通过
:data= "bcalldata"绑定其数据源,其中bcalldata是一个符合上面规范的键值对或者说有指定属性的对象数组。
首先对此数组进行声明
data() { return { //所有的班次信息 bcalldata: [],然后在页面加载完后执行的created中请求后台获取数据
created() { //获取所有班次信息 this.getBcAllData(); },在获取数据的方法中
getBcAllData() { getBcAllDataRequest(this.bc).then((response) => { var allbc = response.rows; allbc.forEach((bc, index) => { this.bcalldata.push({ label: bc.bcmc, key: bc.bcbh, }); }); }); },请求后台获取对象的list,然后遍历这里list,获取其bcbh属性和bcmc属性封装成新对象
并添加进上面的数据源bcalldata数组中。
这样数据就能显示在Transfer的左边框中了。
怎样获取选中的数据的?
上面已经通过v-model= "form.bhbcvalue"
将此控件选中的数据即右边的数据的key即bcbh的数组与form.bhbcvalue进行绑定,就可以取其值来获取
Transfer控件的值。
怎样控制它右边没有添加值不能点击新增或者编辑的确定按钮?
可以使用其change事件来获取左边和右边的数组值,并按照右边数组的值来获取有没有选择。
change事件在右侧元素变化时触发,回调函数参数(当前值、数据移动的方向('left' / 'right')、发生移动的数据 key 数组)
示例:
<el-transfer style="text-align: left; display: inline-block" v-model="form.bhbcvalue" filterable :filter-method="filterMethod" filter-placeholder="请输入名称" :titles="['所有班次', '包含班次']" :button-texts="['到左边', '到右边']" :format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }" @change="handleChange" :data="bcalldata" ></el-transfer>这里就添加了 @change= "handleChange事件
handleChange(value, direction, movedKeys) { if (direction === "right") { movedKeys.forEach((key) => { let index = this.leftvalue.findIndex((item) => item === key); this.leftvalue.splice(index, 1); }); movedKeys.forEach((key) => { this.rightvalue.push(key); }); } else { movedKeys.forEach((key) => { let index = this.rightvalue.findIndex((item) => item === key); this.rightvalue.splice(index, 1); }); movedKeys.forEach((key) => { this.leftvalue.push(key); }); } console.log(this.leftvalue); console.log(this.rightvalue); },此事件中value就是当前值,direction是代表方向:right-右边;left-左边,movedKeys是代表发生移动的数据 key 数组
在上面事件中首先判断方向是在右边的话就遍历发生移动的数组,将此数组遍历,
leftvalue是声明了用来存储左边元素的数组,通过对比左边元素与移动元素相等时,获取其索引并将它在左边的数据中移除。
然后将移动的元素添加进右边的数组。
否则就是移动到左边,逻辑与移动到右边相反。
当前存储左右数组的属性需要提前声明
data() { return { bcalldata: [], leftvalue: [], rightvalue: [],然后在点击新增或者编辑的确定按钮时进行判断
if (this.rightvalue == null || this.rightvalue.length == 0) { this.$alert("请先选择班次", "提示", { confirmButtonText: "确定", }); } else { addBczgl(this.form).then((response) => { if (response.code === 200) { this.msgSuccess("新增成功"); this.open = false; this.getList(); } }); }效果如下