vue+element树形结构右键菜单

环境:vue-admin-template   vue 2.6.10 element-ui 2.7.0

1、自定义组件,文件位置:src/components/mentContext

<template>
    <!-- @mousedown.stop  阻止冒泡事件-->
    <!-- @contextmenu.prevent 阻止右键的默认事件 -->
    <div id="dropMenu"
         :style="style"
         style="display: block;"
         v-show="show"
         @mousedown.stop
         @contextmenu.prevent>
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name:"menuContext",
        data() {
            return {
                triggerShowFn: () => {
                },
                triggerHideFn: () => {
                },
                x: null,
                y: null,
                style: {},
                binded: false
            }
        },
        props: {
            target: null,
            show: Boolean
        },
        mounted() {
            this.bindEvents()
        },
        watch: {
            show(show) {
                if (show) {
                    this.bindHideEvents()
                } else {
                    this.unbindHideEvents()
                }
            },
            target(target) {
                this.bindEvents()
            }
        },
        methods: {
            // 初始化事件
            bindEvents() {
                this.$nextTick(() => {
                    var that = this
                    if (!this.target || this.binded) return
                    this.triggerShowFn = this.contextMenuHandler.bind(this)
                    this.target.addEventListener('contextmenu', this.triggerShowFn)
                    //this.binded = true
                })
            },
            // 取消绑定事件
            unbindEvents() {
                if (!this.target) return
                this.target.removeEventListener('contextmenu', this.triggerShowFn)
            },
            // 绑定隐藏菜单事件
            bindHideEvents() {
                this.triggerHideFn = this.clickDocumentHandler.bind(this)
                document.addEventListener('mousedown', this.triggerHideFn)
                document.addEventListener('mousewheel', this.triggerHideFn)
            },
            // 取消绑定隐藏菜单事件
            unbindHideEvents() {
                document.removeEventListener('mousedown', this.triggerHideFn)
                document.removeEventListener('mousewheel', this.triggerHideFn)
            },
            // 鼠标按压事件处理器
            clickDocumentHandler(e) {
                this.$emit('update:show', false) //隐藏
            },
            // 右键事件事件处理
            contextMenuHandler(e) {
                e.target.click()//这个是因为我需要获取tree节点的数据,所以我通过点击事件去获取数据
                this.x = e.clientX - 240
                this.y = e.clientY - 110
                this.layout()
                this.$emit('update:show', true)  //显示
                e.preventDefault()
                e.stopPropagation()

                this.$emit('targetElement', e.target) //我还要获取右键的DOM元素进行操作
            },
            // 布局
            layout() {
                this.style = {
                    left: this.x + 'px',
                    top: this.y + 'px'
                }
            }
        }
    }
</script>
<style lang="scss">
    #dropMenu {
        position: absolute;
        margin: 0;
        padding: 0;
         80px;
        height: auto;
        border: 1px solid #ccc;
        border-radius: 4px;

        ul {
            list-style: none;
            margin: 0;
            padding: 0;

            li {
                 100%;
                text-align: center;
                height: 30px;
                line-height: 30px;
                background: #eee;
                margin-bottom: 1px;
                cursor: pointer;
            }
        }
    }
</style>

2、调用,src/views 自己的项目目录里

<template>
    <div class="app-container">
        <h2>当前目录:</h2>
        <h4>{{ current_path }}</h4>

        <el-input
                placeholder="输入关键字进行过滤"
                v-model="filterText">
        </el-input>

        <!-- 树形结构 -->
        <el-tree
                id="modelTree"
                ref="tree"
                :data="treedata"
                :props="defaultProps"
                class="filter-tree"
                accordion
                node-key="id"
                :filter-node-method="filterNode"
        >
        </el-tree>

        <!-- 右键菜单 -->
        <menu-context
                :target="contextMenuTarget"
                :show="isShowDrop"
                @update:show="(show) => isShowDrop = show"
                @targetElement="getTargetElement">
            <ul>        <!--按需展示按钮-->
                <li v-show="isShowDelete" @click="deleteOne">删除</li>
                <li v-show="isShowAdd" @click="add">添加</li>
                <li v-show="isShowEdit" @click="editNode">编辑</li>
                <li v-show="isShowLink" @click="linkModels">关联</li>
                <li v-show="isShowMove" @click="move">转移</li>
            </ul>
        </menu-context>

        <el-dialog
                :title="textMap[dialogStatus]"
                :visible.sync="dialogFormVisible"
                :close-on-click-modal="false"
                width="70%"
                top="5vh"
                @open="resetForm('dataForm')"
        >
            <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px">
                <el-form-item v-if="false" label="ID" prop="id">
                    <el-input v-model="temp.id"/>
                </el-form-item>
                <el-form-item label="脚本路径" prop="path">
                    <p>{{ temp.path }}</p>
                </el-form-item>
                <el-form-item label="脚本名称" prop="name">
                    <el-input v-model="temp.name" placeholder="请输入具有辨识性的名称"/>
                </el-form-item>
                <el-form-item label="脚本内容" prop="filebody">
                    <codemirror v-model="temp.filebody" :options="cmOptions"/>
                </el-form-item>
            </el-form>
            <div slot="footer" class="dialog-footer">

                <el-button @click="dialogFormVisible = false">
                    取消
                </el-button>
                <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
                    提交
                </el-button>
            </div>
        </el-dialog>
    </div>
</template>

<script>
    import {Message} from 'element-ui'
    import * as LocalScriptsApi from '@/api/hostmanage'
    // 在线代码编辑器
    import {codemirror} from 'vue-codemirror'
    import 'codemirror/lib/codemirror.css'
    import 'codemirror/mode/yaml/yaml.js'
    import 'codemirror/theme/monokai.css'
    import request from "@/utils/request";
    import store from "@/store";
    import menuContext from '@/components/menuContext'

    let id = 1000;
    export default {
        components: {
            menuContext,
            codemirror
        },
        data() {
            return {
                // 树形节点参数
                filterText: '', // 过滤内容
                treedata: [],  // 属性节点内容
                current_path: '',
                defaultProps: {  //节点内容的key
                    children: 'children',
                    label: 'label'
                },
                // 右键菜单参数
                isShowDrop: false, //右键菜单的是否显示
                contextMenuTarget: null, //右键菜单的DOM对象
                thisformdata: {}, //右键菜单的点击的节点对象
                targetElement: {}, //右键点击的目标对象
                isShowDelete: true, //是否显示菜单中的删除按钮
                isShowEdit: true, //是否显示菜单中的 编辑按钮
                isShowAdd: true, //是否显示菜单中的 添加按钮
                isShowMove: true, //是否显示菜单中的 转移按钮
                isShowLink: true, //是否显示菜单中的 关联按钮

                // 以下为模态框的参数
                temp: {
                    id: undefined,
                    name: '',
                    filebody: '',
                    path:''
                },
                cmOptions: { // codemirror options
                    tabSize: 2,
                    mode: 'text/x-yaml',
                    theme: 'monokai',
                    lineNumbers: true,
                    line: true
                },
                dialogFormVisible: false,
                dialogStatus: '',
                textMap: {
                    update: '修改脚本',
                    create: '新增脚本'
                },
                rules: {
                    name: [{required: true, message: '请输入脚本名称', trigger: 'blur'}],
                    filebody: [{required: true, message: '请输入脚本内容', trigger: 'blur'}],
                },
            };
        },
        created() {
            this.fetchTreeData()
        },
        mounted() {
            this.isShowDrop = false;
            //dom加载完,进行目标dom的设置,直接在data中赋值,会找不到dom
            this.contextMenuTarget = document.querySelector('#modelTree')
        },
        watch: {
            filterText(val) {
                this.$refs.tree.filter(val);
            },
        },
        methods: {
            filterNode(value, data) { // 节点过滤
                if (!value) return true;
                return data.label.indexOf(value) !== -1;
            },
            fetchTreeData() { // 空间树数据
                LocalScriptsApi.queryscripts().then(resp => {
                    if (resp.status === false) {
                        Message({
                            message: response.message || '后端无返回说明信息',
                            type: 'error',
                            duration: 5 * 1000
                        })
                    } else {
                        this.treedata = resp.data.info
                        this.current_path = resp.data.current_path
                    }

                })
            },
            getTargetElement(v) {
                this.targetElement = v
            },
            deleteOne() {
                var that = this
                var nodeIds = [this.thisformdata.nodeId]
                this.$confirm('此操作将删除该节点及其子节点, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning',
                    center: true
                })
                        .then(() => {
                            deleteTreeNode(that.spaceTreeId, nodeIds).then(function (res) {
                                console.log('deleteTreeNode', res)
                                if (res.success) {
                                    that.$message('删除成功!')
                                    that.refreshSpaceTree()
                                }
                            })
                        })
                        .catch(() => {
                        })
            },
            add() {
                this.append(this.thisformdata)
                this.isShowDrop = false
            },
            editNode() {
                var data = this.thisformdata
                var str = "<input type='text' name='label' value='" + data.name + "'>"
                var e = window.event
                var text = this.targetElement
                text.innerHTML = str
                this.isShowDrop = false
                text.lastChild.onblur = function () {
                    data.name = text.lastChild.value
                    text.innerHTML = text.lastChild.value
                    var that = this
                    var space = {
                        nodeId: data.nodeId,
                        orgnazitionalTreeID: data.orgnazitionalTreeID,
                        parentNodeId: data.parentNodeId,
                        name: data.name,
                        nodeType: data.nodeType
                    }
                    //发送请求修改内容=>?有问题明天要修改
                    modifyTreeNode(data.orgnazitionalTreeID, space).then(function (res) {
                        console.log('modifyTreeNode', res)
                    })
                }
            },
            move() {
                if (this.thisformdata.nodeType == 0) {
                    this.$refs.modelLinkSpace.Open([this.thisformdata])
                }
                this.isShowDrop = false
            },
            linkModels() {
                this.linkModel({}, this.thisformdata)
            },
            handleNodeClick(data) {
                console.log('handleNodeClick', data)
                // !核心 : 节点数据被获得
                this.thisformdata = data
                //按需展示不同的按钮
                if (data.nodeType == 1 && !data.children) {
                    this.isShowAdd = true
                    this.isShowLink = true
                    this.isShowMove = true
                } else if (data.nodeType == 0) {
                    this.isShowAdd = false
                    this.isShowLink = false
                    this.isShowMove = true
                } else if (data.nodeType == 1 && data.children && data.children[0].nodeType == 0) {
                    this.isShowAdd = false
                    this.isShowLink = true
                    this.isShowMove = true
                } else if (data.nodeType == 1 && data.children && data.children[0].nodeType == 1) {
                    this.isShowAdd = true
                    this.isShowLink = false
                    this.isShowMove = false
                }
            },
            // 清空表单数据
            resetForm(formName) {
                this.$nextTick(() => {
                    this.$refs[formName].resetFields()
                })
            },
            append(data) {
                console.log(data)
                this.dialogStatus = 'create'
                this.dialogFormVisible = true
                // 自己定义逻辑
            },
        }
    };

</script>

<style lang="scss">

</style>

 参考资料:https://www.cnblogs.com/xuqp/p/11117636.html

原文地址:https://www.cnblogs.com/cherylgi/p/13307037.html