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 0000000..8951969
Binary files /dev/null and b/favicon.ico differ