Browse Source

Merge 2b2a8a9d05 into d2a0076535

pull/25469/merge
Ye Wen 9 hours ago
committed by GitHub
parent
commit
5d5a511481
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 24
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicTextTagHelper.cs
  2. 206
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicTextTagHelperService.cs
  3. 18
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpText.cs
  4. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpTextTagHelper.cs
  5. 302
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpTextTagHelperService.cs

24
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicTextTagHelper.cs

@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
[HtmlTargetElement("abp-dynamic-text", TagStructure = TagStructure.NormalOrSelfClosing)]
public class AbpDynamicTextTagHelper : AbpTagHelper<AbpDynamicTextTagHelper, AbpDynamicTextTagHelperService>
{
[HtmlAttributeName("abp-model")]
public ModelExpression Model { get; set; } = default!;
[HtmlAttributeName("column-size")]
public ColumnSize ColumnSize { get; set; }
[HtmlAttributeName("label-width")]
public ColumnSize LabelWidth { get; set; } = ColumnSize._4;
public AbpDynamicTextTagHelper(AbpDynamicTextTagHelperService tagHelperService)
: base(tagHelperService)
{
}
}

206
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicTextTagHelperService.cs

@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
public class AbpDynamicTextTagHelperService : AbpTagHelperService<AbpDynamicTextTagHelper>
{
protected HtmlEncoder HtmlEncoder { get; }
protected IServiceProvider ServiceProvider { get; }
protected List<ModelExpression> Models = new();
public AbpDynamicTextTagHelperService(
HtmlEncoder htmlEncoder,
IServiceProvider serviceProvider
)
{
HtmlEncoder = htmlEncoder;
ServiceProvider = serviceProvider;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
Models = GetModels(context, output);
NormalizeTagMode(context, output);
var childContent = await output.GetChildContentAsync();
var html = await GetHtmlAsync(context, output);
SetContent(context, output, html, childContent);
SetAttributes(context, output);
}
protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output)
{
output.TagMode = TagMode.StartTagAndEndTag;
output.TagName = "div";
}
protected virtual void SetAttributes(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.AddIfNotContains("class", "abp-dynamic-text");
}
protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, string html, TagHelperContent childContent)
{
var content = childContent.GetContent();
if (content.Contains(AbpFormContentPlaceHolder))
{
content = content.Replace(AbpFormContentPlaceHolder, html);
}
else
{
content = html + content;
}
output.Content.SetHtmlContent(content);
}
protected virtual async Task<string> GetHtmlAsync(TagHelperContext context, TagHelperOutput output)
{
var contentBuilder = new StringBuilder();
foreach (var model in Models)
{
var textTagHelper = GetTextTagHelper(model);
var textHtml = await textTagHelper.RenderAsync(new TagHelperAttributeList(), context, HtmlEncoder, "div", TagMode.StartTagAndEndTag);
if (TagHelper.ColumnSize > ColumnSize.Undefined)
{
var columnSize = $"col-12 col-sm-{(int)TagHelper.ColumnSize}";
var hidden = IsHidden(model.ModelExplorer) ? " d-none" : "";
contentBuilder.AppendLine($"<div class=\"{columnSize}{hidden}\">{textHtml}</div>");
}
else
{
contentBuilder.AppendLine(textHtml);
}
}
if (TagHelper.ColumnSize > ColumnSize.Undefined)
{
contentBuilder.Insert(0, "<div class=\"row\">");
contentBuilder.AppendLine("</div>");
}
return contentBuilder.ToString();
}
protected virtual AbpTextTagHelper GetTextTagHelper(ModelExpression model)
{
var textTagHelper = ServiceProvider.GetRequiredService<AbpTextTagHelper>();
textTagHelper.AspFor = model;
textTagHelper.ViewContext = TagHelper.ViewContext;
var textAttribute = model.ModelExplorer.GetAttribute<AbpText>();
if (textAttribute == null)
{
textTagHelper.LabelWidth = TagHelper.LabelWidth;
return textTagHelper;
}
textTagHelper.Format = textAttribute.Format;
textTagHelper.LabelWidth = textAttribute.LabelWidth ?? TagHelper.LabelWidth;
textTagHelper.SuppressLabel = textAttribute.SuppressLabel;
return textTagHelper;
}
protected bool IsHidden(ModelExplorer model)
{
return model.GetAttribute<HiddenInputAttribute>() != null;
}
protected virtual List<ModelExpression> GetModels(TagHelperContext context, TagHelperOutput output)
{
return TagHelper.Model.ModelExplorer.Properties.Aggregate(new List<ModelExpression>(), ExploreModelsRecursively);
}
protected virtual List<ModelExpression> ExploreModelsRecursively(List<ModelExpression> list, ModelExplorer model)
{
if (model.GetAttribute<DynamicFormIgnore>() != null)
{
return list;
}
if (IsCsharpClassOrPrimitive(model.ModelType) || IsListOfCsharpClassOrPrimitive(model.ModelType))
{
list.Add(ModelExplorerToModelExpressionConverter(model));
return list;
}
if (IsListOfSelectItem(model.ModelType) || IsFile(model.ModelType))
{
list.Add(ModelExplorerToModelExpressionConverter(model));
return list;
}
return model.Properties.Aggregate(list, ExploreModelsRecursively);
}
protected virtual ModelExpression ModelExplorerToModelExpressionConverter(ModelExplorer explorer)
{
var temp = explorer;
var propertyName = explorer.Metadata.PropertyName;
while (temp?.Container?.Metadata?.PropertyName != null)
{
temp = temp.Container;
propertyName = temp.Metadata.PropertyName + "." + propertyName;
}
return new ModelExpression(propertyName ?? string.Empty, explorer);
}
protected virtual bool IsListOfCsharpClassOrPrimitive(Type type)
{
var genericType = type.GenericTypeArguments.FirstOrDefault();
if (genericType == null || !IsCsharpClassOrPrimitive(genericType))
{
return false;
}
return type.ToString().StartsWith("System.Collections.Generic.IEnumerable`") ||
type.ToString().StartsWith("System.Collections.Generic.List`");
}
protected virtual bool IsCsharpClassOrPrimitive(Type? type)
{
if (type == null) return false;
return type.IsPrimitive ||
type.IsValueType ||
type == typeof(string) ||
type == typeof(Guid) ||
type == typeof(DateTime) ||
type == typeof(ValueType) ||
type == typeof(TimeSpan) ||
type == typeof(DateTimeOffset) ||
type.IsEnum;
}
protected virtual bool IsListOfSelectItem(Type type)
{
return type == typeof(List<SelectListItem>) ||
type == typeof(IEnumerable<SelectListItem>);
}
protected virtual bool IsFile(Type type)
{
return typeof(IFormFile).IsAssignableFrom(type) ||
typeof(IEnumerable<IFormFile>).IsAssignableFrom(type);
}
}

18
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpText.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
[AttributeUsage(AttributeTargets.Property)]
public class AbpText : Attribute
{
public ColumnSize? LabelWidth { get; set; }
public bool SuppressLabel { get; set; } = false;
public string? Format { get; set; }
public AbpText()
{
}
}

30
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpTextTagHelper.cs

@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
[HtmlTargetElement("abp-text", TagStructure = TagStructure.NormalOrSelfClosing)]
public class AbpTextTagHelper : AbpTagHelper<AbpTextTagHelper, AbpTextTagHelperService>
{
[HtmlAttributeName("asp-for")]
public ModelExpression AspFor { get; set; } = default!;
[HtmlAttributeName("label")]
public string? Label { get; set; }
[HtmlAttributeName("label-width")]
public ColumnSize? LabelWidth { get; set; }
[HtmlAttributeName("suppress-label")]
public bool SuppressLabel { get; set; } = false;
[HtmlAttributeName("format")]
public string? Format { get; set; }
public AbpTextTagHelper(AbpTextTagHelperService tagHelperService)
: base(tagHelperService)
{
}
}

302
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpTextTagHelperService.cs

@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
public class AbpTextTagHelperService : AbpTagHelperService<AbpTextTagHelper>
{
protected HtmlEncoder HtmlEncoder { get; }
private readonly IAbpTagHelperLocalizer _tagHelperLocalizer;
protected readonly IAbpEnumLocalizer _abpEnumLocalizer;
protected readonly IStringLocalizerFactory _stringLocalizerFactory;
private readonly ColumnSize DefaultLabelWidth = ColumnSize._4;
public AbpTextTagHelperService(
HtmlEncoder htmlEncoder,
IAbpTagHelperLocalizer tagHelperLocalizer,
IAbpEnumLocalizer abpEnumLocalizer,
IStringLocalizerFactory stringLocalizerFactory)
{
HtmlEncoder = htmlEncoder;
_tagHelperLocalizer = tagHelperLocalizer;
_abpEnumLocalizer = abpEnumLocalizer;
_stringLocalizerFactory = stringLocalizerFactory;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var childContent = await output.GetChildContentAsync();
NormalizeTagMode(context, output);
SetAttributes(context, output);
var html = GetHtml(context, output);
SetContent(context, output, html, childContent);
}
protected virtual void NormalizeTagMode(TagHelperContext context, TagHelperOutput output)
{
output.TagMode = TagMode.StartTagAndEndTag;
output.TagName = "div";
}
protected virtual void SetAttributes(TagHelperContext context, TagHelperOutput output)
{
var cssClass = "mb-3";
output.Attributes.AddIfNotContains("class", cssClass);
}
protected virtual void SetContent(TagHelperContext context, TagHelperOutput output, string html, TagHelperContent childContent)
{
var content = childContent.GetContent();
if (!string.IsNullOrEmpty(content))
{
content = html + content;
}
else
{
content = html;
}
output.Content.SetHtmlContent(content);
}
protected virtual string GetHtml(TagHelperContext context, TagHelperOutput output)
{
var label = GetLabel();
var text = GetText();
var fieldId = GetFieldId();
var value = TagHelper.AspFor.Model;
var contentBuilder = new StringBuilder();
var hidden = IsHidden() ? " d-none" : "";
contentBuilder.AppendLine($"<div class=\"row{hidden}\">");
if (!TagHelper.SuppressLabel)
{
var width = TagHelper.LabelWidth ??
TagHelper.AspFor.ModelExplorer.GetAttribute<AbpText>()?.LabelWidth ??
DefaultLabelWidth;
var labelWidth = $"col-{(int)width}";
contentBuilder.AppendLine($"<div class=\"{labelWidth}\">");
contentBuilder.AppendLine(label);
contentBuilder.AppendLine("</div>");
}
contentBuilder.AppendLine($"<div class=\"col\" id=\"{fieldId}\" data-value=\"{HtmlEncoder.Encode(value?.ToString() ?? string.Empty)}\">");
contentBuilder.AppendLine(text);
contentBuilder.AppendLine("</div>");
contentBuilder.AppendLine("</div>");
return contentBuilder.ToString();
}
protected virtual string GetFieldId()
{
return TagHelper.AspFor.Name
.Replace(".", "_")
.Replace("[", "_")
.Replace("]", "_");
}
protected virtual string GetLabel()
{
var label = TagHelper.Label ??
TagHelper.AspFor.Metadata.DisplayName ??
TagHelper.AspFor.Metadata.PropertyName ??
TagHelper.AspFor.Name;
return $"<strong>{HtmlEncoder.Encode(label)}</strong>";
}
protected virtual string GetText()
{
if (IsEnum())
{
return EnumText();
}
if (IsDate())
{
return DateText();
}
if (IsBoolean())
{
return BooleanText();
}
if (IsFile())
{
return FileText();
}
if (IsCollection())
{
return CollectionText();
}
return DefaultText();
}
protected virtual string DefaultText()
{
var value = TagHelper.AspFor.Model;
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return "<span class=\"text-muted\">-</span>";
}
return StringFormat(HtmlEncoder.Encode(value.ToString()!));
}
protected virtual string StringFormat(string? value)
{
var format = GetFormatString();
if (format.IsNullOrEmpty() || value.IsNullOrWhiteSpace())
{
return value ?? string.Empty;
}
return string.Format(format, value);
}
private string GetFormatString()
{
return TagHelper.Format ?? TagHelper.AspFor.ModelExplorer.GetAttribute<AbpText>()?.Format ?? string.Empty;
}
protected virtual string EnumText()
{
var value = TagHelper.AspFor.Model;
if (value == null)
{
return DefaultText();
}
var modelType = value.GetType();
var enumType = modelType.IsEnum ? modelType : Nullable.GetUnderlyingType(modelType);
var containerLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(TagHelper.AspFor.ModelExplorer.Container.ModelType.Assembly);
var localizedMemberName = value == null ? "-" : _abpEnumLocalizer.GetString(enumType!, (int)value,
new[]
{
containerLocalizer,
_stringLocalizerFactory.CreateDefaultOrNull()
}!);
return StringFormat(HtmlEncoder.Encode(localizedMemberName ?? string.Empty));
}
protected virtual string DateText()
{
var value = TagHelper.AspFor.Model;
if (value == null)
{
return DefaultText();
}
var format = GetFormatString();
if (value is DateTime dateTime)
{
return dateTime.ToString(format);
}
if (value is DateTimeOffset dateTimeOffset)
{
return dateTimeOffset.ToString(format);
}
return StringFormat(value.ToString());
}
protected virtual string BooleanText()
{
var value = TagHelper.AspFor.Model;
if (value == null)
{
return DefaultText();
}
if (value is bool boolValue)
{
return boolValue
? "<span class=\"fa fa-check\"></span>"
: "<span class=\"fa fa-times\"></span>";
}
return StringFormat(value.ToString());
}
protected virtual string FileText()
{
return "<span class=\"text-info\"><i class=\"bi bi-file\"></i></span>";
}
protected virtual string CollectionText()
{
var value = TagHelper.AspFor.Model;
if (value == null)
{
return DefaultText();
}
//todo Consider supporting collection render.
if (value is System.Collections.IEnumerable enumerable)
{
var count = 0;
foreach (var item in enumerable)
{
count++;
}
return count.ToString();
}
return StringFormat(value.ToString());
}
protected virtual bool IsHidden()
{
return TagHelper.AspFor.ModelExplorer.GetAttribute<HiddenInputAttribute>() != null;
}
protected virtual bool IsEnum()
{
return TagHelper.AspFor.ModelExplorer.Metadata.IsEnum;
}
protected virtual bool IsDate()
{
var modelType = TagHelper.AspFor.Metadata.ModelType;
return modelType == typeof(DateTime) ||
modelType == typeof(DateTime?) ||
modelType == typeof(DateTimeOffset) ||
modelType == typeof(DateTimeOffset?);
}
protected virtual bool IsBoolean()
{
var modelType = TagHelper.AspFor.Metadata.ModelType;
return modelType == typeof(bool) || modelType == typeof(bool?);
}
protected virtual bool IsFile()
{
var modelType = TagHelper.AspFor.Metadata.ModelType;
return typeof(IFormFile).IsAssignableFrom(modelType) ||
typeof(IEnumerable<IFormFile>).IsAssignableFrom(modelType);
}
protected virtual bool IsCollection()
{
var modelType = TagHelper.AspFor.Metadata.ModelType;
return typeof(System.Collections.IEnumerable).IsAssignableFrom(modelType) &&
modelType != typeof(string);
}
}
Loading…
Cancel
Save