Прикрепление файлов
Обратите внимание
Документация на русском языке может быть устаревшей. Самые последние изменения доступны в документации на английском языке.
Возможно, от исполнителей вам требуется получить не только фотографии, но и видео- или аудиофайлы. В таком случае можно добавить блоки для прикрепления нужных файлов.
Было:
Стало:
Для вашего удобства мы подготовили код для шаблона «Мониторинг объектов в организациях», в котором каждое из полей добавлено по одному разу в первую кнопку для ответа. Используйте этот код для самопроверки. Наши вставки в этом коде вы можете найти поиском слова «кастомизация».
{{#if reviewMode}}
<div class="header-review">
<div class="header-review__title">
{{texts.task_title}}
</div>
<div class="header-review__buttons">
{{#if (equal verdict "ok")}}
<div class="header-review__btn header-review__btn_green">
{{texts.btn_ok.title}}
</div>
{{/if}}
{{#if (equal verdict "no_obj")}}
<div class="header-review__btn header-review__btn_red">
{{texts.btn_no_obj.title}}
</div>
{{/if}}
{{#if (equal verdict "no_org")}}
<div class="header-review__btn header-review__btn_red">
{{texts.btn_no_org.title}}
</div>
{{/if}}
</div>
</div>
{{else}}
<div class="header">
{{texts.task_title}}
</div>
{{/if}}
<div class="info">
{{#if reviewMode}}
<div class="info__review">
<div class="info__review-block">
<div class="info__title">
{{texts.info_name}}
</div>
<div class="info__content">
{{name}}
</div>
</div>
<div class="info__review-block">
<div class="info__title">
{{texts.info_address}}
</div>
<div class="info__content">
{{address}}
</div>
</div>
</div>
{{else}}
<div class="info__block">
<div class="info__title">
{{texts.info_name}}
</div>
<div class="info__content">
{{name}}
</div>
</div>
<div class="info__block">
<div class="info__title">
{{texts.info_address}}
</div>
<div class="info__content">
{{address}}
</div>
</div>
{{/if}}
<div class="info__block">
<div class="info__title">
{{texts.info_description}}
</div>
<div class="info__content">
{{product}}
</div>
</div>
<div class="info__block">
<div class="info__content">
<a href={{image}} target="_blank" class="info__link">Ссылка на изображение товара</a>
</div>
</div>
</div>
{{#if reviewMode}}
<div class="review">
<div class="review__map">
<div id="{{concat 'map_' id}}" style="width: 100%; height: 400px;"></div>
</div>
{{#if (equal verdict "ok")}}
<div class="review__block">
<div class="review__title">
{{texts.btn_ok.question_1.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_facade}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="review__block">
<div class="review__title">
{{texts.btn_ok.question_2.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_obj}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<!-- кастомизация начало фрагмента -->
<!-- фото -->
<div class="review__block">
<div class="review__title">
{{texts.btn_ok.question_new_imgs.title}}
</div>
<div class="review__imgs-grid">
{{#each new_imgs}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<!-- видео -->
<div class="review__block">
<div class="review__title">
{{texts.btn_ok.question_new_video.title}}
</div>
<div class="review__video-grid">
{{#each new_video}}
<div class="review__video-grid-item">
<video width="50%" controls>
<source src={{this}} type="video/mp4">
</video>
</div>
{{/each}}
</div>
</div>
<!-- аудио -->
<div class="review__block">
<div class="review__title">
{{texts.btn_ok.question_new_audio.title}}
</div>
<div class="review__audio-grid">
{{#each new_audio}}
<div class="review__audio-grid-item">
<audio src={{this}} controls type="audio/wav">Невозможно воспроизвести</audio>
</div>
{{/each}}
</div>
</div>
<!-- кастомизация конец фрагмента -->
{{/if}}
{{#if (equal verdict "no_obj")}}
<div class="review__block">
<div class="review__title">
{{texts.btn_no_obj.question_1.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_facade}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="review__block">
<div class="review__title">
{{texts.btn_no_obj.question_2.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_plate_or_address}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="review__block">
<div class="review__title">
{{texts.btn_no_obj.question_3.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_around_obj}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
{{#if comment}}
<div class="review__block">
<div class="review__title">
{{texts.btn_no_obj.question_4.title}}
</div>
<div class="review__comment">
{{field type="textarea" name="comment" width="100%" rows=5}}
</div>
</div>
{{/if}}
{{/if}}
{{#if (equal verdict "no_org")}}
<div class="review__block">
<div class="review__title">
{{texts.btn_no_org.question_1.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_around_org}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="review__block">
<div class="review__title">
{{texts.btn_no_org.question_2.title}}
</div>
<div class="review__imgs-grid">
{{#each imgs_plate_or_address}}
<div class="review__grid-item">
<div class="review__grid-inner">
<img src="{{this}}" class="review__img" data-rotationdeg="0">
<div class="review__rotate-panel">
<span class="review__rotate review__rotate_left">←</span>
<span class="review__rotate review__rotate_right">→</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="review__block">
<div class="review__title">
{{texts.btn_no_org.question_3.title}}
</div>
<div class="review__comment">
{{field type="textarea" name="comment" width="100%" rows=5}}
</div>
</div>
{{/if}}
</div>
{{else}}
<div class="main">
<div class="main__title">
Выберите вариант выполнения задания:
</div>
<div class="main__container">
<div class="main__popup main__popup_hidden">Не выбран ни один вариант ответа</div>
<div class="main__block">
<div class="main__btn main__btn_green">
{{texts.btn_ok.title}}
</div>
<div class="main__content">
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_ok.question_1.title}}
</div>
<div class="main__text">
{{texts.btn_ok.question_1.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_ok.question_1.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_facade" camera=true preview=true compress=false validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_ok.question_2.title}}
</div>
<div class="main__text">
{{texts.btn_ok.question_2.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_ok.question_2.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_obj" camera=true preview=true compress=false validation-show="top-left"}}
</div>
</div>
<!-- кастомизация начало фрагмента -->
<!-- фото -->
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_ok.question_new_imgs.title}}
</div>
<div class="main__text">
{{texts.btn_ok.question_new_imgs.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_ok.question_new_imgs.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="new_imgs" camera=true validation-show="top-left"}}
</div>
</div>
<!-- видео -->
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_ok.question_new_video.title}}
</div>
<div class="main__text">
{{texts.btn_ok.question_new_video.description}}
</div>
<div class="main__video">
{{field type="file" name="new_video" sources="CAMERA GALLERY" fileType="VIDEO" camera=true validation-show="top-left"}}
</div>
</div>
<!-- аудио -->
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_ok.question_new_audio.title}}
</div>
<div class="main__text">
{{texts.btn_ok.question_new_audio.description}}
</div>
<div class="main__audio">
{{field type="file" name="new_audio" sources="RECORDER FILE_MANAGER" fileType="AUDIO" validation-show="top-left"}}
</div>
</div>
<!-- кастомизация конец фрагмента -->
</div>
</div>
<div class="main__block">
<div class="main__btn main__btn_red">
{{texts.btn_no_obj.title}}
</div>
<div class="main__content">
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_no_obj.question_1.title}}
</div>
<div class="main__text">
{{texts.btn_no_obj.question_1.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_no_obj.question_1.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_facade" camera=true validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_no_obj.question_2.title}}
</div>
<div class="main__text">
{{texts.btn_no_obj.question_2.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_no_obj.question_2.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_plate_or_address" camera=true validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_no_obj.question_3.title}}
</div>
<div class="main__text">
{{texts.btn_no_obj.question_3.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_no_obj.question_3.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_around_obj" camera=true validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__content-title">
{{texts.btn_no_obj.question_4.title}}
</div>
<div class="main__text">
{{texts.btn_no_obj.question_4.description}}
</div>
<div class="main__comment">
{{field type="textarea" name="comment" width="100%" rows=5 validation-show="top-left"}}
</div>
</div>
</div>
</div>
<div class="main__block">
<div class="main__btn main__btn_red">
{{texts.btn_no_org.title}}
</div>
<div class="main__content">
<div class="main__content-block">
<div class="main__text main__text_req">
{{texts.btn_no_org.question_1.description}}
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_around_org" camera=true validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__text main__text_req">
{{texts.btn_no_org.question_2.description}}
</div>
<div class="main__ex">
<a href="{{texts.btn_no_org.question_2.example_link_1}}" target="_blank" class="main__ex-link">Пример</a>
</div>
<div class="main__imgs">
{{field type="file-img" name="imgs_plate_or_address" camera=true validation-show="top-left"}}
</div>
</div>
<div class="main__content-block">
<div class="main__content-title main__content-title_req">
{{texts.btn_no_org.question_3.title}}
</div>
<div class="main__text">
{{texts.btn_no_org.question_3.description}}
</div>
<div class="main__comment">
{{field type="textarea" name="comment" width="100%" rows=5 validation-show="top-left"}}
</div>
</div>
</div>
</div>
</div>
</div>
{{/if}}
var texts = {
'task_title': 'Мониторинг объектов в организациях',
'info_name': 'Название организации:',
'info_address': 'Адрес:',
'info_description': 'Описание объекта:',
'btn_ok': {
'title': 'Я нашел объект',
'question_1': {
'title': 'Фото фасада организации',
'description': 'Сделайте 2 фото фасада организации с разных сторон так, чтобы на фото была видна вывеска и название хорошо читалось.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_1_3-min.png'
},
'question_2': {
'title': 'Фото объекта',
'description': 'Сделайте минимум 2 фото объекта с разных сторон так, чтобы был полностью виден объект, его атрибуты, наполнение (если есть) и местоположение внутри организации.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_2-min.png'
},
// кастомизация начало фрагмента
'question_new_imgs': {
'title': 'Фото',
'description': 'Сделайте хотя бы 2 фотографии',
'example_link_1': 'ссылка на пример фото'
},
'question_new_video': {
'title': 'Видео',
'description': 'Запишите видео'
},
'question_new_audio': {
'title': 'Аудио',
'description': 'Запишите аудио'
}
// кастомизация конец фрагмента
},
'btn_no_obj': {
'title': 'Я в организации, но объекта нет',
'question_1': {
'title': 'Фото фасада организации',
'description': 'Сделайте 2 фото фасада организации с разных сторон так, чтобы на фото была видна вывеска и название хорошо читалось.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_1_3-min.png'
},
'question_2': {
'title': 'Фото таблички организации',
'description': 'Сфотографируйте табличку искомого адреса или информационный лист с адресом организации на входе.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_4_5-min.png'
},
'question_3': {
'title': 'Фото окружения',
'description': 'Сфотографируйте место, где должен находиться объект, со всех сторон так, чтобы можно было сделать однозначный вывод об отсутствии объекта.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_6-min.png'
},
'question_4': {
'title': 'Комментарий',
'description': 'Попробуйте узнать причину отсутствия объекта в организации и укажите в комментарии (например: не выставили, не привезли, стоит на складе и т.п.).'
}
},
'btn_no_org': {
'title': 'Организация закрыта или отсутствует',
'question_1': {
'title': 'Фото здания со всех сторон',
'description': 'Сфотографируйте со всех сторон здание или место, где должна находиться организация, так, чтобы можно было убедиться, что нужной организации нет. '
},
'question_2': {
'title': 'Фото таблички организации',
'description': 'Сфотографируйте табличку искомого адреса или информационный лист с адресом организации на входе.',
'example_link_1': 'https://mt-content-public.s3.yandex.net/instructions/toloka_field_templates/obj_org_4_5-min.png'
},
'question_3': {
'title': 'Обязательный комментарий',
'description': 'Напишите причину отсутствия или закрытия организации (например: на ремонте, закрыт, не удалось найти и пр.).'
}
}
};
// Максимальная удаленность исполнителя от магазина в километрах.
var MAX_DISTANCE = 1;
var verdictsOut = ['ok', 'no_obj', 'no_org'];
var injectMaps = function(locale) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.async = true;
script.src = "https://api-maps.yandex.ru/2.1/?load=package.full,vectorEngine.preload&lang=" + locale + "_" + locale.toUpperCase() + "&csp=true";
script.addEventListener('load', resolve);
script.addEventListener('error', reject);
script.addEventListener('abort', reject);
document.head.appendChild(script);
});
};
var map;
exports.Assignment = extend(TolokaAssignment, function (options) {
TolokaAssignment.call(this, options);
var workspaceOptions = this.getWorkspaceOptions();
if (workspaceOptions.isReviewMode) {
map = injectMaps('ru');
}
}, {});
exports.Task = extend(TolokaHandlebarsTask, function (options) {
TolokaHandlebarsTask.call(this, options);
}, {
getTemplateData: function() {
var data = TolokaHandlebarsTask.prototype.getTemplateData.apply(this, arguments);
var workspaceOptions = this.getWorkspaceOptions();
var outputValues = this.getSolution().output_values;
this.setSolutionOutputValue('coordinates', data.coordinates);
this.setSolutionOutputValue('address', data.address);
data.id = this.getTask().id;
data.texts = texts;
if (workspaceOptions.isReviewMode) {
data.reviewMode = true;
if (outputValues.imgs_facade && outputValues.imgs_facade.length > 0) {
data.imgs_facade = [];
for (var i = 0; i < outputValues.imgs_facade.length; i++) {
data.imgs_facade.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.imgs_facade[i] + '/preview');
}
}
if (outputValues.imgs_obj && outputValues.imgs_obj.length > 0) {
data.imgs_obj = [];
for (var i = 0; i < outputValues.imgs_obj.length; i++) {
data.imgs_obj.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.imgs_obj[i] + '/preview');
}
}
if (outputValues.imgs_plate_or_address && outputValues.imgs_plate_or_address.length > 0) {
data.imgs_plate_or_address = [];
for (var i = 0; i < outputValues.imgs_plate_or_address.length; i++) {
data.imgs_plate_or_address.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.imgs_plate_or_address[i] + '/preview');
}
}
if (outputValues.imgs_around_obj && outputValues.imgs_around_obj.length > 0) {
data.imgs_around_obj = [];
for (var i = 0; i < outputValues.imgs_around_obj.length; i++) {
data.imgs_around_obj.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.imgs_around_obj[i] + '/preview');
}
}
if (outputValues.imgs_around_org && outputValues.imgs_around_org.length > 0) {
data.imgs_around_org = [];
for (var i = 0; i < outputValues.imgs_around_org.length; i++) {
data.imgs_around_org.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.imgs_around_org[i] + '/preview');
}
}
if (outputValues.comment) {
data.comment = outputValues.comment;
}
if (outputValues.verdict) {
data.verdict = outputValues.verdict;
}
// кастомизация начало фрагмента
// фото
if (outputValues.new_imgs && outputValues.new_imgs.length > 0) {
data.new_imgs = [];
for (var i = 0; i < outputValues.new_imgs.length; i++) {
data.new_imgs.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_imgs[i] + '/preview');
}
}
// видео
if (outputValues.new_video && outputValues.new_video.length > 0) {
data.new_video = [];
for (var i = 0; i < outputValues.new_video.length; i++) {
data.new_video.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_video[i] + '/preview');
}
}
// аудио
if (outputValues.new_audio && outputValues.new_audio.length > 0) {
data.new_audio = [];
for (var i = 0; i < outputValues.new_audio.length; i++) {
data.new_audio.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_audio[i] + '/preview');
}
}
// кастомизация конец фрагмента
} else {
data.reviewMode = false;
var assId = this.getOptions().assignment._options.assignment.id;
var taskID = this.getTask().id;
var solutionStorage = this.storage.getItem('solution_' + assId);
if (solutionStorage && solutionStorage[taskID]) {
this.setSolutionOutputValue('verdict', solutionStorage[taskID].verdict);
}
}
return data;
},
initFastFileSelector: function() {
var $el = $(this.getDOMElement()),
sources = [],
type = '',
audioRecorder = $el.find('.audioRecorder');
$el.find('.field_file-img__upload_camera').each(function (i,el) {
$(el).on('click',function () {
sources = ['CAMERA'];
type = 'IMAGE';
})
});
$el.find(".field_file-img__label").prepend($("<span/>", {class: "field_file-img__upload gallery"}));
$el.find('.gallery').on('click',function () {
sources = ['GALLERY'];
type = 'IMAGE';
});
var baseGetFile = this.file.getFile.bind(this.file);
this.file.getFile = function(options) {
var promise = baseGetFile(
_.extend(options, {
sources: _.isEmpty(sources) ? ["GALLERY", "CAMERA"] : sources
})
);
sources = ["GALLERY", "CAMERA"];
return promise;
};
},
onRender: function() {
this.rendered = true;
var task = this.getDOMElement();
var that = this;
var workspaceOptions = this.getWorkspaceOptions();
var outputValues = this.getSolution().output_values;
if (workspaceOptions.isReviewMode) {
var reviewImgs = task.querySelectorAll('.review__grid-item');
var initMap = this.initMap.bind(this);
for (var i = 0; i < reviewImgs.length; i++) {
reviewImgs[i].addEventListener('click', this.handleImg);
}
map.then(function() {
ymaps.ready(initMap);
});
} else if (workspaceOptions.isReadOnly) {
var mainBlocks = task.querySelectorAll('.main__block');
var selectedBtnId = verdictsOut.indexOf(outputValues.verdict);
for (var f = 0; f < mainBlocks.length; f++) {
if (f === selectedBtnId) {
mainBlocks[f].querySelector('.main__content').classList.add('main__content_active');
mainBlocks[f].querySelector('.main__btn').classList.add('main__btn_active');
} else {
mainBlocks[f].classList.add('main__block_hidden');
}
}
this.initFastFileSelector();
} else {
var btns = task.querySelectorAll('.main__btn');
var mainBlocks = task.querySelectorAll('.main__block');
var assId = this.getOptions().assignment._options.assignment.id;
var taskID = this.getTask().id;
var solutionStorage = this.storage.getItem('solution_' + assId);
if (solutionStorage && solutionStorage[taskID]) {
if (solutionStorage[taskID].selectedBtnId >= 0) {
var selectedBtnId = solutionStorage[taskID].selectedBtnId;
for (var f = 0; f < mainBlocks.length; f++) {
if (f === selectedBtnId) {
mainBlocks[f].querySelector('.main__content').classList.add('main__content_active');
mainBlocks[f].querySelector('.main__btn').classList.add('main__btn_active');
} else {
mainBlocks[f].classList.add('main__block_hidden');
}
}
}
}
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', this.handleBtn.bind(this, i, mainBlocks));
}
this.initFastFileSelector();
task.querySelector('.main__popup').addEventListener('click', this.handleMainPopup);
}
},
initMap: function() {
var inputValues = this.getTask().input_values;
var outputValues = this.getSolution().output_values;
if (!inputValues.coordinates || inputValues.coordinates === '') {
return;
}
var coordinates = inputValues.coordinates.split(',');
var myMap = new ymaps.Map('map_' + inputValues.id, {
center: coordinates,
zoom: 15
});
var shop = new ymaps.GeoObject({
geometry: {
type: "Point",
coordinates: coordinates
},
properties: {
iconContent: 'Организация'
}
}, {
preset: 'islands#greenStretchyIcon'
});
myMap.geoObjects.add(shop);
if (outputValues.worker_coordinates) {
var workerCoordinates = outputValues.worker_coordinates.split(',');
var worker = new ymaps.GeoObject({
geometry: {
type: "Point",
coordinates: workerCoordinates
},
properties: {
iconContent: 'Исполнитель'
}
}, {
preset: 'islands#blueStretchyIcon'
});
myMap.geoObjects.add(worker);
}
},
handleImg: function(e) {
var img = e.currentTarget.querySelector('.review__img');
if (e.target.classList.contains('review__rotate_left')) {
img.dataset.rotationdeg = parseInt(img.dataset.rotationdeg, 10) - 90;
img.style.transform = 'rotate(' + img.dataset.rotationdeg + 'deg)';
} else if (e.target.classList.contains('review__rotate_right')) {
img.dataset.rotationdeg = parseInt(img.dataset.rotationdeg, 10) + 90;
img.style.transform = 'rotate(' + img.dataset.rotationdeg + 'deg)';
}
if (e.target.classList.contains('review__img') || e.target.classList.contains('review__grid-inner')) {
e.currentTarget.querySelector('.review__grid-inner').classList.toggle('review__grid-inner_zoomed');
}
},
handleBtn: function(i, mainBlocks, e) {
var mainContent = e.currentTarget.parentNode.querySelector('.main__content');
var outputValues = this.getSolution().output_values;
var task = this.getDOMElement();
var assId = this.getOptions().assignment._options.assignment.id;
var taskID = this.getTask().id;
var solutionStorage = this.storage.getItem('solution_' + assId);
var newSolution = {};
newSolution[taskID] = {};
if (!e.currentTarget.classList.contains('main__btn_active')) {
task.querySelector('.main__popup').classList.add('main__popup_hidden');
this.setSolutionOutputValue('verdict', verdictsOut[i]);
e.currentTarget.classList.add('main__btn_active');
mainContent.classList.add('main__content_active');
for (var j = 0; j < mainBlocks.length; j++) {
if (j !== i) {
mainBlocks[j].classList.add('main__block_hidden');
}
}
if (assId) {
if (!solutionStorage) {
newSolution[taskID].selectedBtnId = i;
newSolution[taskID].verdict = verdictsOut[i];
if (i === 0) {
newSolution[taskID].comment = '';
}
this.storage.setItem('solution_' + assId, newSolution, new Date().getTime() + 21600000);
} else {
if (!solutionStorage[taskID]) {
solutionStorage[taskID] = {};
}
solutionStorage[taskID].selectedBtnId = i;
solutionStorage[taskID].verdict = verdictsOut[i];
this.storage.setItem('solution_' + assId, solutionStorage, new Date().getTime() + 21600000);
}
}
} else {
var fields = this._fields;
var deleteBtnsLength = task.querySelectorAll('.main .file__delete').length;
for (var h = 0; h < deleteBtnsLength; h++) {
$(task).find('.main .file__delete').first().trigger('click');
}
this.setSolutionOutputValue('verdict', '');
this.setSolutionOutputValue('comment', '');
e.currentTarget.classList.remove('main__btn_active');
mainContent.classList.remove('main__content_active');
for (var key in fields) {
if (fields.hasOwnProperty(key)) {
for (var p = 0; p < fields[key].length; p++) {
fields[key][p].hideError();
}
}
}
for (var j = 0; j < mainBlocks.length; j++) {
if (j !== i) {
mainBlocks[j].classList.remove('main__block_hidden');
}
}
if (assId) {
if (!solutionStorage) {
newSolution[taskID].selectedBtnId = -1;
newSolution[taskID].verdict = '';
this.storage.setItem('solution_' + assId, newSolution, new Date().getTime() + 21600000);
} else {
if (!solutionStorage[taskID]) {
solutionStorage[taskID] = {};
}
solutionStorage[taskID].selectedBtnId = -1;
solutionStorage[taskID].verdict = '';
this.storage.setItem('solution_' + assId, solutionStorage, new Date().getTime() + 21600000);
}
}
}
},
// Функция определения расстояния между точками по их широте и долготе.
_getDistanceBetweenCoords: function(lat1, lon1, lat2, lon2) {
var Earth = 6371; // Radius of the Earth in km
var x =
(((lon2 - lon1) * Math.PI) / 180) *
Math.cos((((lat1 + lat2) / 2) * Math.PI) / 180);
var y = ((lat2 - lat1) * Math.PI) / 180;
return Earth * Math.sqrt(x * x + y * y);
},
// Функция определения расстояния между двумя точками.
_getDistance: function(coords1, coords2) {
var coordFirst = {
lat: parseFloat(coords1.split(",")[0]),
lon: parseFloat(coords1.split(",")[1])
};
var coordSecond = {
lat: parseFloat(coords2.split(",")[0]),
lon: parseFloat(coords2.split(",")[1])
};
var dist = this._getDistanceBetweenCoords(
coordFirst.lat,
coordFirst.lon,
coordSecond.lat,
coordSecond.lon
);
return dist;
},
checkUserPosition: function(inputValues, outputValues) {
if (outputValues["worker_coordinates"] &&
inputValues["coordinates"] && this._getDistance(outputValues["worker_coordinates"], inputValues["coordinates"]) > MAX_DISTANCE) {
return true;
} else {
return false;
}
},
addError: function (message, field, errors) {
errors || (errors = {
task_id: this.getOptions().task.id,
errors: {}
});
errors.errors[field] = {
message: message
};
return errors;
},
onValidationFail: function (errors) {
TolokaTask.prototype.onValidationFail.call(this, errors);
var task = this.getDOMElement();
_.each(errors.errors, function (error, fieldName) {
if (fieldName === '__TASK__') {
this.showTaskError(error.message);
} else if (fieldName === 'verdict') {
task.querySelector('.main__popup').classList.remove('main__popup_hidden');
} else {
var fields = this._fields[fieldName];
if (fields) {
for (var i = 0; i < fields.length; i++) {
fields[i].showError(error);
}
}
}
}.bind(this));
},
handleMainPopup: function(e) {
e.currentTarget.classList.add('main__popup_hidden');
},
validate: function (solution) {
this.errors = null;
var task = this.getDOMElement();
var input_values = this.getTask().input_values;
if (!solution.output_values.verdict || solution.output_values.verdict === '') {
this.errors = this.addError('Не выбран ни один вариант ответа', "verdict", this.errors);
} else if (solution.output_values.verdict === 'ok') {
if (!solution.output_values.imgs_facade || solution.output_values.imgs_facade.length === 0) {
this.errors = this.addError('Нужно приложить фото организации', "imgs_facade", this.errors);
} else if (solution.output_values.imgs_facade.length < 2) {
this.errors = this.addError('Должно быть хотя бы 2 фото организации', "imgs_facade", this.errors);
}
if (!solution.output_values.imgs_obj || solution.output_values.imgs_obj.length === 0) {
this.errors = this.addError('Нужно приложить фото объекта', "imgs_obj", this.errors);
} else if (solution.output_values.imgs_obj.length < 2) {
this.errors = this.addError('Должны быть хотя бы 2 фотографии объекта', "imgs_obj", this.errors);
}
// кастомизация начало фрагмента
if (!solution.output_values.new_imgs || solution.output_values.new_imgs.length === 0) {
this.errors = this.addError('Нужно приложить фото', "new_imgs", this.errors);
} else if (solution.output_values.new_imgs.length < 2) {
this.errors = this.addError('Должно быть хотя бы 2 фото', "new_imgs", this.errors);
}
if (!solution.output_values.new_video || solution.output_values.new_video.length < 1) {
this.errors = this.addError('Нужно приложить видео', 'new_video', this.errors);
}
if (!solution.output_values.new_audio || solution.output_values.new_audio.length < 1) {
this.errors = this.addError('Нужно приложить аудио', 'new_audio', this.errors);
}
// кастомизация конец фрагмента
} else if (solution.output_values.verdict === 'no_obj') {
if (!solution.output_values.imgs_facade || solution.output_values.imgs_facade.length === 0) {
this.errors = this.addError('Нужно приложить фото организации', "imgs_facade", this.errors);
} else if (solution.output_values.imgs_facade.length < 2) {
this.errors = this.addError('Должно быть хотя бы 2 фото организации', "imgs_facade", this.errors);
}
if (!solution.output_values.imgs_plate_or_address || solution.output_values.imgs_plate_or_address.length === 0) {
this.errors = this.addError('Нужно приложить фото таблички организации', "imgs_plate_or_address", this.errors);
}
if (!solution.output_values.imgs_around_obj || solution.output_values.imgs_around_obj.length === 0) {
this.errors = this.addError('Нужно приложить фотографии окружения', "imgs_around_obj", this.errors);
} else if (solution.output_values.imgs_around_obj.length < 4) {
this.errors = this.addError('Должно быть хотя бы 4 фотографии окружения', "imgs_around_obj", this.errors);
}
} else if (solution.output_values.verdict === 'no_org') {
if (!solution.output_values.imgs_around_org || solution.output_values.imgs_around_org.length === 0) {
this.errors = this.addError('Нужно приложить фото здания', "imgs_around_org", this.errors);
} else if (solution.output_values.imgs_around_org.length < 4) {
this.errors = this.addError('Должно быть хотя бы 4 фотографии здания', "imgs_around_org", this.errors);
}
if (!solution.output_values.imgs_plate_or_address || solution.output_values.imgs_plate_or_address.length === 0) {
this.errors = this.addError('Нужно приложить фото таблички организации', "imgs_plate_or_address", this.errors);
}
if (!solution.output_values.comment || solution.output_values.comment.trim() === '') {
this.errors = this.addError('Нужно написать комментарий', "comment", this.errors);
}
}
if (this.checkUserPosition.call(this, input_values, solution.output_values)) {
this.errors = this.addError('Вы находитесь слишком далеко от организации', "__TASK__", this.errors);
}
if (!solution.output_values.worker_coordinates) {
this.errors = this.addError('Не удалось получить ваши координаты. Пожалуйста, включите геолокацию.', "__TASK__", this.errors);
}
return this.errors || TolokaHandlebarsTask.prototype.validate.call(this, solution);
},
onDestroy: function() {
}
});
exports.TaskSuite = extend(TolokaHandlebarsTaskSuite, function (options) {
TolokaHandlebarsTaskSuite.call(this, options);
}, {
onValidationFail: function (errors) {
TolokaTaskSuite.prototype.onValidationFail.call(this, errors);
var tasks = this.getDOMElement().querySelectorAll('.task');
if (errors && errors.length > 0) {
var firstError;
for (var i = 0; i < errors.length; i++) {
if (errors[i]) {
firstError = errors[i];
break;
}
}
var firstTaskWithError = tasks[parseInt(firstError.task_id, 10)];
this.focusTask(parseInt(firstError.task_id, 10));
_.each(firstError.errors, function (error, fieldName) {
if (fieldName === '__TASK__') {
firstTaskWithError.querySelector('.task__error').scrollIntoView();
} else if (fieldName === 'verdict') {
firstTaskWithError.querySelector('.main__popup').scrollIntoView();
} else {
firstTaskWithError.querySelector('.main__btn_active').parentNode.querySelector('.popup_visible').scrollIntoView();
}
}.bind(this));
}
},
focusTask: function(index) {
TolokaTaskSuite.prototype.focusTask.call(this, index, 'withoutScroll');
}
});
function extend(ParentClass, constructorFunction, prototypeHash) {
constructorFunction = constructorFunction || function () {};
prototypeHash = prototypeHash || {};
if (ParentClass) {
constructorFunction.prototype = Object.create(ParentClass.prototype);
}
for (var i in prototypeHash) {
constructorFunction.prototype[i] = prototypeHash[i];
}
return constructorFunction;
}
.task {
display: block;
max-width: 1200px;
margin: 20px auto;
padding: 0;
border: none;
}
.task-suite {
height: 100%;
}
.header {
font-weight: 700;
background-color: #c0d1db;
padding: 15px;
}
.header-review {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
padding-left: 10px;
border-bottom: 1px solid #eaeaea;
}
.header-review__title {
font-weight: 700;
}
.header-review__btn {
padding: 10px;
text-align: center;
}
.header-review__btn_green {
background-color: #87da84;
}
.header-review__btn_red {
background-color: #ef5f56;
}
.info__review {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
border-bottom: 1px solid #eaeaea;
}
.info__review-block {
padding: 10px 0px;
}
.info__review-block:first-child {
width: 35%;
-ms-flex-negative: 0;
flex-shrink: 0;
margin-right: 15px;
border-right: 1px solid #eaeaea;
}
.info {
padding: 0px 10px;
margin-bottom: 20px;
}
.info__block {
padding: 10px 0px;
border-bottom: 1px solid #eaeaea;
}
.info__title {
padding-bottom: 10px;
}
.info__content {
font-weight: 700;
}
.info__link {
color: #314beb;
text-decoration: none;
}
.main {
padding-bottom: 15px;
padding-right: 15px;
padding-left: 15px;
}
.main__popup {
position: absolute;
bottom: 101%;
left: 50%;
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
margin: 0;
width: 250px;
max-width: 100%;
white-space: normal;
text-align: center;
font-size: 13px;
line-height: 20px;
padding: 2px 8px;
background: hsla(0,100%,70%,.9);
color: #fff;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
z-index: 98;
}
.main__popup_hidden {
display: none;
}
.main__title {
margin-bottom: 20px;
}
.main__container {
padding: 0px 10px;
position: relative;
}
.main__block {
margin-bottom: 10px;
}
.main__block:last-child {
margin-bottom: 0px;
}
.main__block_hidden {
display: none;
}
.main__btn {
padding: 20px;
font-size: 18px;
border: 1px solid #afc0cb;
background-color: #c0d1db;
text-align: center;
}
.main__btn_green.main__btn_active {
border: 1px solid #84fe81;
background-color: #87da84;
}
.main__btn_red.main__btn_active {
background-color: #ef5f56;
}
.main__btn:hover {
cursor: pointer;
}
.main__content {
padding: 0px 5px;
display: none;
}
.main__content_active {
display: block;
}
.main__content-block {
padding: 15px 0px;
}
.main__content-title {
font-weight: 700;
margin-bottom: 15px;
}
.main__text {
font-style: italic;
margin-bottom: 10px;
}
.main__ex {
margin-bottom: 10px;
}
.main__ex-link {
color: #314beb;
text-decoration: none;
}
.main__ex-link:first-child {
margin-right: 5px;
}
.task__error {
left: 0;
right: 0;
top: 0;
width: auto;
}
.review__block {
padding: 15px;
}
.review__title {
padding-bottom: 5px;
font-weight: 700;
}
.review__comment {
padding-top: 10px;
}
.review__imgs-grid {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: 0px -10px;
}
.review__grid-item {
margin: 10px;
width: 350px;
height: 350px;
position: relative;
border: 1px solid #E5E5E5;
border-radius: 5px;
overflow: hidden;
}
.review__grid-inner_zoomed {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.65);
z-index: 1000;
cursor: -webkit-zoom-out;
cursor: zoom-out;
}
.review__img {
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
cursor: -webkit-zoom-in;
cursor: zoom-in;
max-width: 100%;
max-height: 100%;
}
.review__rotate-panel {
position: absolute;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.65);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.review__rotate {
cursor: pointer;
padding: 10px;
color: #fff;
font-size: 20px;
}
.review__map {
padding: 15px;
}
.review__video-grid {
margin: 0px -10px;
}
.review__audio-grid {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: 0px -10px;
}
.review__audio-grid-item {
margin: 10px;
}
.review__video-grid-item {
margin: 10px;
}
.main__video .field,
.main__audio .field {
white-space: normal;
}
.main__video .field_file__files__file,
.main__audio .field_file__files__file {
margin-bottom: 10px;
}
.field_file-img__uploader {
width: 100%;
display: block;
float: none;
margin-right: 0;
}
.field_file-img__upload {
display: inline-block;
width: 50%;
float: left;
}
.field_file-img_with-preview .field_file-img__label {
width: 100%;
}
.field_file-img__files__file_with-preview {
width: 100%;
margin-right: 0px;
height: 200px;
}
.field_type_file-img {
margin-right: 0px;
}
.field_file-img__files {
white-space: normal;
}
.field_file-img__files__file_with-preview .file__name {
white-space: normal;
}
.popup__text {
width: 230px;
}
.field_file-img__files__file_with-preview .file__delete {
width: 35px;
height: 35px;
line-height: 35px;
}
.field_file__files__file {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.icon_clear {
-ms-flex-negative: 0;
flex-shrink: 0;
background-size: 40%;
background: no-repeat 50% 0;
}
.main__content-title_req:after {
display: inline-block;
color: red;
content: '*';
}
.main__text_req:after {
display: inline-block;
color: red;
content: '*';
}
.task:not(.task_focused) .popup_visible.popup_type_error, .task:not(.task_focused) .task__error {
opacity: 1;
}
@media screen and (max-width: 767px) {
.header-review__title {
margin-right: 10px;
padding: 10px 0px;
}
.info__review {
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.info__review-block:first-child {
width: 100%;
-ms-flex-negative: 1;
flex-shrink: 1;
margin-right: 0px;
border-right: none;
border-bottom: 1px solid #eaeaea;
}
}
Редактирование выходной спецификации
Добавьте новые переменные с типом «файл» в выходную спецификацию. В них будут записываться загружаемые исполнителем файлы. Если необходимо, чтобы исполнитель имел возможность загрузить больше одного файла, то отметьте специальный флажок «массив» для переменной.
new_imgs
— массив файлов для фотографий;
new_video
— массив файлов для видео;
new_audio
— массив файлов для аудио.
Для загрузки файлов используются специальные компоненты, о которых можно прочитать в следующих разделах:
Редактирование HTML
-
Код HTML состоит из блоков, описывающих различные элементы интерфейса. Каждый блок может содержать внутри себя другие блоки. Таких уровней вложенности может быть несколько. Например, блок с описанием кнопки ответа содержит в себе блоки с полями для заполнения. Каждое поле тоже содержит в себе другие элементы, например, заголовок и поле для комментария.
Каждый блок оформляется так:
`<div class="наименование_блока">` <!-- код блока, может содержать вложенные блоки --> ... </div>
-
Найдите блок
main
(он начинается со строки<div class="main">
). Внутри него расположены блокиmain__block
, каждый из которых описывает одну из кнопок. Например, в шаблоне «Мониторинг объектов в организациях» есть 3 кнопки для ответа, значит, в блокеmain
у этого шаблона будет 3 блокаmain__block
для каждой из кнопок.У каждой из кнопок есть наименование для обращения к ее свойствам. Например, в шаблоне «Мониторинг объектов в организациях» 3 кнопки называются
btn_ok
,btn_no_obj
иbtn_no_org
. Запомните наименование той кнопки, в код которой добавляете новые поля.Внутри блока
main__block
расположен блокmain__content
, который содержит все поля для выбранной кнопки. Описание каждого отдельного поля расположено в блокахmain__content-block
.Найдите нужную кнопку
main__block
, в ней найдите полеmain__content-block
, после которого вы хотите добавить новое поле и вставьте после него следующий код:<!-- фото --> <div class="main__content-block"> <div class="main__content-title main__content-title_req"> {{texts.btn_ok.question_new_imgs.title}} </div> <div class="main__text"> {{texts.btn_ok.question_new_imgs.description}} </div> <div class="main__ex"> <a href="{{texts.btn_ok.question_new_imgs.example_link_1}}" target="_blank" class="main__ex-link">Пример</a> </div> <div class="main__imgs"> {{field type="file-img" name="new_imgs" camera=true validation-show="top-left"}} </div> </div> <!-- видео --> <div class="main__content-block"> <div class="main__content-title main__content-title_req"> {{texts.btn_ok.question_new_video.title}} </div> <div class="main__text"> {{texts.btn_ok.question_new_video.description}} </div> <div class="main__video"> {{field type="file" name="new_video" sources="CAMERA GALLERY" fileType="VIDEO" camera=true validation-show="top-left"}} </div> </div> <!-- аудио --> <div class="main__content-block"> <div class="main__content-title main__content-title_req"> {{texts.btn_ok.question_new_audio.title}} </div> <div class="main__text"> {{texts.btn_ok.question_new_audio.description}} </div> <div class="main__audio"> {{field type="file" name="new_audio" sources="RECORDER FILE_MANAGER" fileType="AUDIO" validation-show="top-left"}} </div> </div>
В этом коде новые поля добавляются для первой кнопки с наименованием
btn_ok
. Если вы добавили поля для другой кнопки, измените наименованиеbtn_ok
на нужное. -
Обновите режим приемки.
Блок
review
содержит в себе код для каждой кнопки в режиме приемки. Этот код расположен в таких блоках:{{#if (equal verdict "ok")}} <!-- код для кнопки "ok" в режиме приемки --> <div class="review__block"> <!-- код для поля внутри кнопки "ok" в режиме приемки --> ... </div> ... {{/if}}
Переменная
verdict
указана в выходной спецификации, в нее будет передаваться значение ответа для той кнопки, которую нажал исполнитель.Например, в шаблоне «Мониторинг объектов в организациях» для трех кнопок описаны три выходных значения:
ok
,no_obj
иno_org
.Блоки
review__block
содержат описание каждого из полей для данной кнопки.Найдите нужную кнопку по строке
{{#if (equal verdict "значение_ответа_кнопки")}}
, в ней найдите полеreview__block
, после которого вы хотите добавить новое поле, и вставьте после него следующий код:<!-- фото --> <div class="review__block"> <div class="review__title"> {{texts.btn_ok.question_new_imgs.title}} </div> <div class="review__imgs-grid"> {{#each new_imgs}} <div class="review__grid-item"> <div class="review__grid-inner"> <img src="{{this}}" class="review__img" data-rotationdeg="0"> <div class="review__rotate-panel"> <span class="review__rotate review__rotate_left">←</span> <span class="review__rotate review__rotate_right">→</span> </div> </div> </div> {{/each}} </div> </div> <!-- видео --> <div class="review__block"> <div class="review__title"> {{texts.btn_ok.question_new_video.title}} </div> <div class="review__video-grid"> {{#each new_video}} <div class="review__video-grid-item"> <video width="50%" controls> <source src={{this}} type="video/mp4"> </video> </div> {{/each}} </div> </div> <!-- аудио --> <div class="review__block"> <div class="review__title"> {{texts.btn_ok.question_new_audio.title}} </div> <div class="review__audio-grid"> {{#each new_audio}} <div class="review__audio-grid-item"> <audio src={{this}} controls type="audio/wav">Невозможно воспроизвести</audio> </div> {{/each}} </div> </div>
В этом коде новые поля добавляются для первой кнопки с наименованием
btn_ok
. Если вы добавили поля для другой кнопки, измените наименованиеbtn_ok
на нужное.
Редактирование JS
-
Код JS состоит из блоков, описывающих различные элементы интерфейса. Эти блоки могут быть вложенными (кнопки содержат набор полей, поля содержат набор элементов и т. д.). Каждый блок заключен в фигурные скобки.
В общем виде элементы описываются так:
'свойство': 'значение'
Значение тоже может состоять из нескольких свойств, в этом случае оно заключается в фигурные скобки и образует следующий уровень вложенности.
-
В самом начале файла находится константа
texts
, в которой хранятся все необходимые для интерфейса тексты для каждой кнопки.У каждой из кнопок есть наименование для обращения к ее свойствам. Например, в шаблоне «Мониторинг объектов в организациях» 3 кнопки называются
btn_ok
,btn_no_obj
иbtn_no_org
. Запомните наименование той кнопки, в код которой добавляете новый текст.Например, в шаблоне «Мониторинг объектов в организациях» тексты для кнопки
btn_ok
расположены в следующем блоке кода:var texts = { //<общий текст для заголовков> 'btn_ok': { 'title': 'Я нашел объект', 'question_1': { //<тексты для первого поля (фото фасада организации)> }, 'question_2': { //<тексты для второго поля (фото объекта)> } },
-
Чтобы добавить нужные тексты, поставьте запятую после закрывающей фигурной скобки последнего поля и вставьте следующий код:
'question_new_imgs': { 'title': 'Фото', 'description': 'Сделайте хотя бы 2 фотографии', 'example_link_1': 'ссылка на пример фото' }, 'question_new_video': { 'title': 'Видео', 'description': 'Запишите видео' }, 'question_new_audio': { 'title': 'Аудио', 'description': 'Запишите аудио' }
-
Найдите функцию
getTemplateData
. Она содержит несколько блоков следующего вида:if (<условие проверки заполнения поля>) { ... <код для отображения загруженных данных> ... }
Вставьте после любого из таких блоков следующий код. Он нужен для того, чтобы отправить загруженные исполнителем фотографии, видео- и аудиофайлы во входные данные. Это понадобится для отображения данных в режиме приемки:
// фото if (outputValues.new_imgs && outputValues.new_imgs.length > 0) { data.new_imgs = []; for (var i = 0; i < outputValues.new_imgs.length; i++) { data.new_imgs.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_imgs[i] + '/preview'); } } // видео if (outputValues.new_video && outputValues.new_video.length > 0) { data.new_video = []; for (var i = 0; i < outputValues.new_video.length; i++) { data.new_video.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_video[i] + '/preview'); } } // аудио if (outputValues.new_audio && outputValues.new_audio.length > 0) { data.new_audio = []; for (var i = 0; i < outputValues.new_audio.length; i++) { data.new_audio.push(workspaceOptions.apiOrigin + '/api/attachments/' + outputValues.new_audio[i] + '/preview'); } }
-
Добавьте валидацию.
Найдите функцию
validate
. В ней находится код для проверки заполнения полей для каждой из кнопок. Например, в шаблоне «Мониторинг объектов в организациях» этот код выглядит так:if (!solution.output_values.verdict || solution.output_values.verdict === '') { this.errors = this.addError('Не выбран ни один вариант ответа', "verdict", this.errors); } else if (solution.output_values.verdict === 'ok') { // код проверки полей кнопки ok if (!solution.output_values.imgs_facade || solution.output_values.imgs_facade.length === 0) { // код проверки поля imgs_facade } if (!solution.output_values.imgs_obj || solution.output_values.imgs_obj.length === 0) { // код проверки поля imgs_obj } } else if (solution.output_values.verdict === 'no_obj') { // код проверки полей кнопки no_obj } else if (solution.output_values.verdict === 'no_org') { // код проверки полей кнопки no_org }
Значения ответа для кнопок в этом примере, которые передаются в выходную переменную
verdict
, называются так же, как на шаге обновления режима приемки:ok
,no_obj
иno_org
.Найдите блок проверки нужной кнопки и внутри него после любого блока проверки поля вида
if (!solution... ) { // код проверки поля }
добавьте следующий код:
if (!solution.output_values.new_imgs || solution.output_values.new_imgs.length === 0) { this.errors = this.addError('Нужно приложить фото', "new_imgs", this.errors); } else if (solution.output_values.new_imgs.length < 2) { this.errors = this.addError('Должно быть хотя бы 2 фото', "new_imgs", this.errors); } if (!solution.output_values.new_video || solution.output_values.new_video.length < 1) { this.errors = this.addError('Нужно приложить видео', 'new_video', this.errors); } if (!solution.output_values.new_audio || solution.output_values.new_audio.length < 1) { this.errors = this.addError('Нужно приложить аудио', 'new_audio', this.errors); }
Редактирование CSS
В конце блока с кодом CSS вставьте стили для новых полей:
.review__video-grid {
margin: 0px -10px;
}
.review__audio-grid {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: 0px -10px;
}
.review__audio-grid-item {
margin: 10px;
}
.review__video-grid-item {
margin: 10px;
}
.main__video .field,
.main__audio .field {
white-space: normal;
}
.main__video .field_file__files__file,
.main__audio .field_file__files__file {
margin-bottom: 10px;
}