Наведи порядок с помощью дерева

04.06.17

Разработка - Универсальные функции

Быстрая правка данных из динамического списка - их перетаскивание между группировками в режиме иерархического просмотра

Есть динамический список с группировками, и очень хочется навести порядок с помощью обычного драг-эн-дропа, перетаскивая нужные объекты или целые группировки туда-сюда. Идея, думаю, ясна, полезность её мне тоже видится немалой. Теории на тему поведения "демонических списков" в случае перетаскивания уже изрядно (например, //infostart.ru/public/419306/), поэтому предлагаю работающее решение, точнее, оснастку для него.

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

1. Позволяет перетаскивать один или несколько выделенных объектов.
2. При перетаскивании показывает, куда оно "упадёт".
3. Позволяет "бросить" и над элементом (тогда перенесётся в его непосредственного "родителя" по иерархии), и над группировкой любого уровня.
4. При перетаскивании "элемент взяли - над элементом бросили" не дёргает сервер низачем, кроме самого изменения данных объекта.


#Область ПравкаДинамическогоСпискаПеретаскиванием

#Область Описание

// Если ТекущиеДанные и ТекущаяСтрока не перепозиционируется, то до конца перетаскивания хранят 
// исходную строку; в параметре Строка фигурирует значение (ТекущаяСтрока), над которой тащим в данный момент.
// Если ТекущийЭлемент не перепозиционируется, то до конца перетаскивания хранит исходную колонку;
// в параметре Поле фигурирует имя поля, над которым тащим в данный момент.
//
// Переустановка Действия=Перемещение ничего не даёт, несмотря на ДопустимыеДействия.КопированиеИПеремещение
// естественно, курсор не меняется, как нам надо, а меняется сам, когда находится над группировкой (а если его начать
// переопределять, то вообще не меняется, иконка отмены).
//
// В массиве Значение можно передавать любую клиентско-существующую информацию.
//
// Событие Перетаскивание не вызывается и не используется.

#КонецОбласти

#Область СлужебныеМеханизмы

&НаСервере
Функция ПолучитьДанныеБлокаНаСервере(рИмяЭлемента,соотЗначенияПолей)
Попытка
	рСКД=Элементы[рИмяЭлемента].ПолучитьИсполняемуюСхемуКомпоновкиДанных();
	рНастройки=Элементы[рИмяЭлемента].ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
	//
	// нам нужны только Ссылка, но проще отфильтровать их по заполненности, чем перенастраивать Структуру
	//
	Для каждого киз Из соотЗначенияПолей Цикл
		// по-хорошему ещё бы проверку, а нет ли других отборов на это поле; черновик!
		отб=рНастройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
		отб.ЛевоеЗначение=Новый ПолеКомпоновкиДанных(киз.Ключ); // уже содержит путь к данным через точку
		отб.ВидСравнения=ВидСравненияКомпоновкиДанных.Равно;
		отб.ПравоеЗначение=киз.Значение;
		отб.Использование=Истина;
	КонецЦикла;
	//		
	КомпоновщикМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рНастройки,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	ПроцессорКД=Новый ПроцессорКомпоновкиДанных;
	ПроцессорКД.Инициализировать(МакетКомпоновки,,,Истина);
	ПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	трез=Новый ТаблицаЗначений;
	ПроцессорВывода.УстановитьОбъект(трез);
	трез=ПроцессорВывода.Вывести(ПроцессорКД,Истина);
	//
	Возврат трез.ВыгрузитьКолонку("Ссылка");
Исключение
	Сообщить("ПолучитьДанныеБлокаНаСервере, ошибка: "+ОписаниеОшибки());
	Возврат Неопределено;
КонецПопытки;
КонецФункции

&НаСервереБезКонтекста
Функция ИзменитьЗначенияГруппировочныхПолейДляОбъектаНаСервере(рСсылка,соотЗначенияПолей)
	рОбъект=рСсылка.ПолучитьОбъект();
	// использовать ЗаполнитьЗначенияСвойств(рОбъект,соотЗначенияПолей) не рекомендуется
	
	//<ВНИМАНИЕ! ЗДЕСЬ ВСТАВИТЬ КОНКРЕТИКУ ИЗМЕНЕНИЯ ПОЛЕЙ СОГЛАСНО ЛОГИКЕ ПРИКЛАДНОГО РЕШЕНИЯ!>
	//
	// В простейшем случае:
	инфо="";
	Для каждого киз Из соотЗначенияПолей Цикл
		инфо=инфо+Символы.ПС+"Изменяется "+киз.Ключ+" = "+Строка(киз.Значение);		
		//рОбъект[киз.Ключ]=киз.Значение; // например, так
	КонецЦикла;
	Сообщить(инфо);
	
	рОбъект.ОбменДанными.Загрузка=Истина;
	Попытка
		рОбъект.Записать();
		Возврат Истина;
	Исключение
		Сообщить("Ошибка: "+ОписаниеОшибки());
		Возврат Ложь;
	КонецПопытки;	
КонецФункции

&НаКлиенте
Функция ПолучитьЗначенияПолейГруппировок(ЭлементДС,рЭтоГруппировка=Неопределено)
	текдан=ЭлементДС.ТекущаяСтрока;
	Если ТипЗнч(текдан)=Тип("СтрокаГруппировкиДинамическогоСписка") Тогда // находимся где-то на узле дерева
		рЭтоГруппировка=Истина;
		рДанные=текдан;
	Иначе // находимся на конечном элементе дерева
		рЭтоГруппировка=Ложь;
		рДанные=ЭлементДС.ТекущиеДанные.РодительскаяГруппировкаСтроки;
	КонецЕсли;
	//
	// хотя, конечно, можно использовать ЭлементДС.Группировка, но там только поля, а тут сразу ещё и их значения
	соотДанных=Новый Соответствие;
	Пока Истина Цикл
		Если рЭтоГруппировка Тогда // группировочное значение - другого типа, у него может не быть этих полей
			соотДанных.Вставить(рДанные.ИмяГруппировки,рДанные.Ключ);
		Иначе
			мПутьКДанным=СтрРазделить(рДанные.ИмяГруппировки,".",Ложь);
			Если мПутьКДанным.Количество()=1 Тогда // прямое получение
				соотДанных.Вставить(рДанные.ИмяГруппировки,рДанные.Ключ);
			Иначе // разыменование, нужно только первое поле; все эти поля были в колонках, иначе бы не работала группировка
				рПоле=мПутьКДанным.Получить(0);
				Попытка соотДанных.Вставить(рПоле,текдан[рПоле]) Исключение КонецПопытки;			
			КонецЕсли;
		КонецЕсли;
		// к следующей итерации вверх по иерархии
		рДанные=рДанные.РодительскаяГруппировка;
		Если рДанные=Неопределено Тогда Прервать КонецЕсли;
	КонецЦикла;
	//
	Возврат соотДанных;
КонецФункции

#КонецОбласти

#Область ОбработчикиСобытийПеретаскивания

&НаКлиенте
Процедура СписокНачалоПеретаскивания(Элемент, ПараметрыПеретаскивания, Выполнение)
	рЭтоГруппировка=Неопределено; // фиксируем исходную ситуацию
	соотДанных=ПолучитьЗначенияПолейГруппировок(Элемент,рЭтоГруппировка);
	//
	Если рЭтоГруппировка Тогда // тащут не конечную ветку, а группировку, придётся смотреть на сервере
		мСсылок=ПолучитьДанныеБлокаНаСервере(Элемент.Имя,соотДанных);
		Если ТипЗнч(мСсылок)=Тип("Массив") и мСсылок.Количество()>0 Тогда
			// переопределеяем перетаскиваемый состав данных
			ПараметрыПеретаскивания.Значение.Очистить();
			Для каждого знч Из мСсылок Цикл
				Если не ЗначениеЗаполнено(знч) Тогда Продолжить КонецЕсли; // группировочное пусто в Ссылке
				ПараметрыПеретаскивания.Значение.Добавить(знч);
			КонецЦикла;
			// и переопределяем данные для них, т.к. для элемента другие пути и поля
			Элемент.ТекущаяСтрока=мСсылок.Получить(0); // они все одинаковы в этом смысле
			соотДанных=ПолучитьЗначенияПолейГруппировок(Элемент);
		Иначе
			Выполнение=Ложь; Возврат;
		КонецЕсли;
	Иначе
		// ничего не изменяем в перетаскиваемом составе данных
	КонецЕсли;
	//	
	ПараметрыПеретаскивания.Значение.Добавить(соотДанных); // дописываем к самим значениями
КонецПроцедуры

&НаКлиенте
Процедура СписокПроверкаПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
	Элемент.ТекущаяСтрока=Строка; // заодно удобно видеть, куда бросаем (при неудобном указателе мыши)
КонецПроцедуры

&НаКлиенте
Процедура СписокОкончаниеПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
	спЗначение=ПараметрыПеретаскивания.Значение;
	соотПоляИсходного=спЗначение.Получить(спЗначение.Количество()-1); // добавляется последним, служебно
	Если ТипЗнч(соотПоляИсходного)<>Тип("Соответствие") Тогда Возврат КонецЕсли;
	//
	рЭтоГруппировка=Неопределено;
	соотПоляТекущего=ПолучитьЗначенияПолейГруппировок(Элемент,рЭтоГруппировка); // смотрим итоговую ситуацию
	Если рЭтоГруппировка Тогда // бросили над группой 
		мРазыменуемых=Новый Массив;
		Для каждого киз Из соотПоляТекущего Цикл
			Если Найти(киз.Ключ,".")<>0 Тогда мРазыменуемых.Добавить(киз.Ключ) КонецЕсли;
		КонецЦикла;
		Если мРазыменуемых.Количество()>0 Тогда // придётся разыменовать и посмотреть значения исходных полей,
			// а для этого придётся получить фрагмент данных и взять любое его поле, чтобы получить исходное значение.
			// можно сделать для этого отдельный запрос (ПЕРВЫЕ 1), чтобы не тащить возможно большой лишний кусок.
			мСсылок=ПолучитьДанныеБлокаНаСервере(Элемент.Имя,соотПоляТекущего);
			Если ТипЗнч(мСсылок)=Тип("Массив") и мСсылок.Количество()>0 Тогда
				рСсылкаВГруппировке=мСсылок.Получить(мСсылок.Количество()-1); // допустим, последний в выборке
				Для каждого знчРазыменуемое Из мРазыменуемых Цикл
					рПоле=СтрРазделить(знчРазыменуемое,".",Ложь).Получить(0);
					Попытка соотПоляТекущего.Вставить(рПоле,рСсылкаВГруппировке[рПоле]) Исключение КонецПопытки;
				КонецЦикла;			
			КонецЕсли;
		КонецЕсли; // если были поля группировок, являющиеся разыменованиями
	КонецЕсли;
	
	// Сравниваем (меняются только те поля, которые действительно отличают группировку,
	// т.е. если бросили в старшей группе, то меняется её значение у элемента, а значения всех
	// младших групп при этом не меняются. Если бросили в своей группе, ничего не меняется.
	соотИзменённых=Новый Соответствие;
	Для каждого кизИсходного Из соотПоляИсходного Цикл
		знчТекущего=соотПоляТекущего.Получить(кизИсходного.Ключ);
		Если знчТекущего=Неопределено Тогда Продолжить КонецЕсли; // не нашли...
		Если знчТекущего<>кизИсходного.Значение Тогда
			соотИзменённых.Вставить(кизИсходного.Ключ,знчТекущего); // новое значение
		КонецЕсли;
	КонецЦикла;
	//
	Если соотИзменённых.Количество()=0 Тогда Возврат КонецЕсли;
	
	// собственно, изменяем поля объекта на сервере
	рНадоОбновить=Ложь;
	Для й=0 По спЗначение.Количество()-2 Цикл
		рКогоТащим=спЗначение.Получить(й);
		Если ИзменитьЗначенияГруппировочныхПолейДляОбъектаНаСервере(рКогоТащим,соотИзменённых) Тогда
			рНадоОбновить=Истина;
		КонецЕсли;
	КонецЦикла;
	Если рНадоОбновить Тогда
		Элемент.Обновить();
	КонецЕсли;
	
КонецПроцедуры

#КонецОбласти

#КонецОбласти

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

Некоторая специфика в том, что источник и получатель события - один и тот же элемент формы. Тестировалось на 8.3.6 в толстом и тонком клиенте.
Если это кому пригодится, будет неплохо)

Динамический список перетаскивание drag'n'drop

См. также

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

21.05.2024    22784    dimanich70    81    

147

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    4291    3    John_d    11    

57

Универсальные функции Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    21850    atdonya    25    

58

Универсальные функции Программист Платформа 1С v8.3 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    5793    ke.92@mail.ru    17    

65

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    15725    YA_418728146    8    

170

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    3858    62    progmaster    9    

4

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    18849    174    sapervodichka    112    

136
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. VmvLer 05.06.17 13:14 Сейчас в теме
есть статистика об эффективности метода для справочника Номенклатура с количеством элементов от 100к?
2. VmvLer 05.06.17 13:16 Сейчас в теме
+(1) или документов с аналогичным количеством записей в БД
3. Yashazz 4798 05.06.17 17:06 Сейчас в теме
(1) Для больших объёмов надо допиливать в первую очередь место, где у меня помечено "черновик". Там тогда нужно сделать запрос вместо вызова СКД. Ну и, естественно, обработка должна вызываться не в цикле каждый раз (как это в примере, для каждого объекта), а единожды, с передачей массива ссылок на сервер.
Статистики, к сожалению, нету. Делал как приблуду по просьбе жены, на днях)
8. nickperel 5 07.06.17 17:13 Сейчас в теме
(3)
Очень хорошая идея, спасибо. Попробую у себя.
7. nickperel 5 07.06.17 17:10 Сейчас в теме
(1)

(1)
есть статистика об эффективности метода для справочника Номенклатура с количеством элементов от 100к?


Все должно красиво работать. Дин.список тонким клиентом запрашивается в области видимости на форме.
Если сильно вверх\низ, можно группе выделения сменить групп. реквизит.
4. kolya_tlt 89 07.06.17 09:47 Сейчас в теме
рСКД, соотДанных, киз.


как это развидеть?
dj_serega; ixijixi; TreeDogNight; pavlov_dv; bonv; WizaXxX; +6 Ответить
5. Yashazz 4798 07.06.17 13:36 Сейчас в теме
(4) Если вам шашечки, а не ехать, то извольте тут не флудить. А желающие гундеть пусть задумаются, насколько сама БСП соответствует стандартам кода 1С.
6. kolya_tlt 89 07.06.17 15:34 Сейчас в теме
(5) стандарты не для того, чтобы им слепо следовать. это том рекомендаций как, например, толковый словарь. не все же люди говорят и пишут правильно и под одним словом понимают один смысл, но стремится к этому нужно. объяснять зачем был придумал словарь нужно?
9. nickperel 5 07.06.17 17:24 Сейчас в теме
(5)
Если вам шашечки, а не ехать, то извольте тут не флудить. А желающие гундеть пусть задумаются, насколько сама БСП соответствует стандартам кода 1С.


:-) Без "гундеть", тут не могут..

БСП, наверно, ничему, кроме соображений бизнес интересов собственно самой 1С не соответствует.. Та же структура с другого подьезда выдает сертификаты программистам.
Весело.
Каждый раз думаешь из БСП чего-нибудь "дернуть" по быстрому, а вылезает длиииииииинная борода... с граблями...
10. nickperel 5 07.06.17 17:27 Сейчас в теме
(6)
стандарты не для того, чтобы им слепо следовать


Хотелось бы видеть стандарты декларируемые непосредственно в коде конфигураций с которыми имеешь дело. Оно было и есть как-то не совсем и не всегда так..
11. kolya_tlt 89 08.06.17 15:57 Сейчас в теме
(10) почему-то я сейчас вспомнил мемы Кличко :))
dj_serega; +1 Ответить
12. пользователь 09.06.17 08:21
Сообщение было скрыто модератором.
...
13. artemusII 76 12.06.17 12:02 Сейчас в теме
Наверняка в аду есть отдельный котел для тех, кто после знаков препинания и арифметических знаков не ставит пробел...
14. Yashazz 4798 14.06.17 21:00 Сейчас в теме
(13) А ещё для тех, кто реквизиты в объектах и объекты в дереве метаданных не сортирует по алфавиту))) и кто пояснения пишет к процедуре "НекоеВыполнениеДействия" в духе "// эта процедура выполняет некое действие" и точка))) и особенно для тех, кто такие "эргономичные" интерфейсы клепает, что на малый монитор не лезут и крутить надо))) Продолжить?

А вообще интересно, по делу кто-нибудь выскажется, или только гундеть умеем?)
15. Yashazz 4798 15.06.17 15:09 Сейчас в теме
Вообще что характерно: возбухают по поводу стиля кода обычно те, кто сами на ИС мало или ничего не выкладывает. Кто пользуется плодами чужих трудов. Таких вообще год от года всё больше - халявщиков, жирующих на чужом труде. И именно они гневно возмущаются, мол как же так, их величествам глазки ломать неохота - мало что код на дармовщинку, так он ещё и должен быть белым и пушистым. Такое прямо капризное детское возмущение, типа погремушку не того сорта дали.

Собеседую я тут одного "товарисча" на предмет себе в помощники. Уверенно хочет шестизначный оклад. Ставлю задачу, мол, как будете решать? Он выдаёт "скачаю обработку". Причём не то чтоб сильно шаблонная задача-то, не тот случай, когда есть типовые удачные решения. Говорю, а где, а если не найдёте? Он, уверенно: "ну, на ИС есть наверняка, или попрошу кого-нибудь допилить". Я аж дар речи потерял от подобной наглости.

Так и здесь. Мало что код выложен, ещё изволь их величествам имена переменных, чтоб, значитца, ндравилось) Зажрались ребятки. Почитали б на досуге исходники сишного или паскалевского кода - резко бы просветлились, особливо насчёт имён переменных))
16. Yashazz 4798 23.08.17 19:51 Сейчас в теме
Копался в коде печати кассовой книги УТ 11.2. Если очень вежливо, то - такой код наверняка не получил бы 1С-Совместимо. Желающие да посмотрят сами)
Оставьте свое сообщение