diff --git a/Util.sln b/Util.sln index 1df835ea9..9475258d6 100644 --- a/Util.sln +++ b/Util.sln @@ -235,7 +235,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Security", "src\Uti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Security.Tests", "test\Util.Security.Tests\Util.Security.Tests.csproj", "{B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Images", "07-Images", "{1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Images.ImageSharp", "src\Util.Images.ImageSharp\01-Util.Images.ImageSharp.csproj", "{22305349-51C7-4D80-A538-C7C0F4495895}" EndProject @@ -247,7 +247,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-EntityFrameworkCore", "0 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{2864196E-3044-4FEC-B92A-D1D3C521C567}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Images", "07-Images", "{96DFADC5-FAE8-4860-A57A-93F0534B1B7A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{96DFADC5-FAE8-4860-A57A-93F0534B1B7A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Images.ImageSharp.Tests.Integration", "test\Util.Images.ImageSharp.Tests.Integration\Util.Images.ImageSharp.Tests.Integration.csproj", "{5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}" EndProject @@ -345,6 +345,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.FileStorage.Aliyun.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.FileStorage.Abstractions", "src\Util.FileStorage.Abstractions\01-Util.FileStorage.Abstractions.csproj", "{814E42E9-508E-487F-BCD3-0F07AE7D1492}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.QrCode.ZXing", "src\Util.QrCode.ZXing\03-Util.QrCode.ZXing.csproj", "{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.QrCode.ZXing.Tests.Integration", "test\Util.QrCode.ZXing.Tests.Integration\Util.QrCode.ZXing.Tests.Integration.csproj", "{79A40AC0-A182-49D0-AED9-DF8B6835432A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -863,6 +867,14 @@ Global {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Debug|Any CPU.Build.0 = Debug|Any CPU {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.ActiveCfg = Release|Any CPU {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.Build.0 = Release|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.Build.0 = Release|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1034,6 +1046,8 @@ Global {4938D59D-CF50-4090-B936-36DB29EBE344} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} {58C01936-5D8F-4077-8663-EE0E35776D65} = {16175228-03CA-414A-8786-E5BA844110C4} {814E42E9-508E-487F-BCD3-0F07AE7D1492} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6} = {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E} + {79A40AC0-A182-49D0-AED9-DF8B6835432A} = {96DFADC5-FAE8-4860-A57A-93F0534B1B7A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94347832-A36D-4C42-9C4D-B848BD4F5DA9} diff --git a/build/version.props b/build/version.props index e3c985d5b..fab4cbb6d 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 8 0 - 11 + 12 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/src/Util.Core/Helpers/File.cs b/src/Util.Core/Helpers/File.cs index db9f3f332..704501804 100644 --- a/src/Util.Core/Helpers/File.cs +++ b/src/Util.Core/Helpers/File.cs @@ -1,4 +1,4 @@ -namespace Util.Helpers; +namespace Util.Helpers; /// /// 文件流操作 @@ -33,7 +33,7 @@ public static byte[] ToBytes( string data ) { /// 字符编码 public static byte[] ToBytes( string data, Encoding encoding ) { if ( string.IsNullOrWhiteSpace( data ) ) - return new byte[] { }; + return Array.Empty(); return encoding.GetBytes( data ); } @@ -136,7 +136,7 @@ public static string ReadToString( string filePath ) { /// 文件绝对路径 /// 字符编码 public static string ReadToString( string filePath, Encoding encoding ) { - if( System.IO.File.Exists( filePath ) == false ) + if ( System.IO.File.Exists( filePath ) == false ) return string.Empty; using var reader = new StreamReader( filePath, encoding ); return reader.ReadToEnd(); @@ -182,7 +182,7 @@ public static async Task ReadToStringAsync( string filePath ) { /// 文件绝对路径 /// 字符编码 public static async Task ReadToStringAsync( string filePath, Encoding encoding ) { - if( System.IO.File.Exists( filePath ) == false ) + if ( System.IO.File.Exists( filePath ) == false ) return string.Empty; using var reader = new StreamReader( filePath, encoding ); return await reader.ReadToEndAsync(); @@ -295,18 +295,34 @@ public static byte[] ReadToBytes( Stream stream ) { /// 文件绝对路径 /// 内容 public static void Write( string filePath, string content ) { + if ( content.IsEmpty() ) + return; Write( filePath, Convert.ToBytes( content ) ); } + /// + /// 将流写入文件 + /// + /// 文件绝对路径 + /// 内容 + public static void Write( string filePath, Stream content ) { + if ( content == null ) + return; + using ( content ) { + var bytes = ToBytes( content ); + Write( filePath, bytes ); + } + } + /// /// 将字节流写入文件 /// /// 文件绝对路径 /// 内容 public static void Write( string filePath, byte[] content ) { - if( string.IsNullOrWhiteSpace( filePath ) ) + if ( string.IsNullOrWhiteSpace( filePath ) ) return; - if( content == null ) + if ( content == null ) return; CreateDirectory( filePath ); System.IO.File.WriteAllBytes( filePath, content ); @@ -323,6 +339,8 @@ public static void Write( string filePath, byte[] content ) { /// 内容 /// 取消令牌 public static async Task WriteAsync( string filePath, string content, CancellationToken cancellationToken = default ) { + if ( content.IsEmpty() ) + return; await WriteAsync( filePath, Convert.ToBytes( content ), cancellationToken ); } @@ -333,8 +351,12 @@ public static void Write( string filePath, byte[] content ) { /// 内容 /// 取消令牌 public static async Task WriteAsync( string filePath, Stream content, CancellationToken cancellationToken = default ) { - var bytes = await ToBytesAsync( content, cancellationToken ); - await WriteAsync( filePath, bytes, cancellationToken ); + if ( content == null ) + return; + await using ( content ) { + var bytes = await ToBytesAsync( content, cancellationToken ); + await WriteAsync( filePath, bytes, cancellationToken ); + } } /// @@ -344,9 +366,9 @@ public static void Write( string filePath, byte[] content ) { /// 内容 /// 取消令牌 public static async Task WriteAsync( string filePath, byte[] content, CancellationToken cancellationToken = default ) { - if( string.IsNullOrWhiteSpace( filePath ) ) + if ( string.IsNullOrWhiteSpace( filePath ) ) return; - if( content == null ) + if ( content == null ) return; CreateDirectory( filePath ); await System.IO.File.WriteAllBytesAsync( filePath, content, cancellationToken ); @@ -361,7 +383,7 @@ public static void Write( string filePath, byte[] content ) { /// /// 文件绝对路径集合 public static void Delete( IEnumerable filePaths ) { - foreach( var filePath in filePaths ) + foreach ( var filePath in filePaths ) Delete( filePath ); } @@ -370,9 +392,9 @@ public static void Delete( IEnumerable filePaths ) { /// /// 文件绝对路径 public static void Delete( string filePath ) { - if( string.IsNullOrWhiteSpace( filePath ) ) + if ( string.IsNullOrWhiteSpace( filePath ) ) return; - if( System.IO.File.Exists( filePath ) ) + if ( System.IO.File.Exists( filePath ) ) System.IO.File.Delete( filePath ); } @@ -385,7 +407,7 @@ public static void Delete( string filePath ) { /// /// 目录路径 /// 搜索模式 - public static List GetAllFiles( string path,string searchPattern ) { + public static List GetAllFiles( string path, string searchPattern ) { return Directory.GetFiles( path, searchPattern, SearchOption.AllDirectories ) .Select( filePath => new FileInfo( filePath ) ).ToList(); } @@ -400,10 +422,10 @@ public static List GetAllFiles( string path,string searchPattern ) { /// 源文件绝对路径 /// 目标文件绝对路径 /// 目标文件存在时是否覆盖,默认值: false - public static void Copy( string sourceFilePath,string destinationFilePath, bool overwrite = false ) { + public static void Copy( string sourceFilePath, string destinationFilePath, bool overwrite = false ) { if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() ) return; - if( FileExists( sourceFilePath ) == false ) + if ( FileExists( sourceFilePath ) == false ) return; CreateDirectory( destinationFilePath ); System.IO.File.Copy( sourceFilePath, destinationFilePath, overwrite ); @@ -420,9 +442,9 @@ public static void Copy( string sourceFilePath,string destinationFilePath, bool /// 目标文件绝对路径 /// 目标文件存在时是否覆盖,默认值: false public static void Move( string sourceFilePath, string destinationFilePath, bool overwrite = false ) { - if( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() ) + if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() ) return; - if( FileExists( sourceFilePath ) == false ) + if ( FileExists( sourceFilePath ) == false ) return; CreateDirectory( destinationFilePath ); System.IO.File.Move( sourceFilePath, destinationFilePath, overwrite ); diff --git a/src/Util.Core/Helpers/Json.cs b/src/Util.Core/Helpers/Json.cs index 7b41f9e7b..645764ab8 100644 --- a/src/Util.Core/Helpers/Json.cs +++ b/src/Util.Core/Helpers/Json.cs @@ -1,6 +1,6 @@ using Util.SystemTextJson; -namespace Util.Helpers; +namespace Util.Helpers; /// /// Json操作 @@ -23,6 +23,11 @@ public static string ToJson( T value, JsonOptions options ) { /// private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions options ) { var jsonSerializerOptions = new JsonSerializerOptions(); + if ( options.IgnoreEmptyString ) { + jsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver { + Modifiers = { IgnoreEmptyString } + }; + } if ( options.IgnoreNullValues ) jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; if ( options.IgnoreCase ) @@ -37,6 +42,19 @@ private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions option return jsonSerializerOptions; } + /// + /// 忽略空字符串 + /// + private static void IgnoreEmptyString( JsonTypeInfo jsonTypeInfo ) { + if ( jsonTypeInfo.Kind != JsonTypeInfoKind.Object ) + return; + foreach ( JsonPropertyInfo jsonPropertyInfo in jsonTypeInfo.Properties ) { + if ( jsonPropertyInfo.PropertyType == typeof( string ) ) { + jsonPropertyInfo.ShouldSerialize = static ( _, value ) => value.SafeString().IsEmpty() == false; + } + } + } + /// /// 将对象转换为Json字符串 /// @@ -44,14 +62,14 @@ private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions option /// 序列化配置 /// 是否移除双引号 /// 是否将双引号转成单引号 - public static string ToJson( T value, JsonSerializerOptions options = null,bool removeQuotationMarks = false, bool toSingleQuotes = false ) { - return ToJson( value, options, removeQuotationMarks, toSingleQuotes,true ); + public static string ToJson( T value, JsonSerializerOptions options = null, bool removeQuotationMarks = false, bool toSingleQuotes = false ) { + return ToJson( value, options, removeQuotationMarks, toSingleQuotes, true ); } /// /// 将对象转换为Json字符串 /// - private static string ToJson( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes,bool ignoreInterface ) { + private static string ToJson( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes, bool ignoreInterface ) { if ( value == null ) return string.Empty; options = GetToJsonOptions( options ); @@ -87,7 +105,7 @@ private static JsonSerializerOptions GetToJsonOptions( JsonSerializerOptions opt private static string Serialize( T value, JsonSerializerOptions options, bool ignoreInterface ) { if ( ignoreInterface ) { object instance = value; - if( instance != null ) + if ( instance != null ) return JsonSerializer.Serialize( instance, options ); } return JsonSerializer.Serialize( value, options ); diff --git a/src/Util.Core/JsonOptions.cs b/src/Util.Core/JsonOptions.cs index d482fc893..df495358f 100644 --- a/src/Util.Core/JsonOptions.cs +++ b/src/Util.Core/JsonOptions.cs @@ -9,6 +9,7 @@ public class JsonOptions { /// public JsonOptions() { IgnoreNullValues = true; + IgnoreEmptyString = true; IgnoreCase = true; IgnoreInterface = true; } @@ -26,6 +27,10 @@ public JsonOptions() { /// public bool IgnoreNullValues { get; set; } /// + /// 是否忽略空字符串,默认值: true + /// + public bool IgnoreEmptyString { get; set; } + /// /// 转换为Json时,属性名是否使用驼峰形式 /// public bool ToCamelCase { get; set; } diff --git a/src/Util.Core/Usings.cs b/src/Util.Core/Usings.cs index 689e28fa2..d0e96760f 100644 --- a/src/Util.Core/Usings.cs +++ b/src/Util.Core/Usings.cs @@ -22,6 +22,7 @@ global using System.Net.Http; global using System.Xml; global using System.Xml.Linq; +global using System.Text.Json.Serialization.Metadata; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; diff --git a/src/Util.Images.ImageSharp/ImageSharpExtensions.cs b/src/Util.Images.ImageSharp/ImageSharpExtensions.cs new file mode 100644 index 000000000..281086331 --- /dev/null +++ b/src/Util.Images.ImageSharp/ImageSharpExtensions.cs @@ -0,0 +1,69 @@ +using SixLabors.ImageSharp.Drawing; + +namespace Util.Images; + +/// +/// ImageSharp扩展 +/// +public static class ImageSharpExtensions { + /// + /// 转换颜色类型 + /// + /// 颜色 + public static Color ToImageSharpColor( this System.Drawing.Color color ) { + return Color.FromRgba( color.R, color.G, color.B, color.A ); + } + + /// + /// 图片缩放 + /// + /// 图片 + /// 缩放比例 + public static Image Zoom( this Image image, double scale ) { + var width = Util.Helpers.Convert.ToInt( image.Width * scale ); + var height = Util.Helpers.Convert.ToInt( image.Height * scale ); + image.Mutate( t => { + t.Resize( new Size( width, height ) ); + } ); + return image; + } + + /// + /// 设置圆角 + /// + /// 图片 + /// 角度 + public static Image RoundCorners( this Image image, int cornerRadius ) { + image.Mutate( context => RoundCorners( context, cornerRadius ) ); + return image; + } + + /// + /// 设置圆角 + /// + /// 图片处理上下文 + /// 角度 + private static void RoundCorners( IImageProcessingContext context, int cornerRadius ) { + var size = context.GetCurrentSize(); + var corners = BuildCorners( size.Width, size.Height, cornerRadius ); + context.SetGraphicsOptions( new GraphicsOptions { AlphaCompositionMode = PixelAlphaCompositionMode.DestOut } ); + context.Fill( Color.Red, corners ); + } + + /// + /// 构建圆角 + /// + /// 图片宽度 + /// 图片高度 + /// 角度 + private static IPathCollection BuildCorners( int imageWidth, int imageHeight, float cornerRadius ) { + var rect = new RectangularPolygon( -0.5f, -0.5f, cornerRadius, cornerRadius ); + var cornerTopLeft = rect.Clip( new EllipsePolygon( cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius ) ); + var rightPos = imageWidth - cornerTopLeft.Bounds.Width + 1; + var bottomPos = imageHeight - cornerTopLeft.Bounds.Height + 1; + var cornerTopRight = cornerTopLeft.RotateDegree( 90 ).Translate( rightPos, 0 ); + var cornerBottomLeft = cornerTopLeft.RotateDegree( -90 ).Translate( 0, bottomPos ); + var cornerBottomRight = cornerTopLeft.RotateDegree( 180 ).Translate( rightPos, bottomPos ); + return new PathCollection( cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight ); + } +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/ImageWrapper.cs b/src/Util.Images.ImageSharp/ImageWrapper.cs index 666b1463d..9adccfa47 100644 --- a/src/Util.Images.ImageSharp/ImageWrapper.cs +++ b/src/Util.Images.ImageSharp/ImageWrapper.cs @@ -1,6 +1,6 @@ using Util.Images.Commands; -namespace Util.Images; +namespace Util.Images; /// /// 图片操作包装器 @@ -175,18 +175,7 @@ protected Color GetColor( string color ) { /// 获取图片编码器 /// private IImageEncoder GetImageEncoder() { - switch ( _imageType ) { - case Images.ImageType.Png: - return new PngEncoder(); - case Images.ImageType.Gif: - return new GifEncoder(); - case Images.ImageType.Bmp: - return new BmpEncoder(); - case Images.ImageType.Jpg: - return new JpegEncoder(); - default: - return new PngEncoder(); - } + return GetImageEncoder( _imageType ); } #endregion @@ -199,7 +188,7 @@ private IImageEncoder GetImageEncoder() { using var stream = new MemoryStream(); _commands.ForEach( command => command.Invoke( image ) ); await image.SaveAsync( stream, GetImageEncoder(), cancellationToken ); - return await Util.Helpers.File.ToBytesAsync( stream ) ; + return await Util.Helpers.File.ToBytesAsync( stream, cancellationToken ); } #endregion @@ -217,7 +206,40 @@ public string ToBase64() { /// 获取图片格式 /// private IImageFormat GetImageFormat() { - switch ( _imageType ) { + return GetImageFormat( _imageType ); + } + + #endregion + + #region GetImageEncoder + + /// + /// 获取图片编码器 + /// + public static IImageEncoder GetImageEncoder( ImageType type ) { + switch ( type ) { + case Images.ImageType.Png: + return new PngEncoder(); + case Images.ImageType.Gif: + return new GifEncoder(); + case Images.ImageType.Bmp: + return new BmpEncoder(); + case Images.ImageType.Jpg: + return new JpegEncoder(); + default: + return new PngEncoder(); + } + } + + #endregion + + #region GetImageFormat + + /// + /// 获取图片格式 + /// + public static IImageFormat GetImageFormat( ImageType type ) { + switch ( type ) { case Images.ImageType.Png: return PngFormat.Instance; case Images.ImageType.Gif: diff --git a/src/Util.Localization/StringLocalizer.cs b/src/Util.Localization/StringLocalizer.cs index 2e54f5fce..3626d2e44 100644 --- a/src/Util.Localization/StringLocalizer.cs +++ b/src/Util.Localization/StringLocalizer.cs @@ -14,8 +14,8 @@ internal class StringLocalizer : IStringLocalizer { /// /// 本地化资源查找器工厂 public StringLocalizer( IStringLocalizerFactory factory ) { - var assemblyName = new AssemblyName( GetType().Assembly.FullName ); - _localizer = factory.Create( null, assemblyName.FullName ); + var assemblyName = new AssemblyName( GetType().Assembly.FullName! ); + _localizer = factory.Create( string.Empty, assemblyName.FullName ); } /// diff --git a/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj b/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj new file mode 100644 index 000000000..6878773e7 --- /dev/null +++ b/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.QrCode.ZXing + Util.QrCode + Util.QrCode.ZXing是Util应用框架基于ZXing.Net的条形码操作类库,用于二维码生成 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.QrCode.ZXing.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.QrCode.ZXing.xml + + + + + True + False + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs b/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs new file mode 100644 index 000000000..16fcb3a16 --- /dev/null +++ b/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs @@ -0,0 +1,23 @@ +namespace Util.QrCode; + +/// +/// 容错级别 +/// +public enum ErrorCorrectionLevel { + /// + /// 可以纠正最大7%的错误 + /// + L, + /// + /// 可以纠正最大15%的错误 + /// + M, + /// + /// 可以纠正最大25%的错误 + /// + Q, + /// + /// 可以纠正最大30%的错误 + /// + H +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/IQrCodeService.cs b/src/Util.QrCode.ZXing/IQrCodeService.cs new file mode 100644 index 000000000..bfc278fc1 --- /dev/null +++ b/src/Util.QrCode.ZXing/IQrCodeService.cs @@ -0,0 +1,86 @@ +using Util.Images; + +namespace Util.QrCode; + +/// +/// 二维码服务 +/// +public interface IQrCodeService : ITransientDependency { + /// + /// 设置二维码内容 + /// + /// 二维码内容 + IQrCodeService Content( string content ); + /// + /// 设置二维码尺寸 + /// + /// 二维码尺寸 + IQrCodeService Size( QrSize size ); + /// + /// 设置二维码尺寸 + /// + /// 二维码尺寸 + IQrCodeService Size( int size ); + /// + /// 容错处理 + /// + /// 容错级别 + IQrCodeService Correction( ErrorCorrectionLevel level ); + /// + /// 设置图片类型 + /// + /// 图片类型 + IQrCodeService ImageType( ImageType type ); + /// + /// 设置前景色 + /// + /// 前景色 + IQrCodeService Color( Color color ); + /// + /// 设置背景色 + /// + /// 背景色 + IQrCodeService BgColor( Color color ); + /// + /// 设置边距 + /// + /// 边距,单位:像素 + IQrCodeService Margin( int margin ); + /// + /// 设置图标 + /// + /// 图标绝对路径 + IQrCodeService Icon( string path ); + /// + /// 获取二维码流 + /// + Stream ToStream(); + /// + /// 获取二维码字节流 + /// + byte[] ToBytes(); + /// + /// 获取二维码Base64字符串 + /// + string ToBase64(); + /// + /// 保存二维码图片 + /// + /// 文件绝对路径 + void Save( string path ); + /// + /// 获取二维码流 + /// + /// 取消令牌 + Task ToStreamAsync( CancellationToken cancellationToken = default ); + /// + /// 获取二维码字节流 + /// + Task ToBytesAsync( CancellationToken cancellationToken = default ); + /// + /// 保存二维码图片 + /// + /// 文件绝对路径 + /// 取消令牌 + Task SaveAsync( string path, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/QrSize.cs b/src/Util.QrCode.ZXing/QrSize.cs new file mode 100644 index 000000000..eeea34713 --- /dev/null +++ b/src/Util.QrCode.ZXing/QrSize.cs @@ -0,0 +1,19 @@ +namespace Util.QrCode; + +/// +/// 二维码尺寸 +/// +public enum QrSize { + /// + /// 小 + /// + Small = 100, + /// + /// 中 + /// + Middle = 200, + /// + /// 大 + /// + Large = 300 +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/Usings.cs b/src/Util.QrCode.ZXing/Usings.cs new file mode 100644 index 000000000..f7ef8bbe2 --- /dev/null +++ b/src/Util.QrCode.ZXing/Usings.cs @@ -0,0 +1,12 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Drawing; +global using System.IO; +global using SixLabors.ImageSharp.Formats; +global using SixLabors.ImageSharp.Processing; +global using ZXing; +global using ZXing.QrCode; +global using ZXing.ImageSharp.Rendering; +global using SixLabors.ImageSharp.PixelFormats; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs b/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs new file mode 100644 index 000000000..0641a5f46 --- /dev/null +++ b/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs @@ -0,0 +1,228 @@ +using Util.Images; +using SixLabors.ImageSharp; +using CorrectionLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel; + +namespace Util.QrCode.ZXing; + +/// +/// ZXing二维码服务 +/// +public class ZXingQrCodeService : IQrCodeService { + /// + /// 内容 + /// + private string _content; + /// + /// 二维码尺寸 + /// + private int _size; + /// + /// 容错级别 + /// + private CorrectionLevel _level; + /// + /// 图片类型 + /// + private ImageType _imageType; + /// + /// 前景色 + /// + private System.Drawing.Color _foreground; + /// + /// 背景色 + /// + private System.Drawing.Color _background; + /// + /// 边距 + /// + private int _margin; + /// + /// 图标路径 + /// + private string _iconPath; + + /// + /// 初始化二维码服务 + /// + public ZXingQrCodeService() { + _level = CorrectionLevel.L; + _imageType = Images.ImageType.Png; + _foreground = System.Drawing.Color.Black; + _background = System.Drawing.Color.White; + _margin = 0; + Size( 160 ); + } + + /// + public IQrCodeService Content( string content ) { + _content = content; + return this; + } + + /// + public IQrCodeService Size( QrSize size ) { + return Size( size.Value().SafeValue() ); + } + + /// + public IQrCodeService Size( int size ) { + _size = size; + return this; + } + + /// + public IQrCodeService Correction( ErrorCorrectionLevel level ) { + switch ( level ) { + case ErrorCorrectionLevel.L: + _level = CorrectionLevel.L; + break; + case ErrorCorrectionLevel.M: + _level = CorrectionLevel.M; + break; + case ErrorCorrectionLevel.Q: + _level = CorrectionLevel.Q; + break; + case ErrorCorrectionLevel.H: + _level = CorrectionLevel.H; + break; + } + return this; + } + + /// + public IQrCodeService ImageType( ImageType type ) { + _imageType = type; + return this; + } + + /// + public IQrCodeService Color( System.Drawing.Color color ) { + _foreground = color; + return this; + } + + /// + public IQrCodeService BgColor( System.Drawing.Color color ) { + _background = color; + return this; + } + + /// + public IQrCodeService Margin( int margin ) { + _margin = margin; + return this; + } + + /// + public IQrCodeService Icon( string path ) { + _iconPath = path; + return this; + } + + /// + public Stream ToStream() { + var bytes = ToBytes(); + return new MemoryStream( bytes ); + } + + /// + public byte[] ToBytes() { + using var image = GetImage(); + using var stream = new MemoryStream(); + image.Save( stream, GetImageEncoder() ); + return stream.ToArray(); + } + + /// + public string ToBase64() { + using var image = GetImage(); + return image.ToBase64String( GetImageFormat() ); + } + + /// + /// 获取图片格式 + /// + private IImageFormat GetImageFormat() { + return ImageWrapper.GetImageFormat( _imageType ); + } + + /// + public void Save( string path ) { + if ( path.IsEmpty() ) + return; + Util.Helpers.File.CreateDirectory( path ); + using var image = GetImage(); + image.Save( path, GetImageEncoder() ); + } + + /// + public async Task ToStreamAsync( CancellationToken cancellationToken = default ) { + var bytes = await ToBytesAsync( cancellationToken ); + return new MemoryStream( bytes ); + } + + /// + public async Task ToBytesAsync( CancellationToken cancellationToken = default ) { + using var image = GetImage(); + using var stream = new MemoryStream(); + await image.SaveAsync( stream, GetImageEncoder(), cancellationToken ); + return stream.ToArray(); + } + + /// + /// 获取二维码图片 + /// + private Image GetImage() { + if ( _content.IsEmpty() ) + throw new ArgumentException( "必须设置内容" ); + var writer = new BarcodeWriter> { + Format = BarcodeFormat.QR_CODE, + Options = new QrCodeEncodingOptions { + CharacterSet = "UTF-8", + ErrorCorrection = _level, + Margin = _margin, + Width = _size, + Height = _size + }, + Renderer = new ImageSharpRenderer { + Foreground = _foreground.ToImageSharpColor(), + Background = _background.ToImageSharpColor() + } + }; + var result = writer.Write( _content ); + return _iconPath.IsEmpty() ? result : MergeImage( result, Image.Load( _iconPath ) ); + } + + /// + /// 合并图片 + /// + private Image MergeImage( Image image, Image icon ) { + var margin = 10 - _margin; + if ( margin <= 0 ) + margin = 5; + var width = ( image.Width * margin - 46 * margin ) * 1.0f / 46; + icon.Zoom( width / icon.Width ); + icon.RoundCorners( 7 ); + image.RoundCorners( 7 ); + image.Mutate( x => { + x.DrawImage( icon, new SixLabors.ImageSharp.Point( ( image.Width - icon.Width ) / 2, ( image.Height - icon.Height ) / 2 ), 1 ); + } ); + return image; + } + + /// + /// 获取图片编码器 + /// + private IImageEncoder GetImageEncoder() { + return ImageWrapper.GetImageEncoder( _imageType ); + } + + /// + public async Task SaveAsync( string path, CancellationToken cancellationToken = default ) { + if ( path.IsEmpty() ) + return; + Util.Helpers.File.CreateDirectory( path ); + using var image = GetImage(); + await image.SaveAsync( path, GetImageEncoder(), cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Security/05-Util.Security.csproj b/src/Util.Security/05-Util.Security.csproj index c8ac19085..ba3d83f4e 100644 --- a/src/Util.Security/05-Util.Security.csproj +++ b/src/Util.Security/05-Util.Security.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj b/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj index 6aa5a6503..dd501c6bf 100644 --- a/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj +++ b/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/Util.Ui.Angular/Configs/AngularConst.cs b/src/Util.Ui.Angular/Configs/AngularConst.cs index 289e6352c..64e3a112b 100644 --- a/src/Util.Ui.Angular/Configs/AngularConst.cs +++ b/src/Util.Ui.Angular/Configs/AngularConst.cs @@ -45,14 +45,46 @@ public static class AngularConst { /// public const string BindLink = "bind-link"; /// + /// 可见高度 + /// + public const string BindVisibilityHeight = "bind-visibility-height"; + /// + /// 持续时间 + /// + public const string BindDuration = "bind-duration"; + /// + /// Logo + /// + public const string BindLogo = "bind-logo"; + /// + /// 等级 + /// + public const string BindLevel = "bind-level"; + /// + /// 图片 + /// + public const string BindImage = "bind-image"; + /// + /// 填充 + /// + public const string BindPadding = "bind-padding"; + /// /// Html /// public const string BindHtml = "bind-html"; /// + /// 背景色 + /// + public const string BindBgColor = "bind-bg-color"; + /// /// 访问控制 /// public const string BindAcl = "bind-acl"; /// + /// 标签文本 + /// + public const string BindLabelText = "bind-label-text"; + /// /// 触发器 /// public const string BindTrigger = "bind-trigger"; @@ -461,6 +493,10 @@ public static class AngularConst { /// public const string BindIcon = "bind-icon"; /// + /// 图标尺寸 + /// + public const string BindIconSize = "bind-icon-size"; + /// /// href /// public const string BindHref = "bind-href"; diff --git a/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs b/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs index 0de2b6fe2..b3ec27c78 100644 --- a/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs +++ b/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs @@ -133,7 +133,7 @@ private void ConfigConfig() { /// public override void Config() { base.ConfigBase( _config ); - NgModel().FormControl().SpaceItem().OnModelChange() + NgModel().FormControl().OnModelChange() .Required().RequiredMessage() .TableEdit().ValidationExtend() .Name().Branding().PasteDataImages() diff --git a/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs b/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs index fdb669059..00d0c3700 100644 --- a/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using Util.Ui.Angular.TagHelpers; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Affixes.Renders; using Util.Ui.Renders; diff --git a/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs b/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs index 575c21971..fd9372c05 100644 --- a/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs @@ -1,6 +1,5 @@ using Util.Ui.Angular.Builders; using Util.Ui.Angular.Configs; -using Util.Ui.Configs; namespace Util.Ui.NgZorro.Components.Affixes.Builders; diff --git a/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs b/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs index 78deabf9a..fff7992f2 100644 --- a/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs +++ b/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs @@ -1,5 +1,4 @@ using Util.Ui.Builders; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Affixes.Builders; using Util.Ui.Renders; diff --git a/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs b/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs new file mode 100644 index 000000000..b16c26a4e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.BackTops.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.BackTops; + +/// +/// 回到顶部,生成的标签为<nz-back-top></nz-back-top> +/// +[HtmlTargetElement( "util-back-top" )] +public class BackTopTagHelper : AngularTagHelperBase { + /// + /// [nzTemplate],自定义内容, 类型: TemplateRef<void> + /// + public string Template { get; set; } + /// + /// [nzVisibilityHeight],滚动高度达到该值才显示回到顶部按钮, 默认值: 400 + /// + public double VisibilityHeight { get; set; } + /// + /// [nzVisibilityHeight],滚动高度达到该值才显示回到顶部按钮, 默认值: 400 + /// + public string BindVisibilityHeight { get; set; } + /// + /// [nzTarget],设置监听目标元素, 类型: string | Element, 默认值: window + /// + public string Target { get; set; } + /// + /// [nzDuration],回到顶部所需时间,单位: 毫秒, 默认值: 450 + /// + public double Duration { get; set; } + /// + /// [nzDuration],回到顶部所需时间,单位: 毫秒, 默认值: 450 + /// + public string BindDuration { get; set; } + /// + /// (nzClick), 单击回到顶部按钮事件,类型: EventEmitter<boolean> + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BackTopRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs b/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs new file mode 100644 index 000000000..188c4d972 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs @@ -0,0 +1,72 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.BackTops.Builders; + +/// +/// 回到顶部标签生成器 +/// +public class BackTopBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化回到顶部标签生成器 + /// + /// 配置 + public BackTopBuilder( Config config ) : base( config, "nz-back-top" ) { + _config = config; + } + + /// + /// 配置自定义内容 + /// + public BackTopBuilder Template() { + AttributeIfNotEmpty( "[nzTemplate]", _config.GetValue( UiConst.Template ) ); + return this; + } + + /// + /// 配置可见高度 + /// + public BackTopBuilder VisibilityHeight() { + AttributeIfNotEmpty( "[nzVisibilityHeight]", _config.GetValue( UiConst.VisibilityHeight ) ); + AttributeIfNotEmpty( "[nzVisibilityHeight]", _config.GetValue( AngularConst.BindVisibilityHeight ) ); + return this; + } + + /// + /// 配置监听目标 + /// + public BackTopBuilder Target() { + AttributeIfNotEmpty( "[nzTarget]", _config.GetValue( UiConst.Target ) ); + return this; + } + + /// + /// 配置持续时间 + /// + public BackTopBuilder Duration() { + AttributeIfNotEmpty( "[nzDuration]", _config.GetValue( UiConst.Duration ) ); + AttributeIfNotEmpty( "[nzDuration]", _config.GetValue( AngularConst.BindDuration ) ); + return this; + } + + /// + /// 配置事件 + /// + public BackTopBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Template().VisibilityHeight().Target().Duration().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs b/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs new file mode 100644 index 000000000..e468e80f0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.BackTops.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.BackTops.Renders; + +/// +/// 回到顶部渲染器 +/// +public class BackTopRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化回到顶部渲染器 + /// + /// 配置 + public BackTopRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BackTopBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BackTopRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs index e2a70b3f2..cd85424cb 100644 --- a/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs @@ -81,14 +81,6 @@ public TBuilder FormControl() { return (TBuilder)this; } - /// - /// 配置间距项 - /// - public TBuilder SpaceItem() { - this.SpaceItem( _config ); - return (TBuilder)this; - } - /// /// 配置模型变更事件 /// @@ -163,7 +155,7 @@ public virtual TBuilder Pattern() { /// 配置表单属性 /// protected TBuilder ConfigForm() { - return NgModel().FormControl().SpaceItem().OnModelChange() + return NgModel().FormControl().OnModelChange() .Required().RequiredMessage() .MinLength().MinLengthMessage() .MaxLength().EmailMessage() diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs index 67578d54c..28c6a657b 100644 --- a/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs @@ -9,6 +9,10 @@ public abstract class FormControlContainerTagHelperBase : FormContainerTagHelper /// public string LabelText { get; set; } /// + /// 标签文本,自动创建nz-form-label,nz-form-control,nz-form-item容器标签,并设置nz-form-label的内容 + /// + public string BindLabelText { get; set; } + /// /// 标签宽度 /// public string LabelWidth { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs index c6eaedff8..1839e0622 100644 --- a/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs @@ -8,7 +8,7 @@ using Util.Ui.NgZorro.Extensions; using Util.Ui.Renders; -namespace Util.Ui.NgZorro.Components.Base; +namespace Util.Ui.NgZorro.Components.Base; /// /// 表单控件渲染器基类 @@ -66,7 +66,7 @@ public void WriteTo( TextWriter writer, HtmlEncoder encoder ) { var formLabel = GetFormLabel(); var formControl = GetFormControl(); formItem.AppendContent( formLabel ).AppendContent( formControl ); - formItem.WriteTo( writer,encoder ); + formItem.WriteTo( writer, encoder ); } /// @@ -79,7 +79,7 @@ protected virtual void Init() { /// 设置控件Id /// protected void SetControlId() { - if( _shareConfig.AutoLabelFor != true ) + if ( _shareConfig.AutoLabelFor != true ) return; _config.SetAttribute( AngularConst.RawId, _shareConfig.ControlId ); } @@ -103,6 +103,7 @@ protected virtual TagBuilder GetFormLabel() { var builder = new FormLabelBuilder( GetFormLabelConfig() ); builder.Config(); SetLabelText( builder ); + SetBindLabelText( builder ); return builder; } return new EmptyContainerTagBuilder(); @@ -121,6 +122,8 @@ protected Config GetFormLabelConfig() { /// 设置表单标签文本 /// protected virtual void SetLabelText( FormLabelBuilder builder ) { + if ( _shareConfig.LabelText.IsEmpty() ) + return; var options = NgZorroOptionsService.GetOptions(); if ( options.EnableI18n ) { builder.AppendContentByI18n( _shareConfig.LabelText ); @@ -129,16 +132,25 @@ protected virtual void SetLabelText( FormLabelBuilder builder ) { builder.SetContent( _shareConfig.LabelText ); } + /// + /// 设置表单标签文本 + /// + protected virtual void SetBindLabelText( FormLabelBuilder builder ) { + if ( _shareConfig.BindLabelText.IsEmpty() ) + return; + builder.SetContent( "{{" + _shareConfig.BindLabelText + "}}" ); + } + /// /// 获取表单控件 /// protected virtual TagBuilder GetFormControl() { TagBuilder builder = new EmptyContainerTagBuilder(); - if( _config.Id == _shareConfig.Id && _shareConfig.AutoCreateFormControl == true ) + if ( _config.Id == _shareConfig.Id && _shareConfig.AutoCreateFormControl == true ) builder = new FormControlBuilder( _config.CopyRemoveAttributes() ); builder.Config(); AppendControl( builder ); - if( IsAppendValidationTempalte() ) + if ( IsAppendValidationTempalte() ) AppendValidationTempalte( builder ); return builder; } diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs index e87bc483e..023fd697e 100644 --- a/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs @@ -42,4 +42,8 @@ public abstract class FormControlTagHelperBase : FormControlContainerTagHelperBa /// [name],名称 /// public string BindName { get; set; } + /// + /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 + /// + public bool SpaceItem { get; set; } } \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs index 2cca05be6..a74a549c7 100644 --- a/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Cascaders.Renders; +using Util.Ui.NgZorro.Components.Selects.Helpers; using Util.Ui.NgZorro.Enums; using Util.Ui.Renders; @@ -12,6 +12,10 @@ namespace Util.Ui.NgZorro.Components.Cascaders; /// [HtmlTargetElement( "util-cascader" )] public class CascaderTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; /// /// [nzAllowClear],是否允许清除,默认值: true /// @@ -177,10 +181,6 @@ public class CascaderTagHelper : FormControlTagHelperBase { /// public string BindValueProperty { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (nzClear),清除事件,清除值时触发 /// public string OnClear { get; set; } @@ -193,9 +193,16 @@ public class CascaderTagHelper : FormControlTagHelperBase { /// public string OnSelectionChange { get; set; } + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new SelectService( _config ); + service.Init(); + } + /// protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { - var config = new Config( context, output, content ); - return new CascaderRender( config ); + _config.Content = content; + return new CascaderRender( _config ); } } \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs b/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs index 453b027fb..217854a35 100644 --- a/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs +++ b/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs @@ -1,5 +1,4 @@ using Util.Ui.Builders; -using Util.Ui.Configs; using Util.Ui.Extensions; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Cascaders.Builders; diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs index 9721be396..ff8c11a0d 100644 --- a/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs @@ -53,10 +53,6 @@ public class CheckboxTagHelper : FormControlTagHelperBase { /// public string BindLabel { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// [nzChecked],是否选中 /// public string Checked { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs index eb5b78dcf..448f24d3d 100644 --- a/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs @@ -187,10 +187,6 @@ public class DatePickerTagHelper : FormControlTagHelperBase { /// public string BindShowNow { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (nzOnOpenChange),弹出关闭日历事件 /// public string OnOpenChange { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs index 15ab35973..3a928ff24 100644 --- a/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs @@ -177,10 +177,6 @@ public class RangePickerTagHelper : FormControlTagHelperBase { /// public string BindShowTime { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// [(beginDate)],扩展属性, 双向绑定起始日期 /// public string BeginDate { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs index 23c3f8313..52ae95f7e 100644 --- a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs @@ -1,10 +1,10 @@ using Util.Ui.Angular.Extensions; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Forms.Configs; using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Extensions; -namespace Util.Ui.NgZorro.Components.Forms.Builders; +namespace Util.Ui.NgZorro.Components.Forms.Builders; /// /// 表单项标签生成器 @@ -61,6 +61,14 @@ public override FormItemBuilder Justify() { return base.Justify(); } + /// + /// 配置间距项 + /// + public FormItemBuilder SpaceItem() { + this.SpaceItem( _shareConfig.SpaceItem ); + return this; + } + /// /// ngIf /// @@ -75,8 +83,7 @@ public FormItemBuilder AngularIf() { /// public override void Config() { base.ConfigBase( _config ); - AngularIf() - .ConfigRow().TableEdit(); + SpaceItem().AngularIf().ConfigRow().TableEdit(); } /// diff --git a/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs index 312ac72e1..acc173500 100644 --- a/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs +++ b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs @@ -29,6 +29,14 @@ public class FormItemShareConfig : FormShareConfig { /// public string LabelText { get; set; } /// + /// 标签文本 + /// + public string BindLabelText { get; set; } + /// + /// 是否间距项 + /// + public bool SpaceItem { get; set; } + /// /// 是否存在额外提示 /// public bool HasExtra { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs index 9f60057f7..cabc3e89f 100644 --- a/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs +++ b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs @@ -1,9 +1,8 @@ using Util.Ui.Angular.Configs; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Forms.Configs; using Util.Ui.NgZorro.Components.Tables.Configs; -namespace Util.Ui.NgZorro.Components.Forms.Helpers; +namespace Util.Ui.NgZorro.Components.Forms.Helpers; /// /// 表单项共享服务 @@ -72,8 +71,10 @@ public void Init() { InitFormItemShareConfig(); MapToItemShareConfig(); InitControlId(); + SetSpaceItem(); SetShowLabel(); SetLabelText(); + SetBindLabelText(); SetLabelWidth(); SetExtra(); SetSuccessTip(); @@ -139,16 +140,23 @@ private FormShareConfig GetFormShareConfig() { return _config.GetValueFromItems() ?? new FormShareConfig(); } + /// + /// 设置间距项 + /// + private void SetSpaceItem() { + _shareConfig.SpaceItem = _config.GetValue( UiConst.SpaceItem ); + } + /// /// 设置是否显示标签 /// private void SetShowLabel() { - if( _shareConfig.ShowLabel == false ) { + if ( _shareConfig.ShowLabel == false ) { _shareConfig.AutoCreateFormLabel = false; return; } var value = _config.GetValueFromAttributes( UiConst.ShowLabel ); - if( value == false ) + if ( value == false ) _shareConfig.AutoCreateFormLabel = false; } @@ -163,6 +171,17 @@ private void SetLabelText() { AutoCreateFormLabel( true ); } + /// + /// 设置表单标签文本 + /// + private void SetBindLabelText() { + var value = _config.GetValueFromAttributes( AngularConst.BindLabelText ); + if ( string.IsNullOrWhiteSpace( value ) ) + return; + _shareConfig.BindLabelText = value; + AutoCreateFormLabel( true ); + } + /// /// 设置表单标签宽度 /// @@ -229,7 +248,9 @@ private void AutoCreateFormContainer() { /// 是否自动创建表单控件容器 /// private bool IsAutoCreateFormContainer() { - if ( string.IsNullOrWhiteSpace( _shareConfig.LabelText ) == false ) + if ( _shareConfig.LabelText.IsEmpty() == false ) + return true; + if ( _shareConfig.BindLabelText.IsEmpty() == false ) return true; if ( _shareConfig.Align != null ) return true; diff --git a/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs b/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs index dfa8a24b6..5e438f908 100644 --- a/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs +++ b/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs @@ -1,5 +1,4 @@ using Util.Ui.Angular.Configs; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Forms.Configs; using Util.Ui.NgZorro.Enums; diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs index 7a9280e45..cf5de4209 100644 --- a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs @@ -4,7 +4,7 @@ using Util.Ui.NgZorro.Components.Forms.Configs; using Util.Ui.Renders; -namespace Util.Ui.NgZorro.Components.Forms.Renders; +namespace Util.Ui.NgZorro.Components.Forms.Renders; /// /// 表单项渲染器 @@ -56,7 +56,10 @@ private TagBuilder GetFormLabel() { _shareConfig.AutoCreateFormLabel = false; var builder = new FormLabelBuilder( _config ); builder.Config(); - builder.SetContent( _shareConfig.LabelText ); + if ( _shareConfig.LabelText.IsEmpty() == false ) + builder.SetContent( _shareConfig.LabelText ); + if ( _shareConfig.BindLabelText.IsEmpty() == false ) + builder.SetContent( "{{" + _shareConfig.LabelText + "}}" ); return builder; } return new EmptyContainerTagBuilder(); diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs b/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs new file mode 100644 index 000000000..9451d1dee --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs @@ -0,0 +1,84 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.HashCodes.Builders; + +/// +/// 哈希码标签生成器 +/// +public class HashCodeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化哈希码标签生成器 + /// + /// 配置 + public HashCodeBuilder( Config config ) : base( config, "nz-hash-code" ) { + _config = config; + } + + /// + /// 配置值 + /// + public HashCodeBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置标题 + /// + public HashCodeBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置标志 + /// + public HashCodeBuilder Logo() { + AttributeIfNotEmpty( "nzLogo", _config.GetValue( UiConst.Logo ) ); + AttributeIfNotEmpty( "[nzLogo]", _config.GetValue( AngularConst.BindLogo ) ); + return this; + } + + /// + /// 配置展示模式 + /// + public HashCodeBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置样式 + /// + public HashCodeBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置事件 + /// + public HashCodeBuilder Events() { + AttributeIfNotEmpty( "(nzOnCopy)", _config.GetValue( UiConst.OnCopy ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Value().Title().Logo().Mode().Type().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs b/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs new file mode 100644 index 000000000..0340e5098 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.HashCodes.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.HashCodes; + +/// +/// 哈希码,生成的标签为<nz-hash-code></nz-hash-code> +/// +[HtmlTargetElement( "util-hash-code" )] +public class HashCodeTagHelper : AngularTagHelperBase { + /// + /// nzValue,值 + /// + public string Value { get; set; } + /// + /// [nzValue],值 + /// + public string BindValue { get; set; } + /// + /// nzTitle,标题, 默认值: HashCode + /// + public string Title { get; set; } + /// + /// [nzTitle],标题, 默认值: HashCode + /// + public string BindTitle { get; set; } + /// + /// nzLogo,设置右上角的标志, 类型: TemplateRef<void> | string + /// + public string Logo { get; set; } + /// + /// [nzLogo],设置右上角的标志, 类型: TemplateRef<void> | string + /// + public string BindLogo { get; set; } + /// + /// nzMode,展示模式, 可选值: single | double | strip | rect , 默认值: double + /// + public HashCodeMode Mode { get; set; } + /// + /// [nzMode],展示模式, 可选值: single | double | strip | rect , 默认值: double + /// + public string BindMode { get; set; } + /// + /// nzType,样式, 可选值: defalut | primary , 默认值: primary + /// + public HashCodeType Type { get; set; } + /// + /// [nzType],样式, 可选值: defalut | primary , 默认值: primary + /// + public string BindType { get; set; } + /// + /// (nzOnCopy), 复制事件,类型: EventEmitter<string> + /// + public string OnCopy { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new HashCodeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs b/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs new file mode 100644 index 000000000..e25adafac --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.HashCodes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.HashCodes.Renders; + +/// +/// 哈希码渲染器 +/// +public class HashCodeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化哈希码渲染器 + /// + /// 配置 + public HashCodeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new HashCodeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new HashCodeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs index 9f933b5b1..aaacdd651 100644 --- a/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs @@ -121,10 +121,6 @@ public class InputNumberTagHelper : FormControlTagHelperBase { /// public string BindPlaceholder { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (nzFocus),获得焦点事件 /// public string OnFocus { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs index 4935cf955..6625ea63d 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs @@ -185,10 +185,6 @@ public class InputTagHelper : FormControlTagHelperBase { /// public bool AutocompleteSearchKeyword { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (input),输入事件 /// public string OnInput { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs index 13da40d41..5b2aa389f 100644 --- a/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs @@ -114,10 +114,6 @@ public class TextareaTagHelper : FormControlTagHelperBase { /// public bool AllowClear { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (input),输入事件 /// public string OnInput { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs b/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs new file mode 100644 index 000000000..eb445fcf9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs @@ -0,0 +1,130 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.QrCodes.Builders; + +/// +/// 二维码标签生成器 +/// +public class QrCodeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化二维码标签生成器 + /// + /// 配置 + public QrCodeBuilder( Config config ) : base( config, "nz-qrcode" ) { + _config = config; + } + + /// + /// 配置值 + /// + public QrCodeBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置二维码颜色 + /// + public QrCodeBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置背景色 + /// + public QrCodeBuilder BgColor() { + AttributeIfNotEmpty( "nzBgColor", _config.GetValue( UiConst.BgColor ) ); + AttributeIfNotEmpty( "[nzBgColor]", _config.GetValue( AngularConst.BindBgColor ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public QrCodeBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size ) ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置填充 + /// + public QrCodeBuilder Padding() { + AttributeIfNotEmpty( "[nzPadding]", _config.GetValue( UiConst.Padding ) ); + return this; + } + + /// + /// 配置图标地址 + /// + public QrCodeBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon ) ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置图标尺寸 + /// + public QrCodeBuilder IconSize() { + AttributeIfNotEmpty( "nzIconSize", _config.GetValue( UiConst.IconSize ) ); + AttributeIfNotEmpty( "[nzIconSize]", _config.GetValue( AngularConst.BindIconSize ) ); + return this; + } + + /// + /// 配置边框 + /// + public QrCodeBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetBoolValue( UiConst.Bordered ) ); + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( AngularConst.BindBordered ) ); + return this; + } + + /// + /// 配置状态 + /// + public QrCodeBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status ).Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置容错级别 + /// + public QrCodeBuilder Level() { + AttributeIfNotEmpty( "nzLevel", _config.GetValue( UiConst.Level ).Description() ); + AttributeIfNotEmpty( "[nzLevel]", _config.GetValue( AngularConst.BindLevel ) ); + return this; + } + + /// + /// 配置事件 + /// + public QrCodeBuilder Events() { + AttributeIfNotEmpty( "(nzRefresh)", _config.GetValue( UiConst.OnRefresh ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Value().Color().BgColor().Size().Padding().Icon().IconSize() + .Bordered().Status().Level() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs b/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs new file mode 100644 index 000000000..d770302c4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs @@ -0,0 +1,100 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.QrCodes.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.QrCodes; + +/// +/// 二维码,生成的标签为<nz-qrcode></nz-qrcode> +/// +[HtmlTargetElement( "util-qrcode" )] +public class QrCodeTagHelper : AngularTagHelperBase { + /// + /// nzValue,值 + /// + public string Value { get; set; } + /// + /// [nzValue],值 + /// + public string BindValue { get; set; } + /// + /// nzColor,二维码颜色, 默认值: #000 + /// + public string Color { get; set; } + /// + /// [nzColor],二维码颜色, 默认值: #000 + /// + public string BindColor { get; set; } + /// + /// nzBgColor,背景色, 默认值: #FFFFFF + /// + public string BgColor { get; set; } + /// + /// [nzBgColor],背景色, 默认值: #FFFFFF + /// + public string BindBgColor { get; set; } + /// + /// nzSize,尺寸, 默认值: 160 + /// + public double Size { get; set; } + /// + /// [nzSize],尺寸, 默认值: 160 + /// + public string BindSize { get; set; } + /// + /// [nzPadding], 填充, 类型: number | number[] , 默认值: 0 + /// + public string Padding { get; set; } + /// + /// nzIcon,图标地址 + /// + public string Icon { get; set; } + /// + /// [nzIcon],图标地址 + /// + public string BindIcon { get; set; } + /// + /// nzIconSize,图标尺寸, 默认值: 40 + /// + public double IconSize { get; set; } + /// + /// [nzIconSize],图标尺寸, 默认值: 40 + /// + public string BindIconSize { get; set; } + /// + /// [nzBordered],是否显示边框, 默认值: true + /// + public bool Bordered { get; set; } + /// + /// [nzBordered],是否显示边框, 默认值: true + /// + public string BindBordered { get; set; } + /// + /// nzStatus,状态, 可选值: 'active'|'expired' |'loading' , 默认值: 'active' + /// + public QrCodeStatus Status { get; set; } + /// + /// [nzStatus],状态, 可选值: 'active'|'expired' |'loading' , 默认值: 'active' + /// + public string BindStatus { get; set; } + /// + /// nzLevel,容错级别, 可选值: 'L'|'M'|'Q'|'H', 默认值: 'M' + /// + public QrCodeCorrectionLevel Level { get; set; } + /// + /// [nzLevel],容错级别, 可选值: 'L'|'M'|'Q'|'H', 默认值: 'M' + /// + public string BindLevel { get; set; } + /// + /// (nzRefresh), "点击刷新"链接单击事件,类型: EventEmitter<string> + /// + public string OnRefresh { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new QrCodeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs b/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs new file mode 100644 index 000000000..e2d0fdead --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.QrCodes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.QrCodes.Renders; + +/// +/// 二维码渲染器 +/// +public class QrCodeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化二维码渲染器 + /// + /// 配置 + public QrCodeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new QrCodeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new QrCodeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs index 0ce81862a..60da1efa3 100644 --- a/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs @@ -100,7 +100,7 @@ public RadioBuilder Events() { /// public override void Config() { base.ConfigBase( _config ); - NgModel().FormControl().SpaceItem().OnModelChange() + NgModel().FormControl().OnModelChange() .TableEdit() .Name().AutoFocus().Disabled().Value() .Label().Events(); diff --git a/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs index 0df05837c..b79aacf15 100644 --- a/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Radios.Helpers; using Util.Ui.NgZorro.Components.Radios.Renders; diff --git a/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs b/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs index ea6ac4c89..6bcf5d5c2 100644 --- a/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Radios.Helpers; using Util.Ui.NgZorro.Components.Radios.Renders; diff --git a/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs b/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs index 403959018..87e87b476 100644 --- a/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs @@ -289,10 +289,6 @@ public class SelectTagHelper : FormControlTagHelperBase { /// public string BindOptionOverflowSize { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// [nzDropdownRender],设置下拉扩展模板,类型: TemplateRef /// public string DropdownRender { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs b/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs index 54e76ab37..fa2aeb96f 100644 --- a/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Base; using Util.Ui.NgZorro.Components.Checkboxes.Helpers; using Util.Ui.NgZorro.Components.Switches.Renders; @@ -62,10 +61,6 @@ public class SwitchTagHelper : FormControlTagHelperBase { /// public string BindControl { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (click),单击事件 /// public string OnClick { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs index 93b32576d..677374869 100644 --- a/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs @@ -158,10 +158,6 @@ public class TimePickerTagHelper : FormControlTagHelperBase { /// public string BindSuffixIcon { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (nzOpenChange),打开关闭面板事件 /// public string OnOpenChange { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs index aa8858841..307430db9 100644 --- a/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs +++ b/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs @@ -274,10 +274,6 @@ public class TreeSelectTagHelper : FormControlTagHelperBase { /// public string BindVirtualMinBufferPx { get; set; } /// - /// *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 - /// - public bool SpaceItem { get; set; } - /// /// (nzExpandChange),展开收缩节点事件,类型: EventEmitter<NzFormatEmitEvent> /// public string OnExpandChange { get; set; } diff --git a/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs b/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs index ea58388fe..7d333a4a5 100644 --- a/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs +++ b/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs @@ -293,14 +293,6 @@ public UploadBuilder FileListRender() { return this; } - /// - /// 配置间距项 - /// - public UploadBuilder SpaceItem() { - this.SpaceItem( _config ); - return this; - } - /// /// 配置事件 /// @@ -330,7 +322,7 @@ public override void Config() { .ShowUploadList().ShowButton().WithCredentials().OpenFileDialogOnClick() .Preview().PreviewFile().PreviewIsImage() .Remove().Download().TransformFile() - .IconRender().FileListRender().SpaceItem() + .IconRender().FileListRender() .Events(); Model(); AddButton(); diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs new file mode 100644 index 000000000..237c202d4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs @@ -0,0 +1,130 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.WaterMarks.Helpers; + +namespace Util.Ui.NgZorro.Components.WaterMarks.Builders; + +/// +/// 水印标签生成器 +/// +public class WaterMarkBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化水印标签生成器 + /// + /// 配置 + public WaterMarkBuilder( Config config ) : base( config, "nz-water-mark" ) { + _config = config; + } + + /// + /// 配置水印文字内容 + /// + public WaterMarkBuilder Content() { + AttributeIfNotEmpty( "nzContent", _config.GetValue( UiConst.Content ) ); + AttributeIfNotEmpty( "[nzContent]", _config.GetValue( AngularConst.BindContent ) ); + return this; + } + + /// + /// 配置水印宽度 + /// + public WaterMarkBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( AngularConst.BindWidth ) ); + return this; + } + + /// + /// 配置水印高度 + /// + public WaterMarkBuilder Height() { + AttributeIfNotEmpty( "[nzHeight]", _config.GetValue( UiConst.Height ) ); + AttributeIfNotEmpty( "[nzHeight]", _config.GetValue( AngularConst.BindHeight ) ); + return this; + } + + /// + /// 配置旋转角度 + /// + public WaterMarkBuilder Rotate() { + AttributeIfNotEmpty( "[nzRotate]", _config.GetValue( UiConst.Rotate ) ); + AttributeIfNotEmpty( "[nzRotate]", _config.GetValue( AngularConst.BindRotate ) ); + return this; + } + + /// + /// 配置z-index + /// + public WaterMarkBuilder ZIndex() { + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( UiConst.ZIndex ) ); + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( AngularConst.BindZIndex ) ); + return this; + } + + /// + /// 配置水印图片地址 + /// + public WaterMarkBuilder Image() { + AttributeIfNotEmpty( "nzImage", _config.GetValue( UiConst.Image ) ); + AttributeIfNotEmpty( "[nzImage]", _config.GetValue( AngularConst.BindImage ) ); + return this; + } + + /// + /// 配置文字样式 + /// + public WaterMarkBuilder Font() { + AttributeIfNotEmpty( "[nzFont]", GetFont() ); + return this; + } + + /// + /// 获取字体 + /// + private string GetFont() { + var result = _config.GetValue( UiConst.Font ); + if ( result.IsEmpty() == false ) + return result; + var fontType = new FontType { + Color = _config.GetValue( UiConst.FontColor ), + FontSize = _config.GetValue( UiConst.FontSize ), + FontWeight = _config.GetValue( UiConst.FontWeight ), + FontFamily = _config.GetValue( UiConst.FontFamily ), + FontStyle = _config.GetValue( UiConst.FontStyle ) + }; + result = Util.Helpers.Json.ToJson( fontType, new JsonOptions { ToSingleQuotes = true } ); + if ( result == "{}" ) + result = null; + return result; + } + + /// + /// 配置间距 + /// + public WaterMarkBuilder Gap() { + AttributeIfNotEmpty( "[nzGap]", _config.GetValue( UiConst.Gap ) ); + return this; + } + + /// + /// 配置偏移量 + /// + public WaterMarkBuilder Offset() { + AttributeIfNotEmpty( "[nzOffset]", _config.GetValue( UiConst.Offset ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Content().Width().Height().Rotate() + .ZIndex().Image().Font().Gap().Offset(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs new file mode 100644 index 000000000..180597449 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs @@ -0,0 +1,32 @@ +namespace Util.Ui.NgZorro.Components.WaterMarks.Helpers; + +/// +/// 字体类型 +/// +internal class FontType { + /// + /// 字体颜色 + /// + [JsonPropertyName("color")] + public string Color { get; set; } + /// + /// 字体大小 + /// + [JsonPropertyName( "fontSize" )] + public double? FontSize { get; set; } + /// + /// 字体粗细 + /// + [JsonPropertyName( "fontWeight" )] + public string FontWeight { get; set; } + /// + /// 字体类型 + /// + [JsonPropertyName( "fontFamily" )] + public string FontFamily { get; set; } + /// + /// 字体样式 + /// + [JsonPropertyName( "fontStyle" )] + public string FontStyle { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs new file mode 100644 index 000000000..3a08282c9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.WaterMarks.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.WaterMarks.Renders; + +/// +/// 水印渲染器 +/// +public class WaterMarkRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化水印渲染器 + /// + /// 配置 + public WaterMarkRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new WaterMarkBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new WaterMarkRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs new file mode 100644 index 000000000..b55423042 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs @@ -0,0 +1,99 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.WaterMarks.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.WaterMarks; + +/// +/// 水印,生成的标签为<nz-water-mark></nz-water-mark> +/// +[HtmlTargetElement( "util-water-mark" )] +public class WaterMarkTagHelper : AngularTagHelperBase { + /// + /// nzContent,水印文字内容, 类型: string | string[] + /// + public string Content { get; set; } + /// + /// [nzContent],水印文字内容, 类型: string | string[] + /// + public string BindContent { get; set; } + /// + /// [nzWidth],水印宽度, 默认值: 120 + /// + public double Width { get; set; } + /// + /// [nzWidth],水印宽度, 默认值: 120 + /// + public string BindWidth { get; set; } + /// + /// [nzHeight],水印高度, 默认值: 64 + /// + public double Height { get; set; } + /// + /// [nzHeight],水印高度, 默认值: 64 + /// + public string BindHeight { get; set; } + /// + /// [nzRotate],旋转角度, 默认值: -22 + /// + public double Rotate { get; set; } + /// + /// [nzRotate],旋转角度, 默认值: -22 + /// + public string BindRotate { get; set; } + /// + /// [nzZIndex],水印元素的 z-index, 默认值: 9 + /// + public double ZIndex { get; set; } + /// + /// [nzZIndex],水印元素的 z-index, 默认值: 9 + /// + public string BindZIndex { get; set; } + /// + /// nzImage,水印图片地址 + /// + public string Image { get; set; } + /// + /// [nzImage],水印图片地址 + /// + public string BindImage { get; set; } + /// + /// [nzFont],文字样式 + /// + public string Font { get; set; } + /// + /// [nzFont],字体颜色, 默认值: rgba(0,0,0,.15) + /// + public string FontColor { get; set; } + /// + /// [nzFont],字体大小, 默认值: 16 + /// + public double FontSize { get; set; } + /// + /// [nzFont],字体粗细, 可选值: normal | light | weight | number, 默认值: normal + /// + public string FontWeight { get; set; } + /// + /// [nzFont],字体类型, 默认值: sans-serif + /// + public string FontFamily { get; set; } + /// + /// [nzFont],字体样式, 可选值: none | normal | italic | oblique, 默认值: normal + /// + public string FontStyle { get; set; } + /// + /// [nzGap],水印之间的间距, 类型: [number, number], 默认值: [100, 100] + /// + public string Gap { get; set; } + /// + /// [nzOffset],水印距离容器左上角的偏移量, 类型: [number, number], 默认值: [nzGap[0]/2, nzGap[1]/2] + /// + public string Offset { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new WaterMarkRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs b/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs new file mode 100644 index 000000000..fd839d876 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 哈希码展示模式 +/// +public enum HashCodeMode { + /// + /// 单行模式 + /// + [Description( "single" )] + Single, + /// + /// 双行模式 + /// + [Description( "double" )] + Double, + /// + /// 长条模式 + /// + [Description( "strip" )] + Strip, + /// + /// 矩形模式 + /// + [Description( "rect" )] + Rect +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/HashCodeType.cs b/src/Util.Ui.NgZorro/Enums/HashCodeType.cs new file mode 100644 index 000000000..f35611888 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/HashCodeType.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 哈希码样式 +/// +public enum HashCodeType { + /// + /// 默认样式 + /// + [Description( "defalut " )] + Defalut, + /// + /// 主样式 + /// + [Description( "primary" )] + Primary +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs b/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs new file mode 100644 index 000000000..8c58bfcd2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs @@ -0,0 +1,23 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 二维码容错级别 +/// +public enum QrCodeCorrectionLevel { + /// + /// 可以纠正最大7%的错误 + /// + L, + /// + /// 可以纠正最大15%的错误 + /// + M, + /// + /// 可以纠正最大25%的错误 + /// + Q, + /// + /// 可以纠正最大30%的错误 + /// + H +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs b/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs new file mode 100644 index 000000000..302c80e6c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 二维码状态 +/// +public enum QrCodeStatus { + /// + /// active, 正常 + /// + [Description( "active" )] + Active, + /// + /// expired,已过期 + /// + [Description( "expired" )] + Expired, + /// + /// loading, 加载中 + /// + [Description( "loading" )] + Loading +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs b/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs index e2ddd8dd2..5b20ed25a 100644 --- a/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs +++ b/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs @@ -1,87 +1,86 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; - -namespace Util.Ui.NgZorro.Extensions; - -/// -/// ng-zorro扩展 -/// -public static class TagBuilderExtensions { - /// - /// *nzSpaceItem,间距项 - /// - /// 生成器类型 - /// 生成器实例 - /// 值 - public static TBuilder SpaceItem( this TBuilder builder, bool? value ) where TBuilder : TagBuilder { - if( value == true ) - builder.Attribute( "*nzSpaceItem" ); - return builder; - } - - /// - /// *nzSpaceItem,间距项 - /// - /// 生成器类型 - /// 生成器实例 - /// 配置 - public static TBuilder SpaceItem( this TBuilder builder, Config config ) where TBuilder : TagBuilder { - builder.SpaceItem( config.GetValue( UiConst.SpaceItem ) ); - return builder; - } - - /// - /// 宽度 - /// - /// 生成器类型 - /// 生成器实例 - /// 值 - /// 属性名 - public static TBuilder Width( this TBuilder builder, string value,string attributeName = "nzWidth" ) where TBuilder : TagBuilder { - builder.AttributeIfNotEmpty( attributeName, GetPixelValue( value ) ); - return builder; - } - - /// - /// 获取像素值,如果传入数字,后加px,否则按原样返回 - /// - /// 值 - private static string GetPixelValue( string value ) { - if ( value.IsEmpty() ) - return null; - if ( Util.Helpers.Validation.IsNumber( value ) ) - return $"{value}px"; - return value; - } - - /// - /// 添加到内容,支持i18n管道 - /// - /// 生成器类型 - /// 生成器实例 - /// 值 - public static TBuilder AppendContentByI18n( this TBuilder builder, string value ) where TBuilder : TagBuilder { - if ( value.IsEmpty() ) - return builder; - var result = new StringBuilder(); - result.Append( "{{" ); - result.Append( $"'{value}'|i18n" ); - result.Append( "}}" ); - builder.AppendContent( result.ToString() ); - return builder; - } - - /// - /// 添加属性,支持i18n管道 - /// - /// 生成器类型 - /// 生成器实例 - /// 属性名 - /// 值 - public static TBuilder AttributeByI18n( this TBuilder builder, string name, string value ) where TBuilder : TagBuilder { - if ( value.IsEmpty() ) - return builder; - builder.Attribute( name, $"'{value}'|i18n" ); - return builder; - } +using Util.Ui.Builders; + +namespace Util.Ui.NgZorro.Extensions; + +/// +/// ng-zorro扩展 +/// +public static class TagBuilderExtensions { + /// + /// *nzSpaceItem,间距项 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder SpaceItem( this TBuilder builder, bool? value ) where TBuilder : TagBuilder { + if( value == true ) + builder.Attribute( "*nzSpaceItem" ); + return builder; + } + + /// + /// *nzSpaceItem,间距项 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder SpaceItem( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.SpaceItem( config.GetValue( UiConst.SpaceItem ) ); + return builder; + } + + /// + /// 宽度 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + /// 属性名 + public static TBuilder Width( this TBuilder builder, string value,string attributeName = "nzWidth" ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( attributeName, GetPixelValue( value ) ); + return builder; + } + + /// + /// 获取像素值,如果传入数字,后加px,否则按原样返回 + /// + /// 值 + private static string GetPixelValue( string value ) { + if ( value.IsEmpty() ) + return null; + if ( Util.Helpers.Validation.IsNumber( value ) ) + return $"{value}px"; + return value; + } + + /// + /// 添加到内容,支持i18n管道 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder AppendContentByI18n( this TBuilder builder, string value ) where TBuilder : TagBuilder { + if ( value.IsEmpty() ) + return builder; + var result = new StringBuilder(); + result.Append( "{{" ); + result.Append( $"'{value}'|i18n" ); + result.Append( "}}" ); + builder.AppendContent( result.ToString() ); + return builder; + } + + /// + /// 添加属性,支持i18n管道 + /// + /// 生成器类型 + /// 生成器实例 + /// 属性名 + /// 值 + public static TBuilder AttributeByI18n( this TBuilder builder, string name, string value ) where TBuilder : TagBuilder { + if ( value.IsEmpty() ) + return builder; + builder.Attribute( name, $"'{value}'|i18n" ); + return builder; + } } \ No newline at end of file diff --git a/src/Util.Ui/Configs/UiConst.cs b/src/Util.Ui/Configs/UiConst.cs index b1e21abf7..8a2d30cfa 100644 --- a/src/Util.Ui/Configs/UiConst.cs +++ b/src/Util.Ui/Configs/UiConst.cs @@ -25,10 +25,30 @@ public static class UiConst { /// public const string Color = "color"; /// + /// 填充 + /// + public const string Padding = "padding"; + /// + /// 等级 + /// + public const string Level = "level"; + /// + /// 图片 + /// + public const string Image = "image"; + /// + /// 持续时间 + /// + public const string Duration = "duration"; + /// /// 单元格控件 /// public const string CellControl = "cell-control"; /// + /// 可见高度 + /// + public const string VisibilityHeight = "visibility-height"; + /// /// 启用拖动调整尺寸 /// public const string EnableResizable = "enable-resizable"; @@ -89,6 +109,10 @@ public static class UiConst { /// public const string BackgroundColor = "background-color"; /// + /// 背景色 + /// + public const string BgColor = "bg-color"; + /// /// 粘贴图片 /// public const string PasteDataImages = "paste-data-images"; @@ -437,6 +461,10 @@ public static class UiConst { /// public const string OnChange = "on-change"; /// + /// 刷新事件 + /// + public const string OnRefresh = "on-refresh"; + /// /// 获得焦点事件 /// public const string OnFocus = "on-focus"; @@ -445,6 +473,10 @@ public static class UiConst { /// public const string OnBlur = "on-blur"; /// + /// 复制事件 + /// + public const string OnCopy = "on-copy"; + /// /// 键盘按键事件 /// public const string OnKeyup = "on-keyup"; @@ -493,10 +525,38 @@ public static class UiConst { /// public const string Icon = "icon"; /// + /// 图标尺寸 + /// + public const string IconSize = "icon-size"; + /// /// 复制到剪贴板 /// public const string CopyToClipboard = "copy-to-clipboard"; /// + /// 字体 + /// + public const string Font = "font"; + /// + /// 字体颜色 + /// + public const string FontColor = "font-color"; + /// + /// 字体大小 + /// + public const string FontSize = "font-size"; + /// + /// 字体粗细 + /// + public const string FontWeight = "font-weight"; + /// + /// 字体类型 + /// + public const string FontFamily = "font-family"; + /// + /// 字体样式 + /// + public const string FontStyle = "font-style"; + /// /// Font Awesome图标 /// public const string FontAwesomeIcon = "font-awesome-icon"; diff --git a/test/Util.Core.Tests/Helpers/JsonTest.cs b/test/Util.Core.Tests/Helpers/JsonTest.cs index 5b23ef1cb..54f1bab0b 100644 --- a/test/Util.Core.Tests/Helpers/JsonTest.cs +++ b/test/Util.Core.Tests/Helpers/JsonTest.cs @@ -223,4 +223,14 @@ public void TestToJson_NullableLong() { var obj = Json.ToObject( json ); Assert.Equal( 123456789123456789, obj.NullableLong ); } + + /// + /// 测试转成Json - 忽略空字符串 + /// + [Fact] + public void TestToJson_IgnoreEmpty() { + var sample = new JsonTestSample3 { Name = "" }; + var json = Json.ToJson( sample, new JsonOptions { IgnoreEmptyString = true } ); + Assert.Equal( "{}", json ); + } } \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/JsonTestSample.cs b/test/Util.Core.Tests/Samples/JsonTestSample.cs index 0c3ed49ec..ca0a9ea5c 100644 --- a/test/Util.Core.Tests/Samples/JsonTestSample.cs +++ b/test/Util.Core.Tests/Samples/JsonTestSample.cs @@ -123,4 +123,11 @@ public interface IJsonTestSample { public enum TestEnum { Test1 = 1, Test2 = 2 +} + +/// +/// Json测试样例 +/// +public class JsonTestSample3 { + public string Name { get; set; } } \ No newline at end of file diff --git a/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs index df7564bbf..3dcf3d2c2 100644 --- a/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs +++ b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs @@ -15,7 +15,7 @@ public class AvatarManagerTest { /// /// 输出路径 /// - private const string Path = @"D:\Util.Images.Avatar.Tests\{0}.gif"; + private const string Path = "./test/{0}.gif"; /// /// 测试初始化 diff --git a/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs b/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs index 07ca08fba..5d2d61f63 100644 --- a/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs +++ b/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs @@ -20,7 +20,7 @@ public class DrawTextTest { /// /// 输出路径 /// - private const string Path = @"D:\Util.Images.ImageSharp.Tests\{0}.gif"; + private const string Path = "./test/{0}.gif"; /// /// 测试初始化 diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg b/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg new file mode 100644 index 000000000..b591214cc Binary files /dev/null and b/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg differ diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs b/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs new file mode 100644 index 000000000..36c7d8891 --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.QrCode; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs b/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs new file mode 100644 index 000000000..c4893c876 --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs @@ -0,0 +1,151 @@ +using System.Drawing; +using System.Threading.Tasks; +using Util.Images; +using Xunit; +using Xunit.Abstractions; + +namespace Util.QrCode.Tests; + +/// +/// ZXing二维码服务 +/// +public class ZXingQrCodeServiceTest { + /// + /// 二维码服务 + /// + private readonly IQrCodeService _service; + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _output; + /// + /// 内容 + /// + private readonly string _content; + + /// + /// 测试初始化 + /// + public ZXingQrCodeServiceTest( IQrCodeService service, ITestOutputHelper output ) { + _service = service; + _output = output; + _content = "https://gitee.com/util-core/util"; + } + + /// + /// 测试获取二维码流 + /// + [Fact] + public void TestToStream() { + var result = _service.Content( _content ).ToStream(); + Util.Helpers.File.Write( "./test/ToStream.png", result ); + } + + /// + /// 测试获取二维码字节流 + /// + [Fact] + public void TestToBytes() { + var result = _service.Content( _content ).ToBytes(); + Util.Helpers.File.Write( "./test/ToBytes.png", result ); + } + + /// + /// 测试获取二维码Base64字符串 + /// + [Fact] + public void TestToBase64_1() { + var result = _service.Content( _content ).ToBase64(); + string base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADyklEQVR4nO3dQU4jOxRAUZB6/1vm/0lLPYiaCn7xdaXPGQMp4MqK43L519f/PiDy6wNCAiQlQFICJCVAUgIk9dcAPz8/P07x6NOileu7+vOqT6mu/m7Tf5dp3/39jICkBEhKgKQESOrpAHe8Ka/eRE//bisTiR2Trmk/uT4jICkBkhIgKQGSGgnwpDfHV9+8X131WPm6q9f3yI6J2An/NyMgKQGSEiApAZJ6uwB3TBpW3rxPT1bu7u0C5F4ESEqApARI6tYBrrxRn16RmF5FOWl/yivdOkDuT4CkBEhKgKRGAjxp83Z1u9P0CswOJ0xqjICkBEhKgKQESOrpAE96E33Vjv0fJ33dI6f+34yApARISoCkBEjqrwF+HX77z+mP2b36und8EtYUIyApAZISICkBkho5J2THp/Erb+ivXsvKZGDH5Gfl51W3izknhKMJkJQASQmQ1MtWQnZMYHY89errDTeD/7bjNrDvGAFJCZCUAEkJkNTL9oTsWFXYsUdix8rASXtMph8/bCWEowmQlABJCZDUywLcMeGYvpYdhxWedBvY9MTuJ6sjRkBSAiQlQFICJPV0gNO3J01/Gn9VtRl85fednqxc9coDFo2ApARISoCkBEgqXwl5pPqEfvogwZP2mOzY1P4TRkBSAiQlQFICJJWfE3LSrVcrX7di5ZqrAxGnJiZGQFICJCVAUgIk9bIAp59cNX0tj0yvcOw4J6RabZma6BgBSQmQlABJCZDUyJ6QHSsXK3bsuZh+3WnVOSHfMQKSEiApAZISIKmnDyuc/sT/qunH8e7Yw3H6GSMn7G0xApISICkBkhIgqa2HFZ5+MOHVa1l5jR2rDztWb6ZWeYyApARISoCkBEhqJMAdk4YddjyV61+7ves7RkBSAiQlQFICJPX07VhX7VilqL73dDvOO3E7Fm9BgKQESEqApF52O9aKldu2pl93xzkh1d6M6YMTf8IISEqApARISoCkXrYSMm1lQ/z06z6yY4Kw8r2n3qJlBCQlQFICJCVAUiOP6J1WnZGxY0Vix2b16Sd6rawGfccISEqApARISoCkXrYx/arpSc2OW6Cmr+WRlcnAih0TrD8ZAUkJkJQASQmQ1K0DrPZw7FCtmOzeJ3LrALk/AZISICkBknq7AKc3tZ+0yjP9utMTHSsh3I4ASQmQlABJjQR40mrBCqsjno7FP0aApARISoCkng7wpCdmrTjp5PLqKV+79388YgQkJUBSAiQlQFJHnhNy1fT1nfTzdnxvdX1/MgKSEiApAZISICkBkhIgKQGS+g/OvjqVV+EClwAAAABJRU5ErkJggg=="; + _output.WriteLine( result ); + Assert.Equal( base64, result ); + } + + /// + /// 测试获取二维码Base64字符串 - 设置图片类型 + /// + [Fact] + public void TestToBase64_2() { + var result = _service.Content( _content ).ImageType( ImageType.Jpg ).ToBase64(); + string base64 = "data:image/jpeg;base64,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"; + _output.WriteLine( result ); + Assert.Equal( base64, result ); + } + + /// + /// 测试保存二维码图片 + /// + [Fact] + public void TestSave() { + _service.Content( _content ).Save( "./test/Save.png" ); + } + + /// + /// 测试获取二维码流 + /// + [Fact] + public async Task TestToStreamAsync() { + var result = await _service.Content( _content ).ToStreamAsync(); + await Util.Helpers.File.WriteAsync( "./test/ToStreamAsync.png", result ); + } + + /// + /// 测试获取二维码字节流 + /// + [Fact] + public async Task TestToBytesAsync() { + var result = await _service.Content( _content ).ToBytesAsync(); + await Util.Helpers.File.WriteAsync( "./test/ToBytesAsync.png", result ); + } + + /// + /// 测试保存二维码图片 + /// + [Fact] + public async Task TestSaveAsync_1() { + await _service.Content( _content ).SaveAsync( "./test/SaveAsync_1.png" ); + } + + /// + /// 测试保存二维码图片 - 设置二维码尺寸 + /// + [Fact] + public async Task TestSaveAsync_2() { + await _service.Content( _content ).Size( QrSize.Small ).SaveAsync( "./test/SaveAsync_Small.png" ); + await _service.Content( _content ).Size( QrSize.Middle ).SaveAsync( "./test/SaveAsync_Middle.png" ); + await _service.Content( _content ).Size( QrSize.Large ).SaveAsync( "./test/SaveAsync_Large.png" ); + } + + /// + /// 测试保存二维码图片 - 设置前景色 + /// + [Fact] + public async Task TestSaveAsync_3() { + await _service.Content( _content ).Color( Color.Red ).SaveAsync( "./test/SaveAsync_3.png" ); + } + + /// + /// 测试保存二维码图片 - 设置背景色 + /// + [Fact] + public async Task TestSaveAsync_4() { + await _service.Content( _content ).BgColor( Color.Aquamarine ).SaveAsync( "./test/SaveAsync_4.png" ); + } + + /// + /// 测试保存二维码图片 - 设置边距 + /// + [Fact] + public async Task TestSaveAsync_5() { + await _service.Content( _content ).Margin( 5 ).SaveAsync( "./test/SaveAsync_5.png" ); + } + + /// + /// 测试保存二维码图片 - 设置图标 + /// + [Fact] + public async Task TestSaveAsync_6() { + var iconPath = Util.Helpers.Common.GetPhysicalPath( "./Icons/icon.jpg" ); + await _service.Content( _content ).Icon( iconPath ).SaveAsync( "./test/SaveAsync_6.png" ); + } +} \ No newline at end of file diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj b/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj new file mode 100644 index 000000000..3b061dd2c --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj @@ -0,0 +1,35 @@ + + + + $(NetTargetFramework) + false + Util.QrCode + Util.QrCode.Startup + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + + \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs new file mode 100644 index 000000000..26118409b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs @@ -0,0 +1,138 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.BackTops; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.BackTops; + +/// +/// 回到顶部测试 +/// +public class BackTopTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BackTopTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BackTopTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义内容 + /// + [Fact] + public void TestTemplate() { + _wrapper.SetContextAttribute( UiConst.Template, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可见高度 + /// + [Fact] + public void TestVisibilityHeight() { + _wrapper.SetContextAttribute( UiConst.VisibilityHeight, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可见高度 + /// + [Fact] + public void TestBindVisibilityHeight() { + _wrapper.SetContextAttribute( AngularConst.BindVisibilityHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试监听目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Target, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试持续时间 + /// + [Fact] + public void TestDuration() { + _wrapper.SetContextAttribute( UiConst.Duration, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试持续时间 + /// + [Fact] + public void TestBindDuration() { + _wrapper.SetContextAttribute( AngularConst.BindDuration, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs index bc2e6066b..4b82f3f30 100644 --- a/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs @@ -1,4 +1,5 @@ using System.Text; +using Util.Helpers; using Util.Ui.Angular.Configs; using Util.Ui.Configs; using Util.Ui.NgZorro.Components.Cascaders; @@ -27,6 +28,7 @@ public class CascaderTagHelperTest { public CascaderTagHelperTest( ITestOutputHelper output ) { _output = output; _wrapper = new CascaderTagHelper().ToWrapper(); + Id.SetId( "id" ); } /// @@ -559,9 +561,19 @@ public void TestContent() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs index de6883119..4ab0b57a4 100644 --- a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs @@ -234,9 +234,16 @@ public void TestControlSpan() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs index 12dc2846f..2a893c4ce 100644 --- a/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs @@ -518,9 +518,16 @@ public void TestOnOk() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs index cd681ab95..145d97e0f 100644 --- a/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs @@ -485,9 +485,16 @@ public void TestOnOk() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } diff --git a/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs new file mode 100644 index 000000000..bd10a64d5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.HashCodes; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.HashCodes; + +/// +/// 哈希码测试 +/// +public class HashCodeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public HashCodeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new HashCodeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标志 + /// + [Fact] + public void TestLogo() { + _wrapper.SetContextAttribute( UiConst.Logo, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标志 + /// + [Fact] + public void TestBindLogo() { + _wrapper.SetContextAttribute( AngularConst.BindLogo, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展示模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, HashCodeMode.Rect ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展示模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, HashCodeType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制事件 + /// + [Fact] + public void TestOnCopy() { + _wrapper.SetContextAttribute( UiConst.OnCopy, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs index b4daa25e2..585ad4d5c 100644 --- a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs @@ -364,9 +364,16 @@ public void TestOnBlur() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs index 252ae094c..b66267c84 100644 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs @@ -164,6 +164,39 @@ public void TestLabelText() { Assert.Equal( result.ToString(), GetResult() ); } + /// + /// 测试标签文本 - 启用I18n + /// + [Fact] + public void TestLabelText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本 + /// + [Fact] + public void TestBindLabelText() { + _wrapper.SetContextAttribute( AngularConst.BindLabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + /// /// 测试是否显示标签 /// diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.I18n.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.I18n.cs deleted file mode 100644 index 5b0986fcf..000000000 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.I18n.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text; -using Util.Ui.Configs; -using Util.Ui.NgZorro.Configs; -using Xunit; - -namespace Util.Ui.NgZorro.Tests.Inputs { - /// - /// 输入框测试 - 国际化 - /// - public partial class InputTagHelperTest { - /// - /// 测试标签文本 - 启用I18n - /// - [Fact] - public void TestLabelText_I18n() { - NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); - _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); - var result = new StringBuilder(); - result.Append( "" ); - result.Append( "{{'a'|i18n}}" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - } -} - diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs index cc3a009a1..3ee383421 100644 --- a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs @@ -1,4 +1,5 @@ using System.Text; +using Util.Ui.Angular.Configs; using Util.Ui.Configs; using Xunit; @@ -12,9 +13,19 @@ public partial class InputTagHelperTest { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs new file mode 100644 index 000000000..da2f62e1b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs @@ -0,0 +1,282 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.QrCodes; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.QrCodes; + +/// +/// 二维码测试 +/// +public class QrCodeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public QrCodeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new QrCodeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试背景色 + /// + [Fact] + public void TestBgColor() { + _wrapper.SetContextAttribute( UiConst.BgColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试背景色 + /// + [Fact] + public void TestBindBgColor() { + _wrapper.SetContextAttribute( AngularConst.BindBgColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试填充 + /// + [Fact] + public void TestPadding() { + _wrapper.SetContextAttribute( UiConst.Padding, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标地址 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标地址 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标尺寸 + /// + [Fact] + public void TestIconSize() { + _wrapper.SetContextAttribute( UiConst.IconSize, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标尺寸 + /// + [Fact] + public void TestBindIconSize() { + _wrapper.SetContextAttribute( AngularConst.BindIconSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试边框 + /// + [Fact] + public void TestBindBordered() { + _wrapper.SetContextAttribute( AngularConst.BindBordered, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, QrCodeStatus.Expired ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容错级别 + /// + [Fact] + public void TestLevel() { + _wrapper.SetContextAttribute( UiConst.Level, QrCodeCorrectionLevel.H ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容错级别 + /// + [Fact] + public void TestBindLevel() { + _wrapper.SetContextAttribute( AngularConst.BindLevel, 'a' ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试刷新事件 + /// + [Fact] + public void TestOnRefresh() { + _wrapper.SetContextAttribute( UiConst.OnRefresh, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs index e271eb816..e23421988 100644 --- a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs @@ -781,9 +781,16 @@ public void TestOnBlur() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs index d4c98f856..45addcea9 100644 --- a/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs @@ -252,9 +252,16 @@ public void TestOnClick() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs index 2a3a321ff..5bf6335e7 100644 --- a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs @@ -276,9 +276,16 @@ public void TestMentionTrigger() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs index 747f243a2..cebe9ba70 100644 --- a/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs @@ -474,9 +474,16 @@ public void TestOnOpenChange() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs index a15c6ebfc..938b2bc78 100644 --- a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs @@ -695,9 +695,16 @@ public void TestOnOpenChange() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } } diff --git a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs index 215629004..a58961a1c 100644 --- a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs +++ b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs @@ -571,9 +571,16 @@ public void TestOnChange() { /// [Fact] public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); - result.Append( $"{GetButton()}" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( $"{GetButton()}" ); + result.Append( "" ); + result.Append( "" ); Assert.Equal( result.ToString(), GetResult() ); } diff --git a/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs new file mode 100644 index 000000000..09e6fe97e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs @@ -0,0 +1,282 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.WaterMarks; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.WaterMarks; + +/// +/// 水印测试 +/// +public class WaterMarkTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public WaterMarkTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new WaterMarkTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印文字内容 + /// + [Fact] + public void TestContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印文字内容 + /// + [Fact] + public void TestBindContent() { + _wrapper.SetContextAttribute( AngularConst.BindContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印宽度 + /// + [Fact] + public void TestBindWidth() { + _wrapper.SetContextAttribute( AngularConst.BindWidth, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印高度 + /// + [Fact] + public void TestHeight() { + _wrapper.SetContextAttribute( UiConst.Height, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印高度 + /// + [Fact] + public void TestBindHeight() { + _wrapper.SetContextAttribute( AngularConst.BindHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转角度 + /// + [Fact] + public void TestRotate() { + _wrapper.SetContextAttribute( UiConst.Rotate, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转角度 + /// + [Fact] + public void TestBindRotate() { + _wrapper.SetContextAttribute( AngularConst.BindRotate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestZIndex() { + _wrapper.SetContextAttribute( UiConst.ZIndex, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestBindZIndex() { + _wrapper.SetContextAttribute( AngularConst.BindZIndex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印图片地址 + /// + [Fact] + public void TestImage() { + _wrapper.SetContextAttribute( UiConst.Image, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印图片地址 + /// + [Fact] + public void TestBindImage() { + _wrapper.SetContextAttribute( AngularConst.BindImage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字样式 + /// + [Fact] + public void TestFont() { + _wrapper.SetContextAttribute( UiConst.Font, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体颜色 + /// + [Fact] + public void TestFontColor() { + _wrapper.SetContextAttribute( UiConst.FontColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体大小 + /// + [Fact] + public void TestFontSize() { + _wrapper.SetContextAttribute( UiConst.FontSize, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体粗细 + /// + [Fact] + public void TestFontWeight() { + _wrapper.SetContextAttribute( UiConst.FontWeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体类型 + /// + [Fact] + public void TestFontFamily() { + _wrapper.SetContextAttribute( UiConst.FontFamily, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体样式 + /// + [Fact] + public void TestFontStyle() { + _wrapper.SetContextAttribute( UiConst.FontStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestGap() { + _wrapper.SetContextAttribute( UiConst.Gap, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试偏移量 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file