Каталог решений - Наведи порядок с помощью дерева

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

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

В наличии

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

Категория:

Описание

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

has been added to your cart:
Оформление заказа