四、LogicFlow 自定义左侧菜单Menu

在这里插入图片描述

前文

这篇相对来讲就稍微平凡了一点,只要有前端的一些基础就能够轻松完成上图中左侧的菜单,但是为了能够让前后文章能够连贯起来,所以还是要厚着脸皮再写一篇。

有人可能要问了,为啥不将图中的功能完全实现呢,那是因为会直接导致篇幅过长,不利于阅读,思路不够清晰。

下一章节将实现 自定义节点

LogicFlow 介绍

LogicFlow 是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。在工作审批配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。

实现基础界面框架

在这里插入图片描述

<template>
    <div class="demo-wrp">
        <div class="demo-nemu"></div>
        <div class="demo-container">
            <div class="demo-control-wrp"></div>
            <div class="demo-canvas-wrp" ref="diagram"></div>
        </div>
    </div>
</template>

<script>
// 引入LogicFlow核心包
import LogicFlow from '@logicflow/core';
import '@logicflow/core/dist/style/index.css';

export default ({
    name: 'demo',
    data () {
        return {
            // 模拟数据
            mockData: {
                nodes: [
                    { id: "1", type: "rect", x: 100, y: 100, text: "节点1" },
                    { id: "2", type: "circle", x: 300, y: 100, text: "节点2" },
                ],
                edges: [
                    {
                    sourceNodeId: "1",
                    targetNodeId: "2",
                    type: "polyline",
                    text: "连线",
                    startPoint: {
                        x: 140,
                        y: 100,
                    },
                    endPoint: {
                        x: 250,
                        y: 100,
                    },
                    },
                ],
            }
        }
    },

    mounted () {

        // 创建实例
        const lf = new LogicFlow({
            container: this.$refs.diagram,
            // ... 其他的一些配置
        })

        /* 
            开启渲染
            如果不要模拟数据,直接使用 lf.render() 即可。
        **/
        lf.render(this.mockData);

        // 渲染到视图中心为止,否则在左上角显示
        lf.translateCenter();
    }
})

</script>


<style scoped lang="less">
// 定义操控区域高度
@HEADER_HEIGHT: 50px;
// 定义菜单和操控区域背景色
@BG_COLOR: #f3f3f3;

.demo-wrp{
    width: 100%;
    height: 100%;
    overflow: hidden;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;

    .demo-nemu{
        width: 200px;
        height: 100%;
        overflow-y: auto;
        background-color: @BG_COLOR;
        border-right: 1px solid #ddd;

        &::-webkit-scrollbar {
            width: 4px;
            background-color: #eeeeee;
        }

        &::-webkit-scrollbar-track {
            box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.01);
            border-radius: 0;
            background: rgba(0, 0, 0, 0);
        }

        &::-webkit-scrollbar-thumb {
            border-radius: 40px;
            box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
            background: rgba(0, 0, 0, 0);
        }
    }

    .demo-container{
        flex: 1;
        height: 100%;
        background-color: @BG_COLOR;


        .demo-control-wrp{
            width: 100%;
            height: @HEADER_HEIGHT;
            display: flex;
            align-items: center;
            justify-content: flex-end;
            border-bottom: 1px solid #ddd;
            padding: 0 20px;
        }
        .demo-canvas-wrp{
            width: 100%;
            height: calc(100% - @HEADER_HEIGHT);
        }
    }
}

</style>

实现上面的界面布局还是相当简单的,如果对样式感觉很困惑,那就只有多敲多练了。

另外根据如上代码在 vscode 中,样式部分可能会标红,不影响使用,代码也是生效的。

在这里插入图片描述

如果觉得看着不顺眼那也很简单,只需要在同级目录下创建一个 less 格式的文件,然后把样式拷贝进去,再将这个 less 文件引入到当前使用的组件中,如下所示:

<style lang="less" scoped>
	@import url('./index.less');
</style>

实现左侧菜单组件

基础的界面框架已经搭建好了,那么接下来就要实现左侧菜单了,为了让界面结构逻辑更加的清晰,建议是将菜单部分单独作为一个组件进行使用。

在 demo 目录下创建 components 目录,专门用来处理 LogicFlow 相关的组件,到时候如需其他项目也要使用的时候,直接拷贝过去就能投产,极大便利降低使用成本。

<template>
    <ul class="diagram-sidebar">
        <li v-for="groupItem in leftDataList" :key="groupItem.key">
            <h1 class="node-category-title">{{ groupItem.groupName }}</h1>
            <template v-if="Array.isArray(groupItem.nodes) && groupItem.nodes.length > 0">
                <div 
                    v-for="item in groupItem.nodes" 
                    :key="item.type" 
                    class="left-bar-item" 
                    :class="getClassFn"  
                    :style="getItemStyleFn(item)" 
                    @mousedown="dragInNode(item.type)"
                >
                    <img class="icon" :src="item.icon" alt="">
                    <p class="name" :style="`color:${item.borderColor};`">{{item.name}}</p>
                </div>
            </template>
        </li>
    </ul>
</template>

<script>
import { InitNodeData, CanNotEdit } from './NodeEnum';
export default ({
    name: 'leftMenu',
    props: {
        taskDetail: Object,
    },
    data() {
        return {
            leftDataList: [
                {
                    groupName: '分组-1',
                    key: 'marketing',
                    nodes: [
                        {
                            icon: require('../assets/lfCanvas/leftIcon/singleCrowd.svg'),
                            type: 'SINGLE_CROWD',
                            borderColor: 'rgba(71, 98, 254, 1)',
                            backgroundColor: 'rgba(71, 98, 254, 0.1)',
                            name: '单个信息',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/batchCrowd.svg'),
                            type: 'BATCH_CROWD',
                            borderColor: 'rgba(71, 98, 254, 1)',
                            backgroundColor: 'rgba(71, 98, 254, 0.1)',
                            name: '批量信息',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/ruleCrowd.svg'),
                            type: 'RULE_CROWD',
                            borderColor: 'rgba(71, 98, 254, 1)',
                            backgroundColor: 'rgba(71, 98, 254, 0.1)',
                            name: '信息规则',
                        },
                    ],
                },
                {
                    groupName: '分组-2',
                    key: 'target',
                    nodes: [
                        {
                            icon: require('../assets/lfCanvas/leftIcon/goalCrowd.svg'),
                            type: 'GOAL_CROWD',
                            borderColor: 'rgba(64, 158, 255, 1)',
                            backgroundColor: 'rgba(64, 158, 255, 0.1)',
                            name: '目标',
                        },
                    ],
                },
                {
                    groupName: '分组-3',
                    key: 'compute',
                    nodes: [
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-merge.svg'),
                            type: 'MERGE',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '合并',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-split.svg'),
                            type: 'SPLIT',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '拆分',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-unique.svg'),
                            type: 'UNIQUE',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '排重',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-intersection.svg'),
                            type: 'AND',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '交集',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-exclude.svg'),
                            type: 'EXCLUDE',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '排除',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-spread.svg'),
                            type: 'SPREAD',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '智能扩散',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/compute/icon-compute-sort.svg'),
                            type: 'SORT',
                            borderColor: 'rgba(255, 180, 30, 1)',
                            backgroundColor: 'rgba(255, 180, 30, 0.15)',
                            name: '高能排序',
                        },
                    ],
                },
                {
                    groupName: '分组-4',
                    key: 'touch',
                    nodes: [
                        {
                            icon: require('../assets/lfCanvas/leftIcon/dongdong.svg'),
                            type: 'DONGDONG',
                            borderColor: 'rgba(0, 200, 100, 1)',
                            backgroundColor: 'rgba(0, 200, 100, 0.1)',
                            name: '消息',
                        },
                        {
                            icon: require('../assets/lfCanvas/leftIcon/sms.svg'),
                            type: 'SMS',
                            borderColor: 'rgba(0, 200, 100, 1)',
                            backgroundColor: 'rgba(0, 200, 100, 0.1)',
                            name: '短信',
                        },
                    ],
                },
            ],
        }
    },
    computed: {

        // 拖拽和禁用拖拽按钮状态
        getClassFn(){
            return this.canDrag ? 'cursor-not-allowed' : 'cursor-pointer'
        },

        // 判断是否可被拖拽(先注释)
        canDrag() {
            // return this.canvasType === 'detail' || CanNotEdit.includes(this.taskDetail?.canvasStatus?.toString());
        },
        
        canvasType() {
            const QUERY_TYPE = this.$router.history.current.query.type;
            console.log('QUERY_TYPE::', QUERY_TYPE);
            return QUERY_TYPE;
        },
        
    },
    mounted() {

    },
    methods: {
        getMenuIconObj(item) {
            return {
                icon: item.icon,
                name: item.name
            }
        },
        // 动态设置样式
        getItemStyleFn(item){
            return `border: 1px solid ${item.borderColor};background-color:${item.backgroundColor};`
        },
		// 数据按下事件,主要是获取菜单的type类型
        dragInNode(type) {
            const that = this;
            console.log('dragInNode', that.canDrag, that.taskDetail);
            if (that.canDrag) return;

			
            // that.$emit('dragInNode', {
            //     type,
            //     properties: {
            //         ...InitNodeData[type],
            //     },
            // });
        },
    }
})
</script>

<style lang="less" scoped>
.diagram-sidebar {
    user-select: none;
    flex-basis: 200px;
    padding: 13px 25px;

    .node-category-title {
        font-family: PingFang SC;
        font-size: 14px;
        font-weight: 600;
        line-height: 22px;
        text-align: left;
        margin-bottom: 12px;
    }

    .cursor-pointer {
        cursor: pointer;
    }

    .cursor-not-allowed {
        cursor: not-allowed;
    }


    .left-bar-item{
        width: 140px;
        height: 38px;
        border-radius: 8px;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        gap: 8px;
        margin-bottom: 12px;
        padding-left: 24px;
        .icon{
            width: 16px;
            height: 16px;
        }
        .name{
            font-family: PingFang SC;
            font-size: 14px;
            font-weight: 400;
            line-height: 22px;
            text-align: left;
        }
    }
}
</style>

实现效果如下:
在这里插入图片描述

这样就实现了一个自定义的左侧菜单,这样可以根据自己的需要手动引入到对应的组件中进行使用,但是需要注意一点的是在 script标签 中引入的外部文件是否存在,地址是否正确。

将左侧菜单引入到demo组件中

<template>
    <div class="demo-wrp">
        <div class="demo-nemu">
            <LeftMenu :taskDetail="taskDetail" />
        </div>
        <div class="demo-container">
            <div class="demo-control-wrp"></div>
            <div class="demo-canvas-wrp" ref="diagram"></div>
        </div>
    </div>
</template>

<script>

// 引入LogicFlow核心包
import LogicFlow from '@logicflow/core';
import '@logicflow/core/dist/style/index.css';

// 引入组件
import LeftMenu from './components/LeftMenu';


export default ({
    name: 'demo',
    data () {
        return {
            // ...
            
       		taskDetail: {},
       		
            // ...
        }
    },

    components: {
        LeftMenu,
    },

    // 其他...
})

</script>


最后

关于自定义左侧菜单到这里也就结束了,只要会点 Vue 都能够看懂,一点都不难,所以也没啥好讲的。

下一篇将讲解如何将左侧的按钮拖拽到右侧的画布。

这一篇到此结束,拜拜。

在这里插入图片描述

要在uniapp中自定义左侧菜单栏,可以使用uni-ui组件库中的slide-view组件。 首先在页面中引入slide-view组件,然后在页面中定义一个slide-view标签,将菜单栏的内容放在slide-view标签内部。在菜单栏的内容中,可以使用uni-ui提供的list组件或者自定义的组件。 然后在页面的右侧内容区域中,也可以使用slide-view组件,将页面的内容放在slide-view标签内部。需要注意的是,右侧内容区域的slide-view组件需要设置宽度为100%减去菜单栏的宽度。 最后,使用slide-view组件提供的事件监听函数,可以在菜单栏和右侧内容区域之间进行切换。 以下是一个简单的示例代码: ``` <template> <view> <slide-view class="menu" :width="menuWidth" :height="contentHeight" :disable-drag="false" :show-menu="showMenu" @menu-click="toggleMenu"> <!-- 左侧菜单栏 --> <list> <cell>菜单项1</cell> <cell>菜单项2</cell> <cell>菜单项3</cell> </list> </slide-view> <slide-view class="content" :width="contentWidth" :height="contentHeight" :disable-drag="false" :show-menu="showMenu" @menu-click="toggleMenu"> <!-- 右侧内容区域 --> <view> <text>这是右侧内容区域</text> </view> </slide-view> </view> </template> <script> import { slideView, list, cell } from 'uni-ui' export default { components: { slideView, list, cell }, data() { return { showMenu: false, menuWidth: 200, contentWidth: 0, contentHeight: 0 } }, mounted() { // 获取右侧内容区域的宽度和高度 const systemInfo = uni.getSystemInfoSync() const contentWidth = systemInfo.windowWidth - this.menuWidth const contentHeight = systemInfo.windowHeight this.contentWidth = contentWidth this.contentHeight = contentHeight }, methods: { toggleMenu() { this.showMenu = !this.showMenu } } } </script> <style scoped> .menu { position: fixed; top: 0; left: 0; } .content { position: fixed; top: 0; left: 200px; } </style> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值