扫盲低代码-vue3实现低代码平台

15,107 阅读6分钟

低代码盛行多年了,刚好公司最近有项目需要做这块,从萌新小白,到实现程序,一步一步踩过了很多坑,今天就把我踩过的坑一一分享出来,带你扫盲如何实现低代码,今天我们就从0开始,使用vue+pinia实战低代码!

希望通过我的分享,你能收获到低代码是如何实现,工作的等一系列的问题,话不多说,直接上干货!

项目初始化

直接执行以下脚手架命令,生成一个新的项目模板,如有疑问可参考官网cn.vuejs.org/guide/quick…

npm init vue@latest

执行命令,一路选No,在✔ Add Pinia for state management? … No / Yes此条命令时,选择yes,如此项选择了no,后面需自己手动在安装下pinia。

进入项目目录执行

npm install 
npm run dev

此时如果你的项目没有问题,会打开一个本地服务,端口为5173,我们在浏览器直接输入localhost:5173,会打开一个页面,如下图,标志着你已经开启了一段新的旅程!

1.png

ok,我们删除一些不必要的默认文件保持整个项目的整洁,我们把components下所有组件都删除,把app.vue文件整理成如下:

<script setup></script>

<template>
  <div>起航吧,少年郎!</div>
</template>

<style scoped>
body {
  margin: 0;
  padding: 0;
}
</style>

此时查看浏览器你的页面应该变成了一句话,起航吧少年郎!!!

下面我们安装一些必要的npm包,首先我们安装css预处理器 sass和sass-loader,然后我们安装vue-draggable-next(ps:vue3兼容包,老包不兼容vue3),用来处理我们拖曳操作,好的让我们执行 npm install --save sass sass-loader vue-draggable-next,安装完成后,我们在app.vue文件中将样式部分修改为:

<style scoped lang="scss">
body {
  margin: 0;
  padding: 0;
}
</style>

查看页面没有报错,文字正常显示。

项目实现

这部分主要是一步一步的去实现项目

1:实现布局

首先我们在页面完成,页面布局,这部分大家自由去做就行了,做完以后大概就是这个样式

2.png 上部分是个页面头部,左侧放我们的组件,右侧放组件的设置属性,中间部分是渲染区。 我们安装一下element-plus和vue-router,后面我们实现预览和开发组件的时候会用到。 直接按照配置进行就可以: element-ui官网 vue-router官网

2:实现数据定义和拖曳区,渲染区

下面我们实现两个组件,这里我用比较简单的组件来实现操作,后续业务的话可以根据实际去调整! 首先我们在store文件中定义如下json,这里是我们的数据中心,我们所有的组件渲染,页面渲染都需要使用数据去完成

import { defineStore } from "pinia";
export const useLowCodeStore = defineStore({
  id: "lowcode", // id必填,且需要唯一
  state: () => {
    return {
      component: [
        {
          code: "button",
          set: "button-set",
          name: "按钮",
          id: 1, //组件编号不可重复且递增
          props: {
            type: "primary",
          },
        },
        {
          code: "input",
          set: "input-set",
          name: "输入框",
          id: 2, //组件编号不可重复且递增
          props: {
            value: "",
            placeholder: "请输入内容",
          },
        },
      ],
      preview: {},
      nowComponent: {},
    };
  },
  actions: {},
});

回到主页面,调用数据,渲染出对应的组件,并且实现拖曳,我们这里借用了vue-drag-next包帮助我们去做拖曳的事件操作。

下面我们完成可拖曳区代码: 可拖曳实现:

<VueDraggableNext
  v-model="store.component"
  :sort="false"
  :group="{
    name: 'components', //组名为icomponents
    pull: 'clone', //是否允许拖出当前组
    put: false, //是否允许拖入当前组
  }"
>
  <div v-for="item in store.component" :key="item.id" class="tem_btn">
    {{ item.name }}
  </div>
</VueDraggableNext>

渲染区实现

 <VueDraggableNext
  :v-model="store.preview"
  :group="{
    name: 'template',
    pull: false,
    put: true,
  }"
  ghost-class="ghost"
  class="canvas"
>
  <template v-for="item in store.preview" :key="item.id">
    <component
      :is="componentsList[item?.code]"
      :data="item"
    ></component>
  </template>
</VueDraggableNext>

好了到这里我们基本完成了主要的功能,如果你没有出现错误,那么页面应该是下面这个样子

222.png 接下来让我们去实现两个简单的组件

3:实现组件及属性设置

1:先来写个简单的button组件

在components文件夹下新建button文件,在button文件夹下面新建index.vue文件,这个文件是我们的组件,然后新建set.vue文件,这个文件是我们组件属性设置的文件。

我们先来分析下我们定义的数据,button组件接收了两个数据,一个type,用来渲染样式,一个value用来渲染值,我们根据这两个属性去写我们的组件,其代码如下:

// 组件代码
<template>
  <el-button :type="props.data.props.type">{{
    props.data.props.value
  }}</el-button>
</template>
<script setup>
import { watch } from "vue";
const props = defineProps({
  data: Object,
});
</script>
// 属性设置代码
<template>
  <el-button @click="() => set('primary')">默认</el-button>
  <el-button @click="() => set('success')">成功</el-button>
</template>
<script setup>
import { useLowCodeStore } from "../../store/lowcode";
const store = useLowCodeStore();
const set = (str) => {
  const value = store.nowComponent;
  value.props.type = str;
  store.updateNowComponents(value);
};
</script>

然后实现input组件

<template>
  <el-input
    :value="props.data.props.value"
    :placeholder="props.data.props.placeholder"
  ></el-input>
</template>
<script setup>
import { watch } from "vue";
const props = defineProps({
  data: Object,
});
</script>
// 属性设置代码
<template>
  设置value
  <el-input v-model="valueData"></el-input>
  设置placeholder
  <el-input v-model="valueData2"></el-input>
  <el-button @click="() => set()">确认</el-button>
</template>
<script setup>
import { ref } from "vue";
import { useLowCodeStore } from "../../store/lowcode";
const store = useLowCodeStore();
const valueData = ref("");
const valueData2 = ref("");
const set = (str) => {
  const value = store.nowComponent;
  value.props.value = valueData.value;
  value.props.placeholder = valueData2.value;
  store.updateNowComponents(value);
};
</script>

4:实现拖曳渲染

到这里我们基本易经完成了所有的开发工作,下面让我们把流程串起来就可以实现我们的低代码操作了 首先在store中添加如下代码,用来实现我们的业务逻辑

actions: {
    previewData(value) {
      this.preview.push(value);
    },
    nowComponentsData(value) {
      this.nowComponent = value;
    },
    updateNowComponents(value) {
      this.nowComponent = value;
      const index = this.preview.findIndex((item) => item.id === value.id);
      this.preview[index] = value;
    },
  },

在app.vue中添加拖曳事件和引入我们的组件,我这里直接给出完整的代码

<template>
  <div class="low_box">
    <div class="header">
      <div class="title">可视化系统</div>
      <div class="web">pc</div>
      <div class="btn">
        <el-button type="primary">预览</el-button
        ><el-button type="primary">确定</el-button>
      </div>
    </div>
    <div class="content">
      <div class="left">
        <div class="title">组件</div>
        <VueDraggableNext
          v-model="store.component"
          :sort="false"
          :group="{
            name: 'components', //组名为icomponents
            pull: 'clone', //是否允许拖出当前组
            put: false, //是否允许拖入当前组
          }"
          @end="onEnd"
        >
          <div v-for="item in store.component" :key="item.id" class="tem_btn">
            {{ item.name }}
          </div>
        </VueDraggableNext>
      </div>
      <div ref="targetContent" class="center">
        <VueDraggableNext
          :v-model="store.preview"
          :group="{
            name: 'template',
            pull: false,
            put: true,
          }"
          ghost-class="ghost"
          class="canvas"
        >
          <template v-for="item in store.preview" :key="item.id">
            <component
              :is="componentsList[item?.code]"
              :data="item"
            ></component>
          </template>
        </VueDraggableNext>
      </div>
      <div class="right">
        <component :is="componentsList[store?.nowComponent?.set]"></component>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { shallowRef, ref, onBeforeMount, watch } from "vue";
import { VueDraggableNext } from "vue-draggable-next";
import { useLowCodeStore } from "./store/lowcode";
import Button from "./components/Button/index.vue";
import Input from "./components/Input/index.vue";
import ButtonSet from "./components/Button/set.vue";
import InputSet from "./components/Input/set.vue";
const store = useLowCodeStore();
watch(store, (n, l) => {
  console.log("数据", n);
});
const componentsList = {
  button: Button,
  input: Input,
  "button-set": ButtonSet,
  "input-set": InputSet,
};

const onEnd = (obj: any) => {
  const { oldDraggableIndex } = obj;
  store.previewData(store.component[oldDraggableIndex]);
  store.nowComponentsData(store.component[oldDraggableIndex]);
};
</script>
<style lang="scss" scoped>
.low_box {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #f2f2f2;
  .header {
    height: 65px;
    background-color: #fff;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;
  }
  .content {
    display: flex;
    flex-direction: row;
    height: calc(100vh - 66px);
    .left {
      width: 300px;
      height: 100%;
      overflow: hidden;
      background-color: #fff;
      border-top: 1px solid #dddddd;

      .title {
        font-size: 16px;
        color: #333;
        line-height: 50px;
        width: calc(100% - 40px);
        margin-left: 20px;
        border-bottom: 1px solid #f2f2f2;
        clear: both;
      }

      .tem_btn {
        padding: 0px 10px;
        height: 30px;
        line-height: 30px;
        text-align: center;
        font-size: 14px;
        color: #666;
        background-color: #f2f2f2;
        border-radius: 4px;
        cursor: move;
        user-select: none;
        margin-top: 20px;
        float: left;
        margin-left: 20px;
      }
    }
    .center {
      flex: 1;
      padding: 20px;
      background-color: #f2f2f2;
      .canvas {
        background-color: #fff;
        width: 100%;
        height: 100%;
      }
      .ghost {
        background-color: #f00 !important;
      }
    }
    .right {
      width: 300px;
      height: 100%;
      overflow: hidden;
      background-color: #fff;
      border-top: 1px solid #dddddd;
    }
  }
}
</style>

好了,到这里已经完成实现了我们的拖曳操作,渲染,以及属性设置,如果没有错误结果应该如下图

333.png

页面实现

好了上面我们已经实现了低代码操作,那么我们生成的数据,怎么供给到别的项目消费呢?我这里使用多页面的方式去处理的这个问题,这种方式比较简单,能解决90%的业务了吧!

首先在根目录下新建preview文件夹,拷贝一份页面到preview中,修改vite.config.js的配置,增加多页面配置项目:

import { fileURLToPath, URL } from "node:url";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import * as path from "path";
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  plugins: [vue()],
  build: {
    cssCodeSplit: true, // 如果设置为false,整个项目中的所有 CSS 将被提取到一个 CSS 文件中
    sourcemap: false, // 构建后是否生成 source map 文件。如果为 true,将会创建一个独立的 source map 文件
    target: "modules", // 设置最终构建的浏览器兼容目标。默认值是一个 Vite 特有的值——'modules'  还可设置为 'es2015' 'es2016'等
    chunkSizeWarningLimit: 550, // 单位kb  打包后文件大小警告的限制 (文件大于此此值会出现警告)
    assetsInlineLimit: 4096, // 单位字节(1024等于1kb) 小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求。设置为 0 可以完全禁用此项。
    minify: "terser", // 'terser' 相对较慢,但大多数情况下构建后的文件体积更小。'esbuild' 最小化混淆更快但构建后的文件相对更大。
    terserOptions: {
      compress: {
        drop_console: true, // 生产环境去除console
        drop_debugger: true, // 生产环境去除debugger
      },
    },
    rollupOptions: {
      input: {
        main: path.resolve(__dirname, "index.html"),
        preview: path.resolve(__dirname, "preview/index.html"),
      },
    },
  },
  optimizeDeps: {
    include: [
      "@vueuse/core",
      "element-plus",
      "vant",
      "lodash-es",
      "vuedraggable",
    ],
  },
});

此时我们点击确定按钮会打开一个新页面,就是我们渲染出来的页面,这里我们可以使用iframe的方式引入页面,只需要于后端沟通好数据存储,就可以渲染出我们任意拖曳生成的页面。

到这里基本分享完了低代码的思路,我这里也是刚好最近公司需要做一个活动生成系统的项目,专门去看了下,根据自己的经验去实现了需求,可能还有更好的方式,这里抛转引玉了!!!

你那么帅,那么美,留个赞再走吧!

源码地址源码地址:gitee.com/SongTaoo/lo…