!(function(v, g){
g["DataV"] || (g["DataV"] = v());
})(function(){
const zoom = [0, 20, 40, 60, 80, 99];
// 获取唯一序列码
let xid_i = 0;
const getXid = function(){
xid_i++;
return "xid_" + (xid_i);
}
const getInt = function(string){
let i = parseInt(string);
return isNaN(i) ? 0 : i;
}
// 初始化DataV对象
const exports = {};
// 初始化容器内容
const createApp = exports.createApp = function(dom, opt){
return new DataV(dom, opt);
}
// dataV 初始化配置
const opt_def = {
width: "100%",
height: "100%"
}
// dataV容器对象
const DataV = function(dom, opt){
this.opt = Object.assign(opt_def, opt);
this.dom = dom;
this.plugin = []; // 容器内部组件
this.domInit(this.dom);
this.dataVInit(this.dom); // 初始化对象属性
// 创建右键事件对象
this.rightFunctionOpt = new RightFunction({
dom: this.dom,
menu: [{
name: "新增一个div" , value: this.addDiv.bind(this)
}]
});
// 首先威触发dom绑定事件
this.initEvent(this.dom, "contextmenu");
}
const domInit = function(dom){
const opt = this.opt;
dom.style.cssText = `width: ${opt.width}; height: ${opt.height};`;
dom.setAttribute("data-contextmenu", "onDomContextmenu")
}
const dataVInit = function(dom){
// 初始化内部组件事件监听
this.initEvent(dom, "click");
}
const add = function(plugin){
this.plugin.push(plugin);
this.dom.appendChild(plugin.dom);
}
const remove = function(){
}
const initEvent = function(dom, type, fnName){
dom.addEventListener(type,(e) => {
e.preventDefault();
var _this = e.target;
for( ; ; ){
e.target = _this;
if( !_this || !_this.parentElement ) break;
if( _this.dataset[e.type] ) {
const fn = _this.dataset[e.type];
typeof fn === "string" && this[fn] && this[fn](e, this);
}
if( _this == dom ){
// if( this[fnName || "on"+e.type] ) {
// this[fnName || "on"+e.type](e, this);
// break;
// }
break;
}
_this = _this.parentElement;
}
});
}
const DataVonDomContextmenu = function(e){
this.rightFunctionOpt.show(e.pageX, e.pageY);
}
const addDiv = function(){
this.add(new DivElement());
}
DataV.prototype = {
add, // 添加组件
remove, // 删除组件
initEvent, // 初始化组件事件监听
domInit, // 初始化dom
dataVInit, // 初始化数据
onDomContextmenu: DataVonDomContextmenu,
addDiv: addDiv
}
// -------------------- 右键功能
const RightFunctionOpt = {
dom: document.body,
menu: [{ name: "测试", value: ()=>{ alert("测试") }, dom: null}]
}
const RightFunction = function(_opt){
const opt = this.opt = Object.assign(RightFunctionOpt, _opt);
this.dom = this.createDom();
this.initEvent(this.dom, "contextmenu");
this.initEvent(this.dom, "click");
this.render();
this.cssText = `
.\${xid} {
left: \${left};
top: \${top};
position: absolute;
border: solid 1px #000000;
border-radius: 3px;
}
.\${xid} div {
padding: 4px;
border-bottom: 1px #333333;
}
`;
this.datas = {
left: "0px",
top: "0px",
}
this.styled = this.createStyle();
}
RightFunction.prototype = {
createStyle (){
return new Styled(this.dom, this.cssText, this.datas);
},
createDom (){
const div = document.createElement("div");
div.className = "rightFunction";
return div;
},
render (){
const menu = this.opt.menu;
const optionDom = menu.map((options, i)=>{
return `<div data-index='${i}' data-click='selectOption'>${ options.name }</div>`;
})
this.dom.innerHTML = optionDom;
},
show (x, y){
this.datas.left = x + "px";
this.datas.top = y + "px";
this.styled.setDatas(this.datas);
this.opt.dom.appendChild(this.dom);
},
hide (){
this.dom.remove();
},
initEvent: initEvent,
selectOption (e){
const menu = this.opt.menu[e.target.dataset.index]
menu.value && menu.value(menu, this.opt.menu, this);
}
}
// ----------------------- 关于样式管理器
const Styled = function(dom, styled, datas, isOnlyXid){
this.xid = isOnlyXid || getXid();
this.dom = dom;
this.dom.classList.add(this.xid);
this.styled = styled.replace(/\t|\n/g,"");
this.datas = datas;
this.styleElement = this.createStyle();
this.render();
}
Styled.prototype = {
// 生成唯一识别码
createStyle (){
const o = document.head.querySelector("."+this.xid);
if( o ){
return o;
}
const style = document.createElement("style");
document.head.appendChild(style);
style.classList.add(this.xid);
return style;
},
setDatas(opt) {
this.datas = Object.assign(this.datas, opt);
this.render();
},
render (){
// this.styleElement.innerHTML = eval(`()=>{ var {${Object.keys(this.datas)}} = this.datas, xid=${this.xid}; return {} }()`);
this.styleElement.innerHTML = function(){
eval(`var {${Object.keys(this.datas)}} = this.datas, xid="${this.xid}"`);
return eval("\`"+this.styled+"\`");
}.call(this);
}
}
// 一个Div对象
class DivElement {
constructor(arg) {
this.dom = this.createElement();
// 数据模型
this.datas = {};
// 样式数据模型
this.styleDatas = {
position: "",
top: "",
left: "",
transformX: 0,
transformY: 0,
color: "",
width: "",
height: "100px",
fontWeight: "",
fontSize: "12px" ,
padding: "4px 4px 4px 4px",
margin: "4px 4px 4px 4px",
border: "solid 1px #f0f0f0",
borderTop: "solid 1px #ffffff",
borderBottom: "solid 1px #ffffff",
borderLeft: "solid 1px #ffffff",
borderRight: "solid 1px #ffffff",
backgroundColor: "",
backgroundImage: "",
};
// 样式模型
this.style = ` .\${xid} {
color: \${color};
font-size: \${fontSize};
font-weight: \${fontWeight};
// border: \${border};
border-top: \${borderTop};
border-bottom: \${borderBottom};
border-left: \${borderLeft};
border-right: \${borderRight};
padding: \${padding};
margin: \${margin};
height: \${height};
width: \${width};
background-color: \${backgroundColor};
background-image: url(\${backgroundImage});
background-repeat: no-repeat;
background-size: contain;
position: \${position};
top: \${top};
left: \${left};
transform: translate(\${transformX}px, \${transformY}px);
`;
// 样式模型
this.styled = new Styled(this.dom, this.style ,this.styleDatas);
// 蒋婷点击事件
this.initEvent(this.dom, "click");
}
isCanDrop (){
return this.styleDatas.position === "absolute";
}
editOptionChange (ary){
}
// 进入选中状态
showSelectModel (){
// 当前选中模式
selectModel.setPlugin(this);
// 弹出样式编辑
styleEdit.setPlugin(this);
}
clickDiv (){
this.showSelectModel();
}
createElement (){
const div = document.createElement("div");
div.setAttribute("data-click", "clickDiv");
return div;
}
render (){
}
// 渲染数据
renderData (){
}
// 渲染样式
renderStyle (){
}
initEvent (){
return initEvent.apply(this, arguments);
}
onstyleEdit (styleEdit){
// 编辑模块
this.editOption = [{
type: "computed", value: this.styleDatas, change: (e)=> { this.styleDatas = { ...this.styleDatas, ...e }; }
},{
type: "img", name: "背景图片", value: this.styleDatas.backgroundImage, change: (e)=> { this.styleDatas.backgroundImage = e }
},{
type: "select", name: "定位类型", value: this.styleDatas.position, options: [{ name: "全局定位", value: "absolute" }, { name: "默认定位", value: "" }], change: (e)=> { this.styleDatas.position = e; }
}];
styleEdit.onchange = ()=>{
this.styled.setDatas(this.styleDatas);
// 当前选中模式
selectModel.setPlugin(this);
};
}
}
// 选中,操作模块
class SelectModel {
constructor(plugin) {
this.parentPlugin = this.plugin;
this.dom = this.createElement();
this.style = `
.\${xid} { position: absolute; top: \${top}; left: \${left};
width: \${width}; height: \${height}; border: solid 1px #2e78ff;
transform: translate(\${offx}px, \${offy}px);
}
`;
this.styleDatas = { top: "0px", left: "0px", width: "0px", height: "0px", offx: 0, offy: 0 };
this.styled = new Styled(this.dom, this.style, this.styleDatas)
}
setDrop (Drop){
this.drop = new Drop(this.dom, this.dropMoveChange.bind(this), this.dropMoveChangeEnd.bind(this));
}
dropMoveChangeEnd (){
if( !this.plugin.isCanDrop() ) return;
this.styleDatas.top = getInt(this.styleDatas.top) + this.styleDatas.offy + "px";
this.styleDatas.left = getInt(this.styleDatas.left) + this.styleDatas.offx + "px";
this.styleDatas.offy = 0;
this.styleDatas.offx = 0;
this.styled.setDatas(this.styleDatas);
// 联动当前选中的组件
this.plugin.styleDatas.top = getInt(this.plugin.styleDatas.top) + this.plugin.styleDatas.transformY + "px";
this.plugin.styleDatas.left = getInt(this.plugin.styleDatas.left) + this.plugin.styleDatas.transformX + "px";
this.plugin.styleDatas.transformX = 0;
this.plugin.styleDatas.transformY = 0;
this.plugin.styled.setDatas(this.plugin.styleDatas);
}
dropMoveChange (x, y){
if( !this.plugin.isCanDrop() ) return;
this.styleDatas.offx = x;
this.styleDatas.offy = y;
this.styled.setDatas(this.styleDatas);
// 联动当前选中的组件
this.plugin.styleDatas.transformX = x;
this.plugin.styleDatas.transformY = y;
this.plugin.styled.setDatas(this.plugin.styleDatas);
}
createElement (){
const div = document.createElement("div");
return div;
}
// 获取当前组件的dom位置信息
getDomRects(dom){
return dom.getClientRects()[0];
}
setPlugin (plugin){
this.plugin = plugin;
const rect = this.getDomRects(plugin.dom);
this.styleDatas.width = rect.width + "px";
this.styleDatas.height = rect.height + "px";
this.styleDatas.top = rect.top + "px";
this.styleDatas.left = rect.left + "px";
this.styled.setDatas(this.styleDatas);
this.show();
}
show(){
document.body.appendChild(this.dom);
}
hide(){
this.dom.remove();
}
}
// 创建全局得选中管理器
const selectModel = new SelectModel();
// 关于style编辑器
class StyleEdit {
constructor(styleEditOption) {
// 需要控制的内容
this.editOption = [{ name: "颜色", inputType: "color" , type: "input" }];
this.editElement = [];
this.dom = this.createElement();
// this.initEvent(this.dom, "change");
this.getStyled();
}
getStyled (){
this.styled = new Styled(this.dom, `
.\${xid} {
position: absolute; top: 15px; right: 15px;
border: solid 1px #000000; padding: 8px;
background: #fff;
z-index: ${zoom[5]};
}`, {}, this.constructor.xid);
}
createElement (){
const div = document.createElement("div");
div.classList.add("styleEdit");
return div;
}
render (){
this.dom.remove();
this.dom.innerHTML = "";
// 根据配置的可编辑方案填补内容
this.editOption.forEach((e)=>{
this.dom.appendChild(this.getRenderDom(e));
})
}
getRenderDom (opt){
let el = this.editElement.find((element)=>{ return element.type === opt.type });
el = new el(opt);
el.setEdit(this);
return el.dom;
}
setPlugin (plugin){
this.plugin = plugin;
plugin.onstyleEdit(this);
this.editOption = plugin.editOption;
this.render();
this.show();
}
addElementType (EditElement){
this.editElement.push(EditElement);
}
show (){
document.body.appendChild(this.dom);
}
hide (){
this.dom.remove();
}
// 完整得组件变化调用事件 外部定义得 内部不需要实现
onchange (){ }
// 每一行变化触发
changeElement (){
this.onchange();
}
}
// 拖动插件
const Drop = function(dom, fn, efn){
this.dom;
this.cover = this.createCover();
this.setBox(dom);
this.onchange = fn;
this.onchengeEnd = efn;
}
Drop.prototype = {
createCover (){
const divCover = document.createElement("div");
divCover.style.cssText = `
position: absolute;
top:0;
left: 0;
width: 100%;
height:100%;
`
return divCover;
},
setBox : function(dom){
this.element = dom;
dom.addEventListener("mousedown",this.down.bind(this));
},
down : function(event){
// 再地图上创建一个全屏
document.body.appendChild(this.cover);
this._move = this.move.bind(this);
this._up = this.up.bind(this);
// this._enter = this.enter.bind(this);
// this.cover.addEventListener("mouseenter",this.__enter);
this.cover.addEventListener("mousemove",this._move);
this.cover.addEventListener("mouseup",this._up); // 绑定释放事件
},
enter (event){
this.startX = event.pageX;
this.startY = event.pageY;
},
up : function(event){
this.cover.removeEventListener("mousemove",this._move);
this.cover.removeEventListener("mouseup",this._up); // 绑定释放事件
// this.cover.addEventListener("mouseenter",this.__enter);
this._move = null;
this._up = null;
this.startX = null;
this.startY = null;
// this.__enter = null;
this.cover.remove();
this.onchengeEnd();
},
move : function(event){
if( !this.startX ) return this.enter(event);
var left = event.pageX - this.startX;
var top = event.pageY - this.startY;
console.log( this.startX, this.startY, event.pageX, event.pageY)
this.onchange(left, top);
}
}
selectModel.setDrop(Drop);
// 实例化样式编辑实例
const styleEdit = new StyleEdit();
// 输入框的编辑类型
class EditInputElement {
static type = "input";
static xid = getXid();
initEvent (){
return initEvent.apply(this, arguments);
}
constructor(arg) {
this.edit = null;
this.options = arg;
this.dom = this.getRenderDom(arg);
this.initEvent(this.dom, "change");
this.getStyled();
}
getStyled (){
this.styled = new Styled(this.dom, `
.\${xid} {
border: solid 1px #000000; padding: 8px;
}`, {}, this.constructor.xid);
}
getRenderDom ( { name, value, inputType } ){
const div = document.createElement("div");
div.classList.add("styleEditRow");
div.innerHTML = `
<div> ${name} </div>
<div> <input data-change='changeValue' value='${value}' type='${inputType}' ></div>
`
return div;
}
setEdit (edit){
this.edit = edit;
}
changeValue (options){
this.options.change(options.target.value);
this.edit.changeElement(options);
}
}
// 盒子模型编辑插件
class ComputedElement extends EditInputElement {
static type = "computed";
static xid = getXid();
constructor (arg){
super(arg);
this.initEvent(this.dom,"click");
}
getStyled (){
const xid = this.constructor.xid;
const options = this.options.value;
this.styleDatas = {
...options,
borderTop: options.borderTop.split(" "), // "border 1px #ffffff".split(" "),
borderBottom: options.borderBottom.split(" "), // "border 1px #ffffff".split(" "),
borderLeft: options.borderLeft.split(" "), // "border 1px #ffffff".split(" "),
borderRight: options.borderRight.split(" ") // "border 1px #ffffff".split(" "),
};
this.styled = new Styled(this.dom, `
.\${xid} {
border: solid 1px #000000; padding: 8px;
}
.\${xid} .computedBx_${xid} { height: 35px; line-height: 35px; position: relative;text-align: center; font-size: 12px; }
.\${xid} .top_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; left: 0px; width: 100%; height: 4px; background-color: \${borderTop[2]}; box-sizing: border-box; }
.\${xid} .bottom_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; bottom: 0px; left: 0px; width: 100%; height: 4px; background-color: \${borderBottom[2]}; box-sizing: border-box;}
.\${xid} .left_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; left: 0px; width: 4px; height: 100%; background-color: \${borderLeft[2]}; box-sizing: border-box;}
.\${xid} .right_${xid}{ position: absolute; padding: 2px; border: solid 1px #f0f0f0; top: 0px; right: 0px; width: 4px; height: 100%; background-color: \${borderRight[2]}; box-sizing: border-box;}
`, this.styleDatas, this.constructor.xid);
}
getRenderDom ( { name, value, inputType } ){
const div = document.createElement("div");
div.classList.add("styleEditRow");
const xid = this.constructor.xid;
const options = this.options.value;
div.innerHTML = `
<div> 盒子模型 </div>
<div class='computedBx_${xid}'>
<div data-click='border' data-bordertype='borderTop' class='top_${xid}'></div>
<div data-click='border' data-bordertype='borderBottom' class='bottom_${xid}'></div>
<div data-click='border' data-bordertype='borderLeft' class='left_${xid}'></div>
<div data-click='border' data-bordertype='borderRight' class='right_${xid}'></div>
<div data-click='border' data-borderType='border'>border颜色</div>
</div>
<div class='computedBx_wh'>
<div>
<div>宽度</div>
<input type='text' data-change='changeAttr' data-type='width' value='${options.width}' />
</div>
<div>
<div>高度</div>
<input type='text' data-change='changeAttr' data-type='height' value='${options.height}' />
</div>
</div>
`
return div;
}
// 改变属性
changeAttr (e){
this.styleDatas[e.target.dataset.type] = e.target.value;
this.changeValue(this.getJson());
}
border (e){
let type = [e.target.dataset.bordertype];
if( type[0] == "border" ) { type = ["borderTop", "borderBottom", "borderLeft", "borderRight"] }
this.getColor().then((color)=>{
type.forEach((t)=>{
// 修改对应的样式
this.styleDatas[t][2] = color;
})
this.styled.setDatas(this.styleDatas);
this.changeValue(this.getJson());
})
}
getJson (){
return {
...this.styleDatas,
borderTop: this.styleDatas.borderTop.join(" "),
borderBottom: this.styleDatas.borderBottom.join(" "),
borderLeft: this.styleDatas.borderLeft.join(" "),
borderRight: this.styleDatas.borderRight.join(" "),
}
}
getColor (){
return new Promise((resovle)=>{
const ipt = document.createElement("input");
ipt.type = "color";
ipt.onchange = function(e){
resovle(e.target.value);
}
ipt.click();
})
}
changeValue (options){
this.options.change(options);
this.edit.changeElement(options);
}
}
const fs = new FileReader();
// 图片编辑类型
class ImageElement extends EditInputElement {
static type = "img";
static xid = getXid();
constructor (arg){
super(arg);
this.initEvent(this.dom,"click");
}
getStyled (){
const xid = this.constructor.xid;
const options = this.options.value;
this.styleDatas = {
imagesUrl: options
};
this.styled = new Styled(this.dom, `
.\${xid} {
margin: 4px;
padding: 8px;
height: 150px;
width: 100%;
}
.computedBx_\${xid} { background: url(\${imagesUrl}); width: 100%; height: 100%; background-repeat: no-repeat;background-size: contain;}
`, this.styleDatas, this.constructor.xid);
}
getRenderDom ( { name, value, inputType } ){
const div = document.createElement("div");
div.classList.add("styleEditRow");
const xid = this.constructor.xid;
div.innerHTML = `
<div> ${name} </div>
<div class='computedBx_${xid}' data-click='updateImage'>
</div>
`
return div;
}
getImage (){
return new Promise((resovle)=>{
const ipt = document.createElement("input");
ipt.type = "file";
ipt.onchange = function(e){
// resovle(e.target.value);
fs.onload = function(e){
resovle(e.currentTarget.result);
}
fs.readAsDataURL(e.target.files[0]);
}
ipt.click();
})
}
updateImage (){
this.getImage().then((base64)=>{
this.styleDatas.imagesUrl = base64;
this.styled.setDatas(this.styleDatas);
this.changeValue(this.styleDatas);
})
}
changeValue (options){
this.options.change(options.imagesUrl);
this.edit.changeElement(options.imagesUrl);
}
}
// 多选
// 图片编辑类型
class SelectElement extends EditInputElement {
static type = "select";
static xid = getXid();
constructor (arg){
super(arg);
this.initEvent(this.dom,"click");
}
getStyled (){
const xid = this.constructor.xid;
const options = this.options.value;
this.styleDatas = {
// imagesUrl: options
};
this.styled = new Styled(this.dom, `
.\${xid} {
margin: 4px;
padding: 8px;
height: 150px;
width: 100%;
}
`, this.styleDatas, this.constructor.xid);
}
getRenderDom ( { name, value, options } ){
const div = document.createElement("div");
div.classList.add("styleEditRow");
const xid = this.constructor.xid;
div.innerHTML = `
<div> ${name} </div>
<div class='computedBx_${xid}'>
<select data-change="changeValue">
${ options.map((e)=> `<option ${e.value === value && "selected"} value='${e.value}'>${e.name}</option>` ) }
</select>
</div>
`
return div;
}
changeValue (options){
this.options.change(options.target.value);
this.edit.changeElement();
}
}
// input编辑类型
styleEdit.addElementType(EditInputElement);
styleEdit.addElementType(ComputedElement);
styleEdit.addElementType(ImageElement);
styleEdit.addElementType(SelectElement);
return exports;
}, window)