代码操作组态界面图元对象;组态展示代码创建的对象;ht示例代码图元对象视图与组态图纸界面融合;代码对象以html dom元素添加到组态以及CSS控制;以ht.Grid为例派生类用渲染元素暴露属性可配置

1. 代码界面 VS 组态界面

1.1 代码API创建界面

数据可视化前端开发应用体验

1.2 组态拖拽生成界面

2. 代码界面显示到组态

以应用平台代码为例,步骤如下

2.1 原始示例js代码

HTML
<!DOCTYPE html>
<html>
    <head>
        <title>Grid</title>
        <meta charset="UTF-8">   
        <style>
            html, body {
                padding: 0px;
                margin: 0px;                
            }            
            .main {
                margin: 0px;
                padding: 0px;
                position: absolute;
                top: 0px;
                bottom: 0px;
                left: 0px;
                right: 0px;
            }
            .formpane {
                top: 10px;
                right: 10px;   
                background: rgba(230, 230, 230, 0.4);
            }             
        </style>     
        <script src="../../../../lib/core/ht.js"></script>   
        <script src="../../../../lib/plugin/ht-form.js"></script>
        <script>      
            
            for(var i=0; i<3; i++){
                ht.Default.setImage('book' + i, 'res/book' + i + '.jpg');
            }            
            
            function init(){                 
                dataModel = new ht.DataModel();
                graphView = new ht.graph.GraphView(dataModel);
                view = graphView.getView();            

                view.className = 'main';                
                document.body.appendChild(view);    
                window.addEventListener('resize', function (e) {
                    graphView.invalidate();
                }, false);  
                
                grid = new ht.Grid();
                grid.setSize(500, 240);
                grid.setStyle('grid.row.count', 2);
                grid.setStyle('grid.column.count', 5);
                grid.setStyle('grid.border', 8);
                grid.setStyle('grid.gap', 8);
                grid.setStyle('grid.depth', -5),
                grid.setStyle('grid.cell.depth', -1),  
                grid.setStyle('grid.cell.border.color', null);  
                grid.setStyle('grid.background', '#E5BB77');
                grid.setStyle('select.width', 0);
                dataModel.add(grid);
                
                for(var i=0; i<8; i++){
                    var node = new ht.Node();
                    node.setImage('book' + i);
                    node.setStyle('attach.row.index', Math.floor(i / 4));
                    node.setStyle('attach.column.index', i % 4);
                    node.setStyle('attach.padding', -2);
                    node.setStyle('select.width', 0);
                    node.setHost(grid);
                    dataModel.add(node);
                }
                
                graphView.translate(280, 150);
                                                                                
                formPane = new ht.widget.FormPane();
                view = formPane.getView();
                view.className = 'formpane';
                document.body.appendChild(view);
                formPane.setWidth(230);
                formPane.setHeight(200);             
                formPane.addRow(['grid.border',
                    {
                        slider: {                    
                            min: 0,
                            max: 30,
                            step: 1,
                            value: 8,
                            onValueChanged: function(){     
                                grid.s('grid.border', this.getValue());                                 
                            }
                        }
                    }
                ], [90, 0.1]);  
                formPane.addRow(['grid.gap',
                    {
                        slider: {                    
                            min: 0,
                            max: 30,
                            step: 1,
                            value: 8,
                            onValueChanged: function(){     
                                grid.s('grid.gap', this.getValue());                                 
                            }
                        }
                    }
                ], [90, 0.1]);
                formPane.addRow(['grid.depth',
                    {
                        slider: {                    
                            min: -10,
                            max: 10,
                            step: 1,
                            value: -5,
                            onValueChanged: function(){     
                                grid.s('grid.depth', this.getValue());                                 
                            }
                        }
                    }
                ], [90, 0.1]);    
                formPane.addRow(['grid.cell.depth',
                    {
                        slider: {                    
                            min: -10,
                            max: 10,
                            step: 1,
                            value: -1,
                            onValueChanged: function(){     
                                grid.s('grid.cell.depth', this.getValue());                                 
                            }
                        }
                    }
                ], [90, 0.1]);                 
            }
                        
            
        </script>
    </head>
    <body οnlοad="init();">
    </body>
</html>

2.2 复制到平台应用并微调

  • 修改相对路径的资源文件为了便于观察关键效果
  • 将实例化的new ht.DataModel()换成this.dm2d()
  • 将实例化的new ht.graph.GraphView()换成this.g2d()
  • 屏蔽掉document.body.appendChild、window.addEventListener相关代码
  • init()启动加到合适位置主动调用

JavaScript
    init2d() {
        let that = this;

        //1)修改相对路径的资源文件为了便于观察关键效果
        ht.Default.setImage('book0', 'symbols/demo/AiotOS/icons/system/platform-manage.json');
        ht.Default.setImage('book1', 'symbols/demo/AiotOS/icons/system/organize-main-data.json');
        ht.Default.setImage('book2', 'symbols/demo/AiotOS/icons/system/organization-tree.json');
        ht.Default.setImage('book3', 'symbols/demo/AiotOS/icons/system/organize.json');
        ht.Default.setImage('book4', 'symbols/demo/AiotOS/icons/system/price.json');
        ht.Default.setImage('book5', 'symbols/demo/AiotOS/icons/system/role-permissions.json');
        ht.Default.setImage('book6', 'symbols/demo/AiotOS/icons/system/project-manage.json');
        ht.Default.setImage('book7', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
        ht.Default.setImage('book8', 'symbols/demo/AiotOS/icons/system/template-manage.json');
        ht.Default.setImage('book9', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
        ht.Default.setImage('book10', 'symbols/demo/AiotOS/icons/system/image-gallery.json');

        function init() {
            //2)将实例化的new ht.DataModel()换成this.dm2d()
            // var dataModel = that.dm2d()
            var dataModel = new ht.DataModel();
            
            //3)将实例化的new ht.graph.GraphView()换成this.g2d()
            // var graphView = that.g2d()
            var graphView = new ht.graph.GraphView(dataModel);
           
            var view = graphView.getView();

            //4)屏蔽掉document.body.appendChild、window.addEventListener相关代码
            // view.className = 'main';
            // document.body.appendChild(view);
            // window.addEventListener('resize', function(e) {
            //     graphView.invalidate();
            // }, false);

            var grid = new ht.Grid();
            grid.setSize(500, 240);
            grid.setStyle('grid.row.count', 2);
            grid.setStyle('grid.column.count', 5);
            grid.setStyle('grid.border', 8);
            grid.setStyle('grid.gap', 8);
            grid.setStyle('grid.depth', -5),
                grid.setStyle('grid.cell.depth', -1),
                grid.setStyle('grid.cell.border.color', null);
            grid.setStyle('grid.background', '#E5BB77');
            grid.setStyle('select.width', 0);
            dataModel.add(grid);

            for (var i = 0; i < 8; i++) {
                var node = new ht.Node();
                node.setImage('book' + i);
                node.setStyle('attach.row.index', Math.floor(i / 4));
                node.setStyle('attach.column.index', i % 4);
                node.setStyle('attach.padding', -2);
                node.setStyle('select.width', 0);
                node.setHost(grid);
                dataModel.add(node);
            }

            graphView.translate(280, 150);
            var formPane = new ht.widget.FormPane();
            var view = formPane.getView();
            
            //需要加到浏览器全局的地方可换成组态指定视图GraphView的实例的getView()
            //document.body.appendChild(view);
            that.g2d().getView().appendChild(view);
            
            formPane.setWidth(230);
            formPane.setHeight(200);
            formPane.addRow(['grid.border',
                {
                    slider: {
                        min: 0,
                        max: 30,
                        step: 1,
                        value: 8,
                        onValueChanged: function() {
                            grid.s('grid.border', this.getValue());
                        }
                    }
                }
            ], [90, 0.1]);
            formPane.addRow(['grid.gap',
                {
                    slider: {
                        min: 0,
                        max: 30,
                        step: 1,
                        value: 8,
                        onValueChanged: function() {
                            grid.s('grid.gap', this.getValue());
                        }
                    }
                }
            ], [90, 0.1]);
            formPane.addRow(['grid.depth',
                {
                    slider: {
                        min: -10,
                        max: 10,
                        step: 1,
                        value: -5,
                        onValueChanged: function() {
                            grid.s('grid.depth', this.getValue());
                        }
                    }
                }
            ], [90, 0.1]);
            formPane.addRow(['grid.cell.depth',
                {
                    slider: {
                        min: -10,
                        max: 10,
                        step: 1,
                        value: -1,
                        onValueChanged: function() {
                            grid.s('grid.cell.depth', this.getValue());
                        }
                    }
                }
            ], [90, 0.1]);
        }

        //5)init()启动加到合适位置主动调用
        init()

    }

运行效果

3. 组态图纸与代码界面混合

3.1 组态图纸上最好勾选“事件处理”,保证鼠标交互事件传递到代码创建的组件中

 

3.2 拖放图标到组态图纸中

 

3.3 微调代码错开拖放的界面和代码创建的界面位置避免重叠

JavaScript
init2d() {
    let that = this;

    ht.Default.setImage('book0', 'symbols/demo/AiotOS/icons/system/platform-manage.json');
    ht.Default.setImage('book1', 'symbols/demo/AiotOS/icons/system/organize-main-data.json');
    ht.Default.setImage('book2', 'symbols/demo/AiotOS/icons/system/organization-tree.json');
    ht.Default.setImage('book3', 'symbols/demo/AiotOS/icons/system/organize.json');
    ht.Default.setImage('book4', 'symbols/demo/AiotOS/icons/system/price.json');
    ht.Default.setImage('book5', 'symbols/demo/AiotOS/icons/system/role-permissions.json');
    ht.Default.setImage('book6', 'symbols/demo/AiotOS/icons/system/project-manage.json');
    ht.Default.setImage('book7', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
    ht.Default.setImage('book8', 'symbols/demo/AiotOS/icons/system/template-manage.json');
    ht.Default.setImage('book9', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
    ht.Default.setImage('book10', 'symbols/demo/AiotOS/icons/system/image-gallery.json');

    function init() {
        var dataModel = that.dm2d()
        var graphView = that.g2d()
        var view = graphView.getView();

        // view.className = 'main';
        // document.body.appendChild(view);
        // window.addEventListener('resize', function(e) {
        //     graphView.invalidate();
        // }, false);

        var grid = new ht.Grid();
        grid.setSize(500, 240);
        grid.setStyle('grid.row.count', 2);
        grid.setStyle('grid.column.count', 5);
        grid.setStyle('grid.border', 8);
        grid.setStyle('grid.gap', 8);
        grid.setStyle('grid.depth', -5),
            grid.setStyle('grid.cell.depth', -1),
            grid.setStyle('grid.cell.border.color', null);
        grid.setStyle('grid.background', '#E5BB77');
        grid.setStyle('select.width', 0);
        dataModel.add(grid);

        for (var i = 0; i < 8; i++) {
            var node = new ht.Node();
            node.setImage('book' + i);
            node.setStyle('attach.row.index', Math.floor(i / 4));
            node.setStyle('attach.column.index', i % 4);
            node.setStyle('attach.padding', -2);
            node.setStyle('select.width', 0);
            node.setHost(grid);
            dataModel.add(node);
        }

        graphView.translate(500, 350);    //
        var formPane = new ht.widget.FormPane();
        var view = formPane.getView();

        that.g2d().getView().appendChild(view);
        view.style.cursor = 'pointer';    //css样式

        formPane.setWidth(230);
        formPane.setHeight(200);
        formPane.addRow(['grid.border',
            {
                slider: {
                    min: 0,
                    max: 30,
                    step: 1,
                    value: 8,
                    onValueChanged: function() {
                        grid.s('grid.border', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.gap',
            {
                slider: {
                    min: 0,
                    max: 30,
                    step: 1,
                    value: 8,
                    onValueChanged: function() {
                        grid.s('grid.gap', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.depth',
            {
                slider: {
                    min: -10,
                    max: 10,
                    step: 1,
                    value: -5,
                    onValueChanged: function() {
                        grid.s('grid.depth', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.cell.depth',
            {
                slider: {
                    min: -10,
                    max: 10,
                    step: 1,
                    value: -1,
                    onValueChanged: function() {
                        grid.s('grid.cell.depth', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
    }

    init()

}

运行效果

 

4. * 图纸拖放的图元对象传给并替换代码中实例化new的对象

从前面1~3我们知道,代码创建的界面,是完全可以给到组态图纸拖拽框架去展示的,无非就是dataModel、graphView本身是代码中new实例化对象,改成应用平台中获取组态图纸的dm2d和g2d对象替换过去即可(当然对于dm3d和g3d也一样)。同时去掉代码中document.body.appendChild()、window.addEventListener等对浏览器dom的全局操作,因为组态框架已经做了这些。

还有个问题,那就是代码中创建的图元比如ht.Node、ht.Data、ht.Grid等这些视图及其数据模型对应的数据元,如果在代码中实例化,一方面带有外观、位置等属性API代码操作更适合改成在组态中可视化拖拽配置、所见即所得;另一方面对数据模型图元的操作,非界面的比如图元之间层级逻辑关系、点击事件交互处理等,更适合代码来处理。

如果让图元对象在组态界面拖拽实例化,而不要通过代码new,这样界面相关的代码可以省掉直接拖拽编辑即可,同时也能在代码中访问图纸上的图标作为图元对象实例,通过API来做数据逻辑代码处理,这个结果就完美融合了代码逻辑和可视化配置两种方式各自的优势点!

现在具体讲实现方式:

4.1 组态界面拖放的图标类型都是ht.Data及其子类型

图纸上拖放的图元,基本都是ht.Node类型 + 图片image配置(对应API为setImage()),当前示例中ht.Grid就是从ht.Node派生的。图片配置就对应图标symbol,填充数据给ht.Node在数据模型中将界面展示到对应的View中。

4.2 数据模型ht.DataModel有getDataByTag()方法

该方法就是用来根据组态图纸中对图元可视化设置的tag属性,来获取实例化的对象!简言之,拖放一个图标到组态上,就是new了一个图元对象,通过tag就能在代码中获取到这个图元文件实例化的对象用于代码API操作!

4.3 本示例中更进一步,将Grid网格实例化放到组态上

首先要解决的问题就是ht.Grid类型如何能作为图标拖放到组态图纸上,因为目前拖放的绝大部分都是ht.Node类型,即时设置了tag,让代码获取到其对象实例,也没法代替代码中的new ht.Grid(),所以首先要解决组态工具条中扩展ht.Grid类型的内置组件图标

4.3.1 组态图标工具栏扩展内置指定类型的图标

如下图所示,要实现内置图标,并且是指定的ht.Grid类型,用于拖放一个或多个图标进来,结合不同的tag标签,实现在代码中需要多个new ht.Grid对象的地方,都可以通过dm.getDataByTag()用组态可视化拖拽生成的实例。

如下图所示,在client/config.js中,相应位置按照示例规则可以实现追加工具条中的内置图标组件。其中icon配置图标、type就是用来配置类型,这里就用我们需要的ht.Grid,然后iniData中可以配置初始化属性。

比如默认内置的image图片,可以默认为空,因为我们只需要ht.Grid类型对象给到代码,那么内容包括setImage()这个API的操作,都可以在代码中来设置这些属性了!

4.3.2 代码中根据标签tag获取到组态中拖放生成的实例对象

代码如下:

JavaScript
init2d() {

    let that = this;

    ht.Default.setImage('book0', 'symbols/demo/AiotOS/icons/system/platform-manage.json');
    ht.Default.setImage('book1', 'symbols/demo/AiotOS/icons/system/organize-main-data.json');
    ht.Default.setImage('book2', 'symbols/demo/AiotOS/icons/system/organization-tree.json');
    ht.Default.setImage('book3', 'symbols/demo/AiotOS/icons/system/organize.json');
    ht.Default.setImage('book4', 'symbols/demo/AiotOS/icons/system/price.json');
    ht.Default.setImage('book5', 'symbols/demo/AiotOS/icons/system/role-permissions.json');
    ht.Default.setImage('book6', 'symbols/demo/AiotOS/icons/system/project-manage.json');
    ht.Default.setImage('book7', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
    ht.Default.setImage('book8', 'symbols/demo/AiotOS/icons/system/template-manage.json');
    ht.Default.setImage('book9', 'symbols/demo/AiotOS/icons/system/structure-manage.json');
    ht.Default.setImage('book10', 'symbols/demo/AiotOS/icons/system/image-gallery.json');

    function init() {
        //1. 数据模型DataModel用平台应用的
        var dataModel = that.dm2d()     
        //2. 拓扑视图graphView用平台应用的   
        var graphView = that.g2d()
        var view = graphView.getView();
        
        //3. 网格图元grid也用平台应用组态界面拖拽生成的对象!
        // var grid = new ht.Grid();
        var grid = that.dm2d().getDataByTag('grid')

        //4. 去掉对网格图元的尺寸、位置等外观的API调用,由组态配置来决定
        // grid.setSize(600, 300);
        
        grid.setStyle('grid.row.count', 2);
        grid.setStyle('grid.column.count', 6);
        grid.setStyle('grid.border', 8);
        grid.setStyle('grid.gap', 8);
        grid.setStyle('grid.depth', -5);
        grid.setStyle('grid.cell.depth', -1);
        grid.setStyle('grid.cell.border.color', null);
        grid.setStyle('grid.background', '#E5BB77');
        grid.setStyle('select.width', 0);
        
        //5. 数据模型中去掉对该图元的add,因为组态拖放生成对象那一刻就是在数据模型的操作,自动add了!
        // dataModel.add(grid);

        for (var i = 0; i < 8; i++) {
            var node = new ht.Node();
            node.setImage('book' + i);
            node.setStyle('attach.row.index', Math.floor(i / 4));
            node.setStyle('attach.column.index', i % 4);
            node.setStyle('attach.padding', -2);
            node.setStyle('select.width', 0);
            node.setHost(grid);
            dataModel.add(node);
        }

        //6. 拓扑视图也去掉对位置等界面的操作,交给组态
        // graphView.translate(500, 500);
        
        var formPane = new ht.widget.FormPane();
        var view = formPane.getView();

        that.g2d().getView().appendChild(view);
        view.style.cursor = 'pointer';

        formPane.setWidth(230);
        formPane.setHeight(200);
        formPane.addRow(['grid.border',
            {
                slider: {
                    min: 0,
                    max: 30,
                    step: 1,
                    value: 8,
                    onValueChanged: function() {
                        grid.s('grid.border', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.gap',
            {
                slider: {
                    min: 0,
                    max: 30,
                    step: 1,
                    value: 8,
                    onValueChanged: function() {
                        grid.s('grid.gap', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.depth',
            {
                slider: {
                    min: -10,
                    max: 10,
                    step: 1,
                    value: -5,
                    onValueChanged: function() {
                        grid.s('grid.depth', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
        formPane.addRow(['grid.cell.depth',
            {
                slider: {
                    min: -10,
                    max: 10,
                    step: 1,
                    value: -1,
                    onValueChanged: function() {
                        grid.s('grid.cell.depth', this.getValue());
                    }
                }
            }
        ], [90, 0.1]);
    }

    init()
}

运行效果

对比4.3.1的编辑状态,结合代码API调用和图纸拖拽编辑,运行效果如下:

注意,为了保障通过that.g2d().getView().appendChild(view)添加的左上角滑动条事件拖放正常,需要对应用图纸勾选可选中和事件处理,如下图所示:

 

5. * 拖放到组态的(派生类)图元,内置API属性如何开放到图纸配置

到了第4步我们知道可以添加ht.Node的派生类ht.Grid到工具栏,拖放到图纸中能实例化Grid对象根据getDataByTag获取到data数据图元对象后就能用其API了,那么如何这些API能在图纸上配置而不是一定要代码编写调用呢?

首先想到的是渲染元素方案,可以任意将对象的属性暴露出来给图纸配置,但是这里我们并不需要渲染元素提供dom或ht ui组件的实例呈现界面并return对象,只需要用图元对象本身调用api,动态用属性并暴露,其实很简单,渲染元素不一定都得要return和实例化,可以直接用data即可,截图及代码分别如下:

 

JavaScript
data.setStyle('grid.gap',data.ca('grid.gap'))
data.setStyle('grid.row.count', data.ca('grid.row.count'));
data.setStyle('grid.column.count', data.ca('grid.column.count'));
data.setStyle('grid.border', data.ca('grid.border'));
data.setStyle('grid.gap', data.ca('grid.gap'));
data.setStyle('grid.depth', data.ca('grid.depth'));
data.setStyle('grid.cell.depth', data.ca('grid.cell.depth'));
data.setStyle('grid.cell.border.color', data.ca('grid.cell.border.color'));
data.setStyle('grid.background', data.ca('grid.background'));
data.setStyle('select.width', data.ca('grid.select.width'));

let i = 0
data.eachChild((node) => {
    node.setHost(data)
    node.setStyle('attach.row.index', Math.floor(i / data.ca('grid.column.count')));
    node.setStyle('attach.column.index', i % data.ca('grid.column.count'));
    node.setStyle('attach.padding', data.ca('node.attach.padding'));
    node.setStyle('select.width', data.ca('node.select.width'));
    i += 1;
})

可以看到,跟常规渲染元素模板差别很大,渲染元素可以是html dom对象,也可以是ht对象!渲染元素模板,以及设置渲染元素是否编辑状态随着图纸也矢量缩放;内置拖放矢量基本组件类型 ,完全可以只利用传入的data,也就是当前拖放到图纸上实例化的图元对象,直接调用API,用暴露的动态变量即可,无需实例化、以及return,也不需要用cache等其他传入参数!

此外,注意此渲染元素图标放到特定标记的目录分组,给config.js配置工具栏图标用的,用户直接拖放到图纸上,类型会是默认的ht.Node而并非ht.Grid,导致属性暴露后对应API调用传参不起作用甚至可能报错(可能存在派生类有但是基类没有的API接口):

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IOTOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值