Vue3+TypeScript 自己搭建低代码平台【一篇文章精通系列】
一、创建组件相关样式1、创建data.json2、在Home.vue当中引入组件3、创建自定义组件(使用jsx)4、完善基本样式二、设置组件动态数据1、父组件向子组件传递值2、设置动态数据3、使用组件替换4、在左侧物料区使用该组件三、实现拖拽功能1、实现拖拽等一系列功能2、设置鼠标释放居中显示3、实现焦点聚焦到画布组件当中4、完善拖拽组件
Vue3+TypeScript 实现自己搭建低代码平台【一篇文章精通系列】
一、Vite创建Vue3 项目
这里已经有创建好的模板项目
执行使用即可
二、创建组件相关样式
1、创建data.json
{
"container":{
"width": "550px",
"height":"550px",
},
"blocks":{
}
}
2、在Home.vue当中引入组件
<script setup lang="ts">
import data from '../data.json';
import {ref,reactive} from "vue";
const state = ref(data)
</script>
<template >
<div class="demo">
</div>
</template>
<style scoped lang="sass">
</style>
3、创建自定义组件(使用jsx)
在vue3的工程中使用jsx
npm i @vitejs/plugin-vue-jsx -s
import vueJsx from '@vitejs/plugin-vue-jsx'
vueJsx(),
import {defineComponent} from "vue";
export default defineComponent({
setup(){
return ()=> <div>editor</div>
}
})
Home.vue当中引入组件
<script setup lang="ts">
import data from '@/data.json';
import Editor from "@/packages/editor";
import {ref,reactive} from "vue";
const state = ref(data)
</script>
<template >
<div class="demo">
<Editor></Editor>
</div>
</template>
<style scoped lang="sass">
</style>
运行测试
去掉默认样式
在Home.vue当中传递组件对应的值
<script setup lang="ts">
import data from '@/data.json';
import Editor from "@/packages/editor";
import {ref,reactive} from "vue";
const state = ref(data)
</script>
<template >
<div class="demo">
<Editor :data="state"></Editor>
</div>
</template>
<style scoped lang="sass">
</style>
在editor.jsx
当中接受对应的值
import {defineComponent} from "vue";
export default defineComponent({
props:{
data:{
type:Object
}
},
setup(props){
console.log(props.data)
return ()=> <div>editor</div>
}
})
运行测试
4、完善基本样式
设置页面周围空白间隙
#app{
position: fixed;
top:20px;
left:20px;
right:20px;
bottom:20px;
}
import {defineComponent} from "vue";
import './editor.scss'
export default defineComponent({
props:{
data:{
type:Object
}
},
setup(props){
console.log(props.data)
return ()=> <div class="editor">
<div class="editor-left">
左侧物料区
</div>
<div class="editor-top">
菜单栏
</div>
<div class="editor-right">
属性控制栏目
</div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas_content">
内容区域
</div>
</div>
</div>
</div>
}
})
在editor.jsx当中引入scss、
import './editor.scss'
.editor{
width: 100%;
height: 100%;
&-left,
&-right{
position: absolute;
width: 270px;
background: red;
top: 0;
bottom: 0;
}
&-left{
left: 0;
}
&-right{
right: 0;
}
&-top{
position: absolute;
right: 280px;
left: 280px;
height: 80px;
background: blue;
}
&-container{
padding:80px 270px 0;
height: 100%;
box-sizing: border-box;
&-canvas{
overflow: scroll;
height: 86vh;
&__content{
margin: 20px auto;
width:550px;
height:1550px;
background: yellow;
}
}
}
}
三、设置组件动态数据
1、父组件向子组件传递值
{
"container":{
"width": "550",
"height":"550"
},
"blocks":{
}
}
<script setup lang="ts">
import data from '@/data.json';
import Editor from "@/packages/editor";
import {ref,reactive} from "vue";
const state = ref(data)
</script>
<template >
<div class="demo">
<Editor v-model="state"></Editor>
</div>
</template>
<style scoped lang="sass">
</style>
import {defineComponent,computed} from "vue";
import './editor.scss'
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
setup(props){
const data = computed({
get(){
return props.modelValue
}
})
console.log(props.modelValue)
return ()=> <div class="editor">
<div class="editor-left"> 左侧物料区 </div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content">
内容区域
</div>
</div>
</div>
</div>
}
})
2、设置动态数据
删除固定数据
渲染样式
import {defineComponent,computed} from "vue";
import './editor.scss'
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
setup(props){
const data = computed({
get(){
return props.modelValue
}
})
console.log(props.modelValue)
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
return ()=> <div class="editor">
<div class="editor-left"> 左侧物料区 </div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value}>
内容区域
</div>
</div>
</div>
</div>
}
})
渲染内容
import {defineComponent} from "vue";
export default defineComponent({
})
在editor.jsx当中引入EditorBlock
import {defineComponent,computed} from "vue";
import './editor.scss'
import EditorBlock from "@/packages/editor-block.jsx";
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
setup(props){
const data = computed({
get(){
return props.modelValue
}
})
console.log(props.modelValue)
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
return ()=> <div class="editor">
<div class="editor-left"> 左侧物料区 </div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value}>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock block={ block }></EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
})
完善editor-block.jsx
import {defineComponent,computed} from "vue";
import editor from "./editor.jsx";
export default defineComponent({
props:{
block:{type:Object}
},
setup(props){
const blockStyles = computed(()=>({
top:`${props.block.top}px`,
left:`${props.block.left}px`,
zIndex:`${props.block.zIndex}px`,
}))
return ()=>{
return <div class="editor-block" style={blockStyles.value}>这是一个代码块</div>
}
}
})
.editor-block{
position: absolute;
}
完善显示数据
"blocks":[
{
"top": 100,
"left": 100,
"color": "red",
"zIndex": 1,
"key": "text"
},
{
"top": 200,
"left": 200,
"color": "red",
"zIndex": 1,
"key": "button"
},
{
"top": 300,
"left": 300,
"color": "red",
"zIndex": 1,
"key": "input"
}
]
3、使用组件替换
在项目当中已经引入了Element UI Plus
创建左侧要拖动的组件内容
import {ElButton,ElInput} from "element-plus";
// 列表区域可以显示所有的物料
// key对应的组件和映射关系
function createEditorConfig() {
const componentList = [];
const componentMap = {};
return {
componentList,
componentMap,
register:(component)=>{
componentList.push(component);
componentMap[component.key] = component
}
}
}
export let registerConfig = createEditorConfig()
console.log(registerConfig)
registerConfig.register({
label:'文本',
preview:()=>'预览文本',
render:()=>'渲染文本',
key:'text'
})
registerConfig.register({
label: '按钮',
preview: () => <ElButton>预览按钮</ElButton>,
render: () => <ElButton>渲染按钮</ElButton>,
key:'button'
})
registerConfig.register({
label: '按钮',
preview: () => <ElInput placeholder="预览输入框">预览按钮</ElInput>,
render: () => <ElInput placeholder="渲染输入框">渲染按钮</ElInput>,
key:'input'
})
在Home.vue
将editor-config
注册为全局组件
<script setup lang="ts">
import {registerConfig as config} from '@/utils/editor-config'
import data from '@/data.json';
import Editor from "@/packages/editor";
import {ref,reactive, provide} from "vue";
const state = ref(data)
provide('config',config);//(Vue3全局组件通信之provide / inject)
</script>
<template >
<div class="demo">
<Editor v-model="state"></Editor>
</div>
</template>
<style scoped lang="sass">
</style>
将组件渲染到对应的页面上
import {defineComponent,computed,inject} from "vue";
import editor from "./editor.jsx";
export default defineComponent({
props:{
block:{type:Object}
},
setup(props){
const blockStyles = computed(()=>({
top:`${props.block.top}px`,
left:`${props.block.left}px`,
zIndex:`${props.block.zIndex}px`,
}));
const config = inject('config');
console.log(config)
return ()=>{
//通过block的key属性直接获取对应的组件
const component = config.componentMap[props.block.key]
const RenderComponent = component.render();
return <div class="editor-block" style={blockStyles.value}>
{RenderComponent}
</div>
}
}
})
4、在左侧物料区使用该组件
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
const config = inject('config')
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 */}
{config.componentList.map(component =>(
<div className="editor-left-item">
<span>{component.label}</span>
<span>{component.preview()}</span>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value}>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock block={ block }></EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
完善上述样式
&-left{
left: 0;
&-item{
width: 250px;
margin: 20px auto;
display: flex;
justify-content: center;
align-items: center;
background: #FFF;
padding: 20px;
box-sizing: border-box;
cursor: move;
user-select: none;
min-height: 100px;
position: relative;
> span {
position: absolute;
top: 0;
left: 0;
background: #02d0f5;
color: white;
padding: 4px;
}
&::after{
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #888888;
opacity: 0.2;
}
}
}
四、实现拖拽功能
deep-copy 是一个深拷贝工具,可对任意数据进行深度拷贝,
包括 函数 function、正则 RegExp、Map、Set、Date、Array、URL 等等;
支持含循环引用关系的对象的拷贝,并且不会丢失成员的引用关系信息 和 类型信息,
支持扩展,可根据数据类型定制拷贝逻辑,也可指定拷贝深度;
所以,通过它可实现对任意类型的数据进行任意想要的拷贝;
解决拷贝(对象的实现序列化和反序列的问题)
npm install deepcopy
1、实现拖拽等一系列功能
import {defineComponent,computed,inject,ref} from "vue";
import './editor.scss'
import EditorBlock from "@/packages/editor-block.jsx";
import deepcopy from "deepcopy";
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
emits:['update:modelValue'], //触发的事件的名称
setup(props,ctx){
const data = computed({
get(){
return props.modelValue
},
set(newValue){
//emit的主要作用是子组件可以通过使用 emit,让父组件监听到自定义事件 ,以及传递的参数
ctx.emit('update:modelValue',deepcopy(newValue))
}
})
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
const config = inject('config'); //获取定义的全局组件
const containerRef = ref(null);
let currentComponent = null;
const dragenter = (e) =>{
e.dataTransfer.dropEffect = 'move'; //h5 拖动的图标
}
const dragover = (e) =>{
e.preventDefault();
}
const dragleave = (e) =>{
e.dataTransfer.dropEffect = 'none';
}
const drop = (e) =>{
let blocks = data.value.blocks; //内部已经渲染的组件
data.value = {
...data.value,blocks:[
...blocks,
{
top:e.offsetY,
left:e.offsetX,
zIndex:1,
key:currentComponent.key,
alignCenter:true //希望松手的时候可以居中
}
]
}
currentComponent = null;
}
const dragstart = (e,component) => {
//dragenter 进入元素中 添加一个移动的标识
containerRef.value.addEventListener('dragenter',dragenter);
//dragover 在目标元素当中经过 必须要阻止默认行为 否则不能触发drop
containerRef.value.addEventListener('dragover',dragover);
// dragleave 离开元素的时候 需要增加一个禁用标识
containerRef.value.addEventListener('dragleave',dragleave);
// drop 松手的时候 根据拖拽的组件 添加一个组件
containerRef.value.addEventListener('drop',drop);
currentComponent = component;
}
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 可以实现H5的拖拽 */}
{config.componentList.map(component =>(
<div
className="editor-left-item"
draggable
onDragstart={ e =>dragstart(e,component) }
>
<span>{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value} ref={containerRef}>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock block={ block }></EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
})
上述虽然实现了拖拽,但是在鼠标释放的时候没有对应的组件没有实现居中显示
2、设置鼠标释放居中显示
import {defineComponent,computed,inject,ref,onMounted} from "vue";
import editor from "./editor.jsx";
export default defineComponent({
props:{
//得到父组件传递的参数
block:{type:Object}
},
setup(props){
const blockStyles = computed(()=>({
top:`${props.block.top}px`,
left:`${props.block.left}px`,
zIndex:`${props.block.zIndex}px`,
}));
const config = inject('config');
const blockRef = ref(null);
onMounted(()=>{
let {offsetWidth,offsetHeight} = blockRef.value;
if(props.block.alignCenter){
//说明是拖拽松手的时候才渲染生成的页面,其他的默认渲染到页面上的内容不需要居中
props.block.left = props.block.left - offsetWidth / 2;
props.block.top = props.block.top - offsetHeight / 2;
props.block.alignCenter = false;
}
})
return ()=>{
//通过block的key属性直接获取对应的组件
const component = config.componentMap[props.block.key];
const RenderComponent = component.render();
return <div class="editor-block" style={blockStyles.value} ref={blockRef}>
{RenderComponent}
</div>
}
}
})
对上述代码进行重新封装
export function useMenuDragger(containerRef,data,currentComponent){
const dragenter = (e) =>{
e.dataTransfer.dropEffect = 'move'; //h5 拖动的图标
}
const dragover = (e) =>{
e.preventDefault();
}
const dragleave = (e) =>{
e.dataTransfer.dropEffect = 'none';
}
const drop = (e) =>{
let blocks = data.value.blocks; //内部已经渲染的组件
data.value = {
...data.value,blocks:[
...blocks,
{
top:e.offsetY,
left:e.offsetX,
zIndex:1,
key:currentComponent.key,
alignCenter:true //希望松手的时候可以居中
}
]
}
currentComponent = null;
}
const dragstart = (e,component) => {
//dragenter 进入元素中 添加一个移动的标识
containerRef.value.addEventListener('dragenter',dragenter);
//dragover 在目标元素当中经过 必须要阻止默认行为 否则不能触发drop
containerRef.value.addEventListener('dragover',dragover);
// dragleave 离开元素的时候 需要增加一个禁用标识
containerRef.value.addEventListener('dragleave',dragleave);
// drop 松手的时候 根据拖拽的组件 添加一个组件
containerRef.value.addEventListener('drop',drop);
currentComponent = component;
}
const dragend = (e) => {
//dragenter 进入元素中 添加一个移动的标识
containerRef.value.removeEventListener('dragenter',dragenter);
//dragover 在目标元素当中经过 必须要阻止默认行为 否则不能触发drop
containerRef.value.removeEventListener('dragover',dragover);
// dragleave 离开元素的时候 需要增加一个禁用标识
containerRef.value.removeEventListener('dragleave',dragleave);
// drop 松手的时候 根据拖拽的组件 添加一个组件
containerRef.value.removeEventListener('drop',drop);
}
return{
dragstart,
dragend
}
}
在editor.jsx当中引入并使用
import {defineComponent,computed,inject,ref} from "vue";
import './editor.scss'
import EditorBlock from "@/packages/editor-block.jsx";
import {useMenuDragger} from './useMenuDragger.js'
import deepcopy from "deepcopy";
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
emits:['update:modelValue'], //触发的事件的名称
setup(props,ctx){
const data = computed({
get(){
return props.modelValue
},
set(newValue){
//emit的主要作用是子组件可以通过使用 emit,让父组件监听到自定义事件 ,以及传递的参数
ctx.emit('update:modelValue',deepcopy(newValue))
}
})
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
const config = inject('config'); //获取定义的全局组件
const containerRef = ref(null);
let currentComponent = null;
const {dragstart,dragend} = useMenuDragger(containerRef,data,currentComponent)
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 可以实现H5的拖拽 */}
{config.componentList.map(component =>(
<div
className="editor-left-item"
draggable
onDragstart={ e =>dragstart(e,component) }
onDragend = { dragend }
>
<span>{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value} ref={containerRef}>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock block={ block }></EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
})
3、实现焦点聚焦到画布组件当中
import {defineComponent,computed,inject,ref} from "vue";
import './editor.scss'
import EditorBlock from "@/packages/editor-block.jsx";
import {useMenuDragger} from './useMenuDragger.js'
import deepcopy from "deepcopy";
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
emits:['update:modelValue'], //触发的事件的名称
setup(props,ctx){
const data = computed({
get(){
return props.modelValue
},
set(newValue){
//emit的主要作用是子组件可以通过使用 emit,让父组件监听到自定义事件 ,以及传递的参数
ctx.emit('update:modelValue',deepcopy(newValue))
}
})
//设置容器样式 computed设置属性值 根据依赖关系进行缓存的计算 实现动态数据的计算
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
const config = inject('config'); //获取定义的全局组件
const containerRef = ref(null);
let currentComponent = null;
//1、实现菜单拖拽功能
const {dragstart,dragend} = useMenuDragger(containerRef,data,currentComponent)
//2、实现获取焦点
//3.实现拖拽多个元素的功能
const clearBlockFocus = () =>{
data.value.blocks.forEach(block => block.focus = false);
}
const blockMousedown = (e,block)=>{
e.preventDefault();
e.stopPropagation();
//block 上我们规划一个属性 focus 获取焦点后就将 focus 变为true
if(!block.focus){
clearBlockFocus();
block.focus = true; //要清空其他人的 focus 的属性
}else{
block.focus = false;
}
}
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 可以实现H5的拖拽 */}
{config.componentList.map(component =>(
<div className="editor-left-item" draggable onDragstart={ e =>dragstart(e,component) } onDragend = { dragend }
>
<span>{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content" style={containerStyles.value} ref={containerRef}>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock
class = { block.focus ? 'editor-block-focus' : '' }
block={ block }
onMousedown = { (e)=>blockMousedown( e,block) }
>
</EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
})
.editor-block{
position: absolute;
&::after{
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
.editor-block-focus{
&::after{
border: 1px dashed red;
}
}
import {computed} from "vue";
export function useFocus(data,callback){ //获取那些元素没有选中
const focusData = computed(()=>{
let focus = [];
let unfocused = [];
data.value.blocks.forEach(block => (block.focus ? focus:unfocused).push(block))
return {focus,unfocused}
});
const clearBlockFocus = () =>{
data.value.blocks.forEach(block => block.focus = false);
}
const blockMousedown = (e,block)=>{
e.preventDefault();
e.stopPropagation();
//block 上我们规划一个属性 focus 获取焦点后就将 focus 变为true
if(e.shiftKey){
block.focus = !block.focus;
}else{
if(!block.focus){
clearBlockFocus();
block.focus = true; //要清空其他人的 focus 的属性
}else{
block.focus = false;
}
}
callback(e);
}
return {
blockMousedown,
focusData,
}
}
4、完善拖拽组件
import {defineComponent,computed,inject,ref} from "vue";
import './editor.scss'
import EditorBlock from "@/packages/editor-block.jsx";
import {useMenuDragger} from './useMenuDragger.js'
import {useFocus} from './useFocus.js'
import deepcopy from "deepcopy";
import {useBlockDragger} from "@/packages/useBlockDragger.js";
export default defineComponent({
props:{
modelValue:{
type:Object
}
},
emits:['update:modelValue'], //触发的事件的名称
setup(props,ctx){
const data = computed({
get(){
return props.modelValue;
},
set(newValue){
//emit的主要作用是子组件可以通过使用 emit,让父组件监听到自定义事件 ,以及传递的参数
ctx.emit('update:modelValue',deepcopy(newValue));
}
})
//设置容器样式 computed设置属性值 根据依赖关系进行缓存的计算 实现动态数据的计算
const containerStyles = computed( ()=>(
{
width: data.value.container.width+'px',
height: data.value.container.height+'px'
}
))
const config = inject('config'); //获取定义的全局组件
const containerRef = ref(null);
let currentComponent = null;
//1、实现菜单拖拽功能
const {dragstart,dragend} = useMenuDragger(containerRef,data,currentComponent);
let {blockMousedown,focusData,clearBlockFocus} = useFocus(data,(e)=>{
//获取焦点后进行拖拽
mousedown(e)
});
//2、实现组件拖拽
const { mousedown } = useBlockDragger(focusData);
//3.实现拖拽多个元素的功能
const containerMousedown = () => {
clearBlockFocus(); //点击容器让选中的失去焦点
}
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 可以实现H5的拖拽 */}
{config.componentList.map(component =>(
<div className="editor-left-item" draggable onDragstart={ e =>dragstart(e,component) } onDragend = { dragend }
>
<span>{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content"
style={containerStyles.value}
ref={containerRef}
onMousedown={ containerMousedown }
>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock
class = { block.focus ? 'editor-block-focus' : '' }
block={ block }
onMousedown = { (e)=>blockMousedown( e,block) }
>
</EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
})
//1、实现菜单拖拽功能
const {dragstart,dragend} = useMenuDragger(containerRef,data,currentComponent);
let {blockMousedown,focusData,clearBlockFocus} = useFocus(data,(e)=>{
//获取焦点后进行拖拽
mousedown(e)
});
//2、实现组件拖拽
const { mousedown } = useBlockDragger(focusData);
//3.实现拖拽多个元素的功能
const containerMousedown = () => {
clearBlockFocus(); //点击容器让选中的失去焦点
}
return ()=> <div class="editor">
<div class="editor-left">
{/* 根据注册列表 渲染对应的内容 可以实现H5的拖拽 */}
{config.componentList.map(component =>(
<div className="editor-left-item" draggable onDragstart={ e =>dragstart(e,component) } onDragend = { dragend }
>
<span>{component.label}</span>
<div>{component.preview()}</div>
</div>
))}
</div>
<div class="editor-top"> 菜单栏 </div>
<div class="editor-right"> 属性控制栏目 </div>
<div class="editor-container">
{/* 负责参数滚动条 */}
<div class="editor-container-canvas">
{/* 产生内容区域 */}
<div class="editor-container-canvas__content"
style={containerStyles.value}
ref={containerRef}
onMousedown={ containerMousedown }
>
{
(data.value.blocks.map( block=>(
<div>
<EditorBlock
class = { block.focus ? 'editor-block-focus' : '' }
block={ block }
onMousedown = { (e)=>blockMousedown( e,block) }
>
</EditorBlock>
</div>
)))
}
</div>
</div>
</div>
</div>
}
五、源代码地址:https://gitee.com/itbluebox/lowcode-learning-cases-vue3
更多推荐
所有评论(0)