手把手教你怎么搭建一个低代码系统

11 篇文章 0 订阅

对应的视频教程:点击进入

前言

都2021年了,作为前端如果还不了解可视化布局的话,赶紧上车吧。下面就是我这次要分享的系统的效果图:
在这里插入图片描述

代码实现

一、初始化项目

1、创建文件夹、然后终端进入文件夹,并执行vue create my-app
2、启动项目

二、修改目录和搭建系统框架

1、去掉页面的默认内容
2、新建目录:view/components/templates
3、创建四个视图组件:headView.vue / leftView.vue /centerView.vue / rightView.vue
4、将视图组件引入App.vue中,布局调整

三、创建组件库

1、创建一个文本组件

2、导入并且全局注册组件

import textComp from './textComp'

const obj = {
    textComp
}

function install(Vue){
    Object.keys(obj).forEach(key=>{
        Vue.component(key,obj[key])
    })
}

const API = {
    version:'1.0',
    install
}

export default API

3、测试是否注册成功

四、创建处理器
1、创建对应组件的处理器[将组件的属性、数据、模板整理后对外暴露]
import getAttr from "../utils";
const handle = ()=>{
    // 属性
    let attribute = [{
        name:"宽度",
        type:'input',
        key:'width',
        value:100,
        placeholder:'请输入宽度'
    },{
        name:'高度',
        type:'input',
        key:'height',
        value:40,
        placeholder:'请输入高度'
    },{
        name:'字体大小',
        type:'input',
        key:'fontSize',
        value:15,
        placeholder:'请输入字体大小'
    },{
        name:'字体颜色',
        type:'color',
        key:'color',
        value:'red',
        placeholder:'请选择字体颜色'
    }]
    // 数据
    let data = {txt:'这里是默认文本'}
    // 模板
    let template = `<textComp ${getAttr(attribute)} data=${data.txt}></textComp>`
    return {attribute,data,template}
}
export default handle
2、整合处理器统一对外暴露
import textComp from './textComp'

let obj = {
    textComp
}
export default getComponent = (info)=>{
    let component = obj[info.type]()
    return component;
}
五、实现左侧组件菜单
1、定义菜单数据
const menuData = [{
    type:'textComp',
    name:'文本组件'
}]


export default menuData
2、实现菜单
<template>
 <div class="wrapper">
   <div v-for="(item,index) in menuData" class="menuItem">{{item.name}}</div>
 </div>
</template>

<script>
import menuData from './menuList'
export default {
    components: {

    },
    props: {

    },
    data() {
        return {
            menuData:menuData
        };
    },
    computed: {

    },
    created() {

    },
    mounted() {

    },
    methods: {

    },
    watch: {

    },
};
</script>

<style scoped lang="less">
.wrapper{
    width: 243px;
    padding:8px;
    .menuItem{
        background: #eee;
        width:100px;
        height:100px;
        float:left;
        margin:10px;
        text-align: center;
        line-height: 100px;
        cursor:pointer;
        
    }
}
</style>

六、实现拖拽组件到画布

1、让菜单中的组件可拖拽:
给要拖拽的组件加上属性:draggable=“true”

2、将拖拽的组件信息传递给画布:
(1)给菜单项加挂载组件类型和名称
:data-type=“item.type”
:data-name=“item.name”
(2)给菜单项加上拖拽事件
@dragstart=“dragStart”

dragStart(e) {
    let info = { 
        name: e.target.getAttribute("data-name"), 
        type: e.target.getAttribute("data-type"), };
        e.dataTransfer.setData("info", JSON.stringify(info)); },

3、画布接受传递的数据:
(1)定义两个事件:
@dragover=“dragover”
@drop=“drop”
(2)在拖到画布,阻止默认行为,也就是让拖拽的元素可以让画布接收到
e.preventDefault();
(3)获得传递的数据
e.preventDefault();
let info = JSON.parse(e.dataTransfer.getData(“info”));

4、实现组件的挂载
(1)定义一个数组装当前拖拽到页面的组件
components:[],
(2)给组件设置id
info.id = genId();
(3)获得组件
let component = getTemplate(info);
(4)设置组件的位置, 将拖拽的组件放入数组中

let widthItem = component.attribute.find((item) => item.key === "width"); 
let heightItem = component.attribute.find((item) => item.key === "height" );
const width = widthItem.value; 
const height = heightItem.value; 
let left = e.offsetX - width / 2; 
let top = e.offsetY - height / 2; 
if (left < 0) left = 0;
 if (top < 0) top = 0; 
const zIndex = this.zIndex++;
component.position = { offsetLeft: left, offsetTop: top, zIndex: zIndex, };
this.components.push(component);

(5)设置组件的挂载点

<div :id="item.info.id" v-for="(item, index) in components"></div>

(6)挂载组件

import Vue from "vue";
const mountComponent = function(component) {
// 整理data数据
let data = {}
let id = component.info.id
let widthItem = component.attribute.find((item)=>item.key==='width')
let heightItem = component.attribute.find((item)=>item.key==='height')
if(component.attribute){
        component.attribute.forEach((item,index,arr)=>{data[item.key] = item.value })
}

setTimeout(() => { 
let vm = new Vue({ 
        name: id.toString(),
        data() {return data; },
       template: component.template, 
       el: document.getElementById(id),
       mounted() {
              this.$el.id = id; 
              this.$el.style.position = "absolute"; 
              this.$el.style.cursor = "pointer"; 
              this.$el.style.left = `${component.position.offsetLeft}px`; 
              this.$el.style.top = `${component.position.offsetTop}px`; 
              this.$el.style.zIndex = `${component.position.zIndex}`; 
              this.$el.style.width = `${widthItem.value}px`; 
              this.$el.style.height = `${heightItem.value}px`; }});
       }, 200);
};

export default mountComponent; 
七、实现组件的自由移动

1、给画布组件添加选中效果

<!-- 组件选中效果 -->
    <div class="borderStyle" v-if="currComp" :style="setStyleOfBorder"></div>

处理选中组件:

  // 点击选中组件
    checkComp(e) {
      // 这里有个技巧,就是循环找到我们点击的组件,这里使用的正则匹配
      let reg = /\w{8}-\w{4}/;
      let node = e.target;
      let count = 0;
      // 还有node ,且node的id不是组件的id,就继续寻找
      while (node && !reg.test(node.id)) {
        count++;
        node = node.parentNode;
      }

      // 获得匹配到的组件的id

      if (node && node.id) {
        this.currComp = this.components.find((item) => {
          if (item.info.id === node.id) {
            return item;
          }
        });
      } else {
        this.currComp = null;
      }
    },
      
      
  设置选中样式:
    setStyleOfBorder() {
      this.currComp;
      let width = 0;
      let height = 0;
      let left = 0;
      let top = 0;
      let zIndex = 0 
      if (this.currComp) {
        this.currComp.attribute.forEach((item) => {
          if (item.key === "width") {
            width = item.value;
          }
          if (item.key === "height") {
            height = item.value;
          }
          left = this.currComp.position.left;
          top = this.currComp.position.top;
          zIndex = this.currComp.position.zIndex+1
        });
      } else {
      }
      return {
        width: `${width}px`,
        height: `${height}px`,
        left: `${left}px`,
        top: `${top}px`,
        zIndex:`${zIndex}px`
      };
    },
  

2、实现自由移动

//一、设置鼠标按下事件回调
@mousedown='mouseDownStart'


// 二、鼠标按下回调
mouseDownStart(e){
  // 记录鼠标按下瞬间的位置
  this.startPosition.x = e.clientX;
  this.startPosition.y = e.clientY;

  // 注册鼠标移动和鼠标松开的事件
  document.addEventListener('mousemove',this.mouseMove,true)
  document.addEventListener('mouseup',this.mouseUp,true)
},

  //三、 鼠标移动事件
  mouseMove(e){
    // 计算偏移量
    let offsetX = e.clientX - this.startPosition.x;
    let offsetY = e.clientY - this.startPosition.y;

    // 设置组件的位置
    let com = document.getElementById(this.currComp.info.id)
    Object.assign(com.style,{
      left:this.currComp.position.left + offsetX+'px',
      top:this.currComp.position.top + offsetY+'px'
    })

    // 设置选中框位置
    let borderComp = document.getElementById('borderBox')
    Object.assign(borderComp.style,{
      left:this.currComp.position.left + offsetX+'px',
      top:this.currComp.position.top + offsetY+'px'
    })

  },

 //四、鼠标松开事件
 mouseUp(e){
      // 移出事件
      document.removeEventListener('mousemove',this.mouseMove,true)
      document.removeEventListener('mouseup',this.mouseUp,true)
      // 更新组件的数据
      this.currComp.position.left = this.currComp.position.left +(e.clientX - this.startPosition.x )
      this.currComp.position.top = this.currComp.position.top+(e.clientY - this.startPosition.y )
 },

3、边界限制
逻辑: 画布宽度- 组件宽度 = 最大的x偏移
画布高度 - 组件高度 = 最大的y偏移
在这里插入图片描述

// 限制边界
    boundaryLimit(type, num, comp) {
      // 计算出边界值
      let canvas = document.getElementById("canvasBox");

      let canvasWidth = canvas.clientWidth;
      let canvasHeight = canvas.clientHeight;

      let compWidth = 0;
      let compHeight = 0;
      comp.attribute.forEach((item) => {
        if (item.key === "width") {
          compWidth = item.value;
        }
        if (item.key === "height") {
          compHeight = item.value;
        }
      });

      let maxX = canvasWidth - compWidth;
      let maxY = canvasHeight - compHeight;
      let lastNum = 0;
      if (type === "x") {
        if (num < 0) {
          lastNum = 0;
        } else if (num > maxX) {
          lastNum = maxX;
        } else {
          lastNum = num;
        }
      } else if (type === "y") {
        if (num < 0) {
          lastNum = 0;
        } else if (num > maxY) {
          lastNum = maxY;
        } else {
          lastNum = num;
        }
      }
      return lastNum
    },
八、实现右侧的样式和数据界面

在这里插入图片描述
在这里插入图片描述
1、样式界面的绘制

  <!-- 样式 -->
      <div v-if="tabIndex === 0">
        <div class="configItem" v-for="(item, index) in myCurrComp.attribute">
          <span class="label">{{ item.name }}:</span>
          <input
            v-if="item.type === 'input'"
            type="text"
            v-model="item.value"
          />
          <input
            v-if="item.type === 'color'"
            type="color"
            v-model="item.value"
          />
        </div>
      </div>

其中:myCurrComp的值是从外面传进来的
watch: {
    currCompData(val) {
      this.myCurrComp = val;
      this.dataList = val?JSON.stringify(val.data):''
    },
  },

2、数据界面的绘制

  <!-- 数据 -->
      <div v-if="tabIndex === 1">
          <input type="textarea" v-model="dataList">
      </div>

//dataList的数据也是从外面传进来的
watch: {
    currCompData(val) {
      this.myCurrComp = val;
      this.dataList = val?JSON.stringify(val.data):''
    },
  },
九、实现样式和数据的控制

1、实现样式的响应

//一、设置回调
@blur="updateComp"


 // 二、更新组件
    updateComp(){
      //  1、获取组件数据
      let component =  getComponent(this.myCurrComp.info,this.myCurrComp.attribute)
      // 2、设置组件的位置
      component.position = this.myCurrComp.position
      // 3、重新挂载组件
       mountComp(component)
    },

2、实现数据的响应

//给数据组件设置监听
@change="updateComp"

//将数据传递给组件【第三个参数就是数据】
 let component =  getComponent(this.myCurrComp.info,this.myCurrComp.attribute,this.dataList)
十、删除组件和新增一个组件测试

1、删除组件

1、设置右击回调 @contextmenu.prevent=“rightClick” 2、定义回调方法 rightClick(){
if(confirm(“确定要删除这个组件吗?”)) {
document.getElementById(this.currComp.info.id).remove()
}
this.currComp = null; }

2、新增图片组件

照着文本组件,复制就行了,很简单的

3、新增菜单切换组件

一样拷贝即可

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值