Компонент «Внешний вид приложения»

Глобальному администратору системы предоставляется возможность настройки внешнего вида приложения средствами пользовательского интерфейса. Для настройки внешнего вида приложения нужно перейти на страницу /project/description/.

_images/projectdescription.png

Логотип (файл-картинка)

В левой части страницы существует возможность выбрать файл, который будет логотипом системы (кнопка «Выберите файл»). При этом должна стоять галочка в чек-боксе «Показывать логотип в шапке». Рекомендуемые размеры изображения - в районе 100*100 px.

Дополнительный css

В поле «Дополнительный css» возможна настройка стенда при помощи задания дополнительных стилей css. На любой странице системы к элементу body страницы приписываются классы:

<body class="path-URL user-USER_ID role-ROLE_ID">...</>

Здесь:

path-URL
URL - это текущий path, в котором / заменены на -
user-USER_ID
где USER_ID - id текущего пользователя
role-ROLE_ID
где ROLE_ID - id текущей роли

Если необходимо изменить стили для конкретной страницы, раздела, конкретного пользователя или роли, то к этим данным возможно обратиться через соответствующие классы тега body. Любой класс (вида .className {}; ), который мы описываем в project/desciption, при необходимости можно сделать видимым только на определенной странице(или при авторизации под определенным пользователем). Для этого перед стилем из ПД пишем название класса(через пробел), которое будет определять где(для кого) мы хотим применять эти стили:

.path-URL .className {
  //стили пользователя
}

Рассмотрим примеры:

  • Например, размер и отступы заголовка стенда, верхнего меню. Пример такой настройки:

    #branding h1 a { height: 5px; padding-top: 10px; padding-left: 100px; font-size: 40px; } #top-menu { padding-left: 110px; }
    
  • на странице дашбордов необходимо скрыть шапку(класс .header_wrap): мы обращаемся к ней как:

    .path-dashboard .header_wrap {display: none;}
    
  • то же самое, но конкретно для администратора:

    .user-85 .header_wrap {display:none;}
    

Таким образом, перед каждым стилем, который необходимо изменить, существует возможность указать тот объект, для которого конкретно это необходимо изменить: конкретный отчет, пользователь и т.д.

Под этим полем - чек-бокс «Отключить предпросмотр». Если сделать его активным, изменения не будут отображаться в реальном времени, а только после обновления страницы.

Пример 1

Изменение набора цветов легенды в сборе

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный css» следующий код:

.collect-task-new {
background-color: #58c158;
}
.collect-task-overdue {
background-color: #f76060;
}
.collect-task-deadline {
background-color: yellow;
}
.collect-task-returned {
background-color: #fcb047;
}

Пример 2

Задание фона картинкой в Личном кабинете и на странице авторизации.

Необходимо загрузить нужное изображение в реестр (это делается для того, чтобы не было ссылок на внешние источники) и скопировать путь в стили. Пользователь-администратор системы должен перейти в раздел /project/description/ и вставить в поле «Дополнительный css» следующий код:

.profile-content__wrapper {
background-image: url(http://netdb-milk.demo.chtd.ru/media/20171204T130304/wallpapers.png);
background-size: cover;
}
.content--authorization {
background-size: cover;
background-image: url(http://netdb-milk.demo.chtd.ru/media/20171204T130304/wallpapers.png);
}
.authorization-wrapper {
background-color: transparent;
}

Пример 3

Перекрашивание меню

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный css» следующий код:

/* основной цвет меню */
.sidebar {
background: #0a3546;
}
/* цвет иконок и текста в меню */
.main-navigation__link {
color: #4bc5dd;
}
/* цвет фона за логотипом */
.sidebar__header {
background: #4bbdd4;
}
/* цвет шапки(горизонтального подменю) */
.header {
background: #d8effb;
}
/* цвет кнопки сворачивания меню(фон) */
.main-navigation__close {
background: #036186;
}
/* цвет кнопки помощи(фон) */
.main-navigation__help {
background: #03455f;
}

/* выделение пункта меню "Обратиться в техподдержку" */

.li-support {
color: #a7090f !important;
background: #adacac;
}

.li-support:hover {
background: white !important;
}

в поле «Дополнительный JavaScript» также необходимо прописать:

$('#main-menu a[href="mailto:*********@****.com"]').addClass('li-support');

Пример 4

После открытия страницы в поле ввода необходимо автоматически подставлять значение «логин_дата_время»

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный css» следующий код:

/* убираем чекбокс(#search_all_tree) и скрываем кнопку "Добавить новый элемент"(#check_child_name) */
#add_child_form input#check_child_name,
#add_child_form input#search_all_tree {
display: none;
}
#cardholder_card {
padding-left: 10px;
}

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный js» следующий код:

$(document).on('mouseover', 'body', function() {
var login = $('a.user-block__nickname').text();
var d = new Date();
d.setTime(d.getTime() + (3*60*60*1000));
var d_form = d.toISOString().slice(0, -8).replace("T","_");

$('input[name="new_child_name"]').attr('value', login + '_' + d_form );
    //переименовываем кнопку "Все равно добавить" на "Создать ситуацию"
    $('input#submit_child_add').attr('value', 'Создать ситуацию');
    $('input[name="new_child_name"]').attr('style', 'height: 25px');
});

Пример 5

Необходимо убрать верхнюю границу у всех заголовков с названием «hide_top»

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный css» следующий код:

.report-grid .slick-headerrow .slick-complex-header-column[title=hide_top] {
   border-top-width: 0px !important;
   background: rgba(235,235,235) !important;
   pointer-events: none;
}

.report-grid .slick-headerrow .slick-complex-header-column[title=hide_top] .slick-complex-header-title {
   visibility: hidden;
}

.report-grid .slick-headerrow .slick-complex-header-column[title=hide_top] .slick-sort-indicator {
   display: none;
}

Назвать заголовки, которые хочется скрыть «hide_top».

Пример 6

Сделать прилипающий при скроллинге второй уровень меню на странице личного кабинета, списка отчетов, форм и реестров, как на странице отчета, реестра и формы

div.header {
    position: fixed;
    z-index: 99;
    right: 0;
    top: 0;
    left: 61px;
}

.sidebar {
    z-index: 100;
}

div#content {
    margin-top: 61px;
}

.profile-content__wrapper {
    top: 0;
}

Дополнительный JavaScript

Пользователю предоставляется возможность настроить систему, используя свои js-функции и методы, которые будут вставлены в тег <script> страницы. Например, вставка текста на странице, задание анимации, изменение структуры html и т.д.

Пример 1

Изменение набора цветов для графиков в массиве var colors = []; новые цвета

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный JavaScript» следующий код:

$(document).ready(function () {
if (window.d3) {
var colors = ['#548bc3', '#bc595b', '#9fbf64', '#8772ab', '#f3db85', '#54924e','#6ebdbc','#c1728d', '#ff9600','#a9f39b','#cca2d2', '#da8159'];
d3.scale.category10 = function() {
return d3.scale.ordinal().range(colors);
};
d3.scale.category20 = function() {
return d3.scale.ordinal().range(colors);
};
}
if (window.d3v4) {
d3v4.schemeCategory10 = colors;
}
});

Пример 2

Необходимо скрыть горизонтальное меню(шапку) на странице дашборда

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный JavaScript» следующий код:

if(location.href.match(/dashboard/) && !location.href.match(/groups/)) {

$("<style> #contents {top: 0 !important;} .header { display: none;}</style>").appendTo("head");

}

Пример 3

Необходимо в паспорте задания сбора с определенной зоной ответственности и с определенным статусом отображать заданные кнопки

Рассмотрим конкретный пример.

Пользователю с определенной ролью (например «Контролер») в паспортах заданий сбора с определенной зоной ответственности (Например «Качество») и Заданным статусом (например, «новое») должны отображаться кнопки:

  • «Задание выполнено»
  • «Прикрепить/изменить файлы»

После нажатия кнопки «Задание выполнено» (задание сбора переведено в статус «выполнено» и «закрыто» ) у пользователя с этой ролью пропадают все кнопки из паспорта задания.

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный JavaScript» следующий код:
$(document).ready(function() {

  var ROLE_ID = '153296';
  var roleFound = false, areaName;

  // Таблица с данными паспорта
  var passportTable = $('.collect-task-passport').first(),
      roleFound = false, areaName;

  // Есть такая роль у пользователя
  $.each($('body').attr('class').split(/\s+/), function(index, item) {
    var affix = item.substring(0, 4),
        role_id = item.substring(4);
    if ((affix == 'role') && (role_id == '153296')) {
      roleFound = true;
      return;
    }
  });

  // Зона ответственности
  var areaTd = $(passportTable).find('tbody > tr:nth-child(2) > td');
  if (roleFound && areaTd.length > 0) {
    areaName = $(areaTd).text();
    if (areaName == 'Качество (ОДК)') {
      // Прячем ссылки
      $(passportTable).find('tfoot > tr > td > a:not(.task-attach-files)').hide();

      // Прячем кнопки
      $.each($(passportTable).find('tfoot > tr > td > button:not(.task-attach-files)'), function(index, item) {
        var buttonText = $.trim($(item).text());
        if (buttonText != 'Задание выполнено') {
          $(item).hide();
        }
      });
    }
  }
});

Пример 4

Связи в двух реестрах

Представлено два реестра: первый реестр, в котором есть связи, и второй реестр, в котором эти связи должны отображаться.

Связи это массив связанных показателей-справочников из этого реестра. Связей может быть сколько угодно. При вводе данных во второй реестр необходимо при выборе значения первого показатели из связи, чтобы остальные показатели связи подставлялись в соотвествии выбранной связи. Реестры могут не иметь общие фильтры-параметры, а могут иметь один общий фильтр-параметр. Проверка соответствия реестра осуществляется максимум по одному общему фильтру-параметру.

Рассмотрим конкретный пример.

Первый реестр содержит показатель 1, показатель 2, показатель 3. Необходимо, чтобы при заполнении второго реестра при выборе показателя 1, в столбцах показатель 2 и показатель 3, предлагались только те значения, котороые были введены в первом реестр для выбранного значения показателя 1. Реестры имеют один общий фильтр-параметр «Показатель 4» Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный JavaScript» следующий код:

function pageIsRegistryEditor(id) {
   return $('body').hasClass('report-pagepath-registry-' + id + '-form');
}

function bindChoices(boundChoicesRegistry, choiceDescriptorsToBind, bindParamId, bindParam) {
   'use strict';
   report.headersRendered.subscribe(function (e, targetHeaders) {
       var requestData = {pageNum: 0, pageSize: 1000000000};
       if (bindParam) {
           requestData[bindParamId] = _.findWhere(
                   targetHeaders.params.conditions, {fieldDescr: bindParam}).fieldVal;
           requestData.rp_submit = 't';
       }
       $.get(
           '/registry/' + boundChoicesRegistry + '/view/compound/',
           requestData,
           function (response) {
               var sourceDescrColMap = {},
                   sourceColTargetColMap = {},
                   boundChoices,
                   grid = report.getGrid();

               _.each(response.headers.colsMetas, function (meta, idx) {
                   if (_.contains(choiceDescriptorsToBind, meta.descr)) {
                       sourceDescrColMap[meta.descr] = idx;
                   }
               });

               _.each(targetHeaders.colsMetas, function (meta, idx) {
                   if (_.contains(choiceDescriptorsToBind, meta.descr)) {
                       sourceColTargetColMap[sourceDescrColMap[meta.descr]] = idx;
                   }
               });

               boundChoices = _.unique(_.map(response.data.reportData.data, function (item) {
                   var itemData = {};
                   _.each(sourceDescrColMap, function (col, descr) {
                       itemData[sourceColTargetColMap[col]] = item[col].db_value;
                   });
                   return itemData;
               }));

               function setConstraints(cell) {
                   if (!_.contains(_.values(sourceColTargetColMap), cell.cell)) {
                       return;
                   }

                   var item = grid.getData().getItem(cell.row),
                       valuesByCol = {},
                       choices;

                   if (!item) {
                       return;
                   }

                   _.each(sourceColTargetColMap, function (col) {
                       if (col !== cell.cell) {
                           var value = item.getValue(col);
                           if (value) {
                               valuesByCol[col] = parseInt(value);
                           }
                       }
                   });

                   choices = _.unique(_.pluck(_.filter(boundChoices, function (choices) {
                       for (var col in valuesByCol) {
                           if (choices[col] !== valuesByCol[col]) {
                               return false;
                           }
                       }
                       return true;
                   }), cell.cell));

                   item.updateMeta(cell.cell, {constraints: choices});
               }

               grid.onClick.subscribe(function (e, args) {
                   setConstraints(args);
               });

               grid.onKeyDown.subscribe(function (e, args) {
                   setConstraints(args);
               });

               grid.onColumnsReordered.subscribe(function (e, args) {
                   var reorder = _.object(
                           _.values(args.reorder),
                           _.map(_.keys(args.reorder), function (val) {
                               return parseInt(val);
                           })),
                       getNewCol = function (col) {
                           return reorder[col];
                       };

                   for (var key in sourceColTargetColMap) {
                       if (sourceColTargetColMap.hasOwnProperty(key)) {
                           sourceColTargetColMap[key] = getNewCol(sourceColTargetColMap[key]);
                       }
                   }

                   boundChoices = _.map(boundChoices, function (choices) {
                       return _.object(_.map(_.keys(choices), getNewCol),
                               _.values(choices));
                   });
               });
           }, 'json');
   });
}

$(document).ready(function() {

 if (pageIsRegistryEditor(462183)) {
    bindChoices(427346, [234207, 234210, 384665], 'filter_1_0', -2001);
 }
});

Пояснения:

Самой важной является функция bindChoices(427346, [234207, 234210, 384665], „filter_1_0“, -2001).

  • Первый аргумент - реестр, из которого необходимо брать связи, а импенно 427346 - id реестра.
  • Второй - массив связанных показателей-справочников из этого реестра. То есть 234207 - это id показателя 1, 234210 - это id показателя 2,384665 - это id показателя 3.
  • Третий - «имя» параметра в реестре из первого аргумента. Его можно найти таким образом: изменить параметр(Показатель 4) в реестре ввода, посмотреть адресную строку и среди всех записей разделенных & и = - взять ту, которая отвечает за нужный параметр. Например, /registry/427346/form/#rp_submit=t&filter_1_0=76860 - тут filter_1_0.
  • Четвертый - показатель параметра, то есть Показатель 4.

Параметры второго реестра указываются в функции if (pageIsRegistryEditor(462183)), где 462183 - id второго реестра.

Пример 5

Автоматическая вставка значения из одного столбца во второй столбец при заполнении третьего столбца

При заполнении формы, необходимо значения из столбца под номером L формы вставлять соответственно значения в столбец под номером N, при условии , что заполнено значение в столбце M. Р ассмотрим конкретный пример.

Есть форма, у которой заполнен столбец 1. При заполнении столбца 3, необходимо вставлять значения из столбца один в столбец 11.

Для этого пользователю-администратору системы необходимо зайти в раздел /project/description/ и вставить в поле «Дополнительный JavaScript» следующий код:

(function () {
function setItemValue(item, col, value) {
   var meta = item.getMeta(col);
   meta.db_value = value;
   item.setMeta(col, meta, true, true);
}

function initAutoNameFromChoice(targetCol, editCol) {
   report.headersRendered.subscribe(function () {
       var form = report.getForm(),
           utils = NetdbSlickgrid.Utils(),
           targetField = utils.getColFieldByGridCol(targetCol, report),
           choiceCol = 1,
           choices = NetdbSlickgrid.Choices(),
           setTargetValue = function (cell) {
           if ((cell.editCommand.cell === editCol)||(cell.editCommand.cell === choiceCol)) {
               var item = cell.item;
               var choice = item.getMeta(choiceCol).db_value;
               if (choice) {
                   targetChoices = choices.get_values(item.getMeta(choiceCol).descr);
                   var value = choices.get_values(item.getMeta(choiceCol).descr)[choice][0];
                   if (!(item.getMeta(editCol).db_value)) {
                       value = '';
                   }
                   form.editCommandHandler(item, {field: targetField}, {
                       row: item.row,
                       cell: targetCol,
                       serializedValue: value
                   });

               }
            }
           };

       form.onChangesSynced.subscribe(function (e, cells) {
           _.each(cells.cells.dirtyCells, setTargetValue);
       });
   });
}

$(document).ready(function () {
   if ($('body').hasClass('report-pagepath-form-264285')) {
       initAutoNameFromChoice(11, 3);
   }
});
})();

Пояснения:

В данной коде необходимо указать:

  • id формы,в данном случае это ID=264285 , „report-pagepath-form-264285“
  • номер колонки, в которую автоматически вставляется значение, в данном случае это 11, initAutoNameFromChoice(11, 3)
  • номер колонки, которая должна заполниться, чтоб автоматическая вставка выполнилась, в данном случае это 3 , initAutoNameFromChoice(11, 3)
  • номер колонки, из которой должно браться значение, в данном случае это 1, choiceCol = 1

Заголовок и подзаголовок стенда

При установке приложения стенду по умолчанию присваивается заголовок «Демонстрационный стенд NetDB», который отображается в шапке страницы на всех видимых пользователю страницах стенда. Пользователь-администратор может изменить заголовок стенда на соответствующий назначению системы.

Переименовать стенд (изменить заголовок стенда) можно также в редакторе объектов */oditor/#o/-3217*

В поле «Подзаголовок» вводится подзаголовок, который также отображается в шапке страницы, под заголовком стенда.

Описание стенда

Администратор может задать описание стенда, которое будет отображаться в Личном кабинете пользователя (на главной странице системы).

Название заголовка «Описание стенда» можно изменить в соответствующем поле на странице /project/description/. В следующем поле «Описание» нужно ввести текст описания стенда. Его можно писать в rst-разметке и включать/отключать предпросмотр (чек-боксы под полем). Предпросмотр показывается слева от поля редактирования.

Описание стенда перед логином

В поле «Описание стенда перед логином» можно указать текст, который будет отображаться на входной странице приложения (рядом с вводом логина и пароля).

Справочник блоков избранных сущностей

Для того чтобы настроить блоки ключевых сущностей для основных областей деятельности, которые отображаются в Личном кабинете пользователя, необходимо создать справочник блоков избранных сущностей и указать его на странице настройки внешнего вида приложения /project/description/.

В справочнике блоков избранных сущностей для каждого блока нужно указать название одной из основных областей деятельности организации и список сущностей (дашбордов, отчетов, форм), относящихся к данной области деятельности, а также ссылку на файл, содержащий изображение - иконку для визуализации данного блока.

_images/Bloki.png

После того как все необходимые поля в /project/description/ заполнены, нужно нажать кнопку «Сохранить» вверху или внизу страницы. Все изменения вступят в силу.

Настройка темы оформления

В системе есть две темы оформления. Первый вариант оформления стенда выглядит следующим образом:

_images/old_inter_set.png

Второй вариант оформления стенда:

_images/new_inter_set.png

Для настройки второй темы оформления необходимо:

  1. Зайти в редактор объекта с id = -1060 (/oditor/-1060);
  2. Создать исходящую связь (add outlink)
_images/add_out_link.png
  1. Задать параметры связи:
_images/link_space.png
  1. Сохранить изменения (Yes, save link).

Замечание: если стенд создан до 2016 года, необходимо:

  1. Зайти в редактор объекта с id = -3227 (/oditor/-3227);
  2. Удалить входящую связь «Из списка».*

Замечание: далее нужно настроить меню.