# MagicalCoder 可视化布局器 VIP可见
    此为markdown文档，请使用Typora软件访问 或者支持markdown的软件皆可
    
    感谢您选择MagicalCoder可视化布局器,我们提供完善的文档和在线技术支持，保证把布局器集成到您的业务中
    
    初识布局器您可以迫切期望以下问题
    
    1 如何启动
        参考三
    
    2 如何集成到自己系统
        只要您的系统支持浏览器访问 或者内嵌浏览器那就能内嵌进来
        布局器无后端，纯js css html开发而成 您可能要关注的是 如果保存页面数据和下次重新初始化
        可参考第2节 基于xxx的使用文档
    
    3 自定义组件和属性
        参考 六、七
    
    4 更多灵活定制化
        阅读第3/4节 熟练使用布局器API文档与回调接口，与布局器交互
    
    5 是否支持其他UI嵌入
        参考第6节
    
    6 其他
        浏览器兼容：chrome firefox 等各类浏览器都兼容
                 IE9+ (目前大多数互联网公司仅兼容ie10以上，政府机关主流兼容ie9,少部分要求ie8)
                 开发期间请使用chrome获取最优体验
                 实际上还是评估一下使用此布局器的用户群体，如果是内部运营，运维等人员 完全可以统一使用chrome


## 1.1 更新记录

|更新时间|更新版本|新增特性|修复问题|变更文件|
|--|--|--|--|
| 20191230 | v2.2.3 | 增加自由定位 增加拖拉控制宽高 | 条形图悬停无辅助图标| magical-drag-all.css,magical-coder-drag-all.js|
| 20191220 | v2.2.2 | 增加js可视化编辑器 增加事件 | | |

## 1.2 目录结构

### 1.2.1 说明

    代码在nginx/html目录下，如果您仅仅使用layui,那么您只需要关注目录下的magicaldrag目录即可
    如果您使用element那么您需要同时关注lowcode 和 assets目录
    因为element的布局器是基于magicaldrag基础上扩展新增了js脚本布局器，可以让你的布局器更加强大

### 1.2.2 目录详情

    magicaldrag 布局器最精简目录，是纯布局器组件的核心库，也可以独立运行起来
        assets
            drag
            	ui
            		magicalcoder 为了扩展ui没有的新功能，我们提供了一些样式来增强我们布局器功能
                js
                    user 您需要特别关注此目录
                        api.js 布局器的api
                        callback.js 与布局器交互的回调
                        key.js
                        application-env.js 环境变量
                        api-demo.js 演示示例
                        xxx
                            constant.js  布局器主要通过此json数据扩展的，比如自定义组件和属性 各种开关
                            iframe-ui.js 被iframe-xxx.html引入，用来处理每种ui的个性化逻辑 比较初始化js等
    
        index-xxx.html 是入口页 包含以下多个可用于不同业务场景的文件
            index-layui.html layui布局器的启动入口页(基于jquery)  不能使用脚本布局器
            index-element.html element布局器的启动入口页(基于vue) 不能使用脚本布局器
    
            index-page.html 扩展的element布局器入口 可以使用脚本布局器
            index-code.html 扩展的脚本编辑器入口
            index-form.html 扩展的建表单设计器
    
        iframe-xxx.html 这个是被index-xxx.html嵌入的 根据您的需要自行查看代码 布局器采用iframe嵌入方式，所以需要您以http方式来访问入口页
            iframe-element-2.10.1.html      嵌入index-element.html
            iframe-layui-2.5.4.html         嵌入index-layui.html
            iframe-page-element-2.10.1.html 嵌入index-page.html
            iframe-page-layui-2.5.4.html    嵌入index-code.html
    
        start.html 示例入口页
    
    -- 以下目录的代码完全开源 便于您自行扩展和修改--
    lowcode 扩展的新模块，包含了element的可视化脚本编辑器 纯表单设计器
        form 纯表单设计器
        page 扩展的element可视化布局器
        code 扩展的JS脚本编辑器
    
    assets lowcode所依赖的一些资源

## 1.3 启动布局器

### 1.3.1 说明
    布局器采用内嵌iframe，增强了布局器的兼容性和解耦性，不过现代浏览器不支持直接打开iframe有跨域问题，所以我们只要以http方式来访问index-xxx.html即可

### 1.3.2 方式一 使用nginx代理启动

    您拿到我们的布局器 本身已经放在nginx的html下，只要双击启动.bat后,浏览器访问http://localhost:12345/start.html即可

### 1.3.3 方式二 使用idea等工具来启动

    找到start.html 右上角从浏览器打开即可访问

​	![](img/使用IDEA启动布局器方法.gif)

## 1.4 运行方式

    最终目标：通过浏览器可以访问到index-xxx.html页面，把布局器加载出来，同时又能访问到您的后端接口地址

### 1.4.1 方式一 针对前后端分离的项目

    利用nginx做代理
    您可以参考我们的conf/nginx.conf配置 这样就可以浏览器访问布局器地址
    
            location / {
                root   html;
                index  start.html start.htm index.html;
            }

### 1.4.2 方式二 传统未分离的项目

    例如springboot项目 静态资源与html分离 并且修改html引入的资源地址
    static
        assets
        lowcode
        magicaldrag
    templates
        xxx.html
    这可能需要您改动一下xxx.html中的相对引入地址 虽然有点小麻烦，不过肯定是没问题的

## 1.5 集成方式

    我们布局器不像其他的一些js组件，new一个实例，本身布局器页面已经比较庞大了，所以足够作为一个独立的页面存在
    所以您只需要了解 让自己的其他页面 跳转到布局器页面 适当的携带一个参数比如pageId即可，剩下的就交给我们布局器了
    另外 您需要关注 如何把布局器数据保存到后端数据库 以及下次进来如何去从数据库取出数据初始化布局器 具体请参考第2节

### 1.5.1 方式一 页面跳转

    您可以采用页面跳转方式，从上一个业务页面跳转到当前布局器，在url中带参数（推荐）

### 1.5.2 方式二 iframe嵌入

    您也可以使用iframe在当前页面集成此布局器的地址<iframe url="布局器地址"></iframe>.通过iframe来与布局器交互（稍麻烦）

### 1.5.3 方式三 js弹窗

    如果您使用layui:
             var layer = layui.layer;
                $("#openMagicalDrag").click(function () {
                    layer.open({
                        maxmin:true,
                        type:2,
                        content:"/layui?stageFrom=magical_editor&targetElemId=editorHtml"
                    })
                })
            //点击保存按钮的回调
            MagicalCallback.prototype.save_html = function (html, rootNode) {
                window.parent.document.getElementById("父页面的某个结构")
            }


## 1.6 如何自定义组件

    您可能迫切期望如何知道自定义组件
    自定义组件的逻辑在：constant.js 请您打开一个constant.js 对比查看会更加清晰
    注意 不同的index-xxx.html 会加载不同位置的contant.js 比如请至index-xxx.html查看具体加载了哪个位置的constant.js
    例如 index-page.html 加载了lowcode/page/constant.js
    index-layui.html加载了magicaldrag/assets/drag/js/user/ui/layui/2.5.4/constant.js

### 1.6.1 变量配置

#### 1.6.1.1 配置this.components 则会自动出现在左侧拖拽区域

    {
        name:"图表",
        children:[
            {
                name:"条形图",
                icon:"assets/drag/img/left/other1.png",
                html:"<div class='echarts-bar' style='height:400px;'></div>"
            }
        ]
    }
    具体对应关系非常容易看到，主要是html 就是您控件所依赖的html部分，后续咱们的配置就跟着这个为基础
![](img/左侧可拖拽组件.png)

#### 1.6.1.2 配置this.tagClassMapping 影响导航树 控件展现行为等


    key:identifier 支持 标签名 样式名 属性名=属性值 三种方式 尽量保证每个组件唯一
    value:{
        dragInto:是否允许拖拽插入其他元素
        duplicate:是否允许向下复制 不支持函数 duplicateAttr:[属性名] 向下复制需要重新给一个新的属性值 比如id 因为全局不允许重复id
        assistAdd:true|false 鼠标悬停辅助新增孩子按钮
        assistDuplicate:true|false 鼠标悬停辅助向下复制按钮
        assistDelete:true|false 鼠标悬停辅助删除按钮
        assistZoom:true|false 鼠标悬停辅助缩放按钮
        canNotOperate:true|false 设置当前机构是否允许拖拽 默认false
        canZoom:是否允许缩放
        canDelete:是否允许删除此节点
        copy:是否允许拷贝此节点操作
        paste:是否允许其他结构粘贴进来
        onlyParents:[identifier]只能插入到哪些父结构下
        onlyChildren:[identifier] =只接受哪些孩子结构插入进来
        bind:用于vue的动态绑定参数名,针对一些控件必须绑定数据才行否则vue报错
        addOneItems：新增一个条目 ctrl+i array key:新增到哪个identifier下,空表示当前节点的children下 value:有时会新增多个自孩子，html:要添加进去的孩子结构 focus代表添加后是否聚焦孩子
        treeExtraName：额外追加到导航树的内容  attr:当前结构的属性名的值 text:是否追加第一个文本内容
        primary：权限 当一个结构上出现了多种可能(标签名 样式名 属性名=属性值)的identifier映射
                如何确定用哪一个，此时可以使用primary,数值越大权限越高 高权限会覆盖低权限的节点
                成为默认首选
                例如：<a class="test"></a>  那如果出现以下配置 到底哪个才应该被选用节点呢
                "test":{name:"测试"}
                "a":{name:"超链接"}
                此时我们可以这么配置 如果我们点击工作区<a class="test"></a>结构
                则test可被优先选为节点 其他情况则使用a超链接节点
                "test":{primary:1,name:"测试"}
                "a":{name:"超链接"}
        tmpWrapTag:'div'临时包裹标签 有些结构被layui写的是平级 放在了元素标签下面 这样拖拽插件就察觉不到 此时需要我们包裹一个临时标签 比如select layui-table这些
        wrapAfterResize: 当拖拽后是否源码处外面加一个包裹标签 比如layui的select 我们直接在select加上width并不生效，因为layui没把width传递，所以要想实现下拉框宽自定义 只能外面包裹一下，element就比较好只要加在tag上就自动传播出去
    }

####  1.6.1.3 配置this.rightPanel 则控制右侧属性

    /*type:控件类型 参考
            this.type = {
                TEXT:"text",
                TEXTAREA:"textarea",
                SELECT:"select",
                TEXT:"text",
                CHECKBOX:"checkbox",
                SWITCH:"switch",
                COLOR_PICKER:"colorpicker",
                CHECKBOX_ARRAY:"checkbox_array",//一般用于['a','b'] 或者[1,2]这种数组配置 暂时配合chang:ATTR使用 样式还不支持
                FILEUPLOAD:"fileupload",
                HTML:"html",
                SLIDER:"slider",
            }
    
    clearAttr:删除按钮
    extra:layui组件对应的扩展配置
    attrName:属性名
    oneLine:是否在一行
    options: [n:name(属性名|样式名) t:title（标题） c:checked时候的值(string|boolean) u:unchecked时候的值(string|boolean)]
    extend:true是否启用扩展配置 ,extendKey:"icon",如果是扩展配置icon
    htmlAttrs:[{"属性名":"属性值"}] html的标准属性 比如您希望只读 {"readonly":""}
    validate:{"正则表达式":"如果不匹配则弹出提示警告"} 比如：validate:{"^[a-zA-Z][a-zA-Z0-9_]*$":"请输入字母数字或者下划线"}

####        1.6.1.4 参考已经配置的各种场景组件;便可解决您大部分常用配置

####        1.6.1.5 js初始化 具体位置在iframe-ui.js中

    我们知道 如果我们写的js组件，仅仅有html还不够，还需要写js去读取html的属性，然后用自己的逻辑初始化
    初始化有两种方式：
        显式：让用户可以在脚本模块修改初始化逻辑
        隐式：用户不能再修改，一切都在自己封装的js库里进行
    
    显式方式：做法参考iframe-ui.js中的defaultJavascript内容，把初始化组件的逻辑追加到后面
    隐式：请在iframe-xxx.html中引入您的js库，在此库里做好封装，
        找到iframe-ui.js中的IframeUi.prototype.render方法，此方法是每次布局器有变更后都会调用此方法刷新一次布局器，
        把您的初始化方法加到eval(javascript);后面

## 1.7 自定义组件示例

    先让我们来自定义一个组件，然后配合第六段落更深入的理解
    
        echarts统计组件条形图
![](img/自定义示例1.png)

        说明：
        定制一个新组件，首先您要熟悉此组件的初始化方式，其次才是开始集成到布局器
        我们要关注 截图中的 1 2 3个位置


 ### 1.7.1 步骤


        第一步
            位置1：先让组件出现在左侧可拖拽的区域
            开发方式：打开constant.js找到this.components变量，我们加一个配置
                {
                    name:"条形图",//名称
                    icon:"assets/drag/img/left/other1.png",//小图标
                    html:"<div class='echarts-bar' style='height:400px;'></div>"//实际的组件html代码 后续就是根据这一串来进行初始化
                }


        第二步
            位置2：控制组件的导航树关键信息
            开发方式：打开constant.js找到this.tagClassMapping变量，我们加一个key value配置
                "echarts-bar":{name:"条形图",dragInto:false,duplicate:true, copy:true,      paste:false,    canDelete:true},
                注意:"echarts-bar"是这个组件的主键，可以是class 也可以是标签名称入input button等 总之您要保证它的命名空间唯一，不要跟其他组件冲突
                后面则是此组件的配置
                name:"条形图" 导航树节点名称
                dragInto：false 不允许其他组件拖入到本组件
                duplicate：true 右键可以向下复制
                paste:false 不允许其他组件粘贴到本组件
                canDelete:true 可以删除


        第三步
            位置3：控制组件在右侧属性栏显示的各类属性 此模块灵活度很高，一定要参考已有的配置，找到一个跟自己要的场景很相像的 拷贝一下
            开发方式：找到 this.rightPanel 我们在属性标签处加一个条形图的配置
            "echarts-bar":[
                            {type:this.type.TEXT      ,clearAttr:true       ,oneLine:true
                              ,change:this.change.ATTR
                             ,title:'标题',attrName:'text',placeholder:"标题"               },
                          ]
            解析：type:this.type.TEXT 代表是一个输入框属性配置
                clearAttr:true 右侧有删除清空按钮
                oneLine:true   标题和配置在同一行 为了好看
                change:this.change.ATTR  说明我们配置后 会在标签上加个属性
                ,title:'标题'   属性配置的标题
                attrName:'text'  我们将在<div class='echarts-bar' text=''>加text属性配置
                placeholder:"标题" 输入框的placeholder


        第四步
            显式初始化echarts-bar 因为此条形图使用了echarts库，所以我们要先引入此js开源库
            1 打开iframe-xxx.html 查看已经引入了 <script type="text/javascript" src="assets/drag/js/lib/echarts/4.2.1/echarts.min.js"></script>
            2 第二步 打开iframe-ui.js 我们看一下显式初始化(就是用户在使用布局器时 在脚本处可以看到您的这块初始化逻辑，并能自己修改 )
                把this.defaultJavascript内容拷贝出来看看
                //初始化图表 如果您未使用echarts可以删除此方法
                    renderEcharts: function () {
                        // 基于准备好的dom，初始化echarts实例
                        var echartsBars = document.getElementsByClassName('echarts-bar');
                        if (echartsBars && echartsBars.length > 0) {
                            for (var i = 0; i < echartsBars.length; i++) {
                                var myChart = echarts.init(echartsBars[i]);
                                // 指定图表的配置项和数据
                                var option = {
                                    title: {
                                        text: '集成ECharts 入门示例'
                                    },
                                    tooltip: {},
                                    legend: {
                                        data: ['销量']
                                    },
                                    xAxis: {
                                        data: ["衬衫",
                                            "羊毛衫",
                                            "雪纺衫",
                                            "裤子",
                                            "高跟鞋",
                                            "袜子"]
                                    },
                                    yAxis: {},
                                    series: [{
                                        name: '销量',
                                        type: 'bar',
                                        data: [5,
                                            20,
                                            36,
                                            10,
                                            10,
                                            20]
                                    }]
                                };
                                // 使用刚指定的配置项和数据显示图表。
                                myChart.setOption(option);
                            }
                        }
                    }
            这是echarts官方标准的初始化代码，那怎么改造一下，把我们配置的text集成进来呢？
                //初始化图表 如果您未使用echarts可以删除此方法
                    renderEcharts: function () {
                        // 基于准备好的dom，初始化echarts实例
                        var echartsBars = document.getElementsByClassName('echarts-bar');
                        if (echartsBars && echartsBars.length > 0) {
                            for (var i = 0; i < echartsBars.length; i++) {
                                var myChart = echarts.init(echartsBars[i]);
                                var chartsElem = $(echartsBars[i]);
                                var configText = chartsElem.attr("text");
                                // 指定图表的配置项和数据
                                var option = {
                                    title: {
                                        text: configText
                                    },
                                    tooltip: {},
                                    legend: {
                                        data: ['销量']
                                    },
                                    xAxis: {
                                        data: ["衬衫",
                                            "羊毛衫",
                                            "雪纺衫",
                                            "裤子",
                                            "高跟鞋",
                                            "袜子"]
                                    },
                                    yAxis: {},
                                    series: [{
                                        name: '销量',
                                        type: 'bar',
                                        data: [5,
                                            20,
                                            36,
                                            10,
                                            10,
                                            20]
                                    }]
                                };
                                // 使用刚指定的配置项和数据显示图表。
                                myChart.setOption(option);
                            }
                        }
                    }
            然后把代码替换对应this.defaultJavascript中echarts初始化部分。保存下试试看 您改动属性就生效啦
            相信您看完已经大概知道怎么做了 注意我这里用jquery方式取属性text,当然如果您用vue获取其他框架，请自行取属性
                var chartsElem = $(echartsBars[i]);
                var configText = chartsElem.attr("text");
                // 指定图表的配置项和数据
                var option = {
                    title: {
                        text: configText
                    },
    
            主要改动了以上地点，目的就是到结构上取出我们预先配置的属性，然后在初始化的时候，用配置的内容


            从上面的方式可以看出 如果您的业务非常强调用户个性化修改，那可以采用，如果您的业务不需要用户写脚本，那么推荐下一种隐式方式


###            1.7.2 隐式初始化

            第一步：新建myecharts.js 注意请发挥您的想象 不一定要跟我下面的方式一模一样，毕竟面向对象吗，
                    另外您可能还要去后端拉取数据后来初始化ecahrts等都是这个思路，您可以在这个组件加上后端接口下拉，让用户选择后在这里
                    xAxis.data处通过ajax数据拿到后来初始化
                内容：
                function renderEcharts () {
                    // 基于准备好的dom，初始化echarts实例
                    var echartsBars = document.getElementsByClassName('echarts-bar');
                    if (echartsBars && echartsBars.length > 0) {
                        for (var i = 0; i < echartsBars.length; i++) {
                            var myChart = echarts.init(echartsBars[i]);
                            var chartsElem = $(echartsBars[i]);
                            var configText = chartsElem.attr("text");
                            // 指定图表的配置项和数据
                            var option = {
                                title: {
                                    text: configText
                                },
                                tooltip: {},
                                legend: {
                                    data: ['销量']
                                },
                                xAxis: {
                                    data: ["衬衫",
                                        "羊毛衫",
                                        "雪纺衫",
                                        "裤子",
                                        "高跟鞋",
                                        "袜子"]
                                },
                                yAxis: {},
                                series: [{
                                    name: '销量',
                                    type: 'bar',
                                    data: [5,
                                        20,
                                        36,
                                        10,
                                        10,
                                        20]
                                }]
                            };
                            // 使用刚指定的配置项和数据显示图表。
                            myChart.setOption(option);
                        }
                    }
                }
            第二步：在iframe-xxx.html的<head></head>引入进来
            例如：目录请自行放置 不推荐放我们布局器里 否则下次更新要考虑
                <!--echarts 图表工具 这个已经有了-->
                <script type="text/javascript" src="assets/drag/js/lib/echarts/4.2.1/echarts.min.js"></script>
                <script type="text/javascript" src="assets/drag/js/business/user/ui/layui/2.5.4/myecharts.js"></script>
            第三步：调用renderEcharts();
                找到iframe-ui.js新加如下位置
                    //使用eval才行
                    eval(javascript);
                    renderEcharts();
            第四步：刷新布局器试试看 已经生效了
    
            注意：var chartsElem = $(echartsBars[i]);
            这个其实是采用了jquery方式，实际上请您自己来使用比如原生dom方式获取这个有哪些属性配置

### 1.7.2 进阶动态从后端拿到数据 初始化到echarts

    很多人不知道怎么动态的从后端拿数据来初始化控件
    其实很简单，只要您熟悉jquery的ajax 或者axios等工具即可,
    我们布局器更多的解决布局问题，而真正跟您的实际业务强关联的场景，我们允许您编写自己js来处理

    如果您选择layui(jquery)
    function renderEcharts () {
        // 基于准备好的dom，初始化echarts实例
        var echartsBars = document.getElementsByClassName('echarts-bar');
        if (echartsBars && echartsBars.length > 0) {
            for (var i = 0; i < echartsBars.length; i++) {
                $.get("这个是后端提供此控件的数据的地址",{看看echarts控件上配置了哪些参数，那么传给后端吧},function(resData){
                    //resData自己来控制吧 就是把后端的数据拿出来放这里
                    var myChart = echarts.init(echartsBars[i]);
                    var chartsElem = $(echartsBars[i]);
                    var configText = chartsElem.attr("text");
                    // 指定图表的配置项和数据
                    var option = {
                        title: {
                            text: configText
                        },
                        tooltip: {},
                        legend: {
                            data: resData.legendData
                        },
                        xAxis: {
                            data: resData.xAxisData
                        },
                        yAxis: {},
                        series: [{
                            name: '销量',
                            type: 'bar',
                            data: resData.seriesData
                        }]
                    };
                    // 使用刚指定的配置项和数据显示图表。
                    myChart.setOption(option);
                })
            }
        }
    }

    如果您选择element(vue) 使用axios具体可以搜搜它的用法
   function renderEcharts () {
       // 基于准备好的dom，初始化echarts实例
       var echartsBars = document.getElementsByClassName('echarts-bar');
       if (echartsBars && echartsBars.length > 0) {
           for (var i = 0; i < echartsBars.length; i++) {
               axios(
                {url:"这个是后端提供此控件的数据的地址",data:{看看echarts控件上配置了哪些参数，那么传给后端吧}}
               ).then(function(response){
                 //resData自己来控制吧 就是把后端的数据拿出来放这里
                 var resData = response.data;
                  var myChart = echarts.init(echartsBars[i]);
                  var chartsElem = $(echartsBars[i]);
                  var configText = chartsElem.attr("text");
                  // 指定图表的配置项和数据
                  var option = {
                      title: {
                          text: configText
                      },
                      tooltip: {},
                      legend: {
                          data: resData.legendData
                      },
                      xAxis: {
                          data: resData.xAxisData
                      },
                      yAxis: {},
                      series: [{
                          name: '销量',
                          type: 'bar',
                          data: resData.seriesData
                      }]
                  };
                  // 使用刚指定的配置项和数据显示图表。
                  myChart.setOption(option);

               })
           }
       }
   }



### 1.8 后端接口集成交互

    布局器的布局的数据需要持久化保存到数据库，同时再次编辑时也需要从数据库拉取加载出来，此时就需要跟后端交互
    
    如果您使用layui请查看《第2节 基于layui的使用文档.md》
    如果您使用element请查看《第2节 基于element的使用文档.md》
    
    强烈推荐element 当前最流行的前端框架


### 1.9  高阶定制

    请参考第3、4节

### 1.10 如何发布页面

    我们使用布局器开发完成页面后，需要将页面脱离布局器单独运行，
    此时最快的方式就是点击布局器右上角下载按钮，查看运行此布局器所依赖的资源

![](img/发布页面.png)

    不同的ui所依赖的资源都不同，您也可以把这些依赖的资源 用一个新文件夹单独提出来，这样看着更加规整

#### 1.10.1 layui

    <head>
        <!-- 可以让ui自适应手机屏幕-->
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <!-- 引入layui的样式-->
        <link href="/magicaldrag/assets/drag/ui/layui/2.5.4/css/layui.css" rel="stylesheet">
        <!-- 引入magicalcoder独有的样式 注意因为我们扩展的element部分场景的布局样式，所以就必须引入我们的一小部分样式-->
        <link href="/magicaldrag/assets/drag/ui/magicalcoder/1.1.0/magicalcoder.css" rel="stylesheet">
        <!-- 其他需要的资源-->
        <script type="text/javascript" src="/magicaldrag/assets/drag/js/lib/json3.js"></script>
        <script type="text/javascript" src="/magicaldrag/assets/drag/ui/layui/2.5.4/layui.all.js"></script>
        <!-- echarts 图标工具，请根据您自己的情况自行控制是否引入-->
        <script type="text/javascript" src="/magicaldrag/assets/drag/js/lib/echarts/4.2.1/echarts.min.js"></script>
    </head>
    <body>
        {拿出布局器保存的页面html}
        <script>{拿出布局器保存的javascript}</script>
    </body>
    
    PS:注意如果您购买了layui-admin，那么请在iframe-layui-xxx.html引入admin的样式 同时在此head也引入 那么我们布局器就支持了layui-admin

#### 1.10.2 element-ui
    <head>
        <!-- 可以让ui自适应手机屏幕-->
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <!-- 引入element的样式-->
        <link href="/assets/ui/element/2.10.1/element.css" rel="stylesheet">
        <!-- 引入magicalcoder独有的样式 注意因为我们扩展的element部分场景的布局样式，所以就必须引入我们的一小部分样式-->
        <link href="/magicaldrag/assets/drag/ui/magicalcoder/1.1.0/magicalcoder.css" rel="stylesheet">
        <!-- 如果您选择element高级版 可以拖拽编写js的那么请引入我们的这个js 如果您选择无可视化编写js可忽略此文件引入-->
        <script type="text/javascript" src="/assets/js/common.js"></script>
        <!-- 让vue兼容低版本浏览器-->
        <script type="text/javascript" src="/assets/ui/element/2.10.1/browser.min.js"></script>
        <script type="text/javascript" src="/assets/ui/element/2.10.1/browser-polyfill.min.js"></script>
        <!-- 其他需要的资源-->
        <script type="text/javascript" src="/assets/ui/element/2.10.1/json3.js"></script>
        <script type="text/javascript" src="/assets/ui/element/2.10.1/vue.js"></script>
        <script type="text/javascript" src="/assets/ui/element/2.10.1/element.js"></script>
        <script type="text/javascript" src="/assets/ui/element/2.10.1/axios.js"></script>
        <!-- echarts 图标工具，请根据您自己的情况自行控制是否引入-->
        <script type="text/javascript" src="/magicaldrag/assets/drag/js/lib/echarts/4.2.1/echarts.min.js"></script>
    </head>
    <body>
        <div id="magicalDragScene"><template>{拿出布局器保存的页面html}</template></div>
        <script>{拿出布局器保存的javascript}</script>
    </body>
    
    请注意：这个页面您可以做成模板或者其他方式 如果有问题可以咨询我们
    
    如果出现浏览器提示一些xss之类的可以考虑采用此方式转义 str.replaceAll("&lt;","<");或者str.replaceAll("<","&lt;");

### 1.11 建议

#### 1.11.1 如何无缝升级

    由于布局器会持续不断升级，但是我们的api 回调等会向前兼容，如果您直接把业务代码写在callback.js api.js那就非常糟糕了
    正确的做法是：
        1 重写callback.js提供的方法 新建一个您自己的js 在里面重写callback.js提供的方法
        比如 保存按钮点击
        MagicalCallback.prototype.save_html = function (html,rootNode,vueData) {
            //加入你自己的逻辑:保存到数据库等等
        }
        //布局器加载完成 会自动调用此方法
        MagicalCallback.prototype.after_start = function (api) {
            //您可以根据浏览器的入参，来初始化获取数据库保存的数据，来通api.insertHtml(您的html)来初始化布局器
        }
        2 在index-xxx.html的callback.js紧跟着引入此文件 这样就把callback.js的重写了 记得参数和名称一定要一致
