[ELMA3] Раскрывающиеся панели и группировка в отчетах
Раскрывающиеся панели и группировка в отчетах
Удобное представление большого объема информации – важная задача проектирования интерфейса. В этой статье будет описана группировка данных, и помещение данных в раскрывающиеся панели (collapsible panel, «спойлер») с помощью .NET Razor. Более подробно о создании отчетов можно узнать здесь.
Группировка
Для начала работы на странице отчета на вкладке Настройки отображения в блоке Макет отчета необходимо выбрать .NET Razor и перейти к вкладке. Откроется пустой лист для разметки. Нет необходимости все делать вручную, на верхней панели находится Мастер шаблона (подробней о использовании мастера можно узнать в этой статье, с помощью которого будет сгенерирован базовый код макета, который будет выглядеть примерно следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | @ using EleWise.ELMA.BPM.Web.Reports.Extensions @ using System.Data @model EleWise.ELMA.BPM.Web.Reports.Models.ReportParametersInfo @{ //Получение источника данных по имени var data = Model.DataSources[ "Данные" ]; //Выполнение HQL или SQL запроса, содержащегося внутри источника данных, и получение результата DataTable items = data.Get(); } <style> .list th { background: none repeat scroll 0 0 #666666; color: #FFFFFF; padding: 5px; text-align: left; } .list td { border-bottom: 1px solid #CCCCCC; padding: 3px 5px; vertical-align: middle; } </style> <div> <p style= "font-size:20px; text-align:center;" ></p> @* Включает постраничное отображение источника данных *@ @(Html.Pager(Model, data)) @* Заголовки колонок отчета. Название колонок берутся из названий столбцов таблицы, содержащей результат выполнение источника данных *@ <table class = "list" width= "100%" style= "margin-top: 10px; font-size: 11px;" > <tr> <th scope= "col" >@items.Columns[ "Subject" ]</th> <th scope= "col" >@items.Columns[ "Name" ]</th> <th scope= "col" >@items.Columns[ "FullName" ]</th> </tr> @* Результат выполнения источника данных представляет собой таблицу. Пробегаемся по строкам таблицы и отображаем значение столбцов *@ @ foreach (DataRow row in items.Rows) { <tr valign= "top" > <td> @* Считываем значение колонки Subject из текущей строки *@ @row[ "Subject" ] </td> <td> @* Считываем значение колонки Name из текущей строки *@ @row[ "Name" ] </td> <td> @* Считываем значение колонки FullName из текущей строки *@ @row[ "FullName" ] </td> </tr> } </table> </div> |
Отчет же будет выглядеть примерно так:
В первую очередь необходимо отключить пейджинг, т.к. сортировка на разных страницах невозможна. Сделать это можно, удалив следующую конструкцию (ее легко найти по соответствующему комментарию)
@* Включает постраничное отображение источника данных *@
@(Html.Pager(Model, data))
Затем, получаемые данные необходимо отсортировать по какому-то полю. Для этого, добавьте строчку
var groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r["FullName"]);
после существующей записи:
//Выполнение HQL или SQL запроса, содержащегося внутри источника данных, и получение результата
DataTable items = data.GetAll();
Где ["FullName"] – поле по которому будет производиться группировка.
После этого, необходимо найти начало цикла для вывода данных, который выглядит следующим образом:
@* Результат выполнения источника данных представляет собой таблицу. Пробегаемся по строкам таблицы и отображаем значение столбцов *@
@foreach (DataRow row in items.Rows)
И изменить на:
@foreach (DataRow row in gi)
Затем найти комментарий
@* Заголовки колонок отчета. Название колонок берутся из названий столбцов таблицы, содержащей результат выполнение источника данных *@
И вставить еще один цикл под комментарием
@foreach(var gi in groupedItems)
{
Закрывающая скобка цикла должна быть в конце макета отчета, между тегами </table> и </div>
</table>
}
</div>
Код целиком будет выглядеть примерно так:
@using EleWise.ELMA.BPM.Web.Reports.Extensions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | @ using System.Data @model EleWise.ELMA.BPM.Web.Reports.Models.ReportParametersInfo @{ //Получение источника данных по имени var data = Model.DataSources[ "Данные" ]; //Выполнение HQL или SQL запроса, содержащегося внутри источника данных, и получение результата DataTable items = data.Get(); var groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r[ "FullName" ]); } <style> .list th { background: none repeat scroll 0 0 #666666; color: #FFFFFF; padding: 5px; text-align: left; } .list td { border-bottom: 1px solid #CCCCCC; padding: 3px 5px; vertical-align: middle; } </style> <div> <p style= "font-size:20px; text-align:center;" ></p> @* Заголовки колонок отчета. Название колонок берутся из названий столбцов таблицы, содержащей результат выполнение источника данных *@ @ foreach (var gi in groupedItems) { <table class = "list" width= "100%" style= "margin-top: 10px; font-size: 11px;" > <tr> <th scope= "col" >@items.Columns[ "Subject" ]</th> <th scope= "col" >@items.Columns[ "Name" ]</th> <th scope= "col" >@items.Columns[ "FullName" ]</th> </tr> @* Результат выполнения источника данных представляет собой таблицу. Пробегаемся по строкам таблицы и отображаем значение столбцов *@ @ foreach (DataRow row in gi) { <tr valign= "top" > <td> @* Считываем значение колонки Subject из текущей строки *@ Тема задачи </td> <td> @* Считываем значение колонки Name из текущей строки *@ @row[ "Name" ] </td> <td> @* Считываем значение колонки FullName из текущей строки *@ @row[ "FullName" ] </td> </tr> } </table> } </div> |
А отчет примерно следующий вид:
Как видите, данные сгруппированы по ФИО исполнителя.
Раскрывающиеся панели
Теперь скроем группы данных в раскрывающиеся панели.
Сперва необходимо добавить пространство имен
@using EleWise.ELMA.Web.Mvc.Html
А затем: конструкцию
@(Html.CollapsiblePanel()
.Header(string.Format("{0}", SR.T("Панель")))
.SaveState(false)
.Expanded(false)
.Class("Input_Separator")
.Content(@<text>
</text>).Render())
Где между тегами <text> </text> будет находиться то, что мы будем скрывать, а в строке .Header(string.Format("{0}", SR.T("Панель"))) – название панелей. В данный момент это просто строка Панель, туда можно вывести, например, название объекта группировки, в нашем случае – ФИО исполнителя. Сделать это можно заменив строку Панель на
@gi.Key.ToString()
Т.е. на название ключевой колонки, по которой идет группировка. Поскольку в раскрывающихся панелях будут находиться все данные отчета, необходимо чтобы между тегами <text> </text> попала вся часть макета отвечающая за вывод данных. Поэтому первую часть
@(Html.CollapsiblePanel()
.Header(string.Format("{0}", SR.T("Панель")))
.SaveState(false)
.Expanded(false)
.Class("Input_Separator")
.Content(@<text>
</text>).Render())
необходимо вставить сразу после начала первого цикла foreach
@foreach(var gi in groupedItems)
{
@(Html.CollapsiblePanel()
.Header(string.Format("{0}", SR.T(@gi.Key.ToString())))
.SaveState(false)
.Expanded(false)
.Class("Input_Separator")
.Content(@<text>
а закрыть – в самом конце:
</table>
</text>).Render())
}
</div>
до закрытия цикла.
Код целиком будет выглядеть так:
@using EleWise.ELMA.BPM.Web.Reports.Extensions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | @ using System.Data @ using EleWise.ELMA.Web.Mvc.Html @model EleWise.ELMA.BPM.Web.Reports.Models.ReportParametersInfo @{ //Получение источника данных по имени var data = Model.DataSources[ "Данные" ]; //Выполнение HQL или SQL запроса, содержащегося внутри источника данных, и получение результата DataTable items = data.Get(); var groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r[ "FullName" ]); } <style> .list th { background: none repeat scroll 0 0 #666666; color: #FFFFFF; padding: 5px; text-align: left; } .list td { border-bottom: 1px solid #CCCCCC; padding: 3px 5px; vertical-align: middle; } </style> <div> <p style= "font-size:20px; text-align:center;" ></p> @* Заголовки колонок отчета. Название колонок берутся из названий столбцов таблицы, содержащей результат выполнение источника данных *@ @ foreach (var gi in groupedItems) { @(Html.CollapsiblePanel() .Header( string .Format( "{0}" , SR.T(@gi.Key.ToString()))) .SaveState( false ) .Expanded( false ) .Class( "Input_Separator" ) .Content(@<text> <table class = "list" width= "100%" style= "margin-top: 10px; font-size: 11px;" > <tr> <th scope= "col" >@items.Columns[ "Subject" ]</th> <th scope= "col" >@items.Columns[ "Name" ]</th> <th scope= "col" >@items.Columns[ "FullName" ]</th> </tr> @* Результат выполнения источника данных представляет собой таблицу. Пробегаемся по строкам таблицы и отображаем значение столбцов *@ @ foreach (DataRow row in gi) { <tr valign= "top" > <td> @* Считываем значение колонки Subject из текущей строки *@ Тема задачи </td> <td> @* Считываем значение колонки Name из текущей строки *@ @row[ "Name" ] </td> <td> @* Считываем значение колонки FullName из текущей строки *@ @row[ "FullName" ] </td> </tr> } </table> </text>).Render()) } </div> |
А отчет вот так:
Панель Петрова Алексея свернута.
Использование параметров
Вполне вероятно, что Вам захочется менять группировку прямо со страницы вызова отчета, без внесения изменений в Дизайнере. Для этого можно использовать параметры.
Для использования параметров в коде разметки макета отчета, необходимо воспользоваться разделом Код контроллера (может очень долго открываться), куда вместо закомментированных:
//result.Model.UseCustomModel = true;
//result.Model.CustomModel = new string[] {"один", "два", "три"}
добавить следующие строки:
result.Model.UseCustomModel = true;
result.Model.CustomModel = parameters;
После чего в начало макета отчета добавить:
dynamic parameters = Model.CustomModel;
Теперь через переменную parameters можно обращаться к параметрам отчета. В данном примере в качестве параметра используется выпадающий список Gruppirovka, который имеет значения: Исполнитель и Вид деятельности.
Осталось лишь добавить условия на тип группировки и название панелей
if(parameters.Gruppirovka.Value == "Вид деятельности")
{
groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r["Name"]);
}
По умолчанию, переменная groupedItems объявляется с группировкой по ФИО
var groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r["FullName"]);
В случае, если в выпадающем списке выбрана запись Вид деятельности, группировка осуществляется по виду деятельности. А благодаря указания ключевой колонки
.Header(string.Format("{0}", SR.T(@gi.Key.ToString())))
В качестве названия для панелей, названия будут меняться автоматически.
Полный код будет выглядеть примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | @ using EleWise.ELMA.BPM.Web.Reports.Extensions @ using System.Data @ using EleWise.ELMA.Web.Mvc.Html @model EleWise.ELMA.BPM.Web.Reports.Models.ReportParametersInfo @{ //Получение источника данных по имени var data = Model.DataSources[ "Данные" ]; //Выполнение HQL или SQL запроса, содержащегося внутри источника данных, и получение результата dynamic parameters = Model.CustomModel; DataTable items = data.Get(); var groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r[ "FullName" ]); if (parameters.Gruppirovka.Value == "Вид деятельности" ) { groupedItems = items.Rows.Cast<DataRow>().GroupBy(r => r[ "Name" ]); } } <style> .list th { background: none repeat scroll 0 0 #666666; color: #FFFFFF; padding: 5px; text-align: left; } .list td { border-bottom: 1px solid #CCCCCC; padding: 3px 5px; vertical-align: middle; } </style> <div> <p style= "font-size:20px; text-align:center;" ></p> @* Заголовки колонок отчета. Название колонок берутся из названий столбцов таблицы, содержащей результат выполнение источника данных *@ @ foreach (var gi in groupedItems) { @(Html.CollapsiblePanel() .Header( string .Format( "{0}" , SR.T(@gi.Key.ToString()))) .SaveState( false ) .Expanded( false ) .Class( "Input_Separator" ) .Content(@<text> <table class = "list" width= "100%" style= "margin-top: 10px; font-size: 11px;" > <tr> <th scope= "col" >@items.Columns[ "Subject" ]</th> <th scope= "col" >@items.Columns[ "Name" ]</th> <th scope= "col" >@items.Columns[ "FullName" ]</th> </tr> @* Результат выполнения источника данных представляет собой таблицу. Пробегаемся по строкам таблицы и отображаем значение столбцов *@ @ foreach (DataRow row in gi) { <tr valign= "top" > <td> @* Считываем значение колонки Subject из текущей строки *@ Тема задачи </td> <td> @* Считываем значение колонки Name из текущей строки *@ @row[ "Name" ] </td> <td> @* Считываем значение колонки FullName из текущей строки *@ @row[ "FullName" ] </td> </tr> } </table> </text>).Render()) } </div> |
А отчет сгруппированный по видам деятельности так: