低代码开发之vue.draggable的使用(进阶:组件化拖拽生成功能页面)

效果展示:

20240126-161933.gif

前言:随着各公司定制化需求的不断攀升,公司对低代码、组态化的开发需求日渐迫切。也许是研发任务节点将至,也许是为顺应时代潮流,我也是去学习并实践了一番。如图所示调研后是正式上线的一期组态化版本,主要应对不同甲方的多站概览页面的定制化需求,此处为了脱敏,已将相关logo去掉,只保留了功能页面。

功能简介:

左侧组件区域特意做了选中态,小眼睛预览浮框态等交互,右侧内容区域支持增加、删除、退出、重置、预览 、保存、应用等操作,以及组态化最重要的功能点--随意拖拽换位,后期考虑增加属性面板支持对拖拽进来的组件进行宽高、颜色等的二次编辑修饰。

具体实现:

1、实现流程依据

通过json实现,预先定义好描述组件的json,json包含了当前组件数据和当前组件的样式属性数据等,并通过组件生成器将将描述组件的json结合起来渲染出实际组件,当修改样式属性时,组件样式同步更新;

示例json:

json:{
  fieldid:"",
  name:"Input",
  label:"单行文本",
  icon:"input01",
  placeholder:"请输入",
  value:"",
  rules:{},
  style:{},//组件的样式
  setting:{},//组件的其他属性,比如:row:2

实现原理思维导图:


image.png
2、实现详情介绍

此需求基于开源的vue.draggable ^2.24.3,Vue项目首先需要去npm i -S vuedraggable下载vue.draggable相关依赖,并导入注册draggable组件。如果是原生js直接CDN形式引用vuedraggable压缩文件即可。建议没看过我初阶版本博客的小伙伴去看一下上篇文章再来,沿袭上篇代码还是分组件区跟内容区两个group,group要名称一致才可以建立拖拽关系,那么假设我们内容区域group起名module,那么组件内区域应该也命module,结合展开面板组件使用那么json结构如下:

componentsList:[
    {
        key: "1",
        name: "顶部指标栏",
        group: { name: "module", pull: "clone", put: false },
        child: [
            {
                id: 1,
                type: 0,
                col: 24,
                name: "默认样式",
                imgSrc: "TopIndicator",
                componentName: "TopIndicator",
                activeKey: true
            },
            {
                id: 2,
                type: 1,
                col: 24,
                name: "样式一",
                imgSrc: "TopIndicatorOne",
                componentName: "TopIndicator",
                activeKey: false
            },
            {
                id: 3,
                type: 2,
                col: 24,
                name: "样式二",
                imgSrc: "TopIndicatorTwo",
                componentName: "TopIndicator",
                activeKey: false
            }
        ]
    },
    ......
]

组件区域代码

<div class="left-components beautify-scroll">
                    <a-collapse v-model="activeKey">
                        <a-collapse-panel
                            :key="item.key"
                            :header="item.name"
                            v-for="item in componentsList"
                        >
                            <draggable
                                v-model="item.child"
                                draggable=".li"
                                v-bind="dragOptions"
                                :options="{ sort: false, group: item.group }"
                            >
                                <div
                                    v-for="d in item.child"
                                    :key="d.id + 'item'"
                                    :class="[
                                        'li',
                                        d.componentName,
                                        d.col == 12 ? 'w5' : null,
                                        d.activeKey ? 'active' : null
                                    ]"
                                >
                                    <div class="txt">{{ d.name }}</div>
                                    <img
                                        class="img"
                                        :src="
                                            require(`@/assets/images/configuration/${d.imgSrc}.png`)
                                        "
                                        alt=""
                                    />
                                    <div
                                        class="eyes"
                                        @mouseover="panelShow($event, d, item)"
                                        @mouseout="panelHide"
                                    ></div>
                                </div>
                                <div
                                    :class="[
                                        'amplifier-img-box',
                                        panelComponentName
                                    ]"
                                    :style="{ top: panelTop, left: panelLeft }"
                                    v-show="
                                        panelFlag && panelParentKey == item.key
                                    "
                                >
                                    <div class="panel-title">
                                        {{ panelName }}
                                    </div>
                                    <img
                                        class="panel-img"
                                        :src="panelImgSrc"
                                        alt=""
                                    />
                                </div>
                            </draggable>
                        </a-collapse-panel>
                    </a-collapse>
                </div>

内容区json

contentList:[
    {
        id: 1,
        type: 0,
        col: 24,
        name: "默认样式",
        imgSrc: "TopIndicator",
        componentName: "TopIndicator",
        activeKey: false
    },
    {
        id: 4,
        type: 0,
        col: 24,
        name: "默认样式",
        imgSrc: "IncomeIndicators",
        componentName: "IncomeIndicators",
        activeKey: false
    },
  ......
]

内容区代码,实现思路是用:is="item.componentName"去对应组件名注册写好的组件,这样就可以在draggable的渲染布局里面渲染具体组件

        <draggable
                    class="content beautify-scroll"
                    group="module"
                    v-bind="dragOptions"
                    :list="contentList"
                    @change="toChange"
                >
                    <div
                        v-for="(item, index) in contentList"
                        :key="'r' + index"
                        :class="[
                            'item',
                            item.componentName,
                            item.col == 12 ? 'w5' : null
                        ]"
                    >
                            <component
                                :key="'c' + item.componentName + item.type"
                                :is="item.componentName"
                                :isEdit="true"
                                :type="Number(item.type)"
                                @click.native="selectContentItem(item)"
                                :class="[
                                    item.id == contentActiveId ? 'active' : null
                                ]"
                            ></component>
                            <div
                                v-show="item.id == contentActiveId"
                                class="delbtn"
                                @click="del(item, index)"
                            ></div>
                    </div>
          </draggable>

小眼睛显示预览图浮框不建议用hover去做,因为这里样式涉及overflow: hidden;如下图用鼠标事件去做会更好。

methods:

// 鼠标移入显示浮态框
        panelShow(e, d, item) {
            console.log(e, d, item);
            this.panelName = item.name + " - " + d.name;
            this.panelComponentName = d.componentName;
            this.panelImgSrc = require(`@/assets/images/configuration/${d.imgSrc}.png`);
            // 获取窗口宽度
            let windowHeight =
                window.innerHeight ||
                document.documentElement.clientHeight ||
                document.body.clientHeight;
            // 判断当前鼠标位置加上面板位置大于窗口宽度,表示超出不足以显示,定位向上进行定位
            if (e.clientY + 276 > windowHeight) {
                this.panelTop = e.clientY - 296 + "px";
            } else {
                this.panelTop = e.clientY - 40 + "px";
            }

            this.panelLeft = e.clientX + 26 + "px";
            this.panelParentKey = item.key;
            this.panelFlag = true;
        },
        // 鼠标移出隐藏浮态框
        panelHide() {
            this.panelFlag = false;
            this.panelName = "";
            this.panelImgSrc = undefined;
        },

最后再补上一个组件的删除函数,因为内容区删除要联动把组件区选中态去除,所以这里的删除里面要多一些逻辑控制

    del(el, idx) {
            this.contentList.splice(idx, 1);
            // 删除 取消框选态
            this.componentsList.map((item) => {
                item.child.map((i) => {
                    if (i.id == el.id) {
                        i.activeKey = false;
                    }
                });
            });
        },

创作不易,点赞支持!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容