antd中在vue项目中自定义穿梭框
1、完成代码
<template>
<a-modal
title="高危因素选择"
:width="1000"
:visible="riskVisible"
:confirm-loading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
okText="确认"
>
<a-transfer
class="tree-transfer"
:data-source="dataSource"
:target-keys="targetKeys"
:render="item => item.title"
:show-select-all="false"
@change="onChange"
:titles="['可选择高危因素', '已选中高危因素']"
>
<template
slot="children"
slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }"
>
<div v-if="direction === 'left'">
<div class="nav-list">
<div v-for="(item, index) in navList"
:key="index"
@click="handleNavChange(index)"
:class="['nav-item', currentNav === index ? 'current-color' : '']">
{{item.title}}
</div>
</div>
<div class="left-ipt">
<a-input placeholder="搜索可选择内容">
<a-icon slot="prefix" type="search"></a-icon>
</a-input>
<a-button type="primary" class="search-btn">
搜索
</a-button>
</div>
<a-tree
v-if='treeData.length'
:showLine="showLine"
blockNode
checkable
checkStrictly
:tree-data="renderTreeData"
:defaultExpandedKeys="['1', '2']"
:checkedKeys="[...selectedKeys, ...targetKeys]"
@check="
(_, props) => {
onLeftChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect);
}
"
>
<template slot='custom' slot-scope='item'>
<div v-if="item.flag === '1'"> {{ item.title+'('+item.level+')' }}</div>
<div v-if="item.flag === '2'">
<span :style="{width:'20px',height:'10px',background:item.color,padding:'5px 10px',marginRight:'5px'}">{{ item.levelText}}</span>{{ item.title}}
</div>
</template>
</a-tree>
</div>
<div v-if="direction === 'right'">
<div class="left-ipt">
<a-input placeholder="搜索可选择内容">
<a-icon slot="prefix" type="search"></a-icon>
</a-input>
<a-button type="primary" class="search-btn">
搜索
</a-button>
</div>
<div>
<div v-for="item in rightData" :key="item.key">
<a-checkbox @change="(e) => handleRightChange(e, [...selectedKeys], itemSelect, item.key)">
<span :style="{width:'20px',height:'10px',background:item.color,padding:'5px 10px',marginRight:'5px'}">{{ item.levelText}}</span>{{ item.title}}
</a-checkbox>
</div>
</div>
</div>
</template>
</a-transfer>
</a-modal>
</template>
<script>
import { getAction} from '@api/manage'
export default {
props: {
riskVisible: {
type: Boolean,
default: false
}
},
data() {
return {
dataSource: [],
// selectedKeys: [],
confirmLoading: false,
targetKeys: [],
treeData: [],
navList:[
{
title: '颜色分类',
},
{
title: '常用分类'
},
{
title: '疾病分类'
}
],
currentNav: 0,
showLine: true,
rightData: [],
rightSelectKeys: [],
};
},
computed: {
renderTreeData() {
return this.handleTreeData(this.treeData, this.targetKeys);
},
},
created() {
this.initData ();
},
methods: {
initData () {
getAction("/biz/bizFiveColorConfig/getTreeData",null).then(res => {
if (res.success) {
this.treeData = res.result;
let resultTranferData = [];
res.result.forEach(item => {
resultTranferData.push({title: item.title, key: item.key});
if(item.children && item.children !== null) {
item.children.forEach(it => {
resultTranferData.push({
title: it.title,
key: it.key,
});
})
}
})
this.dataSource = resultTranferData;
} else {
this.treeData = [];
this.dataSource = [];
}
})
},
handleTreeData(data, targetKeys = []) {
data.forEach(item => {
item['disabled'] = true;
if (item.children && item.children != null) {
item.children.forEach(it => {
if(targetKeys.includes(it.key)) {
it.disabled = true;
} else {
it.disabled = false
}
})
}
});
return data;
},
handleRightChange(event, checkedKeys, itemSelect, key) {
itemSelect(key, !this.isChecked(checkedKeys, key));
const { target } = event;
if(target.checked) {
this.rightSelectKeys.push(key);
this.rightSelectKeys = [...new Set(this.rightSelectKeys)];
}
},
isChecked(selectedKeys, eventKey) {
return selectedKeys.indexOf(eventKey) !== -1;
},
onChange(targetKeys, direction) {
if(direction == 'right') {
this.targetKeys = targetKeys;
let resultData = [];
this.treeData.forEach(item => {
if(item.children && item.children != null) {}
item.children.forEach(it => {
if(this.targetKeys.includes(it.key)) {
resultData.push(it);
}
})
})
this.rightData = resultData;
} else {
const resKeys = [];
const resRightData = [];
this.rightData.forEach(item => {
if(!this.rightSelectKeys.includes(item.key)) {
resKeys.push(item.key);
resRightData.push(item);
}
})
this.targetKeys = resKeys;
this.rightData = resRightData;
}
},
// 左侧复选框选中
onLeftChecked(_, e, checkedKeys, itemSelect) {
const { eventKey } = e.node;
itemSelect(eventKey, !this.isChecked(checkedKeys, eventKey));
},
// 确认
handleOk() {
const chooseFactorArr = [];
this.treeData.forEach(item => {
let children = [];
if(item.children && item.children != null) {
children = item.children.filter(it => it.disabled);
if(children.length) {
chooseFactorArr.push({
...item,
children
})
}
}
})
this.confirmLoading = true;
setTimeout(() => {
this.$emit('handleRiskVisible', { bool: false, chooseFactorArr})
this.confirmLoading = false;
}, 1000);
},
// 取消
handleCancel() {
this.$emit('handleRiskVisible', { bool: false})
},
// nav切换
handleNavChange(index) {
this.currentNav = index;
}
},
};
</script>
<style scoped lang="less">
.left-ipt {
display: flex;
margin: 15px 0 13px;
}
.search-btn {
margin-left: 8px;
}
.nav-list {
display: flex;
cursor: pointer;
}
.nav-item {
padding: 0 8px;
height: 24px;
line-height: 24px;
background: #EEEEEE;
border-radius: 4px;
margin-right: 16px;
font-size: 12px;
font-weight: 400;
color: #333333;
}
.current-color {
background: #2AB967;
color: white;
}
.right-ipt {
display: flex;
}
/deep/.ant-modal-footer {
border-top: none;
padding: 4px 24px 30px;
}
.tips {
margin-top: 10px;
font-size: 14px;
color: #666666;
line-height: 20px;
}
/deep/.ant-transfer-list-content-item {
padding: 0 12px;
}
/deep/.ant-transfer-list {
border: none;
}
/deep/.ant-transfer-list-header {
border-bottom: none;
border-radius: 0;
}
/deep/.ant-transfer-list-body {
border-radius: 4px;
border: 1px solid #DDDDDD;
}
/deep/.ant-transfer-list-header-selected span:first-child {
position: relative;
left: 100px;
font-size: 12px;
color: #999999;
}
.folder-icon {
width: 14px;
height: 11px;
margin: 4px 0 6px 0;
}
/deep/.ant-transfer-list-header-title {
left: 0;
font-size: 14px;
font-weight: 500;
color: #333333;
}
/deep/.ant-input-affix-wrapper {
color: #999999;
}
/deep/.ant-transfer-list {
width: 100%;
overflow: auto;
}
/deep/.ant-tree.ant-tree-block-node li span.ant-tree-checkbox + .ant-tree-node-content-wrapper {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
</style>
2、以上需要注意的点:
dataSource做为穿梭框的数据源需要是一维数组,并且里面的属性title和key都要为string类型,不然就会报错(dataSource类型不正确)
3、最终实现的效果