Merge branch 'main' of http://10.1.1.5:8418/acdiu/AcdiuTools
This commit is contained in:
127
TagHelpers/BsIconTagHelper.cs
Normal file
127
TagHelpers/BsIconTagHelper.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AcdiuTools.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see langword="Bootstrap Icons"></see> SVG Sprite 封装标签
|
||||
/// 用法: <bsicon i="heart-fill" w="16" h="16" f="red" />
|
||||
/// </summary>
|
||||
[HtmlTargetElement("bsicon")]
|
||||
public class BsIconTagHelper : TagHelper
|
||||
{
|
||||
// 路径指向你存放总 SVG 的位置
|
||||
/// <summary>
|
||||
/// 表示用于引用图标符号的 <see langword="Bootstrap Icons"></see> SVG矢量图库文件的相对路径
|
||||
/// </summary>
|
||||
/// <remarks>此路径应指向包含所有 <see langword="Bootstrap"></see> 图标定义的SVG文件<br/>
|
||||
/// 如果图标库被移动或升级,则需要更新该值</remarks>
|
||||
const string spritePath = "/lib/bootstrap-icons-1.13.1/bootstrap-icons.svg";
|
||||
|
||||
/// <summary>
|
||||
/// 图标名称 (必填)
|
||||
/// </summary>
|
||||
[HtmlAttributeName("i")]
|
||||
public required string IconName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 宽度 (默认为空,若为空时设置了高度,则使用高度的值,否则为元素 ClassName 添加 w-1r 以使其默认宽度为 1rem)
|
||||
/// </summary>
|
||||
[HtmlAttributeName("w")]
|
||||
public string Width { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 高度 (默认为空,若为空时设置了宽度,则使用宽度的值,否则为元素 ClassName 添加 h-1r 以使其默认高度为 1rem)
|
||||
/// </summary>
|
||||
[HtmlAttributeName("h")]
|
||||
public string Height { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 填充颜色 (默认 currentColor 即当前元素颜色)
|
||||
/// </summary>
|
||||
[HtmlAttributeName("f")]
|
||||
public string Fill { get; set; } = "currentColor";
|
||||
|
||||
/// <summary>
|
||||
/// 自定义 class 名称,允许用户添加额外的样式类 (默认空)
|
||||
/// </summary>
|
||||
[HtmlAttributeName("cn")]
|
||||
public string ClassName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的图标名称和属性,处理标签助手以渲染SVG矢量图标
|
||||
/// </summary>
|
||||
/// <remarks>若未指定图标名称或为空,则输出被抑制且不生成SVG<br/>
|
||||
/// 渲染后,该方法会将原始标签替换为<svg>元素并设置标准的SVG属性<br/>
|
||||
/// 包括类名、宽度、高度、填充颜色和图标ID名称。该SVG引用了 <see langword="Bootstrap Icons"></see> 中的图标<br/>
|
||||
/// 使用<use>元素引用的矢量图形</remarks>
|
||||
/// <param name="context">用于标签辅助程序执行的上下文,包含当前HTML标签及其属性的相关信息</param>
|
||||
/// <param name="output">标签辅助器的输出,用于修改渲染的HTML元素及其内容</param>
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(IconName))
|
||||
{
|
||||
output.SuppressOutput(); // 如果没写图标名,则不渲染
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 将外层标签替换为 <svg>
|
||||
output.TagName = "svg";
|
||||
output.TagMode = TagMode.StartTagAndEndTag;
|
||||
|
||||
// 2. 设置 SVG 基础属性
|
||||
// 如果设置了 w 或 h,则优先使用它们,否则使用 wh 的值
|
||||
// 设定机制:
|
||||
// 如果 w 和 h 中任意一个不为空,则使用不为空的 w 或 h 的值同时设置宽高;
|
||||
// 如果 w 和 h 都不为空,则分别使用它们设置宽高;
|
||||
// 如果 w 和 h 都为空,则为其添加默认的 w-1r 和 h-1r 类以设置默认宽高为 1rem
|
||||
if (!string.IsNullOrWhiteSpace(Width) || !string.IsNullOrWhiteSpace(Height))
|
||||
{
|
||||
// 如果用户设置了宽或高,则不添加默认的 w-1r 和 h-1r 类
|
||||
output.Attributes.SetAttribute("width", !string.IsNullOrWhiteSpace(Width) ? Width : Height);
|
||||
output.Attributes.SetAttribute("height", !string.IsNullOrWhiteSpace(Height) ? Height : Width);
|
||||
|
||||
// 继续添加用户自定义的 ClassName(如果有的话)
|
||||
output.Attributes.SetAttribute("class", MergeClassNames(string.IsNullOrWhiteSpace(ClassName) ? "" : $"{ClassName}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有设置宽高,则添加默认的 w-1r 和 h-1r 类并与用户自定义的 ClassName 合并
|
||||
// 如果提供了 ClassName,则判断是否为空,如果不为空则将其与默认的 w-1r 和 h-1r 类合并
|
||||
// 合并时使用 HashSet 来去重,确保不会重复添加相同的类名
|
||||
output.Attributes.SetAttribute("class", MergeClassNames(ClassName));
|
||||
}
|
||||
|
||||
output.Attributes.SetAttribute("fill", Fill);
|
||||
output.Attributes.SetAttribute("viewBox", "0 0 16 16"); // 保证矢量对齐
|
||||
//output.Attributes.SetAttribute("xmlns", "http://www.w3.org/2000/svg");
|
||||
|
||||
// 3. 构造内部的 <use> 节点
|
||||
var content = $@"<use xlink:href=""{spritePath}#{IconName}""></use>";
|
||||
|
||||
output.Content.SetHtmlContent(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并类名的方法,确保默认的 w-1r 和 h-1r 类与用户提供的 ClassName 合并且去重
|
||||
/// </summary>
|
||||
/// <param name="userClassName">用户提供的类名</param>
|
||||
/// <returns>合并且去重后的类名字符串</returns>
|
||||
public static string MergeClassNames(string userClassName)
|
||||
{
|
||||
// 定义默认的类列表
|
||||
var defaultClasses = new HashSet<string> { "w-1r", "h-1r" };
|
||||
// 如果用户提供了 ClassName,则将其拆分成单个类并添加到集合中
|
||||
if (!string.IsNullOrWhiteSpace(userClassName))
|
||||
{
|
||||
var userClasses = userClassName.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var cls in userClasses)
|
||||
{
|
||||
defaultClasses.Add(cls);
|
||||
}
|
||||
}
|
||||
// 将集合中的类名合并成一个字符串返回
|
||||
return string.Join(" ", defaultClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="~/lib/bootstrap-icons-1.13.1/font/bootstrap-icons.min.css" />
|
||||
<link rel="stylesheet" href="~/lib/bootstrap-icons-1.13.1\font\bootstrap-icons.min.css" />
|
||||
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/AcdiuTools.styles.css" asp-append-version="true" />
|
||||
@@ -29,10 +29,12 @@
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<!-- 添加 asp-active-link="true" 属性以指示启用自动激活检测 -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-active-link="true" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||
<a class="nav-link" asp-active-link="true" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<bsicon cn="p-r-t" i="house-fill" /> 首页
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-active-link="true" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
<a class="nav-link" asp-active-link="true" asp-area="" asp-controller="Home" asp-action="Privacy">隐私</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
/* 桌面: 1024px - 1920px */
|
||||
/* 4K屏: 3840px */
|
||||
/* --- 核心字号缩放逻辑 --- */
|
||||
/* 逻辑:在移动端最小为 14px,在 1024px 以上开始线性增长,到 4K 达到 24px */
|
||||
/* 公式:clamp(最小值, 首选值, 最大值) */
|
||||
/* 逻辑: 在移动端最小为 14px,在 1024px 以上开始线性增长,到 4K 达到 24px */
|
||||
/* 公式: clamp(最小值, 首选值, 最大值) */
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -63,3 +63,16 @@
|
||||
color: var(--bs-primary) !important;
|
||||
border-bottom: 2px solid var(--bs-primary);
|
||||
}
|
||||
|
||||
.p-r-t {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.w-1r {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.h-1r {
|
||||
height: 1rem;
|
||||
}
|
||||
Reference in New Issue
Block a user