低代碼開發(fā)之vue.draggable的使用(進(jìn)階:組件化拖拽生成功能頁面)

效果展示:

20240126-161933.gif

前言:隨著各公司定制化需求的不斷攀升,公司對(duì)低代碼、組態(tài)化的開發(fā)需求日漸迫切。也許是研發(fā)任務(wù)節(jié)點(diǎn)將至,也許是為順應(yīng)時(shí)代潮流,我也是去學(xué)習(xí)并實(shí)踐了一番。如圖所示調(diào)研后是正式上線的一期組態(tài)化版本,主要應(yīng)對(duì)不同甲方的多站概覽頁面的定制化需求,此處為了脫敏,已將相關(guān)logo去掉,只保留了功能頁面。

功能簡(jiǎn)介:

左側(cè)組件區(qū)域特意做了選中態(tài),小眼睛預(yù)覽浮框態(tài)等交互,右側(cè)內(nèi)容區(qū)域支持增加、刪除、退出、重置、預(yù)覽 、保存、應(yīng)用等操作,以及組態(tài)化最重要的功能點(diǎn)--隨意拖拽換位,后期考慮增加屬性面板支持對(duì)拖拽進(jìn)來的組件進(jìn)行寬高、顏色等的二次編輯修飾。

具體實(shí)現(xiàn):

1、實(shí)現(xiàn)流程依據(jù)

通過json實(shí)現(xiàn),預(yù)先定義好描述組件的json,json包含了當(dāng)前組件數(shù)據(jù)和當(dāng)前組件的樣式屬性數(shù)據(jù)等,并通過組件生成器將將描述組件的json結(jié)合起來渲染出實(shí)際組件,當(dāng)修改樣式屬性時(shí),組件樣式同步更新;

示例json:

json:{
  fieldid:"",
  name:"Input",
  label:"單行文本",
  icon:"input01",
  placeholder:"請(qǐng)輸入",
  value:"",
  rules:{},
  style:{},//組件的樣式
  setting:{},//組件的其他屬性,比如:row:2

實(shí)現(xiàn)原理思維導(dǎo)圖:


image.png
2、實(shí)現(xiàn)詳情介紹

此需求基于開源的vue.draggable ^2.24.3,Vue項(xiàng)目首先需要去npm i -S vuedraggable下載vue.draggable相關(guān)依賴,并導(dǎo)入注冊(cè)draggable組件。如果是原生js直接CDN形式引用vuedraggable壓縮文件即可。建議沒看過我初階版本博客的小伙伴去看一下上篇文章再來,沿襲上篇代碼還是分組件區(qū)跟內(nèi)容區(qū)兩個(gè)group,group要名稱一致才可以建立拖拽關(guān)系,那么假設(shè)我們內(nèi)容區(qū)域group起名module,那么組件內(nèi)區(qū)域應(yīng)該也命module,結(jié)合展開面板組件使用那么json結(jié)構(gòu)如下:

componentsList:[
    {
        key: "1",
        name: "頂部指標(biāo)欄",
        group: { name: "module", pull: "clone", put: false },
        child: [
            {
                id: 1,
                type: 0,
                col: 24,
                name: "默認(rèn)樣式",
                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
            }
        ]
    },
    ......
]

組件區(qū)域代碼

<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>

內(nèi)容區(qū)json

contentList:[
    {
        id: 1,
        type: 0,
        col: 24,
        name: "默認(rèn)樣式",
        imgSrc: "TopIndicator",
        componentName: "TopIndicator",
        activeKey: false
    },
    {
        id: 4,
        type: 0,
        col: 24,
        name: "默認(rèn)樣式",
        imgSrc: "IncomeIndicators",
        componentName: "IncomeIndicators",
        activeKey: false
    },
  ......
]

內(nèi)容區(qū)代碼,實(shí)現(xiàn)思路是用:is="item.componentName"去對(duì)應(yīng)組件名注冊(cè)寫好的組件,這樣就可以在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>

小眼睛顯示預(yù)覽圖浮框不建議用hover去做,因?yàn)檫@里樣式涉及overflow: hidden;如下圖用鼠標(biāo)事件去做會(huì)更好。

methods:

// 鼠標(biāo)移入顯示浮態(tài)框
        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;
            // 判斷當(dāng)前鼠標(biāo)位置加上面板位置大于窗口寬度,表示超出不足以顯示,定位向上進(jìn)行定位
            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;
        },
        // 鼠標(biāo)移出隱藏浮態(tài)框
        panelHide() {
            this.panelFlag = false;
            this.panelName = "";
            this.panelImgSrc = undefined;
        },

最后再補(bǔ)上一個(gè)組件的刪除函數(shù),因?yàn)閮?nèi)容區(qū)刪除要聯(lián)動(dòng)把組件區(qū)選中態(tài)去除,所以這里的刪除里面要多一些邏輯控制

    del(el, idx) {
            this.contentList.splice(idx, 1);
            // 刪除 取消框選態(tài)
            this.componentsList.map((item) => {
                item.child.map((i) => {
                    if (i.id == el.id) {
                        i.activeKey = false;
                    }
                });
            });
        },

創(chuàng)作不易,點(diǎn)贊支持!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容