1
0
mirror of https://github.com/sahadev/vue-component-creater-ui.git synced 2025-06-04 20:14:05 +08:00

feat: 支持画布上的元素再次拖动

This commit is contained in:
Shangbin 2023-12-06 21:06:55 +08:00
parent 1c11a0c364
commit ca020049d0
21 changed files with 4654 additions and 13029 deletions

11476
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,7 @@
"lint-staged": "^9.5.0",
"sass": "^1.25.0",
"sass-loader": "^8.0.2",
"@vue/cli-service": "^5.0.8",
"vite": "^2.6.14"
},
"eslintConfig": {

View File

@ -220,7 +220,7 @@ export default {
const checkResult = brotherEleEnum().find(item => {
return item.name == this.componentName;
});
return checkResult && checkResult.length != 0;
return checkResult && false;
},
attributeList() {
const result = [];

View File

@ -66,18 +66,6 @@ export function initElementHoverAction(element) {
element.addEventListener('mouseover', event => {
event.stopPropagation();
checkIsInVaildElement(event).then((callbackInfo) => {
currentBroInfo = callbackInfo;
const rect = callbackInfo.target.getBoundingClientRect();
addBroIcon.style = `left:${rect.right + 0}px;top:${rect.top}px;`
}, () => {
setTimeout(() => {
if (!isInBroIcon) {
addBroIcon.style = "top:-100px;"
}
});
})
})
return function () {

View File

@ -6,8 +6,9 @@
*
*/
import { parseComponent } from 'vue-template-compiler/browser';
import { merge, insertPresetAttribute, getSplitTag, replaceRowID, updateLinkTree, findCodeElemNode, findRawVueInfo, removeAllID } from "@/utils/forCode";
import { getRawComponentContent, getRawComponentKey, isObject } from '@/utils/common';
import { merge, insertPresetAttribute, getSplitTag, replaceRowID, updateLinkTree, findCodeElemNode, findRawVueInfo } from "@/utils/forCode";
import { initElement } from '../utils/initRawComponent';
import { getRawComponentContent, getRawComponentKey, isObject, deleteNodeFromParent, isRawComponents, isActiveComponents } from '@/utils/common';
import { createNewCodeGenerator } from "@/libs/code-generator-factory";
import EventEmitter from 'eventemitter3'
import { cloneDeep } from 'lodash-es';
@ -164,6 +165,8 @@ export class MainPanelProvider {
event.stopPropagation();
this.markElement(element);
})
initElement(element);
})
this.initDropEvent();
@ -249,14 +252,20 @@ export class MainPanelProvider {
const newDropObj = JSON.parse(rawInfo);
// 插入预设属性
insertPresetAttribute(newDropObj);
if (isRawComponents(newDropObj)) {
// 使新拖入的代码与原来的做脱离
replaceRowID(newDropObj, '');
// 插入预设属性
insertPresetAttribute(newDropObj);
// 更新到一个Map上面维持引用由于render中统一做了处理所以这段代码是可以删除的 2021年02月04日11:59:10
updateLinkTree(newDropObj);
// 使新拖入的代码与原来的做脱离
replaceRowID(newDropObj, '');
// 更新到一个Map上面维持引用由于render中统一做了处理所以这段代码是可以删除的 2021年02月04日11:59:10
updateLinkTree(newDropObj);
} else if (isActiveComponents(newDropObj)) {
// 移动的情况
deleteNodeFromParent(newDropObj);
}
// 更新代码结构关系
const codeTargetElement = findCodeElemNode(this.currentPointDropInfo.target);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,7 +6,7 @@
<a-button lc-mark type="primary" lc_id="nAzBA7pWsh">Primary Button</a-button>
<a-button lc-mark lc_id="rMeh8dMkYC">Default Button</a-button>
<a-button lc-mark type="dashed" lc_id="5+qgpMthx2">Dashed Button</a-button>
<a-button lc-mark type="text" lc_id="UoFnE3nJv9">Text Button</a-button>
<a-button lc-mark text lc_id="UoFnE3nJv9">Text Button</a-button>
<a-button lc-mark type="link" lc_id="kIu/utVuLA">Link Button</a-button>
<a-button lc-mark type="primary" danger lc_id="BQ4AmOTe8D">Primary</a-button>
<a-button lc-mark danger lc_id="98eGo5RB7m">Default</a-button>

View File

@ -1,8 +1,8 @@
<template lc_id="Bb1LYS7hJm">
<div class="echart-class" lc_id="OT0KmxfA/e">
<div style="font-size: 12px;">
<div style="font-size: 12px">
Tips: Vcc使用的是vue-echarts因此实际使用时也需要将vue-echarts集成至项目中使用
另外由于渲染器本身的原因在将eChart拖入到面板之后<span style="color:red;">会有无法正常展示的问题</span>
另外由于渲染器本身的原因在将eChart拖入到面板之后<span style="color: red">会有无法正常展示的问题</span>
代码本身是没有问题的在预览模式下也可以正常渲染
</div>
<v-chart

View File

@ -29,8 +29,8 @@
</div>
<div lc_id="RdvVbD8tRt">
<div class="demonstration-element" lc_id="MCe1tz7lec">Button 文字按钮</div>
<el-button lc-mark type="text" lc_id="VzeBeEyMRU">文字按钮</el-button>
<el-button lc-mark type="text" disabled lc_id="gGBAHuLrE2">文字按钮</el-button>
<el-button lc-mark text lc_id="VzeBeEyMRU">文字按钮</el-button>
<el-button lc-mark text disabled lc_id="gGBAHuLrE2">文字按钮</el-button>
</div>
<div lc_id="fiJ0b+a2C7">
<div class="demonstration-element" lc_id="NadQOxXAQD">Button 按钮组</div>

View File

@ -62,28 +62,28 @@
<el-col :sm="12" :lg="6" lc_id="deKtOM/g2K">
<el-result icon="success" lc-mark title="成功提示" subtitle="请根据提示进行操作" lc_id="iF6n6EUcmn">
<template #extra lc_id="iJQp/8XeSP">
<el-button type="primary" size="medium" lc_id="UCFmR9EP+q">返回</el-button>
<el-button type="primary" size="default" lc_id="UCFmR9EP+q">返回</el-button>
</template>
</el-result>
</el-col>
<el-col :sm="12" :lg="6" lc_id="1smWMRRuZr">
<el-result icon="warning" lc-mark title="警告提示" subtitle="请根据提示进行操作" lc_id="NIEZCy3gG0">
<template #extra lc_id="c6Unj7eQIv">
<el-button type="primary" size="medium" lc_id="MDEtfbmEZd">返回</el-button>
<el-button type="primary" size="default" lc_id="MDEtfbmEZd">返回</el-button>
</template>
</el-result>
</el-col>
<el-col :sm="12" :lg="6" lc_id="2zio5J9ljq">
<el-result icon="error" lc-mark title="错误提示" subtitle="请根据提示进行操作" lc_id="kLASzKidTD">
<template #extra lc_id="rnIhJqKRjQ">
<el-button type="primary" size="medium" lc_id="MhrCBFBHlN">返回</el-button>
<el-button type="primary" size="default" lc_id="MhrCBFBHlN">返回</el-button>
</template>
</el-result>
</el-col>
<el-col :sm="12" :lg="6" lc_id="h7xNXozZ0s">
<el-result icon="info" lc-mark title="信息提示" subtitle="请根据提示进行操作" lc_id="aDXqAjXsST">
<template #extra lc_id="nHgp3LGSea">
<el-button type="primary" size="medium" lc_id="VURe3Zlhq0">返回</el-button>
<el-button type="primary" size="default" lc_id="VURe3Zlhq0">返回</el-button>
</template>
</el-result>
</el-col>

View File

@ -3,7 +3,7 @@
<div lc_id="tWzGb0zt3I">
<div class="demonstration-element" @click="dialogVisible = true" lc_id="cjFRJjmxCw">Dialog 对话框</div>
<div lc-mark lc_id="dIDt4KsNEb">
<el-button type="text" lc_id="cmG1+fI232">直接拖我看结果</el-button>
<el-button text lc_id="cmG1+fI232">直接拖我看结果</el-button>
<el-dialog title="提示" v-model="dialogVisible" width="30%" :before-close="handleClose" lc_id="W4ORswcE/n">
<span lc_id="8Sb2dnGlHq">这是一段信息</span>
<span footer class="dialog-footer" lc_id="To3nLHp/kA">

View File

@ -107,7 +107,7 @@
<template #header lc_id="fcNx1E9u+l">
<div class="clearfix" lc_id="JjiI6DiLa8">
<span lc_id="HyznGRyy1o">卡片名称</span>
<el-button style="float: right; padding: 3px 0" type="text" lc_id="2BV+GEiJPf">操作按钮</el-button>
<el-button style="float: right; padding: 3px 0" text lc_id="2BV+GEiJPf">操作按钮</el-button>
</div>
<div v-for="o in 4" :key="o" class="text item" lc_id="WrqZOeR62/">{{ "列表内容 " + o }}</div>
</template>

View File

@ -4,6 +4,7 @@
<lc-table></lc-table>
<lc-form ></lc-form>
<lc-image></lc-image>
<lc-menu></lc-menu>
<lc-form-base></lc-form-base>
<lc-dialog></lc-dialog>
<lc-icon ></lc-icon>
@ -23,6 +24,7 @@ import formBase from "./form-base";
import dialog from "./dialog";
import image from "./image";
import final from "./final";
import menu from './menu.vue';
import { deepLCEle } from "@/utils/initRawComponent";
@ -48,6 +50,7 @@ export default {
"lc-dialog": dialog,
"lc-image": image,
"lc-final": final,
"lc-menu": menu
},
};
</script>

View File

@ -0,0 +1,42 @@
<template lc_id="OmgSokKINq">
<div class="demonstration-element" lc_id="P3YgnZ+f9L">Menu 菜单</div>
<el-menu default-active="2" lc-mark lc_id="Ykd21cdEG5" active-text-color="#ffd04b" background-color="#545c64" style="width: 200px;" text-color="#fff">
<el-sub-menu index="1" lc_id="zZkAuMWuD3">
<template #title lc_id="z7nCiOPj+e">
<el-icon lc_id="DgdrgxdBou">
<location lc_id="g/AfKd/h31"></location>
</el-icon>
<span lc_id="CLxUy1/ICc">Navigator One</span>
</template>
<el-menu-item-group title="Group One" lc_id="bmv3H79LU6">
<el-menu-item index="1-1" lc_id="njnKKJBgKA">item one</el-menu-item>
<el-menu-item index="1-2" lc_id="sPEFzNrF/D">item two</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group Two" lc_id="/aJERvHpKE">
<el-menu-item index="1-3" lc_id="Dj6qsVo8uv">item three</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="1-4" lc_id="IhPwshAQ0i">
<template #title lc_id="7gOGvYliVP">item four</template>
<el-menu-item index="1-4-1" lc_id="RPZbUxYVzQ">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="2" lc_id="fpNmVPacmr">
<el-icon lc_id="3S93O9M6BZ">
<icon-menu lc_id="aC5w56Jfie"></icon-menu>
</el-icon>
<span lc_id="cMFe/tho9D">Navigator Two</span>
</el-menu-item>
<el-menu-item index="3" disabled lc_id="rslwNtVgKU">
<el-icon lc_id="MKD3UJ3Gqg">
<document lc_id="OsVpAj1mOT"></document>
</el-icon>
<span lc_id="70yWMx9X+D">Navigator Three</span>
</el-menu-item>
<el-menu-item index="4" lc_id="d4KudeL9g3">
<el-icon lc_id="Uv+alei7wG">
<setting lc_id="qhOEr9XarJ"></setting>
</el-icon>
<span lc_id="HH6bWzTkew">Navigator Four</span>
</el-menu-item>
</el-menu>
</template>

View File

@ -5,7 +5,7 @@
<button lc-mark lc_id="x3FZbZaIxW">Default</button>
<button lc-mark type="primary" lc_id="45rexSehQc">Primary</button>
<button lc-mark type="dashed" lc_id="fKbsFbKR2b">Dashed</button>
<button lc-mark type="text" lc_id="tFTgyHpo+y">Text</button>
<button lc-mark text lc_id="tFTgyHpo+y">Text</button>
<button lc-mark type="info" lc_id="hUGfSo7waH">Info</button>
<button lc-mark type="success" lc_id="BgKXbY5mGG">Success</button>
<button lc-mark type="warning" lc_id="X6QHL5F0XR">Warning</button>

View File

@ -14,7 +14,7 @@ glob(
{
cwd: componentsPath,
absolute: true,
ignore: ["**/element/index.vue", "**/vant/index.vue", "**/iview/index.vue", "**/antd/index.vue", "**/echart/index.vue"],
ignore: ["**/element/index.vue", "**/vant/index.vue", "**/iview/index.vue", "**/antd/index.vue", "**/echart/index.vue", "**/echart/chart.vue"],
},
function (er, files) {
console.info(`正在对${files.length}个文件进行编译...`);

View File

@ -2,65 +2,180 @@ import isEqual from "lodash-es/isEqual";
import { customAlphabet, nanoid } from 'nanoid';
export function getRawComponentKey(__rawVueInfo__) {
return Object.keys(__rawVueInfo__)[0];
return Object.keys(__rawVueInfo__)[0];
}
export function getRawComponentContent(__rawVueInfo__) {
return __rawVueInfo__[getRawComponentKey(__rawVueInfo__)];
return __rawVueInfo__[getRawComponentKey(__rawVueInfo__)];
}
/**
* 获得一个数据节点的父节点
* @param {*} __rawVueInfo__
* @returns
*/
export function findParentNode(__rawVueInfo__) {
const targetDom = findTargetDom(__rawVueInfo__);
if (targetDom) {
const parentDom = findParentDom(targetDom.parentNode);
return findVueInfo(parentDom);
} else {
throw new Error(`没有找到目标DOM节点数据节点信息:`, __rawVueInfo__);
}
}
/**
* 将一个节点从其节点中移除
* @param {*} __rawVueInfo__
*/
export function deleteNodeFromParent(__rawVueInfo__) {
const parentNode = findParentNode(__rawVueInfo__);
const children = getRawComponentContent(parentNode).__children;
const index = children.findIndex(item => isEquals(item, __rawVueInfo__));
children.splice(index, 1);
}
/**
* 获得一个节点对应的数据信息这个函数不负责向上递归查找
* @param {*} element
* @returns
*/
export function findVueInfo(element) {
if (element) {
const lcid = element.attributes.lc_id.nodeValue;
// 获取源代码结构
let rowCode = window.templateSourceMap[lcid];
if (!rowCode) {
// 如果不在templateSourceMap则可能当前的操作是在渲染面板上这部分数据存放在tree中
rowCode = window.tree[lcid];
}
return rowCode;
} else {
return null;
}
}
/**
* 是组件库的组件
* @param {*} __rawVueInfo__
* @returns
*/
export function isRawComponents(__rawVueInfo__) {
const lcid = getVueInfoLcid(__rawVueInfo__);
return !!window.templateSourceMap[lcid];
}
/**
* 是已经被拖入面板的组件
* @param {*} __rawVueInfo__
* @returns
*/
export function isActiveComponents(__rawVueInfo__) {
const lcid = getVueInfoLcid(__rawVueInfo__);
return !!window.tree[lcid];
}
/**
* 校验两个数据节点是否相等由于vue代理的存在用简单===相比的方式已经失效
* @param {*} o1
* @param {*} o2
*/
export function isEquals(o1, o2) {
if (o1 && o2) {
return getVueInfoLcid(o1) == getVueInfoLcid(o2);
} else {
return false;
}
}
/**
* 获得一个DOM节点的组件父DOM节点
* @param {*} parentNode 要传入parentDom
* @returns
*/
export function findParentDom(parentNode) {
if (parentNode.attributes && parentNode.attributes.lc_id) {
return parentNode;
} else if (parentNode.parentNode) {
return findParentDom(parentNode.parentNode);
} else {
return null;
}
}
/**
* 获得一个数据节点的lc_id属性值
* @param {*} __rawVueInfo__
* @returns
*/
export function getVueInfoLcid(__rawVueInfo__) {
const lcid = getRawComponentContent(__rawVueInfo__).lc_id;
return lcid;
}
/**
* 获得一个数据节点的DOM节点
* @param {*} __rawVueInfo__
* @returns
*/
export function findTargetDom(__rawVueInfo__) {
const targetDom = document.querySelector(`[lc_id="${getVueInfoLcid(__rawVueInfo__)}"]`);
return targetDom;
}
/**
* 比较两个对象是否完全相等
*/
export function compareTwoObjectIsEqual(o1, o2) {
return isEqual(o1, o2);
return isEqual(o1, o2);
}
export function isArray(arr) {
return Object.prototype.toString.apply(arr) === "[object Array]";
return Object.prototype.toString.apply(arr) === "[object Array]";
}
export function isObject(obj) {
return Object.prototype.toString.apply(obj) === "[object Object]";
return Object.prototype.toString.apply(obj) === "[object Object]";
}
/**
* @description 生成唯一ID
*/
export function createUniqueId() {
export function createUniqueId() {
const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10);
return nanoid();
}
/**
/**
* 遍历对象添加ID
* @param {*} jsonObj
*/
export function ergodic(jsonObj) {
if (jsonObj) {
for (const key in jsonObj) {
if (jsonObj.hasOwnProperty(key)) {
const element = jsonObj[key];
if (jsonObj) {
for (const key in jsonObj) {
if (jsonObj.hasOwnProperty(key)) {
const element = jsonObj[key];
if (isArray(element)) {
element.forEach((item) => {
if (isObject(item)) {
ergodic(item);
delete item.lc_id;
}
});
} else if (isObject(element)) {
ergodic(element);
} else {
}
}
}
if (isArray(element)) {
element.forEach((item) => {
if (isObject(item)) {
ergodic(item);
delete item.lc_id;
}
});
} else if (isObject(element)) {
ergodic(element);
} else {
}
}
}
// 添加ID
if (!jsonObj["lc_id"]) {
jsonObj["lc_id"] = createUniqueId();
}
}
// 添加ID
if (!jsonObj["lc_id"]) {
jsonObj["lc_id"] = createUniqueId();
}
}
}
/**

View File

@ -1,5 +1,5 @@
import { isObject, isArray, getRawComponentKey, createUniqueId } from '@/utils/common';
import { isObject, isArray, getRawComponentKey, createUniqueId, findParentDom, findVueInfo } from '@/utils/common';
import presetAttribute from "../libs/presetAttribute";
// 将预生成的ID替换否则当有两个组件挂在同一个树上时后一个会将前一个的属性覆盖
@ -35,17 +35,6 @@ export function updateLinkTree(codeObj) {
if (!window.tree) {
window.tree = {};
}
if (!window.treeWithID) {
let innerObj = {};
Object.defineProperty(window, 'treeWithID', {
get: function () {
return innerObj;
},
set: function (newValue) {
innerObj = newValue;
}
})
}
flatCodeObj(codeObj);
}
@ -54,15 +43,8 @@ export function updateLinkTree(codeObj) {
* 获取这个元素所对应的代码结构
*/
export function findRawVueInfo(element) {
// 这里对于form这样的怪物来说会导致指向错乱的问题 2020年12月07日14:39:30
// if (element.__rawVueInfo__) {
// return element.__rawVueInfo__;
// } else
if (window.tree[element.attributes.lc_id.nodeValue]) {
return window.tree[element.attributes.lc_id.nodeValue];
} else {
return findRawVueInfo(element.parentNode);
}
const parentDom = findParentDom(element);
return findVueInfo(parentDom);
}
export function flatCodeObj(codeObj) {
@ -90,12 +72,6 @@ export function flatCodeObj(codeObj) {
newObj.__proto__ = object.__proto__;
delete object.__key__;
window.tree[element] = newObj;
// 这个备份用于生成兄弟组件时获取原始代码所用需要保留ID的信息才可以实现DOM与代码对象的关联
const copy = Object.create(newObj.__proto__);
Object.assign(copy, JSON.parse(JSON.stringify(newObj)));
window.treeWithID[element] = copy;
} else if (key === "__children") {
object.__children.forEach((child) => {
child["__key__"] = key;
@ -160,13 +136,7 @@ export function removeAllID(codeObj) {
export function generateRawInfo(target) {
if (target.attributes.lc_id) {
// 获取源代码结构
const rowCode = window.templateSourceMap[target.attributes.lc_id.nodeValue];
return rowCode;
} else if (target.__vue__) {
// 代表这是一个Vue组件组成的元素这里的逻辑渐渐不再使用
const temp = findVueInfo(target.__vue__.$vnode);
return temp;
return findVueInfo(target);
} else {
// 这是一个普通的元素
const temp = {
@ -179,30 +149,6 @@ export function generateRawInfo(target) {
}
}
/**
* 通过运行时查找这个VNODE的关键信息
*/
export function findVueInfo(vnode) {
const obj = {};
if (vnode.text) {
obj["__text__"] = vnode.text;
} else if (vnode.componentOptions) {
const nodeBaseInfo = vnode.componentOptions;
if (nodeBaseInfo.tag) {
obj[nodeBaseInfo.tag] = nodeBaseInfo.propsData;
}
if (nodeBaseInfo.children && nodeBaseInfo.children.length > 0) {
for (let index = 0; index < nodeBaseInfo.children.length; index++) {
const child = nodeBaseInfo.children[index];
merge(obj[nodeBaseInfo.tag], findVueInfo(child));
}
}
}
return obj;
}
/**
* 这里需要将o2作为o1的子值 这里使用回调方法而不是用Promise的原因为需要严格保证外部的调用时序
*/
@ -248,11 +194,5 @@ export function insertPresetAttribute(vueInfo) {
* 寻找实际的可以代表整个复合组件Dom这是个核心方法根据某个元素查找实际的以Vue组件为单位的最小元素
*/
export function findCodeElemNode(element) {
if (element.attributes && element.attributes.lc_id) {
return element;
} else if (element.parentNode) {
return findCodeElemNode(element.parentNode);
} else {
return null;
}
return findParentDom(element);
}

View File

@ -8,7 +8,7 @@ const TOP = 1,
import { findCodeElemNode, findRawVueInfo } from "@/utils/forCode";
import { getRawComponentContent } from "@/utils/common";
export function initContainerForLine(targetElement, _currentPointer = () => {}) {
export function initContainerForLine(targetElement, _currentPointer = () => { }) {
const crossX = document.querySelector(".x");
const currentPointer = (...args) => {
@ -42,14 +42,16 @@ export function initContainerForLine(targetElement, _currentPointer = () => {})
if (parentElementNode) {
const parentRawInfo = findRawVueInfo(parentElementNode);
const attributes = getRawComponentContent(parentRawInfo);
if (attributes) {
const childrenArray = attributes.__children;
if (parentRawInfo) {
const attributes = getRawComponentContent(parentRawInfo);
if (attributes) {
const childrenArray = attributes.__children;
const index = childrenArray.findIndex((item) => {
return getRawComponentContent(item).lc_id == lc_id;
});
return index;
const index = childrenArray.findIndex((item) => {
return getRawComponentContent(item).lc_id == lc_id;
});
return index;
}
}
}
return -1;

5831
yarn.lock

File diff suppressed because it is too large Load Diff