[ELMA3] Раскрывающиеся панели и группировка в отчетах
Раскрывающиеся панели и группировка в отчетах
Удобное представление большого объема информации – важная задача проектирования интерфейса. В этой статье будет описана группировка данных, и помещение данных в раскрывающиеся панели (collapsible panel, «спойлер») с помощью .NET Razor. Более подробно о создании отчетов можно узнать здесь.
Группировка
Для начала работы на странице отчета на вкладке Настройки отображения в блоке Макет отчета необходимо выбрать .NET Razor и перейти к вкладке. Откроется пустой лист для разметки. Нет необходимости все делать вручную, на верхней панели находится Мастер шаблона (подробней о использовании мастера можно узнать в этой статье, с помощью которого будет сгенерирован базовый код макета, который будет выглядеть примерно следующим образом:
@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
@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
@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())))
В качестве названия для панелей, названия будут меняться автоматически.
Полный код будет выглядеть примерно так:
@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>
А отчет сгруппированный по видам деятельности так: