1С-Битрикс Управление сайтом: Интеграция верстки в компонент оформление заказа

22.10.2019

Проблема внедрения верстки в компонент оформления заказа заключается в том, что html код здесь создается динамически на лету с помощью javascript кода, написанного на библиотеке BX - собственной разработке компании 1С-Битрикс. 

Итак, мы копируем шаблон компонента в свой шаблон сайта в папке local и открываем templaete.php. Здесь мы увидим только заготовки блоков заказа. Да мы можем их менять местами друг с другом, но при этом дизайн самих полей не изменится, изменить порядок полей здесь мы не можем.

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

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

Поэтому нам надо:

  1. Переставить блоки как в верстке

  2. убрать скрытие блоков и кнопки перемещения между блоками

  3.  перенести поля из одного блока в другой по верстке

  4. добавить верстку в блоки и поля - классы стилей, дополнительные div, label

  5. проверить работоспособность оформления заказа

  6. исправить обработчики событий, если они не реагируют на новую верстку

  7.  протестировать разные варианты оформления заказа



В файле template.php блоки заказа отмечены комментариями: 
<!-- BASKET ITEMS BLOCK --> - корзина заказа - таблица товаров
<!-- REGION BLOCK --> - выбор типа плательщика и ввод местоположения - города покупателя
<!-- DELIVERY BLOCK --> выбор службы доставки
<!-- PAY SYSTEMS BLOCK --> выбор способа оплаты
<!-- BUYER PROPS BLOCK --> форма с полями для ввода данных покупателя
<!-- ORDER SAVE BLOCK --> итог и кнопка подтверждения заказа

Так как нам надо сделать все блоки открытым сразу, то кнопки далее или изменить под каждым блоком нам не нужны, можно удалить в каждом блоке код:

<div class="col-xs-12 col-sm-3 text-right"><a href="javascript:void(0)" class="bx-soa-editstep"><?=$arParams['MESS_EDIT']?></a></div>

Переставляем блоки как нужно по верстке и больше ничего в этом файле мы сделать не можем. далее находим в шаблоне файл order_ajax.js

Рассмотрим его логику:

Создается класс BX.Sale.OrderAjaxComponent, объект которого потом будет создан в template.php

При создании объекта этого класса будет вызвана функция init и в ней будут вызываться функции для заполнения блоков.

Первую интересную функцию мы видим 

initFirstSection: function() {
var firstSection = this.orderBlockNode.querySelector('.bx-soa-section.bx-active');
/*BX.addClass(firstSection, 'bx-selected');*/
this.activeSectionId = firstSection.id;
},

вторую строчку надо закомментировать, чтобы все блоки были активны, а не только первый.

Далее обратим внимание на функцию this.editOrder();

Во-первых, надо удалить код, который скрывает блок доставки, если доставки выдали ошибки вычислений:

/*if (this.result.DELIVERY.length > 0) {
   BX.addClass(this.deliveryBlockNode, 'bx-active');
   this.deliveryBlockNode.removeAttribute('style');
}
else
{
  BX.removeClass(this.deliveryBlockNode, 'bx-active');
   this.deliveryBlockNode.style.display = 'none';
}*/

Затем в этой функции вызывается создание каждого блока в цикле:

var sections = this.orderBlockNode.querySelectorAll('.bx-soa-section.bx-active'), i; for (i in sections)
{
if (sections.hasOwnProperty(i))
{
this.editSection(sections[i]);
}
}

Смотрим функцию this.editSection(sections[i]);

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


if (this.result.SHOW_AUTH && section.id != this.authBlockNode.id && section.id != this.basketBlockNode.id)
section.style.display = 'none';
else if (section.id != this.pickUpBlockNode.id)
section.style.display = '';



Во-вторых, видим что проверяется, активен ли блок var active = section.id == this.activeSectionId,

Нам надо, чтобы все блоки были активны, поэтому заменяем этот код на var active = true,

Далее отключаем реагирование на клик по заголовку блока: 

/*BX.unbindAll(titleNode);
if (this.result.SHOW_AUTH)
{
BX.bind(titleNode, 'click', BX.delegate(function(){
this.animateScrollTo(this.authBlockNode);
this.addAnimationEffect(this.authBlockNode, 'bx-step-good');
}, this));
}
else
{
BX.bind(titleNode, 'click', BX.proxy(this.showByClick, this));
editButton = titleNode.querySelector('.bx-soa-editstep');
editButton && BX.bind(editButton, 'click', BX.proxy(this.showByClick, this));
}*/

и далее видим, что вызываются функции для каждого блока:

switch (section.id)
{
case this.authBlockNode.id:

this.editAuthBlock();
break;

case this.basketBlockNode.id:

this.editBasketBlock(active);
break;

case this.regionBlockNode.id:

this.editRegionBlock(active);
break;

case this.paySystemBlockNode.id:

this.editPaySystemBlock(active);
break;

case this.deliveryBlockNode.id:

this.editDeliveryBlock(active);
break;

case this.pickUpBlockNode.id:

this.editPickUpBlock(active);
break;

case this.propsBlockNode.id:

this.editPropsBlock(active);
break;
}

Именно в этих функциях создаются поля каждого блока.

Рассмотрим как перенести поля ФИО, Телефон, E-mail в первый блок между выбором типа плательщика и вводом местоположения.

Первый блок создается функцией this.editRegionBlock(active);

А поля для данных покупателя создаются в функции this.editPropsBlock(active); предлагаю перенести выбор плательщика в блок полей данных покупателя и поставить этот блок первым в template.php.

Поэтому идем разбираться сначала в  функции this.editRegionBlock(active);

А в этой функции есть два варианта дальнейшего развития событий, либо наш блок открыт, либо скрыт. Все зависит от активности блока. поэтому в нашем случае остается один вариант - это вызов функции this.editActiveRegionBlock(true);

И вот только здесь мы наконец-то видим селекторы по верстке. Во-первых, получаем узел верстки, которую мы будем заполнять:


var node = activeNodeMode ? this.regionBlockNode : this.regionHiddenBlockNode,

далее идет очищение узла и заполнение снова. В узле нашего блока мы находим заготовку под контент или создаем ее:

regionContent = node.querySelector('.bx-soa-section-content');

if (!regionContent)
{
regionContent = this.getNewContainer();
node.appendChild(regionContent);
}
else
BX.cleanNode(regionContent);

Верстка создается на Bootstrap:

regionNode = BX.create('DIV', {props: {className: 'bx_soa_location row'}});
regionNodeCol = BX.create('DIV', {props: {className: 'col-xs-12'}});

Далее  видим вызов функции для создания радио кнопок выбора типа плательщика: 
В этой функции мы и добавляем свою верстку или классы стилей, чтоб стилизовать радио-кнопки.


this.getPersonTypeControl(regionNodeCol); - выбор типа плательщика,
this.getProfilesControl(regionNodeCol); - выбор профиля покупателя

Перенесем эти вызовы в функцию для блока полей данных. Смотрим this.editPropsBlock(active);


В начале функции все тоже самое, но для блока полей данных покупателя.

После создания получения заготовки под контент по селектору propsContent = node.querySelector('.bx-soa-section-content');

Добавляем в него наши радио-кнопки типов плательщиков 

this.getPersonTypeControl(propsContent);

Далее создается Div для полей данных

propsNode = BX.create('DIV', {props: {className: 'row'}});

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


propsNode = BX.create('DIV',  {props: {className: 'row form-section'}, children:[BX.create('H4',{props: {className: 'title'},children:["Личные данные"]})]});

И далее переходим к функции this.editPropsItems(propsNode);, где добавляются все свойства заказа.

Из нее мы заберем такие поля как индекс, адрес доставки, чтобы добавить их в блоке местоположения, после поля города. 

В этой функции мы видим, что создается такой объект массив groupIterator = this.propertyCollection.getGroupIterator() - это как раз все свойства заказа для выбранного типа плательщика.
Поэтому поставить заполнение полей раньше типа плательщика не правильно.

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


while (group = groupIterator())
{
propsIterator =  group.getIterator();
while (property = propsIterator())
{
if (
this.deliveryLocationInfo.loc == property.getId()
|| this.deliveryLocationInfo.zip == property.getId()
|| this.deliveryLocationInfo.city == property.getId()
)
continue;

this.getPropertyRowNode(property, propsItemsContainer, false);
}
}

Залогируем с помощью функции console.log( groupIterator); - увидим,  какие параметры имеют наши свойства и уберем из вывода в цикле адрес доставки или поля улица, дом, квартира и т.д.
Создадим массив с ID свойств, которые нужны для вывода в блоке данных покупателя:

var arr=["1","2","3","8","9","10","11","12","13","14","15"];
while (group = groupIterator())
{
propsIterator =  group.getIterator();
while (property = propsIterator())
{
/*личные данные*/
var id=property.getId();
if (arr.indexOf(id)>=0){
this.getPropertyRowNode(property, propsItemsContainer, false);
}
}
}

Как видим в методе this.getPropertyRowNode(property, propsItemsContainer, false); создается само поле для ввода значения.

В этой функции мы можем изменить верстку и добавить свои классы для стилизации поля.

Рассмотрим подробнее эту функцию:

Здесь создается div, который мы можем дополнить своей версткой.
и далее по типу поля вызываются отдельные методы. 
switch (propertyType)
{
case 'LOCATION':
this.insertLocationProperty(property, propsItemNode, disabled);
break;
case 'DATE':
this.insertDateProperty(property, propsItemNode, disabled);
break;
case 'FILE':
this.insertFileProperty(property, propsItemNode, disabled);
break;
case 'STRING':
this.insertStringProperty(property, propsItemNode, disabled);
break;
case 'ENUM':
this.insertEnumProperty(property, propsItemNode, disabled);
break;
case 'Y/N':
this.insertYNProperty(property, propsItemNode, disabled);
break;
case 'NUMBER':
this.insertNumberProperty(property, propsItemNode, disabled);
}

В случае с полями данных, это обычные текстовые поля, поэтому разберем подробнее метод: this.insertStringProperty(property, propsItemNode, disabled);

В данной функции input уже существует, он в нашей переменной property, мы его добавляем в созданный div  - propsItemNode,
и здесь же можем изменить  параметры, для этого добавим такой цикл для соответствия инпутов нашей верстке.
На самом деле здесь обрабатывается один инпут.


var pole =propsItemNode.querySelectorAll('input[type=text]');
if (pole.length){
for (i = 0; i < pole.length; i++){

/*pole[i].setAttribute("placeholder",property.getName());*/
if(property.getValue()!=""){
BX.addClass(pole[i],"filled");
}
if (property.isRequired()){
BX.addClass(pole[i],"required");
}
}
}

А теперь вернемся в метод this.editActiveRegionBlock(true); и добавим там поля адреса доставки:


После this.getDeliveryLocationInput(regionNodeCol); regionNode.appendChild(regionNodeCol);
добавляем код, аналогичный коду в полях данных покупателя, но только в массиве свойств заказа выбраны свойства адреса доставки для физ и юр лиц.

var id,group, property,propsIterator, groupIterator = this.propertyCollection.getGroupIterator();
var arr=["7","19","22","23","24","25","26","27"];
while (group = groupIterator()){
  propsIterator =  group.getIterator();
  while (property = propsIterator()) {
    id=property.getId();
    if (arr.indexOf(id)>=0){
      this.getPropertyRowNode(property, regionNode, false);
   }
 }
}

Итак поля мы переставили, теперь рассмотрим стилизацию блоков доставки и оплаты:

За их наполнение отвечают функции  this.editDeliveryBlock(active); и  this.editPaySystemBlock(active);

находим в них добавление чекбоксов и скрываем лишний контент, например картинку и блок описания. Блок описания удалять нельзя, чтоб не искать, где он заполняется при выборе другого способа оплаты и доставки.


....продолжение следует...


Возврат к списку


Материалы по теме: