el-transfer 的用法

本文介绍了Vue中Transfer穿梭框的使用。涵盖基础用法,通过属性传入数据;可搜索功能,能对大量数据进行搜索过滤;可自定义列表标题、按钮文案等;还能设置数据项属性别名。此外,还提及了穿梭框的Attributes、Slot、Methods和Events等内容。

Transfer 穿梭框

基础用法

Transfer 的数据通过 data 属性传入。数据需要是一个对象数组,每个对象有以下属性:key 为数据的唯一性标识,label为显示文本,disabled 表示该项数据是否禁止转移。目标列表中的数据项会同步到绑定至 v-model 的变量,值为数据项的 key 所组成的数组。当然,如果希望在初始状态时目标列表不为空,可以像本例一样为 v-model 绑定的变量赋予一个初始值。

复制代码

 1 <template>
 2   <el-transfer v-model="value1" :data="data"></el-transfer>
 3 </template>
 4 
 5 <script>
 6   export default {
 7     data() {
 8       const generateData = _ => {
 9         const data = [];
10         for (let i = 1; i <= 15; i++) {
11           data.push({
12             key: i,
13             label: `备选项 ${ i }`,
14             disabled: i % 4 === 0
15           });
16         }
17         return data;
18       };
19       return {
20         data: generateData(),
21         value1: [1, 4]
22       };
23     }
24   };
25 </script>

复制代码

可搜索

在数据很多的情况下,可以对数据进行搜索和过滤。

设置 filterable 为 true 即可开启搜索模式。默认情况下,若数据项的 label 属性包含搜索关键字,则会在搜索结果中显示。你也可以使用 filter-method 定义自己的搜索逻辑。filter-method 接收一个方法,当搜索关键字变化时,会将当前的关键字和每个数据项传给该方法。若方法返回 true,则会在搜索结果中显示对应的数据项。

复制代码

 1 <template>
 2   <el-transfer
 3     filterable
 4     :filter-method="filterMethod"
 5     filter-placeholder="请输入城市拼音"
 6     v-model="value2"
 7     :data="data2">
 8   </el-transfer>
 9 </template>
10 
11 <script>
12   export default {
13     data() {
14       const generateData2 = _ => {
15         const data = [];
16         const cities = ['上海', '北京', '广州', '深圳', '南京', '西安', '成都'];
17         const pinyin = ['shanghai', 'beijing', 'guangzhou', 'shenzhen', 'nanjing', 'xian', 'chengdu'];
18         cities.forEach((city, index) => {
19           data.push({
20             label: city,
21             key: index,
22             pinyin: pinyin[index]
23           });
24         });
25         return data;
26       };
27       return {
28         data2: generateData2(),
29         value2: [],
30         filterMethod(query, item) {
31           return item.pinyin.indexOf(query) > -1;
32         }
33       };
34     }
35   };
36 </script>

复制代码

可自定义

可以对列表标题文案、按钮文案、数据项的渲染函数、列表底部的勾选状态文案、列表底部的内容区等进行自定义。

可以使用 titlesbutton-textsrender-content 和 format 属性分别对列表标题文案、按钮文案、数据项的渲染函数和列表顶部的勾选状态文案进行自定义。对于列表底部的内容区,提供了两个具名 slot:left-footer 和 right-footer。此外,如果希望某些数据项在初始化时就被勾选,可以使用 left-default-checked 和 right-default-checked 属性。最后,本例还展示了 change 事件的用法。注意:由于 jsfiddle 不支持 JSX 语法,所以本例在 jsfiddle 中无法运行。但是在实际的项目中,只要正确地配置了相关依赖,就可以正常运行。

复制代码

 1 <template>
 2   <el-transfer
 3     v-model="value3"
 4     filterable
 5     :left-default-checked="[2, 3]"
 6     :right-default-checked="[1]"
 7     :render-content="renderFunc"
 8     :titles="['Source', 'Target']"
 9     :button-texts="['到左边', '到右边']"
10     :format="{
11       noChecked: '${total}',
12       hasChecked: '${checked}/${total}'
13     }"
14     @change="handleChange"
15     :data="data">
16     <el-button class="transfer-footer" slot="left-footer" size="small">操作</el-button>
17     <el-button class="transfer-footer" slot="right-footer" size="small">操作</el-button>
18   </el-transfer>
19 </template>
20 
21 <style>
22   .transfer-footer {
23     margin-left: 20px;
24     padding: 6px 5px;
25   }
26 </style>
27 
28 <script>
29   export default {
30     data() {
31       const generateData = _ => {
32         const data = [];
33         for (let i = 1; i <= 15; i++) {
34           data.push({
35             key: i,
36             label: `备选项 ${ i }`,
37             disabled: i % 4 === 0
38           });
39         }
40         return data;
41       };
42       return {
43         data: generateData(),
44         value3: [1],
45         renderFunc(h, option) {
46           return <span>{ option.key } - { option.label }</span>;
47         }
48       };
49     },
50 
51     methods: {
52       handleChange(value, direction, movedKeys) {
53         console.log(value, direction, movedKeys);
54       }
55     }
56   };
57 </script>

复制代码

数据项属性别名

默认情况下,Transfer 仅能识别数据项中的 keylabel 和 disabled 字段。如果你的数据的字段名不同,可以使用 props 属性为它们设置别名。

本例中的数据源没有 key 和 label 字段,在功能上与它们相同的字段名为 value 和 desc。因此可以使用props 属性为 key 和 label 设置别名。

复制代码

 1 <template>
 2   <el-transfer
 3     v-model="value4"
 4     :props="{
 5       key: 'value',
 6       label: 'desc'
 7     }"
 8     :data="data3">
 9   </el-transfer>
10 </template>
11 
12 <script>
13   export default {
14     data() {
15       const generateData3 = _ => {
16         const data = [];
17         for (let i = 1; i <= 15; i++) {
18           data.push({
19             value: i,
20             desc: `备选项 ${ i }`,
21             disabled: i % 4 === 0
22           });
23         }
24         return data;
25       };
26       return {
27         data3: generateData3(),
28         value4: []
29       };
30     }
31   };
32 </script>

复制代码

 

Attributes

参数说明类型可选值默认值
dataTransfer 的数据源array[{ key, label, disabled }][ ]
filterable是否可搜索booleanfalse
filter-placeholder搜索框占位符string请输入搜索内容
filter-method自定义搜索方法function
target-order右侧列表元素的排序策略:若为 original,则保持与数据源相同的顺序;若为 push,则新加入的元素排在最后;若为 unshift,则新加入的元素排在最前stringoriginal / push / unshiftoriginal
titles自定义列表标题array['列表 1', '列表 2']
button-texts自定义按钮文案array[ ]
render-content自定义数据项渲染函数function(h, option)
format列表顶部勾选状态文案object{noChecked, hasChecked}{ noChecked: '${checked}/${total}', hasChecked: '${checked}/${total}' }
props数据源的字段别名object{key, label, disabled}
left-default-checked初始状态下左侧列表的已勾选项的 key 数组array[ ]
right-default-checked初始状态下右侧列表的已勾选项的 key 数组array[ ]

Slot

name说明
left-footer左侧列表底部的内容
right-footer右侧列表底部的内容

Methods

方法名说明参数
clearQuery清空某个面板的搜索关键词'left' / 'right',指定需要清空的面板

Events

事件名称说明回调参数
change右侧列表元素变化时触发当前值、数据移动的方向('left' / 'right')、发生移动的数据 key 数组

 

转自: https://www.cnblogs.com/grt322/p/8553346.html

<think>我们正在处理一个关于Element UI的el-transfer组件的问题。用户希望自定义右侧面板的内容。根据引用[1],我们可以使用Scoped Slot来自定义数据项。在引用[1]中,示例代码展示了如何自定义左侧列表的显示,但同样的方法也可以用于右侧。 但是,请注意,el-transfer组件有两个面板:左侧(源列表)和右侧(目标列表)。默认情况下,两个面板的渲染方式相同。如果我们想要分别自定义左右面板,或者只自定义右侧面板,我们需要理解el-transfer的插槽机制。 根据Element UI官方文档,el-transfer提供了名为`left-footer`和`right-footer`的插槽用于底部,以及`default`插槽(即Scoped Slot)用于每个数据项的渲染。但是,这个Scoped Slot是同时作用于左右面板的。也就是说,我们无法通过一个Scoped Slot分别定义左右面板的渲染。 然而,引用[2]提到了一种封装Transfer穿梭组件的方法,支持全流域自定义。引用[3]则展示了如何给右侧面板添加拖拽排序功能。引用[4]介绍了一个自定义的树形穿梭组件。 因此,如果用户需要单独自定义右侧面板,我们可以考虑以下方法: 1. **使用Scoped Slot统一自定义左右面板**:如果左右面板的自定义结构相同,那么我们可以直接使用Scoped Slot。例如: ```html <el-transfer v-model="value" :data="data"> <span slot-scope="{ option }"> <span>{{ option.label }}</span> <span style="float: right; color: #8492a6; font-size: 13px;">{{ option.desc }}</span> </span> </el-transfer> ``` 2. **分别自定义左右面板**:Element UI的el-transfer组件并没有直接提供分别自定义左右面板的插槽。但是,我们可以通过CSS选择器来针对右侧面板进行样式覆盖,或者使用JavaScript操作DOM来修改右侧面板的内容。然而,这种方法不够优雅且可能不稳定。 3. **使用第三方封装组件**:如引用[4]中提到的`tree-transfer`组件,它支持更高级的自定义。如果项目允许,可以考虑使用这样的第三方封装。 4. **自定义渲染函数(render函数)**:如果使用Vue的render函数,我们可以更灵活地控制。但是,el-transfer组件可能不支持分别传入左右面板的渲染函数。 根据引用[1]的示例,我们只能通过Scoped Slot同时自定义左右面板的每一项。所以,如果我们希望右侧面板显示不同的内容,我们可以在数据源中增加一个字段来标识,然后在Scoped Slot中根据这个字段判断当前项是在左侧还是右侧,从而显示不同的内容。但是,这种方法在数据项移动时可能会出现问题。 另一种思路:在Scoped Slot中,我们可以通过当前项是否在`v-model`绑定的数组(即右侧已选列表)中来判断它属于哪个面板。但是,在渲染时,数据项是同时出现在左右面板的(未选中的在左侧,选中的在右侧),所以同一个数据项在左右面板都会出现,只是不同时显示。因此,在Scoped Slot中,我们无法直接知道当前项正在哪个面板渲染。 实际上,在Scoped Slot中,我们只能访问到当前数据项(option)和索引(index),并没有提供面板信息。 因此,如果我们必须分别自定义左右面板,可能需要修改el-transfer的源码或者寻找其他组件。但根据用户需求,我们可能只需要在右侧面板添加一些额外的操作按钮(如删除、排序等),那么我们可以考虑以下方案: - 在右侧面板使用`right-footer`插槽添加一些操作按钮,但这只能添加在面板底部,而不是每一项中。 - 使用Scoped Slot自定义每一项,然后在自定义的模板中,根据当前项是否在右侧(即通过v-model绑定的数组)来决定显示的内容。但是,注意:同一个数据项在左侧和右侧的渲染是使用同一个模板的,所以无法区分。 因此,一个可行的折中方案是:在自定义每一项时,我们判断该项是否在右侧(即已选列表中),如果是,则显示右侧特有的内容;否则,显示左侧的内容。但是,这里有一个问题:当数据项在左侧时,我们自定义了它,当它被移动到右侧后,我们希望它显示不同的内容,那么我们可以这样: ```html <el-transfer v-model="value" :data="data"> <template v-slot:default="{ option }"> <div v-if="value.includes(option.key)"> <!-- 如果当前项在右侧(即已选列表中) --> <!-- 右侧面板的显示方式 --> <span>{{ option.label }}</span> <button @click="handleDelete(option)">删除</button> </div> <div v-else> <!-- 左侧面板的显示方式 --> <span>{{ option.label }}</span> <span style="float: right; color: #8492a6; font-size: 13px;">{{ option.desc }}</span> </div> </template> </el-transfer> ``` 注意:这种方法会导致每次渲染时都要检查当前项是否在右侧,可能会影响性能(如果数据量很大)。同时,由于左右面板的渲染结构不同,可能会造成高度不一致,影响美观。 另外,引用[3]中通过自定义指令实现了右侧面板的拖拽排序,这可以作为一个增强功能。 综上所述,如果用户需要自定义右侧面板的每一项,我们可以使用Scoped Slot,并在模板中根据数据项是否在右侧(通过v-model绑定的数组)来显示不同的内容。 下面是一个完整的示例: ```vue <template> <el-transfer v-model="rightValue" :data="data" :titles="['Source', 'Target']" @change="handleChange" > <template #default="{ option }"> <div v-if="rightValue.includes(option.key)" style="display: flex; justify-content: space-between; width: 100%;"> <span>{{ option.label }}</span> <span> <el-button type="text" @click="handleDelete(option)">删除</el-button> <!-- 其他操作按钮 --> </span> </div> <div v-else style="display: flex; justify-content: space-between; width: 100%;"> <span>{{ option.label }}</span> <span style="color: #8492a6; font-size: 13px;">{{ option.desc }}</span> </div> </template> </el-transfer> </template> <script> export default { data() { const generateData = _ => { const data = []; for (let i = 1; i <= 15; i++) { data.push({ key: i, label: `Option ${i}`, desc: `Description of option ${i}`, }); } return data; }; return { data: generateData(), rightValue: [1, 4], // 初始右侧选中的值 }; }, methods: { handleChange(value, direction, movedKeys) { console.log(value, direction, movedKeys); }, handleDelete(option) { // 从右侧移除该项 const index = this.rightValue.indexOf(option.key); if (index !== -1) { this.rightValue.splice(index, 1); } }, }, }; </script> ``` 在这个示例中,我们根据数据项是否在右侧(`rightValue`数组中)来分别渲染左右面板的每一项。在右侧面板中,我们添加了一个删除按钮,点击后可以将该项从右侧移除。 注意事项: 1. 由于在Scoped Slot中使用了条件渲染,当数据量较大时可能会影响性能。 2. 删除操作是自定义的,el-transfer本身不提供删除单个项的功能,所以我们需要自己实现(通过从右侧数组中移除对应的key)。 另外,引用[3]中提到的拖拽排序功能,如果需要,可以结合自定义指令实现,但这里不再展开。 希望这能解决用户的问题。 §§相关问题§§ 1. 如何在el-transfer中实现左右面板的完全独立自定义? 2. 如何优化el-transfer在大数据量下的性能? 3. 如何实现el-transfer右侧面板的拖拽排序?</think>### 自定义 Element UI el-transfer 右侧面板内容的方法 根据 Element UI 官方文档和引用资料,实现 el-transfer 右侧面板内容自定义主要有以下两种方法: #### 方法一:使用 Scoped Slot(推荐) 这是官方支持的自定义方式,通过插槽可完全控制每个数据项的渲染: ```html <el-transfer v-model="checked" :data="transferData" @change="handleChange" > <!-- 自定义右侧面板内容 --> <template #default="{ option }"> <div class="custom-right-item"> <span>{{ option.label }}</span> <el-tag size="small" style="margin-left: 8px"> {{ option.status }} </el-tag> <el-button size="mini" icon="el-icon-delete" @click="removeItem(option)" style="margin-left: auto" ></el-button> </div> </template> </el-transfer> ``` **关键实现点:** 1. `#default` 插槽作用域可获取当前渲染项 `option` 对象 2. 可通过 `v-if` 条件判断只对右侧面板自定义: ```html <template #default="{ option, direction }"> <div v-if="direction === 'right'"> <!-- 右侧专属内容 --> </div> <div v-else> <!-- 左侧默认内容 --> </div> </template> ``` 3. 支持添加交互元素(如删除按钮)[^1] #### 方法二:使用 render-content 函数 通过 render 函数实现更灵活的控制: ```js <el-transfer :render-content="renderFunc" // 其他配置 ></el-transfer> // JS 部分 methods: { renderFunc(h, option) { if (this.checked.includes(option.key)) { // 判断是否在右侧 return h('div', { class: 'right-item' }, [ h('span', option.label), h('el-tag', { props: { type: 'success' } }, '已激活') ]) } return h('span', option.label) // 左侧保持默认 } } ``` #### 完整示例(添加删除功能) ```vue <template> <el-transfer v-model="checked" :data="data" :titles="['可用资源', '已选资源']" > <template #default="{ option }"> <div class="item-container"> <span>{{ option.label }}</span> <el-button v-if="checked.includes(option.key)" size="mini" type="danger" icon="el-icon-delete" @click.stop="handleRemove(option)" /> </div> </template> </el-transfer> </template> <script> export default { data() { return { checked: [1, 4], data: [ { key: 1, label: '选项1', disabled: false }, { key: 2, label: '选项2', disabled: true }, { key: 3, label: '选项3', disabled: false }, { key: 4, label: '选项4', disabled: false } ] } }, methods: { handleRemove(item) { this.checked = this.checked.filter(key => key !== item.key) } } } </script> <style scoped> .item-container { display: flex; justify-content: space-between; width: 100%; padding: 0 8px; } </style> ``` #### 注意事项 1. 使用 `@click.stop` 阻止事件冒泡避免触发 transfer 的选中事件 2. 通过 CSS 控制自定义内容的布局和样式 3. 右侧面板变化时可监听 `@change` 事件[^1] 4. 需要复杂交互(如拖拽排序)时可结合自定义指令实现[^3]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值