
本篇博客只分享代码
<template>
<div class="tree">
<h1 class="main_app_title">系统菜单维护</h1>
<el-row class="header_row">
<el-col :span="12">
<el-input placeholder="输入关键字进行过滤" v-model="filterText">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
</el-col>
<el-col :span="12">
<lhBtn
size="mini"
shading
:otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,114,24,0.58)','background':'#FE5D18','margin-right':'20px'}"
@click.native="configUpdate"
v-show="updateTree"
>确认更新</lhBtn>
<lhBtn
size="mini"
shading
:otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,147,24,0.58)'}"
@click.native="append(false,data)"
>添加主菜单</lhBtn>
</el-col>
</el-row>
<el-tree
:data="data"
node-key="id"
default-expand-all
draggable
ref="tree"
:expand-on-click-node="false"
@node-drag-enter="handleDragEnter"
:filter-node-method="filterNode"
:allow-drop="allowDrop"
@node-drop="handleDropFinish"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>
<icon-svg :icon-class="data.meta.icon | GetOfString" class="icon_location" />
{{ data.meta.title }}
</span>
<span>
<el-button type="text" v-if="data.children" class="add" size="mini" @click="() => append(true,data)"><icon-svg icon-class="RectangleCopy20" size="18" color="#CACACA" /></el-button>
<el-button type="text" size="mini" class="update" @click="() => update(node,data)"><icon-svg icon-class="weibiaoti2010104" size="13" color="#CACACA" /></el-button></el-button>
<el-button type="text" v-if="node.parent.childNodes.length>1" size="mini" class="remove" @click="() => remove(node, data)"><icon-svg icon-class="RectangleCopy41" size="18" color="#CACACA" /></el-button></el-button>
</span>
</span>
</el-tree>
<el-dialog
:title="appendOrUpdate?'添加':'修改'"
:visible.sync="treeDialogVisible"
:modal-append-to-body="false"
width="55.46%"
top="20.83vh"
>
<el-form
label-position="top"
label-width="80px"
:rules="formTreeRules"
ref="formTree"
:model="formTree"
class="treeForm"
:hide-required-asterisk="true"
>
<el-form-item prop="path" label="path">
<el-input v-model="formTree.path" placeholder="URL"></el-input>
</el-form-item>
<el-form-item prop="componentF" label="component(父级架构组件路径)" v-if="isFatherTree">
<el-input v-model="formTree.componentF" placeholder="父级架构组件路径 如:Layout"></el-input>
</el-form-item>
<el-form-item prop="redirect" label="redirect" v-if="isFatherTree">
<el-input v-model="formTree.redirect" placeholder="重定向 对应子级URL"></el-input>
</el-form-item>
<el-form-item prop="componentC" label="component(子级视图路径)" v-if="!isFatherUpdate">
<el-input v-model="formTree.componentC" placeholder="子级视图"></el-input>
</el-form-item>
<el-form-item prop="meta" label="meta(填写对应的title及icon,也可新增meta属性)">
<el-input v-model="formTree.meta"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<lhBtn
size="small"
@click.native="submitFormTree('formTree')"
:otherStyle="{'box-shadow':'0px 6px 12px 0px rgba(253,147,24,0.58)'}"
>{{appendOrUpdate?'确定添加':'确定修改'}}</lhBtn>
</span>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
@Component({
filters: {
GetOfString(value: any) {
if (!value) return "";
return value.replace(/"/g, "");
}
}
})
export default class Tree extends Vue {
private id = 10000;
public fakeRouter = {
router: [
{
path: "",
component: "Layout",
redirect: "dashboard",
id: "1",
meta: {
title: "首页",
icon: "RectangleCopy34"
},
children: [
{
id: "1",
path: "dashboard",
component: "homePage/index",
meta: {
title: "首页",
icon: "RectangleCopy34"
}
}
]
},
{
path: "/a",
component: "Layout",
redirect: "bpmn",
id: "2",
meta: {
title: "bpmn",
icon: "RectangleCopy56"
},
children: [
{
path: "bpmn",
component: "bpmn/showBpmn",
id: "2",
meta: {
title: "bpmn",
icon: "RectangleCopy56"
}
}
]
},
{
path: "/example",
component: "Layout",
redirect: "icon",
name: "Example",
id: "3",
meta: {
title: "组件",
icon: "RectangleCopy25"
},
children: [
{
path: "icon",
name: "Icon",
component: "icon/index",
id: "4",
meta: {
title: "图标库",
icon: "RectangleCopy37"
}
},
{
path: "tree",
name: "Tree",
component: "tree/index",
id: "5",
meta: {
title: "系统菜单维护",
icon: "RectangleCopy44"
}
},
{
path: "upload",
name: "Upload",
component: "upload/index",
id: "6",
meta: {
title: "上传组件",
icon: "RectangleCopy48"
}
},
{
path: "map",
name: "Map",
component: "map/Map",
id: "7",
meta: {
title: "高德地图",
icon: "dingwei"
}
},
{
path: "effects",
name: "effects",
component: "effects/index",
id: "8",
meta: {
title: "效果组件",
icon: "RectangleCopy18"
}
}
]
}
]
};
public data = JSON.parse(JSON.stringify(this.fakeRouter)).router;
// 搜索
private filterText = "";
@Watch("filterText")
private filterTextWatch(val: any) {
(this.$refs as any).tree.filter(val);
}
// 过滤
private filterNode(value: any, data: any) {
if (!value) return true;
return data.meta.title.indexOf(value) !== -1;
}
// 添加修改弹框
private treeDialogVisible = false;
private num = 0;
@Watch("treeDialogVisible")
private treeDialog(val: boolean) {
if (val) {
if (this.num) {
(this.$refs["formTree"] as any).resetFields();
}
this.num = 1;
}
if (!val) {
this.isFatherUpdate = false;
}
}
// 新增or修改表单
private formTree: any = {
path: "",
componentF: "Layout",
redirect: "",
componentC: "",
meta: `{"title": "","icon": ""}`,
id: ""
};
private formTreeRules = {
path: [{ required: true, message: "请输入path", trigger: "blur" }],
componentF: [
{ required: true, message: "请输入componentF", trigger: "blur" }
],
redirect: [{ required: true, message: "请输入redirect", trigger: "blur" }],
componentC: [
{ required: true, message: "请输入componentC", trigger: "blur" }
],
meta: [
{
validator: (rule: any, value: any, callback: any) => {
if (value === "" || value === `{"title": "","icon": ""}`) {
callback(new Error("请输入meta"));
} else {
callback();
}
},
trigger: "blur"
}
]
};
// 新增or修改对话框 true新增 false修改
private appendOrUpdate = true;
// 父级更新
private isFatherUpdate = false;
// 更新拖拽
private updateTree = false;
// 拖拽完成
private handleDropFinish(
draggingNode: any,
dropNode: any,
dropType: any,
ev: any
) {
this.updateTree = true;
}
// 确定更新 发送请求
private configUpdate() {
// 发送请求
this.updateTree = false;
}
// 父级新增或子级新增
private isFatherTree = true;
private childrenData: any = "";
// 拖拽越级处理
private allowDrop(draggingNode: any, dropNode: any, type: any) {
if (draggingNode.level === dropNode.level) {
if (
dropNode.parent.data.children &&
dropNode.parent.data.children.length > 0 &&
draggingNode.parent.__ob__.value.data.children.length > 1
) {
return type === "prev";
} else if (draggingNode.parent.id === dropNode.parent.id) {
return type === "prev";
}
} else {
// 不同级进行处理
}
}
public append(bool: boolean, data: any) {
this.isFatherTree = !bool;
this.childrenData = data;
this.formTree = JSON.parse(
JSON.stringify({
path: "",
componentF: "Layout",
redirect: "",
componentC: "",
meta: `{"title": "","icon": ""}`,
id: ""
})
);
this.treeDialogVisible = this.appendOrUpdate = true;
}
public remove(node: any, data: any) {
this.$confirm("您确定要删除当前菜单么?", "温馨提示", {
confirmButtonText: "确定删除",
cancelButtonText: "取消",
center: true,
customClass: "myConfirm",
showClose: false
})
.then(() => {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex((d: any) => d.id === data.id);
// 调用后端请求 // 成功直接将页面中的那一条tree删除即可
children.splice(index, 1);
this.$message({
type: "success",
message: "删除成功!"
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
}
// 更新node
public updateNode: any = "";
public update(node: any, dataVal: any) {
this.isFatherTree = dataVal.children ? true : false;
this.childrenData = dataVal;
this.appendOrUpdate = false;
this.updateNode = node;
if (dataVal.children) {
// 父级
this.formTree = JSON.parse(JSON.stringify(this.formTree));
this.$set(this.formTree, "path", dataVal.path);
this.$set(this.formTree, "componentF", dataVal.component);
this.$set(this.formTree, "redirect", dataVal.redirect);
this.$set(this.formTree, "id", dataVal.id);
this.$set(this.formTree, "meta", JSON.stringify(dataVal.meta));
this.isFatherUpdate = true;
} else {
// 子级
this.formTree = JSON.parse(JSON.stringify(this.formTree));
this.$set(this.formTree, "path", dataVal.path);
this.$set(this.formTree, "componentC", dataVal.component);
this.$set(this.formTree, "id", dataVal.id);
this.$set(this.formTree, "meta", JSON.stringify(dataVal.meta));
}
this.treeDialogVisible = true;
}
// 处理tree
private submitFormTree(formName: any) {
// 新增
if (this.appendOrUpdate) {
(this.$refs[formName] as any).validate((valid: boolean) => {
if (valid) {
if (!this.isFatherTree) {
// 子级
this.childrenAdd(this.childrenData);
} else {
// 父级
this.FatherAdd();
}
} else {
return false;
}
});
return;
}
// 更新
const parent = this.updateNode.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex((d: any) => d.id === this.formTree.id);
if (this.isFatherUpdate) {
// 父级更新
console.log("父级更新", this.formTree, children[index]);
// 调用后端请求 // 成功直接将页面中的那一条tree更新即可
children[index].path = this.formTree.path;
children[index].component = this.formTree.componentF;
children[index].redirect = this.formTree.redirect;
children[index].meta = JSON.parse(this.formTree.meta);
} else {
// 子级更新
console.log("子级更新", this.formTree, children[index]);
children[index].path = this.formTree.path;
children[index].component = this.formTree.componentC;
children[index].meta = JSON.parse(this.formTree.meta);
}
this.treeDialogVisible = false;
}
// 子级添加
private childrenAdd(data: any) {
const { path, componentC, meta } = this.formTree;
// 发起后端请求 拿到成功相应后直接push数组即可 减少请求
// 注意:要和后端沟通 需要返回当前这条数据的id push的时候一定要带上id
data.children.push({
path,
id: this.id++, // 后端会自动生成
component: componentC,
meta: JSON.parse(meta)
});
this.treeDialogVisible = false;
}
// 父级添加
private FatherAdd() {
const { path, componentC, meta, componentF, redirect } = this.formTree;
// 发起后端请求 拿到成功相应后直接push数组即可 减少请求
// 注意:要和后端沟通 需要返回当前这条数据的id push的时候一定要带上id
this.data.push({
path,
component: componentF,
redirect,
id: this.id++, // 后端会自动生成
meta: JSON.parse(meta),
children: [
{
path: redirect,
component: componentC,
id: this.id++, // 后端会自动生成
meta: JSON.parse(meta)
}
]
});
this.treeDialogVisible = false;
}
public handleDragEnter(draggingNode: any, dropNode: any, ev: any) {
// console.log("tree drag enter: ", draggingNode.store.data);
}
}
</script>
<style lang="scss" scoped>
.custom-tree-node {
flex: 1;
display: flex;
text-align: left;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
.icon_location {
position: relative;
top: 4px;
}
}
.tree {
box-sizing: border-box;
padding: 12px 20px 0;
position: relative;
.header_row {
margin-bottom: 12px;
::v-deep {
.el-col-12 {
&:first-child {
.el-input {
width: 300px;
}
.el-input__inner {
height: 28px;
border-radius: 4px;
font-size: 12px;
&:focus {
border-color: #fe9817;
outline: 0;
}
}
.el-input__icon {
line-height: 28px;
}
}
&:last-child {
text-align: right;
}
}
}
}
::v-deep {
.el-tree {
overflow: hidden;
> div {
position: relative;
.el-tree-node__content {
height: 36px;
background: #edeef3;
border-radius: 6px;
line-height: 36px;
margin-bottom: 10px;
font-size: 14px;
font-weight: 500;
color: rgba(62, 62, 62, 1);
&:hover {
background: #e8eaf0;
}
}
&::before {
content: "";
width: 1px;
height: calc(100% - 62px);
background-color: #e8eaf0;
position: absolute;
display: inline-block;
left: 12px;
top: 36px;
}
.el-tree-node__children {
> div[role="treeitem"] {
position: relative;
.el-tree-node__content {
height: 36px;
background: #f4f5f8;
border-radius: 6px;
line-height: 36px;
margin-bottom: 8px;
width: calc(100% - 24px);
position: relative;
right: -24px;
padding-left: 0px !important;
font-size: 14px;
font-weight: 400;
color: rgba(62, 62, 62, 1);
&:hover {
background: #e8eaf0;
}
}
.el-tree-node__children {
.el-tree-node__content {
right: -48px;
width: calc(100% - 48px);
position: relative;
background: #f9fafb;
&:hover {
background: #e8eaf0;
}
&::before {
content: "";
width: 36px;
height: 1px;
background-color: #e8eaf0;
position: absolute;
display: inline-block;
left: -36px;
top: 17px;
}
}
}
&::before {
content: "";
width: 12px;
height: 1px;
background-color: #e8eaf0;
position: absolute;
display: inline-block;
left: 13px;
top: 17px;
}
}
}
}
.el-button {
width: 24px;
height: 24px;
background: rgba(255, 255, 255, 1);
border-radius: 50%;
padding: 0;
line-height: 28px;
}
.add,
.remove {
line-height: 20px;
line-height: 31px;
}
}
.el-dialog__wrapper {
.el-dialog {
background: rgba(243, 244, 247, 1);
border-radius: 6px;
}
.el-dialog__headerbtn .el-dialog__close {
color: #cacaca;
font-weight: 700;
font-size: 22px;
.el-dialog__title {
line-height: 24px;
font-size: 20px;
font-weight: 500;
color: rgba(62, 62, 62, 1);
}
}
.el-dialog__header {
padding: 18px 20px 12px 32px;
}
.el-dialog__body {
padding: 0 32px;
}
.el-dialog__footer {
padding: 20px 32px;
}
.treeForm {
.el-form-item__error {
padding-top: 0px;
top: -14px;
width: 100%;
display: inline-block;
text-align: right;
box-sizing: border-box;
padding-right: 12px;
}
.el-form-item {
margin-bottom: 12px;
background-color: #fff;
border-radius: 6px;
padding: 6px 0 0 12px;
box-sizing: border-box;
&:last-child {
margin-bottom: 0;
}
}
.el-form-item__label {
padding: 0;
font-size: 14px;
font-weight: 500;
color: rgba(62, 62, 62, 1);
line-height: 20px;
}
.el-form-item__content {
height: 38px;
line-height: 38px;
}
.el-input__inner {
border: none;
height: 18px;
line-height: 18px;
padding-left: 0;
}
}
}
}
}
</style>
网友评论