PredifinedReports için tasarım ve düzenleme
This commit is contained in:
parent
d8763d6dee
commit
566a0ae0e5
11 changed files with 450 additions and 96 deletions
|
|
@ -20,6 +20,7 @@ using Sozsoft.Platform.Enums;
|
|||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
using Volo.Abp.Users;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -76,6 +77,7 @@ public class DynamicFormReport : XtraReport
|
|||
var defaultValueHelper = scope.ServiceProvider.GetRequiredService<DefaultValueHelper>();
|
||||
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
|
||||
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
|
||||
if (!await authManager.CanAccess(listFormCode))
|
||||
{
|
||||
|
|
@ -117,7 +119,7 @@ public class DynamicFormReport : XtraReport
|
|||
|
||||
ConfigurePage();
|
||||
ConfigureDataSource(selectQuery, connectionString, dataSourceType);
|
||||
await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration));
|
||||
await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration), currentUser.UserName);
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +157,8 @@ public class DynamicFormReport : XtraReport
|
|||
IReadOnlyList<FormReportField> fields,
|
||||
IDictionary<string, object> row,
|
||||
IStringLocalizer<PlatformResource> localizer,
|
||||
string imageBaseUrl)
|
||||
string imageBaseUrl,
|
||||
string currentUserName)
|
||||
{
|
||||
Bands.Clear();
|
||||
var formGroups = GetFormGroups(listForm, fields).ToList();
|
||||
|
|
@ -176,19 +179,15 @@ public class DynamicFormReport : XtraReport
|
|||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
|
||||
|
||||
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);
|
||||
DynamicReportImageHelper.AddCurrentUserNameToFooter(bottomMargin.Controls, currentUserName, pageWidth);
|
||||
|
||||
bottomMargin.Controls.Add(new XRPageInfo
|
||||
{
|
||||
PageInfo = PageInfo.DateTime,
|
||||
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 7.5F),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
bottomMargin.Controls.Add(new XRPageInfo
|
||||
{
|
||||
TextFormatString = "Sayfa {0} / {1}",
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 8, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
TextAlignment = TextAlignment.MiddleRight
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Sozsoft.Platform.Enums;
|
|||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
using Volo.Abp.Users;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ public class DynamicGridReport : XtraReport
|
|||
var defaultValueHelper = scope.ServiceProvider.GetRequiredService<DefaultValueHelper>();
|
||||
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
|
||||
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
|
||||
if (!await authManager.CanAccess(listFormCode))
|
||||
{
|
||||
|
|
@ -142,6 +144,11 @@ public class DynamicGridReport : XtraReport
|
|||
defaultValueHelper,
|
||||
dataSourceType);
|
||||
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
||||
var criteriaText = DynamicReportImageHelper.BuildReportCriteriaText(
|
||||
queryParams.Filter,
|
||||
queryParams.Sort,
|
||||
fields,
|
||||
localizer);
|
||||
reportColumns = await ApplyImageCountsAsync(reportColumns, dynamicDataRepository, selectQuery, connectionString, imageBaseUrl);
|
||||
|
||||
ConfigurePage(reportColumns);
|
||||
|
|
@ -149,7 +156,7 @@ public class DynamicGridReport : XtraReport
|
|||
selectQuery,
|
||||
connectionString,
|
||||
dataSourceType);
|
||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl);
|
||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText);
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
|
|
@ -236,13 +243,15 @@ public class DynamicGridReport : XtraReport
|
|||
ListForm listForm,
|
||||
IReadOnlyList<ReportColumn> fields,
|
||||
IStringLocalizer<PlatformResource> localizer,
|
||||
string imageBaseUrl)
|
||||
string imageBaseUrl,
|
||||
string currentUserName,
|
||||
string criteriaText)
|
||||
{
|
||||
Bands.Clear();
|
||||
|
||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = string.IsNullOrWhiteSpace(criteriaText) ? 32 : 50 };
|
||||
var pageHeader = new PageHeaderBand { HeightF = 28 };
|
||||
var detailHeight = fields.Any(x => x.IsImage)
|
||||
? DynamicReportImageHelper.GetGridImageHeight()
|
||||
|
|
@ -262,21 +271,16 @@ public class DynamicGridReport : XtraReport
|
|||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
|
||||
DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth);
|
||||
|
||||
var generatedAt = new XRPageInfo
|
||||
{
|
||||
PageInfo = PageInfo.DateTime,
|
||||
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 7.5F),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
};
|
||||
bottomMargin.Controls.Add(generatedAt);
|
||||
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);
|
||||
DynamicReportImageHelper.AddCurrentUserNameToFooter(bottomMargin.Controls, currentUserName, pageWidth);
|
||||
|
||||
var pageInfo = new XRPageInfo
|
||||
{
|
||||
TextFormatString = "Sayfa {0} / {1}",
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 8, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
TextAlignment = TextAlignment.MiddleRight
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using DevExpress.XtraReports.UI;
|
|||
using Sozsoft.Languages.Entities;
|
||||
using Sozsoft.Platform.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.Users;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -35,11 +36,47 @@ public partial class DynamicReport : XtraReport
|
|||
|
||||
// Language verilerini yükle
|
||||
LoadLanguageParameters();
|
||||
AddHeaderGeneratedAt();
|
||||
AddFooterCurrentUserName();
|
||||
AddFooterLine();
|
||||
|
||||
pageInfo1.TextFormatString = "{0:d}";
|
||||
pageInfo1.Visible = false;
|
||||
xrRichText1.BeforePrint += XrRichText1_BeforePrint;
|
||||
}
|
||||
|
||||
private void AddFooterLine()
|
||||
{
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
DynamicReportImageHelper.AddFooterTopLine(BottomMargin.Controls, pageWidth);
|
||||
pageInfo1.TopF = 8;
|
||||
pageInfo2.TopF = 8;
|
||||
}
|
||||
|
||||
private void AddHeaderGeneratedAt()
|
||||
{
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
DynamicReportImageHelper.AddGeneratedAtToHeader(ReportHeader.Controls, pageWidth);
|
||||
}
|
||||
|
||||
private void AddFooterCurrentUserName()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_serviceProvider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
DynamicReportImageHelper.AddCurrentUserNameToFooter(BottomMargin.Controls, currentUser.UserName, pageWidth);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadLanguageParameters()
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ internal static class DynamicReportImageHelper
|
|||
private const string ImageSourceFunctionName = "SozReportImageSource";
|
||||
private const string FormatValueFunctionName = "SozReportFormatValue";
|
||||
private static readonly Regex ReportPagingRegex = new(
|
||||
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
||||
@"\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?\s*$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
private static readonly HttpClient HttpClient = new(new HttpClientHandler
|
||||
{
|
||||
|
|
@ -271,6 +271,94 @@ internal static class DynamicReportImageHelper
|
|||
return Localize(localizer, caption, field.FieldName);
|
||||
}
|
||||
|
||||
public static void AddGeneratedAtToHeader(XRControlCollection controls, float pageWidth)
|
||||
{
|
||||
var width = Math.Min(260F, pageWidth * 0.4F);
|
||||
controls.Add(new XRPageInfo
|
||||
{
|
||||
PageInfo = PageInfo.DateTime,
|
||||
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth - width, 0, width, 18),
|
||||
Font = new DevExpress.Drawing.DXFont("Arial", 8F),
|
||||
TextAlignment = TextAlignment.MiddleRight,
|
||||
CanGrow = false
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddCurrentUserNameToFooter(XRControlCollection controls, string userName, float pageWidth)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controls.Add(new XRLabel
|
||||
{
|
||||
Text = userName,
|
||||
BoundsF = new System.Drawing.RectangleF(0, 8, pageWidth / 2, 18),
|
||||
Font = new DevExpress.Drawing.DXFont("Arial", 7.5F),
|
||||
TextAlignment = TextAlignment.MiddleLeft,
|
||||
CanGrow = false,
|
||||
Multiline = false
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddReportCriteriaToHeader(
|
||||
XRControlCollection controls,
|
||||
string criteriaText,
|
||||
float pageWidth)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(criteriaText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
controls.Add(new XRLabel
|
||||
{
|
||||
Text = criteriaText,
|
||||
BoundsF = new System.Drawing.RectangleF(0, 28, pageWidth, 18),
|
||||
Font = new DevExpress.Drawing.DXFont("Arial", 7.5F),
|
||||
ForeColor = System.Drawing.Color.FromArgb(100, 116, 139),
|
||||
TextAlignment = TextAlignment.MiddleLeft,
|
||||
CanGrow = true,
|
||||
Multiline = true
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddFooterTopLine(XRControlCollection controls, float pageWidth)
|
||||
{
|
||||
controls.Add(new XRLine
|
||||
{
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 2),
|
||||
ForeColor = System.Drawing.Color.FromArgb(203, 213, 225),
|
||||
LineWidth = 1
|
||||
});
|
||||
}
|
||||
|
||||
public static string BuildReportCriteriaText(
|
||||
string filter,
|
||||
string sort,
|
||||
IEnumerable<ListFormField> fields,
|
||||
IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
var fieldCaptions = CreateFieldCaptionMap(fields, localizer);
|
||||
var filterText = FormatFilterText(filter, fieldCaptions);
|
||||
var sortText = FormatSortText(sort, fieldCaptions);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filterText))
|
||||
{
|
||||
parts.Add("Filtre: " + filterText);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(sortText))
|
||||
{
|
||||
parts.Add("Sıralama: " + sortText);
|
||||
}
|
||||
|
||||
return string.Join(" | ", parts);
|
||||
}
|
||||
|
||||
public static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
|
|
@ -485,7 +573,7 @@ internal static class DynamicReportImageHelper
|
|||
string editorType,
|
||||
out string text)
|
||||
{
|
||||
return TryFormatReportValue(value, field.SourceDbType, field.Format, editorType, out text);
|
||||
return TryFormatReportValue(value, field.SourceDbType, GetEffectiveFormat(field), editorType, out text);
|
||||
}
|
||||
|
||||
public static string CreateFormatValueExpression(
|
||||
|
|
@ -493,7 +581,7 @@ internal static class DynamicReportImageHelper
|
|||
ListFormField field,
|
||||
string editorType)
|
||||
{
|
||||
return $"{FormatValueFunctionName}({valueExpression}, '{field.SourceDbType}', '{EscapeExpressionString(field.Format)}', '{EscapeExpressionString(editorType)}')";
|
||||
return $"{FormatValueFunctionName}({valueExpression}, '{field.SourceDbType}', '{EscapeExpressionString(GetEffectiveFormat(field))}', '{EscapeExpressionString(editorType)}')";
|
||||
}
|
||||
|
||||
public static string FormatReportDate(DateTime value)
|
||||
|
|
@ -627,6 +715,190 @@ internal static class DynamicReportImageHelper
|
|||
: value ? "Yes" : "No";
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> CreateFieldCaptionMap(
|
||||
IEnumerable<ListFormField> fields,
|
||||
IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
return (fields ?? [])
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName))
|
||||
.GroupBy(x => x.FieldName, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(
|
||||
x => x.Key,
|
||||
x => GetLocalizedFieldCaption(x.First(), localizer),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string FormatSortText(string sort, IReadOnlyDictionary<string, string> fieldCaptions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sort))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var items = sort
|
||||
.Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(item =>
|
||||
{
|
||||
var parts = item.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fieldName = parts[0].Trim('"');
|
||||
var direction = parts.Length > 1 ? parts[1] : "asc";
|
||||
var directionText = string.Equals(direction, "desc", StringComparison.OrdinalIgnoreCase)
|
||||
? "azalan"
|
||||
: "artan";
|
||||
|
||||
return $"{GetFieldCaption(fieldName, fieldCaptions)} {directionText}";
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x));
|
||||
|
||||
return string.Join(", ", items);
|
||||
}
|
||||
|
||||
private static string FormatFilterText(string filter, IReadOnlyDictionary<string, string> fieldCaptions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(filter);
|
||||
return FormatFilterElement(document.RootElement, fieldCaptions);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatFilterElement(JsonElement element, IReadOnlyDictionary<string, string> fieldCaptions)
|
||||
{
|
||||
if (element.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return FormatJsonValue(element);
|
||||
}
|
||||
|
||||
var items = element.EnumerateArray().ToList();
|
||||
if (items.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (items.Count >= 3 &&
|
||||
items[0].ValueKind == JsonValueKind.String &&
|
||||
items[1].ValueKind == JsonValueKind.String)
|
||||
{
|
||||
return FormatFilterCondition(items[0].GetString(), items[1].GetString(), items[2], fieldCaptions);
|
||||
}
|
||||
|
||||
if (items.Count == 2 &&
|
||||
items[0].ValueKind == JsonValueKind.String &&
|
||||
items[0].GetString() == "!")
|
||||
{
|
||||
var text = FormatFilterElement(items[1], fieldCaptions);
|
||||
return string.IsNullOrWhiteSpace(text) ? null : "değil (" + text + ")";
|
||||
}
|
||||
|
||||
var parts = new List<string>();
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].ValueKind == JsonValueKind.String &&
|
||||
IsFilterConnector(items[i].GetString()))
|
||||
{
|
||||
parts.Add(FormatFilterConnector(items[i].GetString()));
|
||||
continue;
|
||||
}
|
||||
|
||||
var text = FormatFilterElement(items[i], fieldCaptions);
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
parts.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
private static string FormatFilterCondition(
|
||||
string fieldName,
|
||||
string operation,
|
||||
JsonElement value,
|
||||
IReadOnlyDictionary<string, string> fieldCaptions)
|
||||
{
|
||||
var caption = GetFieldCaption(fieldName, fieldCaptions);
|
||||
var op = FormatFilterOperation(operation);
|
||||
var formattedValue = FormatJsonValue(value);
|
||||
|
||||
return string.IsNullOrWhiteSpace(formattedValue)
|
||||
? $"{caption} {op}"
|
||||
: $"{caption} {op} {formattedValue}";
|
||||
}
|
||||
|
||||
private static string FormatFilterOperation(string operation)
|
||||
{
|
||||
return operation?.ToLowerInvariant() switch
|
||||
{
|
||||
"=" => "=",
|
||||
"<>" => "eşit değil",
|
||||
">" => ">",
|
||||
">=" => ">=",
|
||||
"<" => "<",
|
||||
"<=" => "<=",
|
||||
"contains" => "içerir",
|
||||
"notcontains" => "içermez",
|
||||
"startswith" => "ile başlar",
|
||||
"endswith" => "ile biter",
|
||||
"between" => "arasında",
|
||||
"anyof" => "şunlardan biri",
|
||||
"noneof" => "şunlardan hiçbiri",
|
||||
"isblank" => "boş",
|
||||
"isnotblank" => "boş değil",
|
||||
_ => operation
|
||||
};
|
||||
}
|
||||
|
||||
private static string FormatJsonValue(JsonElement value)
|
||||
{
|
||||
return value.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => value.GetString(),
|
||||
JsonValueKind.Number => value.GetRawText(),
|
||||
JsonValueKind.True => "Evet",
|
||||
JsonValueKind.False => "Hayır",
|
||||
JsonValueKind.Null => string.Empty,
|
||||
JsonValueKind.Array => string.Join(", ", value.EnumerateArray().Select(FormatJsonValue)),
|
||||
_ => value.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetFieldCaption(string fieldName, IReadOnlyDictionary<string, string> fieldCaptions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fieldName))
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
return fieldCaptions.TryGetValue(fieldName, out var caption)
|
||||
? caption
|
||||
: fieldName;
|
||||
}
|
||||
|
||||
private static bool IsFilterConnector(string value)
|
||||
{
|
||||
return string.Equals(value, "and", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(value, "or", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string FormatFilterConnector(string value)
|
||||
{
|
||||
return string.Equals(value, "or", StringComparison.OrdinalIgnoreCase) ? "veya" : "ve";
|
||||
}
|
||||
|
||||
private static bool IsBooleanField(string editorType, DbType dbType)
|
||||
{
|
||||
return editorType is PlatformConsts.EditorTypes.dxCheckBox or PlatformConsts.EditorTypes.dxSwitch ||
|
||||
|
|
@ -635,12 +907,70 @@ internal static class DynamicReportImageHelper
|
|||
|
||||
private static string GetCustomFormat(string format)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(format) ? null : format;
|
||||
if (string.IsNullOrWhiteSpace(format))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var customFormat = TryGetFormatOption(format, "displayFormat") ??
|
||||
TryGetFormatOption(format, "format") ??
|
||||
format.Trim();
|
||||
|
||||
return NormalizeDateTimeFormat(customFormat);
|
||||
}
|
||||
|
||||
private static string GetEffectiveFormat(ListFormField field)
|
||||
{
|
||||
return TryGetFormatOption(field?.EditorOptions, "displayFormat") ??
|
||||
TryGetFormatOption(field?.EditorOptions, "format") ??
|
||||
field?.Format;
|
||||
}
|
||||
|
||||
private static string TryGetFormatOption(string json, string propertyName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(json);
|
||||
if (document.RootElement.ValueKind != JsonValueKind.Object ||
|
||||
!TryGetLookupProperty(document.RootElement, propertyName, out var value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.ValueKind == JsonValueKind.String ? value.GetString() : null;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeDateTimeFormat(string format)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(format))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return format.Trim() switch
|
||||
{
|
||||
PlatformConsts.GridColumnOptions.ColumnValueFormatShortDate => "d",
|
||||
PlatformConsts.GridColumnOptions.ColumnValueFormatShortTime => "t",
|
||||
PlatformConsts.GridColumnOptions.ColumnValueFormatLongTime => "T",
|
||||
PlatformConsts.GridColumnOptions.ColumnValueFormatShortDateShortTime => "g",
|
||||
PlatformConsts.GridColumnOptions.ColumnValueFormatLongDateLongTime => "G",
|
||||
_ => format.Trim()
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsDateField(DbType dbType)
|
||||
{
|
||||
return dbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset;
|
||||
return dbType is DbType.Date or DbType.DateTime or DbType.DateTime2 or DbType.DateTimeOffset or DbType.Time;
|
||||
}
|
||||
|
||||
private static bool TryConvertToDateTime(object value, out DateTime date)
|
||||
|
|
@ -653,6 +983,9 @@ internal static class DynamicReportImageHelper
|
|||
case DateTimeOffset dateTimeOffset:
|
||||
date = dateTimeOffset.LocalDateTime;
|
||||
return true;
|
||||
case TimeSpan timeSpan:
|
||||
date = DateTime.Today.Add(timeSpan);
|
||||
return true;
|
||||
}
|
||||
|
||||
var text = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,14 @@ internal static class DynamicReportSqlBuilder
|
|||
}
|
||||
|
||||
var sourceAlias = QuoteSqlIdentifier(SourceAlias);
|
||||
return $"SELECT {sourceAlias}.*, {string.Join(", ", lookupSelectParts)} FROM ({NormalizeSql(selectQuery)}) AS {sourceAlias}";
|
||||
var normalizedSql = NormalizeSql(selectQuery);
|
||||
var orderBy = ExtractOrderBy(normalizedSql);
|
||||
if (!string.IsNullOrWhiteSpace(orderBy))
|
||||
{
|
||||
normalizedSql = OrderByRegex.Replace(normalizedSql, string.Empty).Trim();
|
||||
}
|
||||
|
||||
return $"SELECT {sourceAlias}.*, {string.Join(", ", lookupSelectParts)} FROM ({normalizedSql}) AS {sourceAlias}{orderBy}";
|
||||
}
|
||||
|
||||
private static string CreateLookupSelectPart(
|
||||
|
|
@ -139,6 +146,12 @@ internal static class DynamicReportSqlBuilder
|
|||
return sql.Trim().TrimEnd(';');
|
||||
}
|
||||
|
||||
private static string ExtractOrderBy(string sql)
|
||||
{
|
||||
var match = OrderByRegex.Match(sql);
|
||||
return match.Success ? " " + match.Value.Trim() : string.Empty;
|
||||
}
|
||||
|
||||
private static string QuoteSqlIdentifier(string identifier)
|
||||
{
|
||||
return $"\"{identifier.Replace("\"", "\"\"")}\"";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using Sozsoft.Platform.Enums;
|
|||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
using Volo.Abp.Users;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -73,6 +74,7 @@ public class DynamicTreeReport : XtraReport
|
|||
var defaultValueHelper = scope.ServiceProvider.GetRequiredService<DefaultValueHelper>();
|
||||
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
|
||||
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
|
||||
if (!await authManager.CanAccess(listFormCode))
|
||||
{
|
||||
|
|
@ -153,13 +155,18 @@ public class DynamicTreeReport : XtraReport
|
|||
dataSourceType);
|
||||
|
||||
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
||||
var criteriaText = DynamicReportImageHelper.BuildReportCriteriaText(
|
||||
queryParams.Filter,
|
||||
queryParams.Sort,
|
||||
fields,
|
||||
localizer);
|
||||
var sourceRows = await ReadRowsAsync(dynamicDataRepository, selectQuery, connectionString);
|
||||
var orderedRows = OrderTreeRows(sourceRows, treeOptions).ToList();
|
||||
reportColumns = ApplyImageCounts(reportColumns, orderedRows, imageBaseUrl);
|
||||
|
||||
ConfigurePage(reportColumns);
|
||||
ConfigureDataSource(orderedRows, reportColumns);
|
||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl);
|
||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText);
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
|
|
@ -408,13 +415,15 @@ public class DynamicTreeReport : XtraReport
|
|||
ListForm listForm,
|
||||
IReadOnlyList<ReportColumn> fields,
|
||||
IStringLocalizer<PlatformResource> localizer,
|
||||
string imageBaseUrl)
|
||||
string imageBaseUrl,
|
||||
string currentUserName,
|
||||
string criteriaText)
|
||||
{
|
||||
Bands.Clear();
|
||||
|
||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = string.IsNullOrWhiteSpace(criteriaText) ? 32 : 50 };
|
||||
var pageHeader = new PageHeaderBand { HeightF = 28 };
|
||||
var detailHeight = fields.Any(x => x.IsImage)
|
||||
? DynamicReportImageHelper.GetGridImageHeight()
|
||||
|
|
@ -434,20 +443,16 @@ public class DynamicTreeReport : XtraReport
|
|||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
|
||||
DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth);
|
||||
|
||||
bottomMargin.Controls.Add(new XRPageInfo
|
||||
{
|
||||
PageInfo = PageInfo.DateTime,
|
||||
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 7.5F),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);
|
||||
DynamicReportImageHelper.AddCurrentUserNameToFooter(bottomMargin.Controls, currentUserName, pageWidth);
|
||||
|
||||
bottomMargin.Controls.Add(new XRPageInfo
|
||||
{
|
||||
TextFormatString = "Sayfa {0} / {1}",
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 8, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
TextAlignment = TextAlignment.MiddleRight
|
||||
});
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
var keyValue = pair.Split('=', 2);
|
||||
if (keyValue.Length == 2 && !string.IsNullOrWhiteSpace(keyValue[0]))
|
||||
{
|
||||
parameters[keyValue[0]] = keyValue[1];
|
||||
parameters[DecodeQueryComponent(keyValue[0])] = DecodeQueryComponent(keyValue[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -375,6 +375,11 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
return decoded;
|
||||
}
|
||||
|
||||
private static string DecodeQueryComponent(string value)
|
||||
{
|
||||
return DecodeRepeatedly(value ?? string.Empty);
|
||||
}
|
||||
|
||||
public override Dictionary<string, string> GetUrls()
|
||||
{
|
||||
// Returns a dictionary that contains the report names (URLs) and display names.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { ROUTES_ENUM } from '@/routes/route.constant'
|
|||
import { GanttRef } from 'devextreme-react/cjs/gantt'
|
||||
import { useStoreState } from '@/store'
|
||||
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||
import { createReportQueryString } from '../report/reportRouteParams'
|
||||
|
||||
export interface ISelectBoxData {
|
||||
value?: string
|
||||
|
|
@ -208,7 +209,7 @@ const createDynamicListReportUrl = (
|
|||
searchParams.set('sort', sort)
|
||||
}
|
||||
|
||||
const queryString = searchParams.toString()
|
||||
const queryString = createReportQueryString(searchParams)
|
||||
return queryString ? `${reportUrl}?${queryString}` : reportUrl
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,30 +58,6 @@ const loadViewerCss = (isDarkMode: boolean) => {
|
|||
if (themeLink.href !== themeHref) {
|
||||
themeLink.href = themeHref
|
||||
}
|
||||
|
||||
if (!document.getElementById(VIEWER_DARK_OVERRIDES_ID)) {
|
||||
const style = document.createElement('style')
|
||||
style.id = VIEWER_DARK_OVERRIDES_ID
|
||||
style.textContent = `
|
||||
.report-viewer-shell {
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
.report-viewer-shell-dark {
|
||||
background: #111827;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.report-viewer-shell-dark .dxrd-preview,
|
||||
.report-viewer-shell-dark .dxrd-preview-wrapper,
|
||||
.report-viewer-shell-dark .dxrd-toolbar-wrapper,
|
||||
.report-viewer-shell-dark .dxrd-right-panel,
|
||||
.report-viewer-shell-dark .dxrd-search-wrapper {
|
||||
background-color: #111827;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
||||
const DevexpressReportDesigner: React.FC = () => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ZoomAutoBy } from 'devexpress-reporting/viewer/constants'
|
|||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { MODE_DARK } from '@/constants/theme.constant'
|
||||
import { useStoreState } from '@/store'
|
||||
import { createReportUrl } from './reportRouteParams'
|
||||
import { createReportQueryString, createReportUrl } from './reportRouteParams'
|
||||
|
||||
const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme'
|
||||
const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides'
|
||||
|
|
@ -60,30 +60,6 @@ const loadViewerCss = (isDarkMode: boolean) => {
|
|||
if (themeLink.href !== themeHref) {
|
||||
themeLink.href = themeHref
|
||||
}
|
||||
|
||||
if (!document.getElementById(VIEWER_DARK_OVERRIDES_ID)) {
|
||||
const style = document.createElement('style')
|
||||
style.id = VIEWER_DARK_OVERRIDES_ID
|
||||
style.textContent = `
|
||||
.report-viewer-shell {
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
.report-viewer-shell-dark {
|
||||
background: #111827;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.report-viewer-shell-dark .dxrd-preview,
|
||||
.report-viewer-shell-dark .dxrd-preview-wrapper,
|
||||
.report-viewer-shell-dark .dxrd-toolbar-wrapper,
|
||||
.report-viewer-shell-dark .dxrd-right-panel,
|
||||
.report-viewer-shell-dark .dxrd-search-wrapper {
|
||||
background-color: #111827;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
||||
const DevexpressReportViewer: React.FC = () => {
|
||||
|
|
@ -177,7 +153,7 @@ const DevexpressReportViewer: React.FC = () => {
|
|||
searchParams.set('cultureName', reportCultureName)
|
||||
}
|
||||
|
||||
const search = searchParams.toString()
|
||||
const search = createReportQueryString(searchParams)
|
||||
return createReportUrl({ report, id, listFormCode }, search ? `?${search}` : '')
|
||||
}, [report, id, listFormCode, location.search, reportCultureName])
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ export const normalizeReportRouteParams = ({
|
|||
}
|
||||
}
|
||||
|
||||
export const createReportQueryString = (searchParams: URLSearchParams) =>
|
||||
Array.from(searchParams.entries())
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join('&')
|
||||
|
||||
export const createReportUrl = (
|
||||
params: ReportRouteParams,
|
||||
search: string,
|
||||
|
|
@ -47,6 +52,6 @@ export const createReportUrl = (
|
|||
if (listFormCode) pathSegments.push(listFormCode)
|
||||
|
||||
const path = pathSegments.map((segment) => encodeURIComponent(segment)).join('/')
|
||||
const queryString = searchParams.toString()
|
||||
const queryString = createReportQueryString(searchParams)
|
||||
return queryString ? `${path}?${queryString}` : path
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue