1
0
mirror of https://github.com/sahadev/vue-component-creater-ui.git synced 2025-09-28 07:13:21 +08:00
This commit is contained in:
shangbin
2021-04-09 20:06:44 +08:00
commit e87ebafdcb
97 changed files with 33753 additions and 0 deletions

108
src/libs/bro-ele-config.js Normal file
View File

@@ -0,0 +1,108 @@
import { getRawComponentKey } from '@/utils/common'
import { replaceRowID, removeAllID, updateLinkTree, linkRelationShip } from '@/utils/forCode'
export function brotherEleEnum() {
return [{
name: 'el-option',
ifInDoc: false // 这个组件是否默认在Dom上展示如果不展示则添加后不需要更新Dom否则需要更新Dom
},
{
name: 'el-table-column',
ifInDoc: false
},
{
name: 'el-checkbox',
ifInDoc: true
},
{
name: 'el-radio',
ifInDoc: true
}];
}
function checkIsInVaildElement(event) {
return new Promise((resolve, reject) => {
const target = event.path.find(item => item.attributes.lc_id.nodeValue);
// 获取带有ID的原始结构这个ID用于生成一个不同ID的副本
const __rawVueInfo__ = window.treeWithID && window.treeWithID[target.attributes.lc_id.nodeValue];
if (target && __rawVueInfo__) {
const key = getRawComponentKey(__rawVueInfo__);
const result = brotherEleEnum().find(item => item.name === key && item.ifInDoc);
if (result) {
resolve({ target, __rawVueInfo__ });
} else {
reject();
}
}
})
}
export function copyBroCode(__rawVueInfo__){
// 初始化原型
let newDropObj = Object.create(__rawVueInfo__.__proto__);
// 拷贝内部属性
Object.assign(newDropObj, JSON.parse(JSON.stringify(__rawVueInfo__)));
newDropObj.__proto__.parentCodeNode.__children.push(newDropObj);
}
/**
* 这个方法是给控制区域使用的,增加兄弟组件时,控制区域会实时更新
* @param {*} element
*/
export function initElementHoverAction(element) {
let currentBroInfo = null;
const addBroIcon = document.querySelector('.add-bro');
let isInBroIcon = false;
addBroIcon.addEventListener('mouseover', event => {
isInBroIcon = true;
})
addBroIcon.addEventListener('mouseout', event => {
isInBroIcon = false;
})
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 () {
// 初始化原型
let newDropObj = Object.create(currentBroInfo.__rawVueInfo__.__proto__);
// 拷贝内部属性
Object.assign(newDropObj, JSON.parse(JSON.stringify(currentBroInfo.__rawVueInfo__)));
// 有__key__键可以使在通过updateLinkTree更新结构时内部的原型指向外部的原型
newDropObj.__key__ = "__children";
// 使新拖入的代码与原来的做脱离
const newHtmlCode = replaceRowID(newDropObj, currentBroInfo.target.outerHTML);
// 这里不能是任意的target必须是已存在代码树有引用链的节点
currentBroInfo.target.parentNode.insertAdjacentHTML("beforeend", newHtmlCode);
newDropObj.__proto__.parentCodeNode.__children.push(newDropObj);
// 将所有子节点指向父节点
linkRelationShip(newDropObj);
// 更新到一个tree上面维持引用
updateLinkTree(newDropObj);
// 删除所有的ID
removeAllID(newDropObj);
}
}

View File

@@ -0,0 +1,600 @@
'use strict';
//该文件会遍历Object获取关键的class,事件,data, 最终拼装为一个完整的SFC文件
Object.defineProperty(exports, '__esModule', { value: true });
// 导出组件模板文件
function vueTemplate () {
return `
<template> <!--在此自动生成--> </template>
<script>
export default {
props: [],
components: {},
data() {
return {
// 在此自动生成
// $datas
};
},
watch: {},
computed: {},
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
destoryed() {},
methods: {
// 在此自动生成
request() {
// 网络请求,可选
},
// $eventMethods
},
fillter: {},
};
</script>
<style scoped>
/* 在此自动生成 */
/** $stylesTemplate */
</style>
`;
}
// 生成一个方法
function generateFunction(functionName) {
return `${functionName}(){}`;
}
// 生成一个class
function generateClass(className) {
return `.${className}{}`;
}
// 生成一个键值对
function generateData(dataName) {
return `${dataName}:''`;
}
// 合成方法集
function convertMethods(set) {
const methodsStr = [...set].map(generateFunction);
return methodsStr.join(",\n");
}
// 合成style集
function convertStyles(set) {
const classStr = [...set].map(generateClass);
return classStr.join("\n");
}
// 合成data集
function convertDatas(set, options) {
let dataStr = [...set].map(generateData);
// 回调外部,使外部作用最后结果
if (options.convertDataResult) {
dataStr = options.convertDataResult(dataStr);
}
return dataStr.join(",\n");
}
// 从模板中替换方法
function replaceMethods(template, set) {
return template.replace("// $eventMethods", convertMethods(set));
}
// 从模板中替换样式
function replaceStyles(template, set) {
return template.replace("/** $stylesTemplate */", convertStyles(set));
}
// 从模板中替换样式
function replaceDatas(template, set, options) {
const defaultCode = convertDatas(set, options);
return template.replace("// $datas", defaultCode);
}
// const fakeCall = function(a) {return a;};
// const fakeCallNoReturn = function() {};
function buildOptions(options, defaultOptions, props) {
var newOptions = {};
if (!options) {
return defaultOptions; //if there are not options
}
for (let i = 0; i < props.length; i++) {
if (options[props[i]] !== undefined) {
newOptions[props[i]] = options[props[i]];
} else {
newOptions[props[i]] = defaultOptions[props[i]];
}
}
return newOptions;
}
const defaultOptions = {
attributeNamePrefix: '@_',
attrNodeName: false,
textNodeName: '#text',
ignoreAttributes: true,
cdataTagName: false,
cdataPositionChar: '\\c',
format: true,
indentBy: ' ',
supressEmptyNode: false,
tagValueProcessor: function (a) {
return a;
},
attrValueProcessor: function (a) {
return a;
},
attributeProtectArray: [] // 哪些属性的值为''但需要渲染出来默认如果value为''就不生成key=value只生成key
};
const props = [
'attributeNamePrefix',
'attrNodeName',
'textNodeName',
'ignoreAttributes',
'cdataTagName',
'cdataPositionChar',
'format',
'indentBy',
'supressEmptyNode',
'tagValueProcessor',
'attrValueProcessor',
'attributeProtectArray'
];
function Parser(options) {
this.options = buildOptions(options, defaultOptions, props);
if (this.options.ignoreAttributes || this.options.attrNodeName) {
this.isAttribute = function (/*a*/) {
return false;
};
} else {
this.attrPrefixLen = this.options.attributeNamePrefix.length;
this.isAttribute = isAttribute;
}
if (this.options.cdataTagName) {
this.isCDATA = isCDATA;
} else {
this.isCDATA = function (/*a*/) {
return false;
};
}
this.replaceCDATAstr = replaceCDATAstr;
this.replaceCDATAarr = replaceCDATAarr;
if (this.options.format) {
this.indentate = indentate;
this.tagEndChar = '>\n';
this.newLine = '\n';
} else {
this.indentate = function () {
return '';
};
this.tagEndChar = '>';
this.newLine = '';
}
if (this.options.supressEmptyNode) {
this.buildTextNode = buildEmptyTextNode;
this.buildObjNode = buildEmptyObjNode;
} else {
this.buildTextNode = buildTextValNode;
this.buildObjNode = buildObjectNode;
}
this.buildTextValNode = buildTextValNode;
this.buildObjectNode = buildObjectNode;
}
Parser.prototype.parse = function (jObj) {
return this.j2x(jObj, 0).val;
};
Parser.prototype.j2x = function (jObj, level) {
let attrStr = '';
let val = '';
const keys = Object.keys(jObj);
const len = keys.length;
for (let i = 0; i < len; i++) {
const key = keys[i];
if (typeof jObj[key] === 'undefined') ; else if (jObj[key] === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (jObj[key] instanceof Date) {
val += this.buildTextNode(jObj[key], key, '', level);
} else if (key === '__children'){ // 生成子节点
const item = jObj[key];
if(item instanceof Array){
item.forEach(i =>{
const result = this.j2x(i, level + 1);
val += result.val;
});
} else
if (typeof item === 'object') {
console.info(`不应该出现的意外`);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
else if (typeof jObj[key] !== 'object') {
//premitive type
const attr = this.isAttribute(key);
if (key === '__text__') {
val = jObj[key] + val; // 2020年12月14日19:35:54 文本内容通常在子节点之前
continue;
}
if (attr) {
if (typeof jObj[key] === "boolean" && jObj[key]) {
attrStr += ` ${key} `;
} else if(jObj[key] || this.options.attributeProtectArray.includes(key)){
attrStr += ' ' + key + '="' + this.options.attrValueProcessor('' + jObj[key]) + '"';
} else {
attrStr += ' ' + key;
}
} else if (this.isCDATA(key)) {
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAstr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAstr('', jObj[key]);
}
} else {
//tag value
if (key === this.options.textNodeName) {
if (jObj[this.options.cdataTagName]) ; else {
val += this.options.tagValueProcessor('' + jObj[key]);
}
} else {
val += this.buildTextNode(jObj[key], key, '', level);
}
}
}
else if (Array.isArray(jObj[key])) {
//repeated nodes
if (this.isCDATA(key)) {
val += this.indentate(level);
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAarr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAarr('', jObj[key]);
}
} else {
//nested nodes
const arrLen = jObj[key].length;
for (let j = 0; j < arrLen; j++) {
const item = jObj[key][j];
if (typeof item === 'undefined') ; else if (item === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (typeof item === 'object') {
const result = this.j2x(item, level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
}
} else {
//nested node
if (this.options.attrNodeName && key === this.options.attrNodeName) {
const Ks = Object.keys(jObj[key]);
const L = Ks.length;
for (let j = 0; j < L; j++) {
attrStr += ' ' + Ks[j] + '="' + this.options.attrValueProcessor('' + jObj[key][Ks[j]]) + '"';
}
} else {
const result = this.j2x(jObj[key], level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
}
}
}
return { attrStr: attrStr, val: val };
};
function replaceCDATAstr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata + ']]' + this.tagEndChar;
} else {
return str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata + ']]' + this.tagEndChar);
}
}
function replaceCDATAarr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata.join(']]><![CDATA[') + ']]' + this.tagEndChar;
} else {
for (let v in cdata) {
str = str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata[v] + ']]>');
}
return str + this.newLine;
}
}
function buildObjectNode(val, key, attrStr, level) {
if (attrStr && !val.includes('<')) {
if (key === "img" || key === "input") {
return (this.indentate(level) + '<' + key + attrStr + '/>');
}
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
val +
//+ this.newLine
// + this.indentate(level)
'</' +
key +
this.tagEndChar
);
} else {
return (
this.indentate(level) +
'<' +
key +
attrStr +
this.tagEndChar +
val +
//+ this.newLine
this.indentate(level) +
'</' +
key +
this.tagEndChar
);
}
}
function buildEmptyObjNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildObjectNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
//+ this.newLine
}
}
function buildTextValNode(val, key, attrStr, level) {
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
this.options.tagValueProcessor(val) +
'</' +
key +
this.tagEndChar
);
}
function buildEmptyTextNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildTextValNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
}
}
function indentate(level) {
return this.options.indentBy.repeat(level);
}
function isAttribute(name /*, options*/) {
return true;
}
function isCDATA(name) {
return name === this.options.cdataTagName;
}
//formatting
//indentation
//\n after each closing or self closing tag
// 类定义放入其中
let classSet = new Set();
// 事件放入其中
let methodSet = new Set();
// 数据引用放入其中
let dataSet = new Set();
const rawAdd = Set.prototype.add;
Set.prototype.add = function (value) {
if (typeof value === "string" && checkKeyword(value))
rawAdd.apply(this, arguments);
};
// 解析后的Json对象
let jsonObj = null;
function checkKeyword(value) {
return value != "true" && value != "false";
}
function clearDataSet() {
classSet.clear();
methodSet.clear();
dataSet.clear();
}
/**
* 直接输入Json文本
* @param {*} json
*/
function outputVueCode(json, options = {}) {
jsonObj = JSON.parse(json);
return outputVueCodeWithJsonObj(jsonObj, options);
}
/**
* 输入Json对象
* @param {*} jsonObj
*/
function outputVueCodeWithJsonObj(_jsonObj, options = {}) {
jsonObj = _jsonObj;
parseJson(_jsonObj);
// 对集合进行排序
dataSet = sort(dataSet);
methodSet = sort(methodSet);
classSet = sort(classSet);
// 生成执行结果
return generateResult(options);
}
function sort(set) {
return new Set(Array.from(set).sort());
}
// 递归解析Json
function parseJson(json) {
for (const key in json) {
if (json.hasOwnProperty(key)) {
const value = json[key];
if (value instanceof Array) {
value.forEach((item) => parseJson(item));
} else if (value instanceof Object) {
parseJson(value);
} else {
deliveryResult(key, value);
}
}
}
}
// 将所有需要替换的内容通过装饰器逐步替换
function replaceKeyInfo(options) {
return replaceStyles(
replaceDatas(
replaceMethods(replaceHtmlTemplate(getVueTemplate()), methodSet),
dataSet,
options
),
classSet
);
}
function replaceHtmlTemplate(template) {
const defaultOptions = {
attributeNamePrefix: "@_",
attrNodeName: false, //default is false
textNodeName: "#text",
ignoreAttributes: false,
cdataTagName: "__cdata", //default is false
cdataPositionChar: "\\c",
format: true,
indentBy: " ",
supressEmptyNode: false,
attributeProtectArray: [] // 哪些属性的值为''但需要渲染出来默认如果value为''就不生成key=value只生成key
};
const parser = new Parser(defaultOptions);
// 只面向代码生成使用故jsonObj.template不能变更2020年12月15日16:04:28
const xml = parser.parse(jsonObj.template);
return template.replace("<!--在此自动生成-->", xml);
}
// 分发解析结果
function deliveryResult(key, value) {
if (key === "class") {
const classes = value.split(" ");
classes.forEach((item) => {
// 处理多个空字符串
if (!item) return;
classSet.add(item);
});
} else if (/^v-on/g.test(key) || /^@/g.test(key)) {
// 匹配@,v-on
let expresionArray = null;
if (checkIsVar(value)) {
methodSet.add(value);
} else if ((expresionArray = findVarFormExpression(value)).length > 0) {
// 如果是表达式的话,则一定代表是变量参与了运算
expresionArray.forEach((element) => {
dataSet.add(element);
});
}
// TODO 支持自定义传参情况handleJump(scope.row.lc_id, scope.row.name)
} else if (/^v-/g.test(key) || /^:+/g.test(key)) {
// 匹配v-,:(v-bind)
let expresionArray = null;
if (checkIsVar(value)) {
dataSet.add(value);
} else if ((expresionArray = findVarFormExpression(value)).length > 0) {
expresionArray.forEach((element) => {
dataSet.add(element);
});
}
} else if (key === "__text__") {
// 匹配v-text,{{}}
if (/[{]{2}.+[}]{2}/g.test(value)) {
// 用于匹配v-text {{}}
const temp = findVarFormExpression(value);
temp.forEach((element) => {
dataSet.add(element);
});
}
} else ;
}
/**
* 检查这个值是不是符合一个变量的规则, 这里情况特殊,不可以以大写字母开头,以驼峰命名为准
* @param {*} value
*/
function checkIsVar(value) {
return /^[_a-z]{1}[_0-9a-zA-Z]*$/g.test(value);
}
/**
* 从表达式中提取变量,这里情况特殊,不可以以大写字母开头,以驼峰命名为准
* @param {*} expression
*/
function findVarFormExpression(expression) {
if (typeof expression === "string") {
let temp = expression.match(/[_a-z]{1}[_0-9a-zA-Z]*/g);
if (!temp) {
temp = [];
}
return temp;
} else {
return [];
}
}
function generateResult(options) {
// 需要输出的结果有:
// 1.html template
// 1) 支持解析v-model/@click/
// 2.script template
// 3.style template
// 返回一个格式化后的字符串
return replaceKeyInfo(options);
}
function getVueTemplate() {
return vueTemplate();
}
exports.clearDataSet = clearDataSet;
exports.outputVueCode = outputVueCode;
exports.outputVueCodeWithJsonObj = outputVueCodeWithJsonObj;

625
src/libs/bundle-core-esm.js Normal file
View File

@@ -0,0 +1,625 @@
//该文件会遍历Object获取关键的class,事件,data, 最终拼装为一个完整的SFC文件
// 导出组件模板文件
function vueTemplate () {
return `
<template> <!--在此自动生成--> </template>
<script>
export default {
props: [],
components: {},
data() {
return {
// $datas
};
},
watch: {},
computed: {},
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
destoryed() {},
methods: {
request() {
},
// $eventMethods
},
fillter: {},
};
</script>
<style scoped>
/** $stylesTemplate */
</style>
`;
}
// 生成一个方法
function generateFunction(functionName) {
return `${functionName}: function(){}`;
}
// 生成一个class
function generateClass(className) {
return `.${className}{}`;
}
// 生成一个键值对
function generateData(dataName) {
return `${dataName}:''`;
}
// 合成方法集
function convertMethods(set, options) {
let methodsStr = [...set].map(generateFunction);
// 回调外部,使外部作用最后结果
if (options.convertMethodResult) {
methodsStr = options.convertMethodResult(methodsStr);
}
return methodsStr.join(",\n");
}
// 合成style集
function convertStyles(set, options) {
let result = '';
// 因为set的结果不好解析所以优先由业务处解析再交给默认处理方式。不过业务处需要将已处理的值从set中删除否则会有两条样式
if (options.preConvertStyleResult) {
result = options.preConvertStyleResult(set);
}
const classStr = [...set].map(generateClass);
return classStr.join("\n") + '\n' + result;
}
// 合成data集
function convertDatas(set, options) {
let dataStr = [...set].map(generateData);
// 回调外部,使外部作用最后结果
if (options.convertDataResult) {
dataStr = options.convertDataResult(dataStr);
}
return dataStr.join(",\n");
}
// 从模板中替换方法
function replaceMethods(template, set, options) {
return template.replace("// $eventMethods", convertMethods(set, options));
}
// 从模板中替换样式
function replaceStyles(template, set, options) {
return template.replace("/** $stylesTemplate */", convertStyles(set, options));
}
// 从模板中替换样式
function replaceDatas(template, set, options) {
const defaultCode = convertDatas(set, options);
return template.replace("// $datas", defaultCode);
}
// const fakeCall = function(a) {return a;};
// const fakeCallNoReturn = function() {};
function buildOptions(options, defaultOptions, props) {
var newOptions = {};
if (!options) {
return defaultOptions; //if there are not options
}
for (let i = 0; i < props.length; i++) {
if (options[props[i]] !== undefined) {
newOptions[props[i]] = options[props[i]];
} else {
newOptions[props[i]] = defaultOptions[props[i]];
}
}
return newOptions;
}
const defaultOptions = {
attributeNamePrefix: '@_',
attrNodeName: false,
textNodeName: '#text',
ignoreAttributes: true,
cdataTagName: false,
cdataPositionChar: '\\c',
format: true,
indentBy: ' ',
supressEmptyNode: false,
tagValueProcessor: function (a) {
return a;
},
attrValueProcessor: function (a) {
return a;
},
singleTags: [],
attributeProtectArray: [] // 哪些属性的值为''但需要渲染出来默认如果value为''就不生成key=value只生成key
};
const props = [
'attributeNamePrefix',
'attrNodeName',
'textNodeName',
'ignoreAttributes',
'cdataTagName',
'cdataPositionChar',
'format',
'indentBy',
'supressEmptyNode',
'tagValueProcessor',
'attrValueProcessor',
'singleTags',
'attributeProtectArray'
];
function Parser(options) {
this.options = buildOptions(options, defaultOptions, props);
if (this.options.ignoreAttributes || this.options.attrNodeName) {
this.isAttribute = function (/*a*/) {
return false;
};
} else {
this.attrPrefixLen = this.options.attributeNamePrefix.length;
this.isAttribute = isAttribute;
}
if (this.options.cdataTagName) {
this.isCDATA = isCDATA;
} else {
this.isCDATA = function (/*a*/) {
return false;
};
}
this.replaceCDATAstr = replaceCDATAstr;
this.replaceCDATAarr = replaceCDATAarr;
if (this.options.format) {
this.indentate = indentate;
this.tagEndChar = '>\n';
this.newLine = '\n';
} else {
this.indentate = function () {
return '';
};
this.tagEndChar = '>';
this.newLine = '';
}
if (this.options.supressEmptyNode) {
this.buildTextNode = buildEmptyTextNode;
this.buildObjNode = buildEmptyObjNode;
} else {
this.buildTextNode = buildTextValNode;
this.buildObjNode = buildObjectNode;
}
this.buildTextValNode = buildTextValNode;
this.buildObjectNode = buildObjectNode;
}
Parser.prototype.parse = function (jObj) {
return this.j2x(jObj, 0).val;
};
Parser.prototype.j2x = function (jObj, level) {
let attrStr = '';
let val = '';
const keys = Object.keys(jObj);
const len = keys.length;
for (let i = 0; i < len; i++) {
const key = keys[i];
if (typeof jObj[key] === 'undefined') ; else if (jObj[key] === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (jObj[key] instanceof Date) {
val += this.buildTextNode(jObj[key], key, '', level);
} else if (key === '__children') { // 生成子节点
const item = jObj[key];
if (item instanceof Array) {
item.forEach(i => {
const result = this.j2x(i, level + 1);
val += result.val;
});
} else
if (typeof item === 'object') {
console.info(`不应该出现的意外`);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
else if (typeof jObj[key] !== 'object') {
//premitive type
const attr = this.isAttribute(key);
if (key === '__text__') {
val = jObj[key] + val; // 2020年12月14日19:35:54 文本内容通常在子节点之前
continue;
}
if (attr) {
if (typeof jObj[key] === "boolean" && jObj[key]) {
attrStr += ` ${key} `;
} else if (jObj[key] || this.options.attributeProtectArray.includes(key)) {
attrStr += ' ' + key + '="' + this.options.attrValueProcessor('' + jObj[key]) + '"';
} else {
attrStr += ' ' + key;
}
} else if (this.isCDATA(key)) {
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAstr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAstr('', jObj[key]);
}
} else {
//tag value
if (key === this.options.textNodeName) {
if (jObj[this.options.cdataTagName]) ; else {
val += this.options.tagValueProcessor('' + jObj[key]);
}
} else {
val += this.buildTextNode(jObj[key], key, '', level);
}
}
}
else if (Array.isArray(jObj[key])) {
//repeated nodes
if (this.isCDATA(key)) {
val += this.indentate(level);
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAarr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAarr('', jObj[key]);
}
} else {
//nested nodes
const arrLen = jObj[key].length;
for (let j = 0; j < arrLen; j++) {
const item = jObj[key][j];
if (typeof item === 'undefined') ; else if (item === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (typeof item === 'object') {
const result = this.j2x(item, level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
}
} else {
//nested node
if (this.options.attrNodeName && key === this.options.attrNodeName) {
const Ks = Object.keys(jObj[key]);
const L = Ks.length;
for (let j = 0; j < L; j++) {
attrStr += ' ' + Ks[j] + '="' + this.options.attrValueProcessor('' + jObj[key][Ks[j]]) + '"';
}
} else {
const result = this.j2x(jObj[key], level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
}
}
}
return { attrStr: attrStr, val: val };
};
function replaceCDATAstr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata + ']]' + this.tagEndChar;
} else {
return str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata + ']]' + this.tagEndChar);
}
}
function replaceCDATAarr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata.join(']]><![CDATA[') + ']]' + this.tagEndChar;
} else {
for (let v in cdata) {
str = str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata[v] + ']]>');
}
return str + this.newLine;
}
}
function buildObjectNode(val, key, attrStr, level) {
if (attrStr && !val.includes('<')) {
if (key === "img" || key === "a-icon" || key === "input" || (this.options.singleTags && this.options.singleTags.includes(key))) {
return (this.indentate(level) + '<' + key + attrStr + '/>');
}
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
val +
//+ this.newLine
// + this.indentate(level)
'</' +
key +
this.tagEndChar
);
} else {
return (
this.indentate(level) +
'<' +
key +
attrStr +
this.tagEndChar +
val +
//+ this.newLine
this.indentate(level) +
'</' +
key +
this.tagEndChar
);
}
}
function buildEmptyObjNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildObjectNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
//+ this.newLine
}
}
function buildTextValNode(val, key, attrStr, level) {
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
this.options.tagValueProcessor(val) +
'</' +
key +
this.tagEndChar
);
}
function buildEmptyTextNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildTextValNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
}
}
function indentate(level) {
return this.options.indentBy.repeat(level);
}
function isAttribute(name /*, options*/) {
return true;
}
function isCDATA(name) {
return name === this.options.cdataTagName;
}
//formatting
//indentation
//\n after each closing or self closing tag
const rawAdd = Set.prototype.add;
Set.prototype.add = function (value) {
if (typeof value === "string" && checkKeyword(value))
rawAdd.apply(this, arguments);
};
function checkKeyword(value) {
return value != "true" && value != "false";
}
function sort(set) {
return new Set(Array.from(set).sort());
}
function replaceHtmlTemplate(template, jsonObj) {
const defaultOptions = {
attributeNamePrefix: "@_",
attrNodeName: false, //default is false
textNodeName: "#text",
ignoreAttributes: false,
cdataTagName: "__cdata", //default is false
cdataPositionChar: "\\c",
format: true,
indentBy: " ",
supressEmptyNode: false,
attributeProtectArray: [] // 哪些属性的值为''但需要渲染出来默认如果value为''就不生成key=value只生成key
};
const parser = new Parser(defaultOptions);
// 只面向代码生成使用故jsonObj.template不能变更2020年12月15日16:04:28
const xml = parser.parse(jsonObj.template);
return template.replace("<!--在此自动生成-->", xml);
}
function getVueTemplate() {
return vueTemplate();
}
/**
* 获取这个变量的实际key
* @param {*} value
*/
function getVarName(value) {
let result = null;
if (/^[_a-z]{1}[_0-9a-zA-Z]*$/g.test(value)) {
result = value;
} else if (value.indexOf('.') > 0 && getVarName(value.split('.')[0])) { //这个case用于处理xxx.yy的情况需提取出xxx
result = value.split('.')[0];
} else if (value.indexOf('in') > 0) { // 匹配v-for="xx in yy", 提取yy
const temp = value.split(' in ');// 防止匹配到index这样容易混淆的变量
if (temp.length === 2) {
result = getVarName(temp[1].trim());
}
}
return result;
}
/**
* 从表达式中提取变量,这里情况特殊,不可以以大写字母开头,以驼峰命名为准
* @param {*} expression
*/
function findVarFormExpression(expression) {
if (typeof expression === "string") {
let temp = expression.match(/[_a-z]{1}[_0-9a-zA-Z]*/g);
if (!temp) {
temp = [];
}
return temp;
} else {
return [];
}
}
class CodeGenerator {
constructor(options = {}) {
this.options = options;
// 解析后的Json对象
this.jsonObj = null;
// 类定义放入其中
this.classSet = new Set();
// 事件放入其中
this.methodSet = new Set();
// 数据引用放入其中
this.dataSet = new Set();
}
clearDataSet() {
this.classSet.clear();
this.methodSet.clear();
this.dataSet.clear();
}
/**
* 直接输入Json文本
* @param {*} json
*/
outputVueCode(json) {
this.jsonObj = JSON.parse(json);
return this.outputVueCodeWithJsonObj(jsonObj);
}
/**
* 输入Json对象
* @param {*} jsonObj
*/
outputVueCodeWithJsonObj(_jsonObj) {
this.jsonObj = _jsonObj;
// 解析对象
this.parseJson(_jsonObj);
// 对集合进行排序
this.dataSet = sort(this.dataSet);
this.methodSet = sort(this.methodSet);
this.classSet = sort(this.classSet);
// 生成执行结果
return this.generateResult();
}
// 将所有需要替换的内容通过装饰器逐步替换
replaceKeyInfo() {
// 将对象转换为html并替换
const templateTemp = replaceHtmlTemplate(getVueTemplate(), this.jsonObj);
// 生成方法
const methodTemp = replaceMethods(templateTemp, this.methodSet, this.options);
// 生成data
const dataTemp = replaceDatas(methodTemp, this.dataSet, this.options);
// 生成class
const styleTemp = replaceStyles(dataTemp, this.classSet, this.options);
return styleTemp;
}
// 分发解析结果
deliveryResult(key, value) {
if (key === "class") {
const classes = value.split(" ");
classes.forEach((item) => {
// 处理多个空字符串
if (!item) return;
this.classSet.add(item);
});
} else if (/^v-on/g.test(key) || /^@/g.test(key)) {
// 匹配@,v-on
if (getVarName(value)) {
this.methodSet.add(value);
}
} else if (/^v-/g.test(key) || /^:+/g.test(key)) {
// 优先使Method消费因为有的:也是method
if (this.options.checkIsMethodDirectives && this.options.checkIsMethodDirectives(key)) {
value = getVarName(value);
value && this.methodSet.add(value);
} else
// 业务侧可能会全部消费/^:+/g.test(key)
if (this.options.checkIsDataDirectives && this.options.checkIsDataDirectives(key)) {
value = getVarName(value);
value && this.dataSet.add(value);
} else {
this.options.unSupportedKey && this.options.unSupportedKey(key, value);
}
} else if (key === "__text__") {
// 匹配v-text,{{}}
if (/[{]{2}.+[}]{2}/g.test(value)) {
// 用于匹配v-text {{}}
const temp = findVarFormExpression(value);
temp.forEach((element) => {
this.dataSet.add(element);
});
}
} else {
// 通过回调给业务实现方做处理
this.options.unSupportedKey && this.options.unSupportedKey(key, value);
}
}
generateResult() {
// 需要输出的结果有:
// 1.html template
// 1) 支持解析v-model/@click/
// 2.script template
// 3.style template
// 返回一个格式化后的字符串
return this.replaceKeyInfo();
}
// 递归解析Json
parseJson(json) {
for (const key in json) {
if (json.hasOwnProperty(key)) {
const value = json[key];
if (value instanceof Array) {
value.forEach((item) => this.parseJson(item));
} else if (value instanceof Object) {
this.parseJson(value);
} else {
this.deliveryResult(key, value);
}
}
}
}
}
export { CodeGenerator };

View File

@@ -0,0 +1,78 @@
'use strict';
//该文件用于解析HTML输出为Object对象
Object.defineProperty(exports, '__esModule', { value: true });
const htmlparser2 = require("htmlparser2");
function getNodeContent(node) {
return node[Object.keys(node)[0]];
}
/**每个节点的表示方法为:
{
tagname: {
key1: value1,
key2: value2,
__children: [
{
}
]
}
}*/
function generateNewNode(tagName, attributes = {}) {
// 构建新节点
const newNode = {};
newNode[tagName] = attributes;
attributes.__children = [];
return newNode;
}
function parseHtml(htmlData) {
return new Promise((resolve, reject) => {
// 根节点
const root = generateNewNode('root');
// 当前访问的节点
let currentAccessObject = root;
// 之前访问的节点数组
let lastAccessStack = [root];
// options docment: https://github.com/fb55/htmlparser2/wiki/Parser-options
const parser = new htmlparser2.Parser({
onopentag(tagname, attributes) {
const newNode = generateNewNode(tagname, attributes);
lastAccessStack.push(newNode);
getNodeContent(currentAccessObject).__children.push(newNode);
currentAccessObject = newNode;
},
ontext(text) {
if (text.trim()) {
getNodeContent(currentAccessObject).__text__ = text.trim();
}
},
onclosetag(tagname) {
lastAccessStack.pop();
currentAccessObject = lastAccessStack[lastAccessStack.length - 1];
},
onend(){
resolve(root);
},
onerror(error) {
reject(error);
}
});
parser.write(
htmlData
);
parser.end();
})
}
async function html2Json(htmlData) {
return await parseHtml(htmlData);
}
exports.html2Json = html2Json;

View File

@@ -0,0 +1,74 @@
//该文件用于解析HTML输出为Object对象
const htmlparser2 = require("htmlparser2");
function getNodeContent(node) {
return node[Object.keys(node)[0]];
}
/**每个节点的表示方法为:
{
tagname: {
key1: value1,
key2: value2,
__children: [
{
}
]
}
}*/
function generateNewNode(tagName, attributes = {}) {
// 构建新节点
const newNode = {};
newNode[tagName] = attributes;
attributes.__children = [];
return newNode;
}
function parseHtml(htmlData) {
return new Promise((resolve, reject) => {
// 根节点
const root = generateNewNode('root');
// 当前访问的节点
let currentAccessObject = root;
// 之前访问的节点数组
let lastAccessStack = [root];
// options docment: https://github.com/fb55/htmlparser2/wiki/Parser-options
const parser = new htmlparser2.Parser({
onopentag(tagname, attributes) {
const newNode = generateNewNode(tagname, attributes);
lastAccessStack.push(newNode);
getNodeContent(currentAccessObject).__children.push(newNode);
currentAccessObject = newNode;
},
ontext(text) {
if (text.trim()) {
getNodeContent(currentAccessObject).__text__ = text.trim();
}
},
onclosetag(tagname) {
lastAccessStack.pop();
currentAccessObject = lastAccessStack[lastAccessStack.length - 1];
},
onend(){
resolve(root);
},
onerror(error) {
reject(error);
}
});
parser.write(
htmlData
);
parser.end();
})
}
async function html2Json(htmlData) {
return await parseHtml(htmlData);
}
export { html2Json };

View File

@@ -0,0 +1,318 @@
'use strict';
// 该文件用于将Object对象转换为HTML
Object.defineProperty(exports, '__esModule', { value: true });
// const fakeCall = function(a) {return a;};
// const fakeCallNoReturn = function() {};
function buildOptions(options, defaultOptions, props) {
var newOptions = {};
if (!options) {
return defaultOptions; //if there are not options
}
for (let i = 0; i < props.length; i++) {
if (options[props[i]] !== undefined) {
newOptions[props[i]] = options[props[i]];
} else {
newOptions[props[i]] = defaultOptions[props[i]];
}
}
return newOptions;
}
const defaultOptions = {
attributeNamePrefix: '@_',
attrNodeName: false,
textNodeName: '#text',
ignoreAttributes: true,
cdataTagName: false,
cdataPositionChar: '\\c',
format: true,
indentBy: ' ',
supressEmptyNode: false,
tagValueProcessor: function (a) {
return a;
},
attrValueProcessor: function (a) {
return a;
},
attributeProtectArray: [] // 哪些属性的值为''但需要渲染出来默认如果value为''就不生成key=value只生成key
};
const props = [
'attributeNamePrefix',
'attrNodeName',
'textNodeName',
'ignoreAttributes',
'cdataTagName',
'cdataPositionChar',
'format',
'indentBy',
'supressEmptyNode',
'tagValueProcessor',
'attrValueProcessor',
'attributeProtectArray'
];
function Parser(options) {
this.options = buildOptions(options, defaultOptions, props);
if (this.options.ignoreAttributes || this.options.attrNodeName) {
this.isAttribute = function (/*a*/) {
return false;
};
} else {
this.attrPrefixLen = this.options.attributeNamePrefix.length;
this.isAttribute = isAttribute;
}
if (this.options.cdataTagName) {
this.isCDATA = isCDATA;
} else {
this.isCDATA = function (/*a*/) {
return false;
};
}
this.replaceCDATAstr = replaceCDATAstr;
this.replaceCDATAarr = replaceCDATAarr;
if (this.options.format) {
this.indentate = indentate;
this.tagEndChar = '>\n';
this.newLine = '\n';
} else {
this.indentate = function () {
return '';
};
this.tagEndChar = '>';
this.newLine = '';
}
if (this.options.supressEmptyNode) {
this.buildTextNode = buildEmptyTextNode;
this.buildObjNode = buildEmptyObjNode;
} else {
this.buildTextNode = buildTextValNode;
this.buildObjNode = buildObjectNode;
}
this.buildTextValNode = buildTextValNode;
this.buildObjectNode = buildObjectNode;
}
Parser.prototype.parse = function (jObj) {
return this.j2x(jObj, 0).val;
};
Parser.prototype.j2x = function (jObj, level) {
let attrStr = '';
let val = '';
const keys = Object.keys(jObj);
const len = keys.length;
for (let i = 0; i < len; i++) {
const key = keys[i];
if (typeof jObj[key] === 'undefined') ; else if (jObj[key] === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (jObj[key] instanceof Date) {
val += this.buildTextNode(jObj[key], key, '', level);
} else if (key === '__children'){ // 生成子节点
const item = jObj[key];
if(item instanceof Array){
item.forEach(i =>{
const result = this.j2x(i, level + 1);
val += result.val;
});
} else
if (typeof item === 'object') {
console.info(`不应该出现的意外`);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
else if (typeof jObj[key] !== 'object') {
//premitive type
const attr = this.isAttribute(key);
if (key === '__text__') {
val = jObj[key] + val; // 2020年12月14日19:35:54 文本内容通常在子节点之前
continue;
}
if (attr) {
if (typeof jObj[key] === "boolean" && jObj[key]) {
attrStr += ` ${key} `;
} else if(jObj[key] || this.options.attributeProtectArray.includes(key)){
attrStr += ' ' + key + '="' + this.options.attrValueProcessor('' + jObj[key]) + '"';
} else {
attrStr += ' ' + key;
}
} else if (this.isCDATA(key)) {
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAstr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAstr('', jObj[key]);
}
} else {
//tag value
if (key === this.options.textNodeName) {
if (jObj[this.options.cdataTagName]) ; else {
val += this.options.tagValueProcessor('' + jObj[key]);
}
} else {
val += this.buildTextNode(jObj[key], key, '', level);
}
}
}
else if (Array.isArray(jObj[key])) {
//repeated nodes
if (this.isCDATA(key)) {
val += this.indentate(level);
if (jObj[this.options.textNodeName]) {
val += this.replaceCDATAarr(jObj[this.options.textNodeName], jObj[key]);
} else {
val += this.replaceCDATAarr('', jObj[key]);
}
} else {
//nested nodes
const arrLen = jObj[key].length;
for (let j = 0; j < arrLen; j++) {
const item = jObj[key][j];
if (typeof item === 'undefined') ; else if (item === null) {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (typeof item === 'object') {
const result = this.j2x(item, level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
} else {
val += this.buildTextNode(item, key, '', level);
}
}
}
} else {
//nested node
if (this.options.attrNodeName && key === this.options.attrNodeName) {
const Ks = Object.keys(jObj[key]);
const L = Ks.length;
for (let j = 0; j < L; j++) {
attrStr += ' ' + Ks[j] + '="' + this.options.attrValueProcessor('' + jObj[key][Ks[j]]) + '"';
}
} else {
const result = this.j2x(jObj[key], level + 1);
val += this.buildObjNode(result.val, key, result.attrStr, level);
}
}
}
return { attrStr: attrStr, val: val };
};
function replaceCDATAstr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata + ']]' + this.tagEndChar;
} else {
return str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata + ']]' + this.tagEndChar);
}
}
function replaceCDATAarr(str, cdata) {
str = this.options.tagValueProcessor('' + str);
if (this.options.cdataPositionChar === '' || str === '') {
return str + '<![CDATA[' + cdata.join(']]><![CDATA[') + ']]' + this.tagEndChar;
} else {
for (let v in cdata) {
str = str.replace(this.options.cdataPositionChar, '<![CDATA[' + cdata[v] + ']]>');
}
return str + this.newLine;
}
}
function buildObjectNode(val, key, attrStr, level) {
if (attrStr && !val.includes('<')) {
if (key === "img" || key === "input") {
return (this.indentate(level) + '<' + key + attrStr + '/>');
}
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
val +
//+ this.newLine
// + this.indentate(level)
'</' +
key +
this.tagEndChar
);
} else {
return (
this.indentate(level) +
'<' +
key +
attrStr +
this.tagEndChar +
val +
//+ this.newLine
this.indentate(level) +
'</' +
key +
this.tagEndChar
);
}
}
function buildEmptyObjNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildObjectNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
//+ this.newLine
}
}
function buildTextValNode(val, key, attrStr, level) {
return (
this.indentate(level) +
'<' +
key +
attrStr +
'>' +
this.options.tagValueProcessor(val) +
'</' +
key +
this.tagEndChar
);
}
function buildEmptyTextNode(val, key, attrStr, level) {
if (val !== '') {
return this.buildTextValNode(val, key, attrStr, level);
} else {
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
}
}
function indentate(level) {
return this.options.indentBy.repeat(level);
}
function isAttribute(name /*, options*/) {
return true;
}
function isCDATA(name) {
return name === this.options.cdataTagName;
}
//formatting
//indentation
//\n after each closing or self closing tag
exports.Parser = Parser;

View File

@@ -0,0 +1,77 @@
// 代码生成对象工厂,每次初始化需要获取一个新的实例,所以工厂方法模式最为适用
import { CodeGenerator } from "./bundle-core-esm";
import { checkIsDataDirectives, checkIsMethodDirectives } from '@/libs/directiveCheck';
const stringifyObject = require("stringify-object");
export function createNewCodeGenerator() {
return new CodeGenerator({
convertDataResult: function (dataCodeArr) {
let result = dataCodeArr;
// 干扰数据结果
if (dataCodeArr.length > 0) {
result = dataCodeArr.map((item) => {
const kav = item.split(":");
const key = kav[0];
// 这里获取的是原始data数据
if (window.dataSourceMap[key] || window.dataSourceMap[key] == 0) {
return `${key}: ${stringifyObject(window.dataSourceMap[key], {
indent: " ",
singleQuotes: false,
})}`;
} else {
return item;
}
});
}
return result;
},
convertMethodResult(methodCodeArr) {
let result = methodCodeArr;
if (methodCodeArr && methodCodeArr.length > 0) {
result = methodCodeArr.map(methodItem => {
const kav = methodItem.split(":");
const key = kav[0];
// 这里获取的是原始data数据
if (window.methodSourceMap[key]) {
return `${key}: ${window.methodSourceMap[key]}`;
} else {
return methodItem;
}
})
}
return result;
},
preConvertStyleResult(selectorSet) {
let result = '';
const selectorValues = selectorSet.values();
const selectorKeys = Object.keys(window.styleSourceMap);
for (const selector of selectorValues) {
// styleSourceMap中保留了所有的原始选择器这里只处理class的
const findResults = selectorKeys.filter(key => {
return key === `.${selector}` || key.indexOf(`.${selector} `) >= 0 || key.indexOf(` .${selector}`) >= 0;
})
if (findResults && findResults.length > 0) {
findResults.forEach(findResult => {
result += `${findResult} { ${window.styleSourceMap[findResult]} }\n`;
})
selectorSet.delete(selector);
}
}
return result;
},
checkIsDataDirectives,
checkIsMethodDirectives,
unSupportedKey: function (key, value) {
// 对于这一类需要警示,因为可能是被遗漏的
if (/^v-/g.test(key) || /^:+/g.test(key)) {
console.warn(`可能遗漏了这些: ${key}: ${value}`);
} else {
console.info(`unsupport key: ${key}: ${value}`);
}
}
});
}

View File

@@ -0,0 +1,28 @@
export function checkIsDataDirectives(key) {
return [
':prop',
'v-for',
':data',
':src',
':model',
':rules',
':total',
':current-page',
'v-model',
'v-if',
':reverse',
':show-file-list',
':file-list'].includes(key) || /^:+/g.test(key);
}
export function checkIsMethodDirectives(key) {
return [
':before-close',
':on-preview',
':on-remove',
':before-remove',
':on-exceed',
':on-success',
':format',
':before-upload'].includes(key);
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

440
src/libs/main-panel.js Normal file
View File

@@ -0,0 +1,440 @@
/**
* 这个文件主要用于将结构渲染出来,并提供一些辅助的能力
*
* 这个过程的核心可以简化为:
* 结构 -> vue代码 -> html
*
*/
import { parseComponent } from 'vue-template-compiler/browser';
import Vue from 'vue';
import { merge, insertPresetAttribute, getSplitTag, replaceRowID, updateLinkTree, findCodeElemNode, findRawVueInfo, removeAllID } from "@/utils/forCode";
import { getRawComponentContent, getRawComponentKey, isObject } from '@/utils/common';
import { createNewCodeGenerator } from "@/libs/code-generator-factory";
const EventEmitter = require('eventemitter3');
const { cloneDeep } = require('lodash');
/**
* 主控制面板辅助类,用于代码的生成与绘制
*/
export class MainPanelProvider {
constructor() {
// 开启编辑模式
this.editMode = true;
this.eventEmitter = new EventEmitter();
this.currentPointPositionAfterIndex = -1;
// 存储所有的渲染记录, 保存副本
this.renderStack = [];
this.redoStack = [];
}
/**
* 对内渲染
* @param {*} rawDataStructure
*/
_render(rawDataStructure) {
this._rawDataStructure = rawDataStructure;
// 对外只提供副本,防止外面污染内部
const codeStructureCopy = cloneDeep(rawDataStructure);
this.eventEmitter.emit('codeStructureUpdated', codeStructureCopy);
this.initCodeGenerator();
// 生成展示代码
const copyForShow = cloneDeep(rawDataStructure);
removeAllID(copyForShow);
console.groupCollapsed('---> for code generator warn <---');
const codeForShow = this.codeGenerator.outputVueCodeWithJsonObj(copyForShow);
this.eventEmitter.emit("codeCreated", codeForShow);
const readyForMoutedElement = this.createMountedElement();
// 生成原始代码
const code = this.codeGenerator.outputVueCodeWithJsonObj(rawDataStructure);
console.groupEnd();
const { template, script, styles, customBlocks } = parseComponent(code);
let newScript = script.content.replace("export default", "")
newScript = newScript.replace(/\s+/g, "");
const componentOptions = (new Function(`return ${newScript}`))();
const res = Vue.compile(template.content);
componentOptions.render = function () {
const rootVNode = res.render.apply(this, arguments);
return rootVNode;
};
componentOptions.staticRenderFns = res.staticRenderFns;
// 渲染当前代码
new Vue(componentOptions).$mount(readyForMoutedElement);
// 拍平数据结构
this.editMode && this.flatDataStructure(rawDataStructure);
// 开启编辑模式
this.editMode && this.enableEditMode();
return this;
}
/**
* 对外渲染函数:将一个指定的数据结构渲染出来,并会保存这次的渲染记录
* @param {*} rawDataStructure
*/
render(rawDataStructure) {
this._render(rawDataStructure);
return this;
}
setEditMode(editMode) {
this.editMode = editMode;
this.reRender();
}
/**
* 初始化代码编译
*/
initCodeGenerator() {
this.codeGenerator = createNewCodeGenerator();
}
getControlPanelRoot() {
return document.getElementById('render-control-panel');
}
/**
* 生成一个新的待挂载容器
*/
createMountedElement() {
const renderControlPanel = this.getControlPanelRoot();
const child = document.createElement('div');
// 清空子节点
while (renderControlPanel.firstChild) {
renderControlPanel.removeChild(renderControlPanel.firstChild)
}
renderControlPanel.appendChild(child);
return child;
}
/**
* 初始化组件的一些事件
*/
initComonentsEvent() {
const renderControlPanel = this.getControlPanelRoot();
const elements = renderControlPanel.querySelectorAll("[lc_id]");
elements.forEach(element => {
element.addEventListener("click", event => {
event.stopPropagation();
this.markElement(element);
})
})
this.initDropEvent();
}
/**
* 高亮当前编辑元素等处理,并将当前编辑的元素指针指向这个节点
*/
markElement(realNode) {
// 处理之前的状态
if (this.currentEditElement) {
this.currentEditElement.classList.remove("mark-element");
}
if (realNode) {
this.currentEditElement = realNode;
const rawVueInfo = this.markElementInner(realNode);
// 对外只提供副本,防止外面污染内部
const codeRawInfoCopy = cloneDeep(rawVueInfo);
this.eventEmitter.emit("selected", codeRawInfoCopy);
} else {
this.eventEmitter.emit("selected", null);
}
}
/**
* 这个方法会返回节点所对应的代码结构
* @param {*} realNode
* @returns
*/
markElementInner(realNode) {
realNode.classList.add("mark-element");
const rawVueInfo = findRawVueInfo(realNode);
// 显示当前组件的名称
realNode.setAttribute('lc-component-name', getRawComponentKey(rawVueInfo));
return rawVueInfo;
}
/**
* 清除选中
*/
clearElementSelect() {
this.markElement(null);
}
/**
* 设置新组建要放入的位置与对象
* @param {*} index
*/
setDropInfo(dropInfo) {
this.currentPointDropInfo = dropInfo;
}
/**
* 初始化拖拽事件
*/
initDropEvent() {
const renderControlPanel = this.getControlPanelRoot();
renderControlPanel.addEventListener("dragenter", (event) => {
event.preventDefault();
});
renderControlPanel.addEventListener("dragleave", (event) => {
event.preventDefault();
});
renderControlPanel.addEventListener("dragover", (event) => {
// Specifying Drop Targets
event.preventDefault();
});
// 拖入预览容器释放时的处理
renderControlPanel.addEventListener("drop", (event) => {
if(!this.currentPointDropInfo.target){
return;
}
const data = event.dataTransfer.getData("text/plain");
const [, , , , rawInfo] = data.split(getSplitTag());
const newDropObj = JSON.parse(rawInfo);
// 插入预设属性
insertPresetAttribute(newDropObj);
// 使新拖入的代码与原来的做脱离
replaceRowID(newDropObj, '');
// 更新到一个Map上面维持引用由于render中统一做了处理所以这段代码是可以删除的 2021年02月04日11:59:10
updateLinkTree(newDropObj);
// 更新代码结构关系
const codeTargetElement = findCodeElemNode(this.currentPointDropInfo.target);
if (codeTargetElement) {
let temp = findRawVueInfo(codeTargetElement);
this.backup();
// 合并
merge(getRawComponentContent(temp), newDropObj, this.currentPointDropInfo.index, () => {
this.eventEmitter.emit('onMerged');
});
this.render(window.tree.root);
}
});
}
/**
* 开启编辑模式,并禁用默认的事件,添加编辑事件
*/
enableEditMode() {
const renderControlPanel = this.getControlPanelRoot();
// 这种方式可以禁用原节点所有的事件
const elClone = renderControlPanel.cloneNode(true);
renderControlPanel.parentNode.replaceChild(elClone, renderControlPanel);
this.eventEmitter.emit("mounted", elClone);
// 事件初始化
this.initComonentsEvent();
}
/**
* 将数据拍平以id: data 的方式联结
* @param {*} rawDataStructure
*/
flatDataStructure(rawDataStructure) {
updateLinkTree(rawDataStructure);
}
/**
* 选中某个一个元素时
*/
onSelectElement(callback) {
this.subscribeEventListener("selected", callback);
return this;
}
/**
* 当根节点挂载后
* @param {*} callback
*/
onRootElementMounted(callback) {
this.subscribeEventListener("mounted", callback);
return this;
}
/**
* 当Vue代码生成后
* @param {*} callback
*/
onCodeCreated(callback) {
this.subscribeEventListener("codeCreated", callback);
return this;
}
/**
* 当代码结构更新后
* @param {*} callback
*/
onCodeStructureUpdated(callback) {
this.subscribeEventListener("codeStructureUpdated", callback);
return this;
}
/**
* 当代码合并完成后
* @param {*} callback
*/
onMerged(callback) {
this.subscribeEventListener("onMerged", callback);
return this;
}
/**
* 当节点删除后
* @param {*} callback
*/
onNodeDeleted(callback) {
this.subscribeEventListener("onNodeDeleted", callback);
return this;
}
/**
* 重新渲染
*/
reRender() {
this._render(this._rawDataStructure);
return this;
}
/**
* 保存属性
* @param {*} resultList
* @param {*} rawInfo
*/
saveAttribute(resultList, lc_id) {
const param = resultList;
const object = getRawComponentContent(window.tree[lc_id]);
this.backup();
// 这里是为了满足当有属性删除的情况保留保留属性__children lc-mark lc_id
for (const key in object) {
if (object.hasOwnProperty(key) &&
key != "__children" && key != "lc-mark" && key != "lc_id" &&
!isObject(object[key]/** 值为Object的情况目前已经没有了2021年02月04日11:56:28 */)) {
delete object[key];
}
}
param.forEach((element) => {
object[element.key] = element.value;
});
// 渲染当前的变更
this.render(this._rawDataStructure);
return this;
}
/**
* 移除一个节点
* @param {*} lc_id
*/
remove(lc_id) {
const readyForDeleteElement = document.querySelector(`[lc_id="${lc_id}"]`);
if (readyForDeleteElement) {
const parentElementNode = findCodeElemNode(readyForDeleteElement.parentElement);
if (parentElementNode) {
const parentRawInfo = findRawVueInfo(parentElementNode);
const attributes = getRawComponentContent(parentRawInfo);
if (attributes) {
const childrenArray = attributes.__children;
const index = childrenArray.findIndex((item) => {
return getRawComponentContent(item).lc_id == lc_id;
});
this.backup();
childrenArray.splice(index, 1);
this.eventEmitter.emit("onNodeDeleted");
// 渲染当前的变更
this.render(this._rawDataStructure);
} else {
console.warn(`没有发现原始代码属性lc_id: ${lc_id}`);
}
} else {
console.warn(`没有发现lc_id: ${lc_id}所对应的父Dom节点`);
}
} else {
console.warn(`没有发现lc_id: ${lc_id}所对应的Dom节点`);
}
}
/**
* 事件订阅
* @param {*} event -> mounted:当新的根节点被挂载 | selected:选择了某个节点 | codeCreated:生成了代码 | codeStructureUpdated:代码结构更新
* @param {*} listener
*/
subscribeEventListener(event, listener) {
this.eventEmitter.on(event, listener);
}
/**
* 备份当前结构
*/
backup() {
this.renderStack.push(cloneDeep(this._rawDataStructure));
// 当重新变更结构时,重做栈要置空
this.redoStack = [];
}
/**
* 撤销
*/
undo() {
if (this.renderStack.length > 0) {
// 存储当前的渲染到重做栈中
this.redoStack.push(cloneDeep(this._rawDataStructure));
const readyForRender = this.renderStack.pop();
this._render(readyForRender);
}
}
/**
* 重做
*/
redo() {
if (this.redoStack.length > 0) {
this.renderStack.push(cloneDeep(this._rawDataStructure));
const readyForRender = this.redoStack.pop();
this._render(readyForRender);
}
}
}

View File

@@ -0,0 +1,35 @@
export default {
"el-button": {
"@click": "onButtonClick",
size: "small",
},
"el-radio-group": {
"v-model": "radio",
},
"el-radio": {
":label": 0,
},
"el-checkbox-group": {
},
"el-link": {
"@click": "onClickLink",
},
"el-select": {
size: "small",
},
"el-input": {
"v-model": "input",
placeholder: "请输入内容",
size: "small",
class: "input",
},
"el-pagination": {
"@size-change": "handleSizeChange",
"@current-change": "handleCurrentChange",
":current-page": "currentPage",
":page-sizes": "[10, 20, 50, 100]",
":page-size": "10",
layout: "total, sizes, prev, pager, next, jumper",
":total": "234",
},
};

51
src/libs/split-init.js Normal file
View File

@@ -0,0 +1,51 @@
import Split from 'split.js'
export function splitInit() {
let sizes = localStorage.getItem('split-sizes')
let sizeCodes = localStorage.getItem('split-sizes-code')
if (sizes) {
sizes = JSON.parse(sizes)
} else {
sizes = [50, 50] // default sizes
}
if (sizeCodes) {
sizeCodes = JSON.parse(sizeCodes)
} else {
sizeCodes = [70, 30] // default sizes
}
Split(['.base-component-container', '.main-container'], {
sizes: sizes,
gutterSize: 4,
minSize: [400, 375],
gutter: (index, direction) => {
const gutter = document.createElement('div')
gutter.className = `gutter gutter-${direction}`
gutter.addEventListener("mousemove", () => {
gutter.style.cursor = 'col-resize';
})
return gutter
}, onDragEnd: function (sizes) {
localStorage.setItem('split-sizes', JSON.stringify(sizes))
},
})
// Split(['.preview-container', '.render-wrap'], {
// sizes: sizeCodes,
// gutterSize: 4,
// direction: 'vertical',
// minSize: [100, 100],
// gutter: (index, direction) => {
// const gutter = document.createElement('div')
// gutter.className = `gutter gutter-${direction}`
// gutter.addEventListener("mousemove", () => {
// gutter.style.cursor = 'row-resize';
// })
// return gutter
// }, onDragEnd: function (sizes) {
// localStorage.setItem('split-sizes-code', JSON.stringify(sizes))
// },
// })
}

2
src/libs/v2-tree.js Normal file

File diff suppressed because one or more lines are too long