gifts2017

Добавление функционала "поиск по строке" для динамического списка управляемого приложения на основе Подбора в УНФ

Опубликовал Alena Saveleva (AlenaSa) в раздел Программирование - Практика программирования

Пример реализации "поиска по строке" для любой колонки динамического списка управляемого приложения на основе использования Отбора и сортировки списка.

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

В описанном ниже примере поиск  вместе с сортировкой по полю реализован для колонок Код, Артикул, Наименование.

Добавляем  Реквизиты формы ПоискКод, ПоискАртикул, ПоискНаименование с типом «Строка».Вносим их в Группу Поиск на форме.

Программируем  события поля ввода АвтоПодбор:

поле ввода ПоискАртикул

 

 &НаКлиенте
Процедура ПоискАртикулАвтоПодбор(Элемент, Текст, ДанныеВыбора, Ожидание, СтандартнаяОбработка)
   
// Вставить содержимое обработчика.
   
ПоискКод = "";
   
ПоискНаименование = "";
     Если
СокрЛП(Текст) = "" Тогда
         
СписокЗапасов.Отбор.Элементы.Очистить();
          возврат;
     КонецЕсли;
    
Установитьпорядок("Артикул");
    
УстановитьПоискПоСтрокеНаСервере(Текст, "Артикул");
КонецПроцедуры

 процедура
Установитьпорядок(ИмяПоля)
     Если НЕ (
СписокЗапасов.Порядок.элементы.количество() = 1 и СписокЗапасов.Порядок.элементы[0].поле = Новый ПолеКомпоновкиДанных(ИмяПоля))  тогда
        
СписокЗапасов.Порядок.элементы.очистить();
        
НЭлемент = СписокЗапасов.Порядок.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
        
НЭлемент.Использование = Истина;
        
НЭлемент.Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
        
НЭлемент.ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных.Возр;
        
НЭлемент.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Обычный;
     конецЕсли;
 КонецПроцедуры

&НаСервере
Процедура УстановитьПоискПоСтрокеНаСервере(Текст, ИмяКолонки)
    
ПостроительЗапроса = новый построительЗапроса;
    
ПостроительЗапроса.Текст = "ВЫБРАТЬ
                                |   Номенклатура.Ссылка
                                |ИЗ
                                |   Справочник.Номенклатура КАК Номенклатура
                                |ГДЕ
                                |   ПОДСТРОКА(Номенклатура."
+ ИмяКолонки + " , 1, &Длина) = &Зн
                                |{ГДЕ
                                |   Номенклатура.Ссылка.*}"
;
   
ПостроительЗапроса.Параметры.Вставить("Длина", СтрДлина(Текст));
   
ПостроительЗапроса.Параметры.Вставить("Зн", Текст);

    Если Не
СписокЗапасов.Параметры.элементы[2].Значение = Справочники.Номенклатура.ПустаяСсылка() тогда
       
НЭлемент = ПостроительЗапроса.Отбор.Добавить("Ссылка");
       
НЭлемент.ВидСравнения = ВидСравнения.ВИерархии;
       
НЭлемент.Значение = СписокЗапасов.Параметры.элементы[2].Значение;
       
НЭлемент.Использование = Истина;
    КонецЕсли;
   
ПостроительЗапроса.Выполнить();
   
ТЗ = Построительзапроса.Результат.Выгрузить();
   
Сп = новый СписокЗначений;
   
Сп.ЗагрузитьЗначения(ТЗ.ВыгрузитьКолонку("Ссылка"));

   
СписокЗапасов.Отбор.Элементы.Очистить();
    
НЭлемент = СписокЗапасов.Отбор.Элементы.добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    
Нэлемент.левоеЗначение = новый ПолекомпоновкиДанных("Номенклатура");
    
НЭлемент.ВидСравнения  = ВидСравненияКомпоновкиДанных.ВСписке;
    
НЭлемент.ПравоеЗначение = Сп;
    
НЭлемент.Использование  = истина;
КонецПроцедуры

&НаКлиенте
Процедура ПоискАртикулОкончаниеВводаТекста(Элемент, Текст, ДанныеВыбора, СтандартнаяОбработка)
   
// Вставить содержимое обработчика.
   
Этаформа.ТекущийЭлемент = Элементы.СписокЗапасов;
КонецПроцедуры

Таким образом получаем , что при наборе первых букв искомого варианта в поле «по артикулу» , например,  группы поиска,  происходит сортировка списка по колонке Артикул и отбор тех строк, артикул которых начинается с набранных символов. 

результат работы кода

Тут , конечно, был бы удачнее не отбор, а позиционирование на первом найденном в списке элементе .  Т.е.  мне бы хотелось в тексте запроса Построителя  добавить «Первые 1» и дальше просто позиционироваться на найденной ссылке, но как найти позицию в динамическом списке ? Ответа на этот вопрос  не нашла.

Буду рада любым предложениям по оптимизации этого варианта решения вопроса или ссылкам на другой вариант решения.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. mozz mozz (mozz) 24.04.13 15:51
Моя реализация (можно искать по фрагментам наименования):
Добавляем текстовый реквизит СтрокаПоиска команду ПоказатьВсе (если нашли больше 50 элементов, то выводим 50 и если пользователь хочет то все остальные) и текстовую декорацию ПредупреждениеПоиск

Использованы в том числе и Ваши идеи.

Это в форме подбора:
&НаКлиенте
Процедура ОткючитьФильтр(Элемент, СтандартнаяОбработка)
	СписокЗапасов.Отбор.Элементы.Очистить();
	Элементы.СписокЗапасов.Отображение = ОтображениеТаблицы.ИерархическийСписок;
	Элементы.ПредупреждениеПоиск.Заголовок = "";
	СтрокаПоиска = "";
	Элементы.ПоказатьВсе.Видимость = Ложь;
КонецПроцедуры

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

&НаСервере
Процедура УстановитьОтборПоНайденному(ТаблицаРезультата)
	Сп = новый СписокЗначений;
    Сп.ЗагрузитьЗначения(ТаблицаРезультата.ВыгрузитьКолонку("Ссылка"));

     СписокЗапасов.Отбор.Элементы.Очистить();
     НЭлемент = СписокЗапасов.Отбор.Элементы.добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
     Нэлемент.левоеЗначение = новый ПолекомпоновкиДанных("Номенклатура");
     НЭлемент.ВидСравнения  = ВидСравненияКомпоновкиДанных.ВСписке;
     НЭлемент.ПравоеЗначение = Сп;
     НЭлемент.Использование  = Истина;
   	
КонецПроцедуры

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

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

...Показать Скрыть


А это в общем модуле ОбщегоНазначенияВызовСервера

Функция РезультатЗапросаПоиска(СписокФрагментов, ИмяСправочника, Первые="") Экспорт
	
	текЗапрос = Новый Запрос;
	ТекстФрагментов = "";
	Для Каждого текСтрока Из СписокФрагментов Цикл
		текНомерСтроки = СписокФрагментов.Индекс(текСтрока);
		текФрагмент = текСтрока.Значение;
		//Для каждого искомого фрагмента создадим фильтр в запросе,
		//Ищем в наименовании, полном наименовании с транслитом и без
		ТекстФрагментов = ТекстФрагментов + "
			|	" + ?(текНомерСтроки > 0, "И ", "") + "(Наименование ПОДОБНО ""%"" + &ФрагментКириллица" + текНомерСтроки + " + ""%""
			|	ИЛИ НаименованиеПолное ПОДОБНО ""%"" + &ФрагментКириллица" + текНомерСтроки + " + ""%""
			|	ИЛИ Наименование ПОДОБНО ""%"" + &Фрагмент1Транслит" + текНомерСтроки + " + ""%""
			|	ИЛИ НаименованиеПолное ПОДОБНО ""%"" + &Фрагмент1Транслит" + текНомерСтроки + " + ""%""
			|	ИЛИ Наименование ПОДОБНО ""%"" + &Фрагмент2Транслит" + текНомерСтроки + " + ""%""
			|	ИЛИ НаименованиеПолное ПОДОБНО ""%"" + &Фрагмент2Транслит" + текНомерСтроки + " + ""%"")"; 
		//и установим параметры
		текЗапрос.УстановитьПараметр("ФрагментКириллица" + текНомерСтроки, текФрагмент);	
		СписокТранслита = Транслит(текФрагмент);
		текЗапрос.УстановитьПараметр("Фрагмент1Транслит" + текНомерСтроки, СписокТранслита[0].Значение);	 
		текЗапрос.УстановитьПараметр("Фрагмент2Транслит" + текНомерСтроки, СписокТранслита[1].Значение);	 
	КонецЦикла;
	текЗапрос.Текст = "ВЫБРАТЬ "+Первые+"
		|	Вложение.Ссылка,
		|	Вложение.Наименование
		|ИЗ
		|	(ВЫБРАТЬ
		|		"+ИмяСправочника+".Ссылка КАК Ссылка,
		|		"+ИмяСправочника+".Наименование КАК Наименование,
		|		ВЫРАЗИТЬ("+ИмяСправочника+".НаименованиеПолное КАК СТРОКА(200)) КАК НаименованиеПолное
		|	ИЗ
		|		Справочник."+ИмяСправочника+" КАК "+ИмяСправочника+"
		|	ГДЕ
		|		НЕ "+ИмяСправочника+".ЭтоГруппа) КАК Вложение    
		|	ГДЕ 
		|		" + ТекстФрагментов + "
		|УПОРЯДОЧИТЬ ПО
		|	Вложение.Ссылка.ПометкаУдаления";
	Возврат текЗапрос.Выполнить().Выгрузить();
	
КонецФункции

//Параллельно кириллице будем искать фрагменты на латинице,
//в двух вариантах из-за различия транслитирации некоторых букв
Функция Транслит(Фрагмент)
	СписокТранслита = Новый СписокЗначений;
	Кириллица = "абвгдежзийклмнопрстуфхц";               
	Латиница1 = "abvgdegzijklmnoprstufhc";
	Латиница2 = "abwgdejzijqlmnoprstufhc";
	КириллицаДиграф = "ч ш ";                                                  
	ЛатиницаДиграф = "chsh"; 
	Транслит1 = "";
	Транслит2 = "";
	Для позиция = 1 По СтрДлина(Фрагмент) Цикл
		текБуква = Сред(Фрагмент, позиция, 1);
		вШаблоне = Найти(Кириллица, текБуква);
		Если вШаблоне > 0 Тогда
			Транслит1 = Транслит1 + Сред(Латиница1, вШаблоне, 1);
			Транслит2 = Транслит2 + Сред(Латиница2, вШаблоне, 1);
			Продолжить;
		КонецЕсли;
	    вШаблоне = Найти(КириллицаДиграф, текБуква);
		Если вШаблоне > 0 Тогда
			Транслит1 = Транслит1 + Сред(ЛатиницаДиграф, вШаблоне, 2);
			Транслит2 = Транслит2 + Сред(ЛатиницаДиграф, вШаблоне, 2);
			Продолжить;
		КонецЕсли;
		Транслит1 = Транслит1 + текБуква;
		Транслит2 = Транслит2 + текБуква;
	КонецЦикла;
	СписокТранслита.Добавить(Транслит1);
	СписокТранслита.Добавить(Транслит2);
	Возврат СписокТранслита;	
КонецФункции
...Показать Скрыть
2. Николай Денисов (dentoma51) 11.12.13 15:26
AlenaSa,благодарю Вас! Я воспользовался Вашей разработкой функционала "Поиск по строке".
Не знаю,может не правильно,но сделал так. В УФ добавил группу Поиск с симвоьным полем для ввода фрагмента наименования.

В обработке события при изменении поля поиска так:
&НаКлиенте
Процедура ПоискПоНаименованиюПриИзменении(Элемент)
// Вставить содержимое обработчика.
Если СокрЛП(Элемент.ТекстРедактирования) = "" Тогда
Список.Отбор.Элементы.Очистить();
возврат;
КонецЕсли;
//Установитьпорядок("Артикул");
УстановитьПоискПоСтрокеНаСервере(Элемент.ТекстРедактирования, "Наименование");
КонецПроцедуры

&НаСервере
Процедура УстановитьПоискПоСтрокеНаСервере(Текст, ИмяКолонки)
ПостроительЗапроса = новый построительЗапроса;
ПостроительЗапроса.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Наименование ПОДОБНО &Зн"
;

ПостроительЗапроса.Параметры.Вставить("Зн", "%"+Текст+"%");

ПостроительЗапроса.Выполнить();
ТЗ = Построительзапроса.Результат.Выгрузить();
Сп = новый СписокЗначений;
Сп.ЗагрузитьЗначения(ТЗ.ВыгрузитьКолонку("Ссылка"));

Список.Отбор.Элементы.Очистить();
НЭлемент = Список.Отбор.Элементы.добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Нэлемент.левоеЗначение = новый ПолекомпоновкиДанных("Контрагент");
НЭлемент.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
НЭлемент.ПравоеЗначение = Сп;
НЭлемент.Использование = истина;
КонецПроцедуры


Вроде бы работает. Учитывая,что УФ только начинаю изучать,то рад и этому. Спасибо.
3. Alena Saveleva (AlenaSa) 11.12.13 21:45
Спасибо всем за комментарии. Рада, что пригодилась публикация и есть развитие идеи.
Сама сейчас вернулась к УПП, которое ещё на обычном приложении.
УНФ не пользуется пока что на Украине популярностью, хотя мне очень понравилась, когда изучала и предлагала клиентам.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа