D3.js: пузырьки
D3.js – JS-библиотека, которая используя технологии HTML, SVG и CSS помогает строит очень мощные визуальные конструкции. Сегодня я начну цикл статей как ее можно использовать с CRM. А начнем мы с пузырчатой диаграммы.
У стандартного объекта Организация есть лукап Головная организация, с помощью которого можно выстроить цепочку иерархии холдинговой структуры. С помощью D3.js выведем все Организации в диаграмме кружками, размер которых будет зависеть от Годового дохода каждой из них. При этом Организации относящиеся к одному холдингу будут нарисованы рядом и закрашены одним цветом.
Нам понадобится:
- 10-ый ослик – потому что в нем имеются некоторые необходимы для D3 CSS3 свойства;
- CRM 2011 с => 12 ролапом – потому что только после 12 ролапа CRM 2011 работает в 10-ом ослике не в режиме совместимости;
Далее создайте в CRM следующие Веб-ресурсы:
- jquery-1.9.1.min.js
- json2.js
- CrmRestKit.js – JS-библиотека, для более простого выполнения REST-запросов
- d3.v3.min.js
Создайте следующие 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="Размер клиентов" />
Идем смотреть…
Круто. Если бы еще д3 c отчетами подружить. На сервере данные подготавливать, а на клиенте только показывать. Как это в ССРС работает…