Кастомизация
22
Май
1

D3.js: пузырьки

D3.js – JS-библиотека, которая используя технологии HTML, SVG и CSS помогает строит очень мощные визуальные конструкции. Сегодня я начну цикл статей как ее можно использовать с CRM. А начнем мы с пузырчатой диаграммы.

У стандартного объекта Организация есть лукап Головная организация, с помощью которого можно выстроить цепочку иерархии холдинговой структуры. С помощью D3.js выведем все Организации в диаграмме кружками, размер которых будет зависеть от Годового дохода каждой из них. При этом Организации относящиеся к одному холдингу будут нарисованы рядом и закрашены одним цветом.

Нам понадобится:

  • 10-ый ослик – потому что в нем имеются некоторые необходимы для D3 CSS3 свойства;
  • CRM 2011 с => 12 ролапом – потому что только после 12 ролапа CRM 2011 работает в 10-ом ослике не в режиме совместимости;

Далее создайте в CRM следующие Веб-ресурсы:

Создайте следующие HTM Веб-ресурс bubble.html:

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <style>
        body {
            text-align: center;
            margin: 0px;
            padding: 0px;
        }
        text {
            font: 10px sans-serif;
        }
    </style>
</head>
<body>
    <!-- Подключаем нужные JS Веб-ресурсы -->
    <script src="/WebResources/ClientGlobalContext.js.aspx" type="text/javascript" ></script>
    <script type="text/jscript" src="/WebResources/new_jquery1.9.1.min.js"></script>
    <script type="text/jscript" src="/WebResources/new_json2.js"></script>
    <script type="text/jscript" src="/WebResources/new_CrmRestKit.js"></script>    
    <script type="text/jscript" src="/WebResources/new_d3.v3.min.js"></script>
    <script>
        /*

        Формируем JSON-объект следующего вида из Организаций:
        {
            "name": "Organizations",
            "children": [
                {
                    "name": "Org1_root",
                    "children": [
                        {
                            "name": "Org1_root",
                            "size": 10000
                        },
                        {
                            "name": "Org2",
                            "size": 8000
                        }
                    ]
                },
                {
                    "name": "Org3_root",
                    "children": [
                        {
                            "name": "Org3_root",
                            "size": 11000
                        },
                        {
                            "name": "Org4",
                            "size": 4000
                        }
                    ]
                }
            ]
        }

        */
        var root = {
            "name": "Organizations"
        }
        var children = new Array();

        // Начинаем с возвращения Организаций не имеющих головных - т.е. верхушек холдинга
        CrmRestKit.ByQuery('Account', ['AccountId', 'Name', 'Revenue'], "ParentAccountId/Id eq null", false)
            .fail(window.globalFail)
            .done(function (data, status, xhr) {
                var results = data.d.results;

                for (var item in results) {
                    var childrenItem = new Object();
                    childrenItem["name"] = results[item].Name;

                    children2 = new Array();

                    var children2item = new Object();
                    children2item["name"] = results[item].Name;
                    children2item["size"] = results[item].Revenue.Value;
                    children2.push(children2item);
                    
                    // Запрашиваем всю цепочку иерархии начиная с головной
                    retrieveHierarchy(results[item].AccountId, children2);

                    childrenItem["children"] = children2;
                    children.push(childrenItem);
                }
            });
        root["children"] = children;

        // Скармиливаем JSON-объект D3
        var diameter = document.documentElement.offsetHeight,
            format = d3.format(",d"),
            color = d3.scale.category20c();

        var bubble = d3.layout.pack()
            .sort(null)
            .size([diameter, diameter])
            .padding(1.5);

        var svg = d3.select("body").append("svg")
            .attr("width", diameter)
            .attr("height", diameter)
            .attr("class", "bubble");
    
        var node = svg.selectAll(".node")
            .data(bubble.nodes(classes(root))
            .filter(function (d) { return !d.children; }))
            .enter().append("g")
            .attr("class", "node")
            .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });

        node.append("title")
            .text(function (d) { return d.className + ": " + format(d.value); });

        node.append("circle")
            .attr("r", function (d) { return d.r; })
            .style("fill", function (d) { return color(d.packageName); });

        node.append("text")
            .attr("dy", ".3em")
            .style("text-anchor", "middle")
            .text(function (d) { return d.className.substring(0, d.r / 3); });

        function classes(root) {
            var classes = [];

            function recurse(name, node) {
                if (node.children) node.children.forEach(function (child) { recurse(node.name, child); });
                else classes.push({ packageName: name, className: node.name, value: node.size });
            }

            recurse(null, root);
            return { children: classes };
        }

        d3.select(self.frameElement).style("height", diameter + "px");

        // Функция которая шерстит иерархию Организаций начиная с присланной корневой Организации
        function retrieveHierarchy(Id, children2) {

            CrmRestKit.ByQuery('Account', ['AccountId', 'Name', 'Revenue'], "ParentAccountId/Id eq guid'" + Id + "'", false)
            .fail(window.globalFail)
            .done(function (data, status, xhr) {
                var results = data.d.results;
                for (var item in results) {
                    var children2item = new Object();
                    children2item["name"] = results[item].Name;
                    children2item["size"] = results[item].Revenue.Value;
                    children2.push(children2item);

                    retrieveHierarchy(results[item].AccountId, children2);
                }
            });
        }
    </script>
</body>
</html>

Это основа и делает она следующие вещи:

  • Подключаем различные JS-библиотеки, которые мы загрузили в Веб-ресурсы;
  • Далее посредством REST-запросов формирует иерархию холдинга, начиная с Организаций у которых нет Головных организаций. Сформированную иерархию помещаем в JSON-объект, которую можно скормить D3 библиотеки;
  • Ну, и собственно формируем диаграмму.

Далее выгрузите сайтмап и добавьте в него ссылку на наш HTML Веб-ресурс:

<SubArea Id="AccSize" Url="$webresource:new_bubble.html" Icon="$webresource:new_circle16.png" Title="Размер клиентов" />

Идем смотреть…


Комментарии (1)
  • skfd 22.05.2013

    Круто. Если бы еще д3 c отчетами подружить. На сервере данные подготавливать, а на клиенте только показывать. Как это в ССРС работает…

*

code