From 013c60e7bf19c1990a8ff29bdf4e9840130b5213 Mon Sep 17 00:00:00 2001 From: GunYeng Date: Sun, 26 Apr 2026 22:50:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Acdiu.AspNetCore.Mvc.Commons.csproj | 21 ++++ Constants/UIConstants.cs | 38 +++++++ Extensions/StringExtensions.cs | 34 ++++++ TagHelpers/BsIconTagHelper.cs | 166 ++++++++++++++++++++++++++++ favicon.ico | Bin 0 -> 57207 bytes 5 files changed, 259 insertions(+) create mode 100644 Acdiu.AspNetCore.Mvc.Commons.csproj create mode 100644 Constants/UIConstants.cs create mode 100644 Extensions/StringExtensions.cs create mode 100644 TagHelpers/BsIconTagHelper.cs create mode 100644 favicon.ico diff --git a/Acdiu.AspNetCore.Mvc.Commons.csproj b/Acdiu.AspNetCore.Mvc.Commons.csproj new file mode 100644 index 0000000..fd3b38e --- /dev/null +++ b/Acdiu.AspNetCore.Mvc.Commons.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + enable + enable + favicon.ico + True + + + + + + + + + + + + + diff --git a/Constants/UIConstants.cs b/Constants/UIConstants.cs new file mode 100644 index 0000000..1e84b4c --- /dev/null +++ b/Constants/UIConstants.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Acdiu.AspNetCore.Mvc.Commons.Constants +{ + /// + /// 全局 UI 常量定义 + /// + public static class UIConstants + { + /// + /// 资源路径常量 + /// + public static class Paths + { + /// + /// 默认 Bootstrap Icons SVG Sprite 文件路径 + /// + public const string DefaultIconSprite = "/lib/bootstrap-icons-1.13.1/bootstrap-icons.svg"; + } + + /// + /// BS SVG 图标默认需要添加的 CSS 类名 + /// + public static class Classes + { + /// + /// 默认宽度类名,适配响应式根字号(1rem)。如果用户未指定宽度,则默认添加这些类名以确保图标大小适中且响应式。用户可以通过添加其他类名覆盖这些默认值。 + /// + public const string DefaultWidth = "w-1r"; + /// + /// 默认高度类名,适配响应式根字号(1rem)。如果用户未指定高度,则默认添加这些类名以确保图标大小适中且响应式。用户可以通过添加其他类名覆盖这些默认值。 + /// + public const string DefaultHeight = "h-1r"; + } + } +} diff --git a/Extensions/StringExtensions.cs b/Extensions/StringExtensions.cs new file mode 100644 index 0000000..6579ca6 --- /dev/null +++ b/Extensions/StringExtensions.cs @@ -0,0 +1,34 @@ +using Acdiu.AspNetCore.Mvc.Commons.Constants; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Acdiu.AspNetCore.Mvc.Commons.Extensions +{ + /// + /// 字符串扩展方法类,提供与 UI 相关的字符串处理功能。 + /// + public static class StringExtensions + { + /// + /// 转换为响应式类名,默认包含默认宽高 "w-1r h-1r" 类名,并且用户可以添加其他类名,最终返回一个包含所有不重复的类名的字符串。 + /// + /// 一个包含所有不重复的类名的字符串 + public static string ToResponsiveClass(this string userClassName) + { + var classes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + UIConstants.Classes.DefaultWidth, + UIConstants.Classes.DefaultHeight + }; + + if (!string.IsNullOrWhiteSpace(userClassName)) + { + var userParts = userClassName.Split([' '], StringSplitOptions.RemoveEmptyEntries); + foreach (var item in userParts) classes.Add(item); + } + + return string.Join(" ", classes); + } + } +} diff --git a/TagHelpers/BsIconTagHelper.cs b/TagHelpers/BsIconTagHelper.cs new file mode 100644 index 0000000..02e1210 --- /dev/null +++ b/TagHelpers/BsIconTagHelper.cs @@ -0,0 +1,166 @@ +using Acdiu.AspNetCore.Mvc.Commons.Constants; +using Acdiu.AspNetCore.Mvc.Commons.Extensions; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.VisualBasic; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Acdiu.AspNetCore.Mvc.Commons.TagHelpers +{ + /// + /// SVG Sprite 封装标签。 + /// 支持自动缩放逻辑:若未指定宽高,则默认添加 w-1r/h-1r 类名以适配响应式根字号。 + /// + /// + /// 用法: <bsicon i="heart-fill" w="20" f="red" /> + /// 或: <bsicon icon="alarm" width="1rem" cn="my-style" /> + /// + [HtmlTargetElement("bsicon")] + public class BsIconTagHelper : TagHelper + { + // 内部逻辑变量 + private string _iconName = string.Empty; + private string _width = string.Empty; + private string _height = string.Empty; + private string _fill = "currentColor"; + private string _className = string.Empty; + private string _customPath = string.Empty; + + #region 属性定义 (支持别名) + + /// + /// 图标名称 (必填)。对应 SVG 中的 ID。 + /// + [HtmlAttributeName("i")] + public string I { get => _iconName; set => _iconName = value; } + + /// + /// 图标名称 (别名)。 + /// + [HtmlAttributeName("icon")] + public string Icon { get => _iconName; set => _iconName = value; } + + /// + /// 宽度。支持数字或 CSS 单位(如 16, 1rem)。 + /// 若为空则尝试使用高度值,若均为空则添加默认响应式类名。 + /// + [HtmlAttributeName("w")] + public string W { get => _width; set => _width = value; } + + /// + /// 宽度 (别名)。 + /// + [HtmlAttributeName("width")] + public string Width { get => _width; set => _width = value; } + + /// + /// 高度。支持数字或 CSS 单位。 + /// + [HtmlAttributeName("h")] + public string H { get => _height; set => _height = value; } + + /// + /// 高度 (别名)。 + /// + [HtmlAttributeName("height")] + public string Height { get => _height; set => _height = value; } + + /// + /// 填充颜色。默认为 currentColor。 + /// + [HtmlAttributeName("f")] + public string F { get => _fill; set => _fill = value; } + + /// + /// 填充颜色 (别名)。 + /// + [HtmlAttributeName("fill")] + public string Fill { get => _fill; set => _fill = value; } + + /// + /// 自定义 CSS 类名。 + /// + [HtmlAttributeName("c")] + public string C { get => _className; set => _className = value; } + + /// + /// 自定义 CSS 类名 (别名)。 + /// + [HtmlAttributeName("cn")] + public string CN { get => _className; set => _className = value; } + + /// + /// 自定义 CSS 类名 (别名)。 + /// + [HtmlAttributeName("class")] + public string ClassName { get => _className; set => _className = value; } + + /// + /// 自定义 SVG Sprite 路径。 + /// 不填则使用系统默认常量路径。 + /// + [HtmlAttributeName("p")] + public string P { get => _customPath; set => _customPath = value; } + + /// + /// 自定义 SVG Sprite 路径。 + /// 不填则使用系统默认常量路径。 + /// + [HtmlAttributeName("path")] + public string Path { get => _customPath; set => _customPath = value; } + + /// + /// 自定义 SVG Sprite 路径。 + /// 不填则使用系统默认常量路径。 + /// + [HtmlAttributeName("path")] + public string CustomPath { get => _customPath; set => _customPath = value; } + + #endregion + + /// + /// 处理标签渲染逻辑 + /// + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (string.IsNullOrWhiteSpace(_iconName)) + { + output.SuppressOutput(); + return; + } + + // 1. 设置基础标签属性 + output.TagName = "svg"; + output.TagMode = TagMode.StartTagAndEndTag; + output.Attributes.SetAttribute("fill", _fill); + output.Attributes.SetAttribute("viewBox", "0 0 16 16"); + + // 2. 处理宽高与类名逻辑 + bool hasW = !string.IsNullOrWhiteSpace(_width); + bool hasH = !string.IsNullOrWhiteSpace(_height); + + if (hasW || hasH) + { + // 只要设置了任意一个,就手动赋值 width/height 属性 + output.Attributes.SetAttribute("width", hasW ? _width : _height); + output.Attributes.SetAttribute("height", hasH ? _height : _width); + + // 设置类名(不添加默认响应式类) + output.Attributes.SetAttribute("class", _className.ToResponsiveClass()); + } + else + { + // 均未设置,则应用响应式默认类名并去重 + output.Attributes.SetAttribute("class", _className.ToResponsiveClass()); + } + + // 3. 构建内部内容 + string finalPath = string.IsNullOrWhiteSpace(_customPath) + ? UIConstants.Paths.DefaultIconSprite + : _customPath; + + output.Content.SetHtmlContent($@""); + } + } +} diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..89519699e9f493c0bf9d12c9fef8c715efe644c4 GIT binary patch literal 57207 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 literal 0 HcmV?d00001