Skip to content

Commit adaebbb

Browse files
committed
1. 新增二维码操作 Util.QrCode.IQrCodeService .
2. 新增二维码组件标签 <util-qrcode> . 3. 新增水印组件标签 <util-water-mark> . 4. 新增哈希码组件标签 <util-hash-code> . 5. 新增回到顶部组件标签 <util-back-top> . 6. Json操作类 Util.Helpers.Json 的 JsonOptions 参数新增 IgnoreEmptyString 属性, 用于忽略空字符串. 7. 修复表单组件 SpaceItem 属性引起验证指令引用变量作用域错误导致的bug.
1 parent cf0f68b commit adaebbb

File tree

92 files changed

+3124
-265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+3124
-265
lines changed

Util.sln

+16-2
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Security", "src\Uti
235235
EndProject
236236
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Security.Tests", "test\Util.Security.Tests\Util.Security.Tests.csproj", "{B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}"
237237
EndProject
238-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Images", "07-Images", "{1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}"
238+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}"
239239
EndProject
240240
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Images.ImageSharp", "src\Util.Images.ImageSharp\01-Util.Images.ImageSharp.csproj", "{22305349-51C7-4D80-A538-C7C0F4495895}"
241241
EndProject
@@ -247,7 +247,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-EntityFrameworkCore", "0
247247
EndProject
248248
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{2864196E-3044-4FEC-B92A-D1D3C521C567}"
249249
EndProject
250-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Images", "07-Images", "{96DFADC5-FAE8-4860-A57A-93F0534B1B7A}"
250+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{96DFADC5-FAE8-4860-A57A-93F0534B1B7A}"
251251
EndProject
252252
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}"
253253
EndProject
@@ -345,6 +345,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.FileStorage.Aliyun.Tes
345345
EndProject
346346
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.FileStorage.Abstractions", "src\Util.FileStorage.Abstractions\01-Util.FileStorage.Abstractions.csproj", "{814E42E9-508E-487F-BCD3-0F07AE7D1492}"
347347
EndProject
348+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.QrCode.ZXing", "src\Util.QrCode.ZXing\03-Util.QrCode.ZXing.csproj", "{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}"
349+
EndProject
350+
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}"
351+
EndProject
348352
Global
349353
GlobalSection(SolutionConfigurationPlatforms) = preSolution
350354
Debug|Any CPU = Debug|Any CPU
@@ -863,6 +867,14 @@ Global
863867
{814E42E9-508E-487F-BCD3-0F07AE7D1492}.Debug|Any CPU.Build.0 = Debug|Any CPU
864868
{814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.ActiveCfg = Release|Any CPU
865869
{814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.Build.0 = Release|Any CPU
870+
{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
871+
{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
872+
{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
873+
{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.Build.0 = Release|Any CPU
874+
{79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
875+
{79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.Build.0 = Debug|Any CPU
876+
{79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.ActiveCfg = Release|Any CPU
877+
{79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.Build.0 = Release|Any CPU
866878
EndGlobalSection
867879
GlobalSection(SolutionProperties) = preSolution
868880
HideSolutionNode = FALSE
@@ -1034,6 +1046,8 @@ Global
10341046
{4938D59D-CF50-4090-B936-36DB29EBE344} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2}
10351047
{58C01936-5D8F-4077-8663-EE0E35776D65} = {16175228-03CA-414A-8786-E5BA844110C4}
10361048
{814E42E9-508E-487F-BCD3-0F07AE7D1492} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2}
1049+
{3F5120A6-9CA4-4B25-9507-86E9E5571DB6} = {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}
1050+
{79A40AC0-A182-49D0-AED9-DF8B6835432A} = {96DFADC5-FAE8-4860-A57A-93F0534B1B7A}
10371051
EndGlobalSection
10381052
GlobalSection(ExtensibilityGlobals) = postSolution
10391053
SolutionGuid = {94347832-A36D-4C42-9C4D-B848BD4F5DA9}

build/version.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<VersionMajor>8</VersionMajor>
44
<VersionMinor>0</VersionMinor>
5-
<VersionPatch>11</VersionPatch>
5+
<VersionPatch>12</VersionPatch>
66
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
77
<VersionSuffix></VersionSuffix>
88
</PropertyGroup>

src/Util.Core/Helpers/File.cs

+40-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Util.Helpers;
1+
namespace Util.Helpers;
22

33
/// <summary>
44
/// 文件流操作
@@ -33,7 +33,7 @@ public static byte[] ToBytes( string data ) {
3333
/// <param name="encoding">字符编码</param>
3434
public static byte[] ToBytes( string data, Encoding encoding ) {
3535
if ( string.IsNullOrWhiteSpace( data ) )
36-
return new byte[] { };
36+
return Array.Empty<byte>();
3737
return encoding.GetBytes( data );
3838
}
3939

@@ -136,7 +136,7 @@ public static string ReadToString( string filePath ) {
136136
/// <param name="filePath">文件绝对路径</param>
137137
/// <param name="encoding">字符编码</param>
138138
public static string ReadToString( string filePath, Encoding encoding ) {
139-
if( System.IO.File.Exists( filePath ) == false )
139+
if ( System.IO.File.Exists( filePath ) == false )
140140
return string.Empty;
141141
using var reader = new StreamReader( filePath, encoding );
142142
return reader.ReadToEnd();
@@ -182,7 +182,7 @@ public static async Task<string> ReadToStringAsync( string filePath ) {
182182
/// <param name="filePath">文件绝对路径</param>
183183
/// <param name="encoding">字符编码</param>
184184
public static async Task<string> ReadToStringAsync( string filePath, Encoding encoding ) {
185-
if( System.IO.File.Exists( filePath ) == false )
185+
if ( System.IO.File.Exists( filePath ) == false )
186186
return string.Empty;
187187
using var reader = new StreamReader( filePath, encoding );
188188
return await reader.ReadToEndAsync();
@@ -295,18 +295,34 @@ public static byte[] ReadToBytes( Stream stream ) {
295295
/// <param name="filePath">文件绝对路径</param>
296296
/// <param name="content">内容</param>
297297
public static void Write( string filePath, string content ) {
298+
if ( content.IsEmpty() )
299+
return;
298300
Write( filePath, Convert.ToBytes( content ) );
299301
}
300302

303+
/// <summary>
304+
/// 将流写入文件
305+
/// </summary>
306+
/// <param name="filePath">文件绝对路径</param>
307+
/// <param name="content">内容</param>
308+
public static void Write( string filePath, Stream content ) {
309+
if ( content == null )
310+
return;
311+
using ( content ) {
312+
var bytes = ToBytes( content );
313+
Write( filePath, bytes );
314+
}
315+
}
316+
301317
/// <summary>
302318
/// 将字节流写入文件
303319
/// </summary>
304320
/// <param name="filePath">文件绝对路径</param>
305321
/// <param name="content">内容</param>
306322
public static void Write( string filePath, byte[] content ) {
307-
if( string.IsNullOrWhiteSpace( filePath ) )
323+
if ( string.IsNullOrWhiteSpace( filePath ) )
308324
return;
309-
if( content == null )
325+
if ( content == null )
310326
return;
311327
CreateDirectory( filePath );
312328
System.IO.File.WriteAllBytes( filePath, content );
@@ -323,6 +339,8 @@ public static void Write( string filePath, byte[] content ) {
323339
/// <param name="content">内容</param>
324340
/// <param name="cancellationToken">取消令牌</param>
325341
public static async Task WriteAsync( string filePath, string content, CancellationToken cancellationToken = default ) {
342+
if ( content.IsEmpty() )
343+
return;
326344
await WriteAsync( filePath, Convert.ToBytes( content ), cancellationToken );
327345
}
328346

@@ -333,8 +351,12 @@ public static void Write( string filePath, byte[] content ) {
333351
/// <param name="content">内容</param>
334352
/// <param name="cancellationToken">取消令牌</param>
335353
public static async Task WriteAsync( string filePath, Stream content, CancellationToken cancellationToken = default ) {
336-
var bytes = await ToBytesAsync( content, cancellationToken );
337-
await WriteAsync( filePath, bytes, cancellationToken );
354+
if ( content == null )
355+
return;
356+
await using ( content ) {
357+
var bytes = await ToBytesAsync( content, cancellationToken );
358+
await WriteAsync( filePath, bytes, cancellationToken );
359+
}
338360
}
339361

340362
/// <summary>
@@ -344,9 +366,9 @@ public static void Write( string filePath, byte[] content ) {
344366
/// <param name="content">内容</param>
345367
/// <param name="cancellationToken">取消令牌</param>
346368
public static async Task WriteAsync( string filePath, byte[] content, CancellationToken cancellationToken = default ) {
347-
if( string.IsNullOrWhiteSpace( filePath ) )
369+
if ( string.IsNullOrWhiteSpace( filePath ) )
348370
return;
349-
if( content == null )
371+
if ( content == null )
350372
return;
351373
CreateDirectory( filePath );
352374
await System.IO.File.WriteAllBytesAsync( filePath, content, cancellationToken );
@@ -361,7 +383,7 @@ public static void Write( string filePath, byte[] content ) {
361383
/// </summary>
362384
/// <param name="filePaths">文件绝对路径集合</param>
363385
public static void Delete( IEnumerable<string> filePaths ) {
364-
foreach( var filePath in filePaths )
386+
foreach ( var filePath in filePaths )
365387
Delete( filePath );
366388
}
367389

@@ -370,9 +392,9 @@ public static void Delete( IEnumerable<string> filePaths ) {
370392
/// </summary>
371393
/// <param name="filePath">文件绝对路径</param>
372394
public static void Delete( string filePath ) {
373-
if( string.IsNullOrWhiteSpace( filePath ) )
395+
if ( string.IsNullOrWhiteSpace( filePath ) )
374396
return;
375-
if( System.IO.File.Exists( filePath ) )
397+
if ( System.IO.File.Exists( filePath ) )
376398
System.IO.File.Delete( filePath );
377399
}
378400

@@ -385,7 +407,7 @@ public static void Delete( string filePath ) {
385407
/// </summary>
386408
/// <param name="path">目录路径</param>
387409
/// <param name="searchPattern">搜索模式</param>
388-
public static List<FileInfo> GetAllFiles( string path,string searchPattern ) {
410+
public static List<FileInfo> GetAllFiles( string path, string searchPattern ) {
389411
return Directory.GetFiles( path, searchPattern, SearchOption.AllDirectories )
390412
.Select( filePath => new FileInfo( filePath ) ).ToList();
391413
}
@@ -400,10 +422,10 @@ public static List<FileInfo> GetAllFiles( string path,string searchPattern ) {
400422
/// <param name="sourceFilePath">源文件绝对路径</param>
401423
/// <param name="destinationFilePath">目标文件绝对路径</param>
402424
/// <param name="overwrite">目标文件存在时是否覆盖,默认值: false</param>
403-
public static void Copy( string sourceFilePath,string destinationFilePath, bool overwrite = false ) {
425+
public static void Copy( string sourceFilePath, string destinationFilePath, bool overwrite = false ) {
404426
if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() )
405427
return;
406-
if( FileExists( sourceFilePath ) == false )
428+
if ( FileExists( sourceFilePath ) == false )
407429
return;
408430
CreateDirectory( destinationFilePath );
409431
System.IO.File.Copy( sourceFilePath, destinationFilePath, overwrite );
@@ -420,9 +442,9 @@ public static void Copy( string sourceFilePath,string destinationFilePath, bool
420442
/// <param name="destinationFilePath">目标文件绝对路径</param>
421443
/// <param name="overwrite">目标文件存在时是否覆盖,默认值: false</param>
422444
public static void Move( string sourceFilePath, string destinationFilePath, bool overwrite = false ) {
423-
if( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() )
445+
if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() )
424446
return;
425-
if( FileExists( sourceFilePath ) == false )
447+
if ( FileExists( sourceFilePath ) == false )
426448
return;
427449
CreateDirectory( destinationFilePath );
428450
System.IO.File.Move( sourceFilePath, destinationFilePath, overwrite );

src/Util.Core/Helpers/Json.cs

+23-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Util.SystemTextJson;
22

3-
namespace Util.Helpers;
3+
namespace Util.Helpers;
44

55
/// <summary>
66
/// Json操作
@@ -23,6 +23,11 @@ public static string ToJson<T>( T value, JsonOptions options ) {
2323
/// </summary>
2424
private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions options ) {
2525
var jsonSerializerOptions = new JsonSerializerOptions();
26+
if ( options.IgnoreEmptyString ) {
27+
jsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver {
28+
Modifiers = { IgnoreEmptyString }
29+
};
30+
}
2631
if ( options.IgnoreNullValues )
2732
jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
2833
if ( options.IgnoreCase )
@@ -37,21 +42,34 @@ private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions option
3742
return jsonSerializerOptions;
3843
}
3944

45+
/// <summary>
46+
/// 忽略空字符串
47+
/// </summary>
48+
private static void IgnoreEmptyString( JsonTypeInfo jsonTypeInfo ) {
49+
if ( jsonTypeInfo.Kind != JsonTypeInfoKind.Object )
50+
return;
51+
foreach ( JsonPropertyInfo jsonPropertyInfo in jsonTypeInfo.Properties ) {
52+
if ( jsonPropertyInfo.PropertyType == typeof( string ) ) {
53+
jsonPropertyInfo.ShouldSerialize = static ( _, value ) => value.SafeString().IsEmpty() == false;
54+
}
55+
}
56+
}
57+
4058
/// <summary>
4159
/// 将对象转换为Json字符串
4260
/// </summary>
4361
/// <param name="value">目标对象</param>
4462
/// <param name="options">序列化配置</param>
4563
/// <param name="removeQuotationMarks">是否移除双引号</param>
4664
/// <param name="toSingleQuotes">是否将双引号转成单引号</param>
47-
public static string ToJson<T>( T value, JsonSerializerOptions options = null,bool removeQuotationMarks = false, bool toSingleQuotes = false ) {
48-
return ToJson( value, options, removeQuotationMarks, toSingleQuotes,true );
65+
public static string ToJson<T>( T value, JsonSerializerOptions options = null, bool removeQuotationMarks = false, bool toSingleQuotes = false ) {
66+
return ToJson( value, options, removeQuotationMarks, toSingleQuotes, true );
4967
}
5068

5169
/// <summary>
5270
/// 将对象转换为Json字符串
5371
/// </summary>
54-
private static string ToJson<T>( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes,bool ignoreInterface ) {
72+
private static string ToJson<T>( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes, bool ignoreInterface ) {
5573
if ( value == null )
5674
return string.Empty;
5775
options = GetToJsonOptions( options );
@@ -87,7 +105,7 @@ private static JsonSerializerOptions GetToJsonOptions( JsonSerializerOptions opt
87105
private static string Serialize<T>( T value, JsonSerializerOptions options, bool ignoreInterface ) {
88106
if ( ignoreInterface ) {
89107
object instance = value;
90-
if( instance != null )
108+
if ( instance != null )
91109
return JsonSerializer.Serialize( instance, options );
92110
}
93111
return JsonSerializer.Serialize( value, options );

src/Util.Core/JsonOptions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class JsonOptions {
99
/// </summary>
1010
public JsonOptions() {
1111
IgnoreNullValues = true;
12+
IgnoreEmptyString = true;
1213
IgnoreCase = true;
1314
IgnoreInterface = true;
1415
}
@@ -26,6 +27,10 @@ public JsonOptions() {
2627
/// </summary>
2728
public bool IgnoreNullValues { get; set; }
2829
/// <summary>
30+
/// 是否忽略空字符串,默认值: true
31+
/// </summary>
32+
public bool IgnoreEmptyString { get; set; }
33+
/// <summary>
2934
/// 转换为Json时,属性名是否使用驼峰形式
3035
/// </summary>
3136
public bool ToCamelCase { get; set; }

src/Util.Core/Usings.cs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
global using System.Net.Http;
2323
global using System.Xml;
2424
global using System.Xml.Linq;
25+
global using System.Text.Json.Serialization.Metadata;
2526
global using Microsoft.Extensions.Configuration;
2627
global using Microsoft.Extensions.DependencyInjection;
2728
global using Microsoft.Extensions.Hosting;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using SixLabors.ImageSharp.Drawing;
2+
3+
namespace Util.Images;
4+
5+
/// <summary>
6+
/// ImageSharp扩展
7+
/// </summary>
8+
public static class ImageSharpExtensions {
9+
/// <summary>
10+
/// 转换颜色类型
11+
/// </summary>
12+
/// <param name="color">颜色</param>
13+
public static Color ToImageSharpColor( this System.Drawing.Color color ) {
14+
return Color.FromRgba( color.R, color.G, color.B, color.A );
15+
}
16+
17+
/// <summary>
18+
/// 图片缩放
19+
/// </summary>
20+
/// <param name="image">图片</param>
21+
/// <param name="scale">缩放比例</param>
22+
public static Image Zoom( this Image image, double scale ) {
23+
var width = Util.Helpers.Convert.ToInt( image.Width * scale );
24+
var height = Util.Helpers.Convert.ToInt( image.Height * scale );
25+
image.Mutate( t => {
26+
t.Resize( new Size( width, height ) );
27+
} );
28+
return image;
29+
}
30+
31+
/// <summary>
32+
/// 设置圆角
33+
/// </summary>
34+
/// <param name="image">图片</param>
35+
/// <param name="cornerRadius">角度</param>
36+
public static Image RoundCorners( this Image image, int cornerRadius ) {
37+
image.Mutate( context => RoundCorners( context, cornerRadius ) );
38+
return image;
39+
}
40+
41+
/// <summary>
42+
/// 设置圆角
43+
/// </summary>
44+
/// <param name="context">图片处理上下文</param>
45+
/// <param name="cornerRadius">角度</param>
46+
private static void RoundCorners( IImageProcessingContext context, int cornerRadius ) {
47+
var size = context.GetCurrentSize();
48+
var corners = BuildCorners( size.Width, size.Height, cornerRadius );
49+
context.SetGraphicsOptions( new GraphicsOptions { AlphaCompositionMode = PixelAlphaCompositionMode.DestOut } );
50+
context.Fill( Color.Red, corners );
51+
}
52+
53+
/// <summary>
54+
/// 构建圆角
55+
/// </summary>
56+
/// <param name="imageWidth">图片宽度</param>
57+
/// <param name="imageHeight">图片高度</param>
58+
/// <param name="cornerRadius">角度</param>
59+
private static IPathCollection BuildCorners( int imageWidth, int imageHeight, float cornerRadius ) {
60+
var rect = new RectangularPolygon( -0.5f, -0.5f, cornerRadius, cornerRadius );
61+
var cornerTopLeft = rect.Clip( new EllipsePolygon( cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius ) );
62+
var rightPos = imageWidth - cornerTopLeft.Bounds.Width + 1;
63+
var bottomPos = imageHeight - cornerTopLeft.Bounds.Height + 1;
64+
var cornerTopRight = cornerTopLeft.RotateDegree( 90 ).Translate( rightPos, 0 );
65+
var cornerBottomLeft = cornerTopLeft.RotateDegree( -90 ).Translate( 0, bottomPos );
66+
var cornerBottomRight = cornerTopLeft.RotateDegree( 180 ).Translate( rightPos, bottomPos );
67+
return new PathCollection( cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight );
68+
}
69+
}

0 commit comments

Comments
 (0)