首先el-tree-transfer的地址 github文档:https://github.com/hql7/tree-transfer
效果图
安装及使用
npm install el-tree-transfer --save
//或者
npm i el-tree-transfer -S
然后在局部引入并注册组件就可以使用了
import treeTransfer from “el-tree-transfer”; // 引入
<!-- 使用树形穿梭框组件 -->
<tree-transfer
:from_data="deptOptions"
:to_data="toData"
:defaultProps="{ label: 'label', children: 'children' }"
@add-btn="add"
:pid="parentId"
@remove-btn="remove"
height="540px"
filter
:title="treeTitle"
:openAll="openAll"
placeholder="请输入菜单名称"
class="tarnsfer_tree"
:key="treeKey"
>
</tree-transfer>
methods:{
// 切换模式 现有树形穿梭框模式transfer 和通讯录模式addressList
changeMode() {
if (this.mode == "transfer") {
this.mode = "addressList";
} else {
this.mode = "transfer";
}
},
// 监听穿梭框组件添加
add(fromData,toData,obj){
// 树形穿梭框模式transfer时,返回参数为左侧树移动后数据、右侧树移动后数据、移动的{keys,nodes,halfKeys,halfNodes}对象
// 通讯录模式addressList时,返回参数为右侧收件人列表、右侧抄送人列表、右侧密送人列表
console.log("fromData:", fromData);
console.log("toData:", toData);
console.log("obj:", obj);
},
// 监听穿梭框组件移除
remove(fromData,toData,obj){
// 树形穿梭框模式transfer时,返回参数为左侧树移动后数据、右侧树移动后数据、移动的{keys,nodes,halfKeys,halfNodes}对象
// 通讯录模式addressList时,返回参数为右侧收件人列表、右侧抄送人列表、右侧密送人列表
console.log("fromData:", fromData);
console.log("toData:", toData);
console.log("obj:", obj);
}
},
components:{ treeTransfer } // 注册
}
注意!!!
一定要定义pid,否则穿梭过去的结构不是树状的!!!而pid是自定义的,我这里是自己定义的parentId
以下是必须做的步骤
props: {
// el-tree node-key 必须唯一
node_key: {
type: String,
default: "id",
},
// 自定义 pid参数名,因为后端给的参数名为id
pid: {
type: String,
default: "parentId",
},
},
data里面定义
parentId: "parentId",
处理数据
/** 查询部门树结构 */
getDeptTreeselect() {
deptTreeselect().then((response) => {
this.deptOptions = response.data;
this.deptOptions.forEach((item) => {
//el-tree-transfer组件的第一个pid必须为0,二级parentId必须是父级的id
item.parentId = 0;
if (item.children) {
item.children.forEach((val) => {
val.parentId = item.id;
});
}
});
});
},
最后附上全部完整代码
<template>
<el-dialog
v-bind="dialogConfig"
:visible.sync="visibleTmpl"
:before-close="beforeClose"
append-to-body
width="70%"
custom-class="add_menu_dialog"
>
<el-form
:model="form"
:label-position="'right'"
label-width="100px"
size="small"
ref="addForm"
>
<!-- title部分 -->
<div class="title_contant">
<!-- tab栏 -->
<div class="tabBar">
<div
class="tab-item"
:class="tabType === 1 ? 'active' : ''"
:style="{
'--theme': theme,
}"
@click="tabType = 1"
>
<i class="el-icon-date"> web</i>
</div>
<div
class="tab-item"
:class="tabType === 2 ? 'active' : ''"
:style="{
'--theme': theme,
}"
@click="tabType = 2"
>
<i class="el-icon-menu"> App1</i>
</div>
<div
class="tab-item"
:class="tabType === 3 ? 'active' : ''"
:style="{
'--theme': theme,
}"
@click="tabType = 3"
>
<i class="el-icon-menu"> App2</i>
</div>
</div>
<!-- y右侧title -->
<div class="right_title">已选菜单</div>
</div>
<!-- 使用树形穿梭框组件 -->
<tree-transfer
:from_data="deptOptions"
:to_data="toData"
:defaultProps="{ label: 'label', children: 'children' }"
@add-btn="add"
:pid="parentId"
@remove-btn="remove"
height="540px"
filter
:title="treeTitle"
:openAll="openAll"
placeholder="请输入菜单名称"
class="tarnsfer_tree"
:key="treeKey"
>
</tree-transfer>
<!-- 功能选择左侧 -->
<div class="open_check">
<el-checkbox
v-model="openAll"
@change="handleCheckedTreeExpand($event, 'dept')"
>展开/折叠</el-checkbox
>
<el-checkbox
v-model="form.deptCheckStrictly"
@change="handleCheckedTreeConnect($event, 'dept')"
>父子联动</el-checkbox
>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="handleSave" type="primary">保 存</el-button>
<el-button @click="handleBack">取 消</el-button>
</span>
</el-dialog>
</template>
<script>
const defaultDialogProps = {
title: "菜单选择",
width: "500px",
};
import { treeselect as deptTreeselect } from "@/api/system/dept";
import { mapState } from "vuex";
import variables from "@/assets/styles/variables.scss";
import treeTransfer from "el-tree-transfer"; // 引入
export default {
components: {
treeTransfer,
},
data() {
return {
// 弹窗显示/隐藏
visibleTmpl: this.visible,
form: this.initFormData(),
isView: true,
options: {
specialTypes: [],
categorys: [], // 专业类别
},
dialogTitle: "",
defaultProps: {
children: "children",
label: "label",
},
toData: [],
treeKey: 1,
// 部门列表
deptOptions: [],
openAll: true,
deptExpand: true,
deptNodeAll: false,
deptExpand_right: true, // 右侧选项
deptNodeAll_right: false, // 右侧选项
deptOptions_rignt: [], // 已选菜单
tabType: 1, //tab栏
selected: [],
treeTitle: ["全选/不全选", "全选/不全选"],
parentId: "parentId",
};
},
props: {
visible: {
type: Boolean,
default: false,
},
dialogProps: {
type: Object,
default: () => {},
},
rulesProps: {
type: Object,
default: () => {},
},
// el-tree node-key 必须唯一
node_key: {
type: String,
default: "id",
},
// 自定义 pid参数名,因为后端给的参数名为id
pid: {
type: String,
default: "parentId",
},
},
created() {
this.getDeptTreeselect();
},
methods: {
// 查询
searchInfo() {},
// 初始化表单
initFormData() {
return {
// id: "",
matterName: "",
deptCheckStrictly: true,
};
},
// 提交
handleSave() {
this.loading = true;
const msg = this.rulesProps.id ? "编辑" : "新增";
this.$refs.addForm.validate().then(() => {
if (this.rulesProps.id) {
this.$services["spsi/rules"]
.getUpdateSave({ ...this.form }, { msg: true })
.then(() => {
this.$notify.success(msg + "监管规则成功!");
this.$emit("submit");
this.$emit("update:visible", false);
this.loading = false;
})
.catch((err) => {
console.error(err);
});
} else {
this.$services["spsi/rules"]
.getSave({ ...this.form }, { msg: true })
.then(() => {
this.$notify.success(msg + "监管规则成功!");
this.$emit("submit");
this.$emit("update:visible", false);
})
.catch((err) => {
console.error(err);
});
}
});
},
// 树权限(展开/折叠)
handleCheckedTreeExpand(value, type) {
if (!value) {
this.openAll = false;
} else {
this.openAll = true;
}
this.treeKey++;
},
// 树权限(父子联动)
handleCheckedTreeConnect(value, type) {
if (type == "dept_right") {
this.form.menuCheckStrictly = value ? true : false;
} else if (type == "dept") {
this.form.deptCheckStrictly = value ? true : false;
}
},
/** 查询部门树结构 */
getDeptTreeselect() {
deptTreeselect().then((response) => {
this.deptOptions = response.data;
this.deptOptions.forEach((item) => {
//el-tree-transfer组件的第一个pid必须为0,二级parentId必须是父级的id
item.parentId = 0;
if (item.children) {
item.children.forEach((val) => {
val.parentId = item.id;
});
}
});
});
},
// 关闭弹窗
beforeClose(done) {
this.$emit("update:visible", false);
done();
},
handleBack() {
this.$emit("update:visible", false);
},
// 搜索节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
// 切换模式 现有树形穿梭框模式transfer 和通讯录模式addressList
changeMode() {
if (this.mode == "transfer") {
this.mode = "addressList";
} else {
this.mode = "transfer";
}
},
// 监听穿梭框组件添加
add(fromData, toData, obj) {
// 树形穿梭框模式transfer时,返回参数为左侧树移动后数据、右侧树移动后数据、移动的{keys,nodes,halfKeys,halfNodes}对象
// 通讯录模式addressList时,返回参数为右侧收件人列表、右侧抄送人列表、右侧密送人列表
console.log("fromData:", fromData);
console.log("toData:", toData);
console.log("obj:", obj);
//追加选中数据ID
this.selected.concat(obj.keys);
},
// 监听穿梭框组件移除
remove(fromData, toData, obj) {
//移除删除项数据
this.selected = this.selected.filter(
(item) => obj.keys.indexOf(item) == -1
);
},
},
computed: {
dialogConfig() {
return Object.assign({}, defaultDialogProps, this.dialogProps);
},
...mapState(["settings"]),
theme() {
return this.$store.state.settings.theme;
},
variables() {
return variables;
},
},
watch: {
visible(n) {
this.visibleTmpl = n;
},
},
};
</script>
<style lang="scss">
.add_menu_dialog {
.el-select {
width: 100%;
}
.el-dialog__header {
border-bottom: 1px solid #e5e6e7;
}
.title_contant {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
.tabBar {
height: 50px;
border-radius: 2px;
display: flex;
align-items: center;
border-bottom: 1px solid #f6f6f6;
padding-left: 20px;
.tab-item {
padding: 0 26px;
font-size: 16px;
font-weight: 400;
height: 50px;
line-height: 50px;
box-sizing: border-box;
cursor: pointer;
&.active {
// color: #1890ff;
border-bottom: 2px solid #{"var(--theme)"} !important;
color: #{"var(--theme)"} !important;
}
}
}
.right_title,
.tabBar {
border: 1px solid #e5e6e7;
border-bottom: none;
width: 46%;
}
.right_title {
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
color: #262626;
line-height: 50px;
padding-left: 12px;
}
}
.wl-transfer {
.transfer-title {
font-size: 14px;
background: none;
}
.transfer-left,
.transfer-right {
width: 46%;
border-radius: 0;
}
.transfer-right {
border-top: 0;
}
}
.open_check {
position: absolute;
top: 147px;
left: 153px;
}
}
</style>