mirror of
https://github.com/sahadev/vue-component-creater-ui.git
synced 2025-09-28 07:13:21 +08:00
init
This commit is contained in:
108
src/libs/bro-ele-config.js
Normal file
108
src/libs/bro-ele-config.js
Normal 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);
|
||||
}
|
||||
}
|
600
src/libs/bundle-core-common.js
Normal file
600
src/libs/bundle-core-common.js
Normal 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
625
src/libs/bundle-core-esm.js
Normal 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 };
|
78
src/libs/bundle-html2json-common.js
Normal file
78
src/libs/bundle-html2json-common.js
Normal 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;
|
74
src/libs/bundle-html2json-esm.js
Normal file
74
src/libs/bundle-html2json-esm.js
Normal 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 };
|
318
src/libs/bundle-json2html-common.js
Normal file
318
src/libs/bundle-json2html-common.js
Normal 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;
|
77
src/libs/code-generator-factory.js
Normal file
77
src/libs/code-generator-factory.js
Normal 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}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
28
src/libs/directiveCheck.js
Normal file
28
src/libs/directiveCheck.js
Normal 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);
|
||||
}
|
BIN
src/libs/element-ui/fonts/element-icons.ttf
Normal file
BIN
src/libs/element-ui/fonts/element-icons.ttf
Normal file
Binary file not shown.
BIN
src/libs/element-ui/fonts/element-icons.woff
Normal file
BIN
src/libs/element-ui/fonts/element-icons.woff
Normal file
Binary file not shown.
1
src/libs/element-ui/index.css
Normal file
1
src/libs/element-ui/index.css
Normal file
File diff suppressed because one or more lines are too long
1
src/libs/element-ui/index.js
Normal file
1
src/libs/element-ui/index.js
Normal file
File diff suppressed because one or more lines are too long
440
src/libs/main-panel.js
Normal file
440
src/libs/main-panel.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/libs/presetAttribute.js
Normal file
35
src/libs/presetAttribute.js
Normal 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
51
src/libs/split-init.js
Normal 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
2
src/libs/v2-tree.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user