Что, если мы создадим такой интерфейс в 1С, чтобы он был удобнее, чем в Excel? Да не просто удобнее — а чтобы сотрудники сказали: «Ого, это же как игра!».
С вами снова Ведущий специалист модуля разработки 1С Михеев Антон. Давайте вместе сделаем эту игру идею реальностью.
Представьте, что Excel — это склад, вид сверху. Синие ячейки —стеллажи, в них лежат товары. Да, на множестве складов топология нарисована именно в Excel. Сотрудникам так понятнее и удобнее: закрашивать ячейки в таблице куда проще, чем разбираться, как заполнять справочники с кучей непонятных цифр в 1С.

Задачка: на стеллаже три полки. На первой — конфеты, на второй — печеньки, на третьей — сиропы. Как пользователю понять, что нужно взять печеньку со второй полки?
Ответ: рисовать всю эту красоту в изометрии! Изометрия — это старая добрая технология, которая:
не нагружает процессор (в отличие от 3D, где ваш ПК может начать мечтать о пенсии);
позволяет рассмотреть предмет под углом в 30∘;
даёт возможность переключать углы обзора (ну, или хотя бы сделать четыре фиксированных вида — этого хватит).

Как это сделать в 1С?
Здесь нам поможет встроенный редактор HTML внутри 1С. Если вы не знали, то 1С использует платформу WebKit, позаимствованную у айфонов (лучшие мировые практики, да).
Шаг 1. Создаём внешнюю обработку и добавляем в неё Поле HTML документа.

Шаг 2. Создаём три макета: HTML, JS и CSS.

Макет HTML — это каркас страницы. Вставляем код:
<!DOCTYPE html> <HTML> <HEAD>@CSS</HEAD> <BODY> <h1>Изометрическая сетка 10 × 10 с перетаскиваемыми объектами</h1> <div id='grid-container'></div> @JS </BODY> </HTML>
Макет JS — это логика. Здесь мы:
создаём сетку из ячеек;
расставляем объекты (конфеты, печеньки, сиропы);
добавляем возможность перетаскивать их мышкой (drag & drop);
подсвечиваем ячейки при наведении;
проверяем, занята ли ячейка, прежде чем положить туда новый объект.

Добавим код в макет
window.onload = function() { const gridContainer = document.getElementById('grid-container'); const occupiedCells = new Set(); // Хранит занятые ячейки // Массив контейнеров с параметрами const containers = [@Containers]; // Создаём ячейки сетки for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { const cell = document.createElement('div'); cell.className = 'cell'; cell.style.left = ${j * 90}px; cell.style.top = ${i * 50}px; cell.dataset.row = i; cell.dataset.col = j; // Добавляем параметры x и y в качестве атрибутов ячейки cell.setAttribute('data-x', i); cell.setAttribute('data-y', j); gridContainer.appendChild(cell); } } // Расставляем объекты из массива контейнеров containers.forEach(container => { const item = document.createElement('div'); item.className = 'draggable-item'; item.textContent = container.text; item.draggable = true; item.style.backgroundColor = container.color; item.style.zIndex = parseInt(container.x / container.y * 100,10); const Top = document.createElement('div'); Top.className = 'top'; Top.textContent = container.text; Top.style.backgroundColor = container.color; const Front = document.createElement('div'); Front.className = 'front'; Front.style.backgroundColor = container.color; const Right = document.createElement('div'); Right.className = 'right'; Right.textContent = container.text; Right.style.backgroundColor = container.color; item.appendChild(Top); item.appendChild(Front); item.appendChild(Right); // Добавляем все параметры в качестве атрибутов draggable-item item.setAttribute('data-color', container.color); item.setAttribute('data-text', container.text); item.setAttribute('data-x', container.x); item.setAttribute('data-y', container.y); item.setAttribute('data-z', container.z); // Помещаем объект в ячейку согласно координатам x и y const targetCell = document.querySelector(.cell[data-row="${container.x}"][data-col="${container.y}"]); if (targetCell) { targetCell.appendChild(item); // Помечаем ячейку как занятую occupiedCells.add(${container.x},${container.y}); } }); let draggedItem = null; let originalCell = null; // Обработчик начала перетаскивания function handleDragStart(e) { draggedItem = this; originalCell = this.parentElement; e.dataTransfer.setData('text/plain', null); // Невидимость Ghost при перетаскивании var img = document.createElement("img"); img.src = ""; e.dataTransfer.setDragImage(img, 0, 0); } // Обработчик окончания перетаскивания document.addEventListener('dragend', () => { if (draggedItem) { draggedItem = null; originalCell = null; } }); // Обработчики для всех ячеек сетки document.querySelectorAll('.cell').forEach(cell => { // Обработчик наведения — выделение фона и границы cell.addEventListener('mouseenter', () => { cell.style.backgroundColor = 'rgba(135, 206, 235, 0.3)'; // Полупрозрачный голубой cell.style.borderColor = 'blue'; cell.style.zIndex = '10'; // Выводим поверх других элементов cell.style.boxShadow = '0 0 8px rgba(0, 123, 255, 0.5)'; // Дополнительная подсветка }); cell.addEventListener('mouseleave', () => { // Восстанавливаем исходный вид cell.style.backgroundColor = ''; cell.style.borderColor = '#999'; cell.style.zIndex = ''; cell.style.boxShadow = ''; }); cell.addEventListener('dragover', (e) => e.preventDefault()); cell.addEventListener('drop', (e) => { e.preventDefault(); if (!draggedItem) return; const row = cell.dataset.row; const col = cell.dataset.col; const cellKey = ${row},${col}; // Проверяем, занята ли ячейка if (occupiedCells.has(cellKey)) { alert('Ячейка уже занята! Выберите другую.'); return; } // Освобождаем предыдущую ячейку if (originalCell) { const origRow = originalCell.dataset.row; const origCol = originalCell.dataset.col; occupiedCells.delete(${origRow},${origCol}); } // Обновляем атрибуты draggable-item с новыми координатами draggedItem.setAttribute('data-x', row); draggedItem.setAttribute('data-y', col); // Помещаем объект в новую ячейку — он автоматически занимает всю площадь ячейки cell.appendChild(draggedItem); occupiedCells.add(cellKey); }); }); // Инициализируем обработчики перетаскивания для всех объектов на поле document.querySelectorAll('.draggable-item').forEach(item => { item.addEventListener('dragstart', handleDragStart); }); document.querySelectorAll('.draggable-item').forEach(item => { item.addEventListener('mouseenter', () => { document.querySelectorAll('.draggable-item').forEach(otherItem => { if (otherItem !== item) { otherItem.classList.add('dimmed'); } }); }); item.addEventListener('mouseleave', () => { document.querySelectorAll('.draggable-item').forEach(otherItem => { otherItem.classList.remove('dimmed'); }); }); }); // Запрещаем контекстное меню по правой кнопке мыши document.addEventListener('contextmenu', e => e.preventDefault()); }
Макет CSS — это стиль. Здесь мы задаём цвета, размеры, тени, углы поворота и прочие красоты. Изометрия достигается через transform: rotate(-30deg) skewX(30deg) — магия, а не код!
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; text-align: center; } #grid-container { width: 1000px; height: 500px; position: relative; transform: rotate(-30deg) skewX(30deg); overflow: hidden; margin: 0 auto; } .cell { position: absolute; width: 90px; height: 48px; border-right: 1px dashed #999; border-bottom: 1px dashed #999; box-sizing: border-box; } /* Границы для крайних ячеек */ .cell[data-row="0"] { border-top: 1px dashed #999; } .cell[data-col="0"] { border-left: 1px dashed #999; } .draggable-item { width: 100%; height: 100%; background-color: #4CAF50; color: white; text-align: center; line-height: 48px; /* Совпадает с высотой ячейки */ cursor: move; user-select: none; border-radius: 4px; position: relative; /* Относительное позиционирование внутри ячейки */ } .draggable-item.dimmed { opacity: 0.5; transition: opacity 0.2s ease; /* Плавное изменение прозрачности */ } .top{ filter: brightness(120%); background-color: #4CAF50; color: white; transform: translateX(30px) translateY(-80px); position: absolute; width: 90px; height: 48px; } .front{ filter: brightness(90%); color: white; transform: translateY(-66px) skewY(-45deg) ; position: absolute; width: 30px; height: 48px; } .right{ filter: brightness(110%); color: white; transform: translateX(15px) translateY(-33px) skewX(-45deg) ; position: absolute; width: 90px; height: 33px; }
Код на сервере: магия данных
В обработчике ПриСозданииНаСервере пишем код, который:
Берёт макеты HTML, JS и CSS.
Подставляет CSS и JS в HTML (через @CSS и @JS).
Делает запрос к регистру накопления, чтобы получить координаты ячеек и информацию о контейнерах.
Формирует массив контейнеров с параметрами (цвет, текст, координаты).
Подставляет этот массив в HTML (вместо @Containers).
Добавим код ПриСозданииНаСервере:
bsl ОбъектВнешнейОбработки = РеквизитФормыВЗначение("Объект"); HTML = ОбъектВнешнейОбработки.ПолучитьМакет("МакетHTML").ПолучитьТекст(); JS = "<script>" + ОбъектВнешнейОбработки.ПолучитьМакет("МакетJS").ПолучитьТекст() + "</script>"; CSS = "<style>" + ОбъектВнешнейОбработки.ПолучитьМакет("МакетCSS").ПолучитьТекст() + "</style>"; HTML = СтрЗаменить(HTML,"@CSS",CSS); HTML = СтрЗаменить(HTML,"@JS",JS); //Тут пишем запрос который получит Координаты ячейки и Контейнер в ней Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ | укЗаполненностьЯчеекОстатки.Ячейка.Ряд КАК X, | укЗаполненностьЯчеекОстатки.Ячейка.Ярус КАК Z, | укЗаполненностьЯчеекОстатки.Ячейка.Позиция КАК Y |ИЗ | РегистрНакопления.укЗаполненностьЯчеек.Остатки КАК укЗаполненностьЯчеекОстатки |ГДЕ | укЗаполненностьЯчеекОстатки.Ячейка.Ряд < 10 | И укЗаполненностьЯчеекОстатки.Ячейка.Ярус = 1 | И укЗаполненностьЯчеекОстатки.Ячейка.Позиция < 10 | |УПОРЯДОЧИТЬ ПО | X Возр, | Y ВОЗР"; РезультатЗапроса = Запрос.Выполнить(); Выборка = РезультатЗапроса.Выбрать(); Результат = ""; Пока Выборка.Следующий() Цикл ТекСтрока = СтрШаблон("{ color: '#00008b', text: '%1', x: %2, y: %3, z: %4 },", "К-"+ Выборка.X + "-" + Выборка.Y , Выборка.X, Выборка.Y, Выборка.Z); Результат = Результат + ТекСтрока; КонецЦикла; Результат = Лев(Результат, СтрДлина(Результат) - 1); HTML = СтрЗаменить(HTML,"@Containers", Результат);
«Запрос в 1С — это как заклинание: если произнести правильно, получишь желаемое. Если нет — получишь ошибку и желание всё бросить».
Что в итоге?
На выходе получается склад с изометрией! Ого, скажете вы, и будете правы. Это реально работает внутри 1С и не тормозит (в отличие от 3D, которое может заставить ваш компьютер задуматься о смысле жизни).
Что можно добавить:
Подсветку ячеек: красным — «Возьми отсюда», зелёным — «Поставь сюда».
Динамическую подгрузку задач на перемещение.
Отображение остатков прямо на контейнерах.
Уведомления: «Внимание! На полке с печеньками осталось 2 штуки!».
Получается уже не просто 1С, а почти игра!

Почему не 3D?
Вы спросите: «А почему не использовать 3D?» Ответ прост:
1С не сможет быстро выполнять качественный рендеринг моделей — всё будет тормозить.
Оптимизация 3D‑движка в 1С не даёт качественной детализации.
Возможность крутить камеру только сбивает пользователя.
«3D в 1С — это как попытка запустить Cyberpunk 2077 на калькуляторе: идея крутая, но результат печальный».
Финальный аккорд
Зафиксируем угол для изометрии и прорисуем все объекты лишь под одним углом (максимум 4 картинки на объект). И вот он — весь склад перед вами, с хорошей детализацией, без тормозов и без необходимости изучать квантовую физику.


Как вам идейка? Удобно ли будет пользоваться детализированной интерактиивной топологией склада? ?

Комментарии (28)

DvoiNic
24.06.2026 01:45Выглядит симпотично, но (имхо) абсолютно бесполезно.

Jedy_N Автор
24.06.2026 01:45Не согласен. Удобно использовать для раскопок контейнера и для перемещения по складу. Легко определить заполненность склада /ячейки если добавить ярусность. Если использовать стеллажи то удобно понимать где находится к примеру опасный груз или какой-либо вид номенклатуры добавив подсветку по отбору.

DvoiNic
24.06.2026 01:45Все это делается и в 2D. Ну и в "плоском складе" (напр, "контейнерах на площадке") это еще более-менее наглядно, а в обычном, чаще всего встречающемся, складе - будет нагромождение.
Перемещение должно как минимум улетать обратно в УС.
Ну и т.д.
На кого расчитана эта игрулька? кто будет мышой мышить эти контейнеры? Кладовщик? комплектовщик? специалист по УТЗ? (и главный вопрос - традиционный китайский: Анахуа?)

Jedy_N Автор
24.06.2026 01:45Нвпример это удобно для крановщика на контейнерном терминале чтобы выполнять к примеру "раскопку" контейнеров. Это дает наглядность. Также удобно подсвечивать опасные грузы или поиск номенклатуры по виду. Да для этого нужно будет добавить ярусность и отборы с подсветкой, но это того стоит:)

DvoiNic
24.06.2026 01:45ну сделайте тогда вместо
укЗаполненностьЯчеекОстатки.Ячейка.Ярус = 1хотя быукЗаполненностьЯчеекОстатки.Ячейка.Ярус < 5и покажите, что получилось

Jedy_N Автор
24.06.2026 01:45Все верно, получится примерно такое! Я же только идею бросил, но смотрю многим интересно:) Пора на инфостарт?)


DvoiNic
24.06.2026 01:45И какая наглядность для контейнера первого яруса на линии 7 в ряду 4? офигенная?
Для какого-нибудь грузового двора порта, может, и подойдет - для презентации, но не более. (и у них наверняка уже есть своя система)
На инфостарт - почему бы и нет? там места для всякого хлама немеряно. Как примерчик возможностей - вполне себе адекватно.
Нвпример это удобно для крановщика на контейнерном терминале чтобы выполнять к примеру "раскопку" контейнеров
крановщику не нужна "наглядность". Ему нужна инструкция: "ехай 7-2, хватай контейнер, ехай 3-3, ставь контейнер, ехай 7-2, хватай контейнер, ехай 3-3, ставь контейнер, ехай 7-2, хватай контейнер, ехай на погрузку, ставь на контейнеровоз, делай перекур"

Jedy_N Автор
24.06.2026 01:45Ок. А где этот 7-2 будет подсвечено. А то что он не самый верхний также будет подсвечено. Действия к выполнению будут подписаны и кнопочка Взял 7-2 потом место пдсвечивается, текст "Поставь на погрузчик в х ячейку" и кнопка Поставил на погрузчик.
А тут администратор хочет подключиться к рабочему потоку и посмотреть какие сейчас задачи выполняются и вот ему все подсвечено что Иван Иванович крановщик тянет контейнер из 7-2...
Если контейнер не видно под другими то есть такое свойство у html объектов как прозрачность, выделяем нужный контейнер а остальные делаем с прозрачностью 70%
А для склада также удобно - возьми паллету со стеллажа 3-2 и вот стеллаж подсвечивается. Кнопочка "Взял", положи на 7-7 и кнопочка "положил" удобно и наглядно, особенно если текучка кадров и кладовщики новенькие и не знают где этот стеллаж. Я думаю такая визуализация ускорит работу!

DvoiNic
24.06.2026 01:45Ок. А где этот 7-2 будет подсвечено
а зачем? ему нужно видеть текущий ряд и линию. И всё. Может, следующий контейнер возьмет второй кран и унесет не на 3-3, а на 8-10. Ему нужно знать не конечную цель, а одно атомарное действие.
Действия к выполнению будут подписаны и кнопочка Взял
Совершенно верно. И 3Д тут не нужно от слова совсем. Текст и 2 кнопки (у меня даже дублирование голосом сделано, и голосовой ввод. правда, честно говоря, голосовой ввод лажает сильно. по крайней мере, пока лажает )
вот ему все подсвечено что Иван Иванович крановщик тянет контейнер из 7-2..
И зачем это в 3Д? Текстом будет понятнее:
Исполнитель: Кран№1. Задание перемещение 7-2 -> 3.3. Этап: Перемещение груза
А для склада также удобно - возьми паллету со стеллажа 3-2 и вот стеллаж подсвечивается.
Вы на складе давно бывали? На каком устройстве это всё должно изображаться? И, главное, зачем? все линии, ряды, стеллажи, ячейки на нормальном складе маркированы.

Jedy_N Автор
24.06.2026 01:45Бывал на складе.
На устройствах типа планшет или тсд (если экран позволяет)
На мониторе установленном на складе с подсветкой текущих задач, удобно для старших кладовщиков и админов.
Для обучения персонала.
Для демонстраций гостям.

DvoiNic
24.06.2026 01:45На устройствах типа планшет или тсд (если экран позволяет)
вы предлагаете таскать с собой планшет? ТСД-то неудобно, приходится с "кольцом" экспериментировать. (хотя склады разные бывают, конечно - на 40% ТСД вполне,еще на 30% туды-сюды. планшет только на кран ставить, даже на погрузчике он неудобен). Средненький складик, в каждом помещении 6 рядов, в ряду 40 стеллажей, в стеллаже 4 яруса, на ярусе от 1 до 3 ячеек. Попробуйте это уместить в 3д на 480*800.
На мониторе установленном на складе с подсветкой текущих задач
покажите на мониторе 20 задач одновременно... оххххренеть как удобно
для старших кладовщиков
а зачем? им интересно исполнение [в срок], а не текущая задача. Если кто-то следит за текущими задачами - значит, система неправильно построена... не те метрики отслеживаются.
Для обучения персонала.
...хватает напечатаной на бумаге карты склада с нумерацией. Да, естественно, нумерация должна быть построена не от балды, а вдумчиво.
Для демонстраций гостям.
я об этом, как об единственной пользе, сразу и сказал...

Jedy_N Автор
24.06.2026 01:45Если вы считаете что данный функционал не применим то это не так, было бы желание его адаптировать.
Есть ли в нем необходимость на складе - нет. Достаточно просто текста.
Стоит ли развивать решение- вам решать это идея и пробник функционала.
Стоит ли использовать html + js в 1с? В информационных мониторах и сложных системах точно да - быстрее, функциональнее чем формы 1с.
Еще раз скажу что это идея и пробник функционала 3d на складе. Использовать его или нет - решать вам. Лично мы данный функционал развиваем и используем:)

DvoiNic
24.06.2026 01:45Стоит ли использовать html + js в 1с? В информационных мониторах и сложных системах точно да - быстрее, функциональнее чем формы 1с.
с этим - абсолютно согласен. Несколько сервисов отдают html-странички (выработка комплектовщиков за смену, или готовность маршрутов), которые отображаются на старых мониторах, подключенных к дешевым андроидным видеоплеерам. Просто, быстро, дешево.

unkas42
24.06.2026 01:45Вот только вендор обновляет движок WebKit в платформе ну очень нечасто. Бывает так, что нельзя просто взять код js и применить в 1С, требуется адаптация.

Jedy_N Автор
24.06.2026 01:45Согласен, но можно не использовать сложные функции js, стандартные методы легко поддерживаются, а возможности 1с сильно расширяются.

mixsture
24.06.2026 01:45Как вам идейка? Удобно ли будет пользоваться детализированной интерактиивной топологией склада?
Красиво, но не удобно. Если вы хотите изометрию как некоторую эмуляцию 3д, то наверно хотели рисовать в 3х осях? Тогда где высоты? Вот чтоб в одном месте было 4 ячейки по высоте, в другом 8, в третьем 10. Ибо сейчас, когда высота всегда 1, это просто наклоненный лист бумаги и он ничем не отличается по функционалу от 2д представления (или даже ухудшает, т.к. drag&drop работает, думаю, по плоскости пола, а все остальное лишь отвлекает).

Jedy_N Автор
24.06.2026 01:45
Ну если немножко пошаманить то можно получить вот такую красоту с ярусностью.

fosihas
24.06.2026 01:45Вместо кода, из собственной конфигурации. Приложили чтонибуть что можно открыть в типовой, лучше обрабтткой/расширением.
А так симпатично

Jedy_N Автор
24.06.2026 01:45Дак это ж код для внешней обработки. Накиньте в него генерацию контейнеров на основе типовой и получите результат:) Один запрос от программиста и готово)

DvoiNic
24.06.2026 01:45CSS'а в примере нет

Jedy_N Автор
24.06.2026 01:45body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; text-align: center; } #grid-container { width: 1000px; height: 500px; position: relative; transform: rotate(-30deg) skewX(30deg); overflow: hidden; margin: 0 auto; } .cell { position: absolute; width: 90px; height: 48px; border-right: 1px dashed #999; border-bottom: 1px dashed #999; box-sizing: border-box; } /* Границы для крайних ячеек / .cell[data-row=“0”] { border-top: 1px dashed #999; } .cell[data-col=“0”] { border-left: 1px dashed #999; } .draggable-item { width: 100%; height: 100%; background-color: #4CAF50; color: white; text-align: center; line-height: 48px; / Совпадает с высотой ячейки / cursor: move; user-select: none; border-radius: 4px; position: relative; / Относительное позиционирование внутри ячейки / } .draggable-item.dimmed { opacity: 0.5; transition: opacity 0.2s ease; / Плавное изменение прозрачности */ } .top{ filter: brightness(120%); background-color: #4CAF50; color: white; transform: translateX(30px) translateY(-80px); position: absolute; width: 90px; height: 48px; } .front{ filter: brightness(90%); color: white; transform: translateY(-66px) skewY(-45deg) ; position: absolute; width: 30px; height: 48px; } .right{ filter: brightness(110%); color: white; transform: translateX(15px) translateY(-33px) skewX(-45deg) ; position: absolute; width: 90px; height: 33px; }

DvoiNic
24.06.2026 01:45чойта не работает. на наборе {{1,1,2},{2,1,2},{4,1,2},{5,1,2},{6,1,2}}

Jedy_N Автор
24.06.2026 01:45Нужно ярусность добавлять в css, в js иначе контейнера друг на друге будут нарисованы без учета высоты.

DvoiNic
24.06.2026 01:45Я взял ваш код, ваш html, ваш js, ваш css. И только данные взял не из регистра, а просто накидал набор (с учетом вашего же условия
укЗаполненностьЯчеекОстатки.Ячейка.Ярус = 1). И не работает.
Pavel_nobranch
идея интересная. а вообще тема использования html в 1С не раскрыта ни в ютуб ни где либо.
Jedy_N Автор
Html в 1С действительно расширяет возможности. Можно не НаКлиенте реализовать интерфейсы но и прокинуть их во внешку через http сервисы:)
Подписывайтесь на наш блог и узнаете еще много интересных кейсов:)