统一 Widget GetData 返回类型为公开类 KpiData 将多个 Widget 组件(如 BookCountWidget、DocCountWidget、NavCountWidget、TodayDocsWidget、TotalHitsWidget、WordCountWidget)的 GetData 方法返回类型由匿名对象统一替换为新公开类 KpiData,解决跨程序集 dynamic 绑定时因匿名类型 internal 导致的 RuntimeBinderException。KpiData 定义 Value、Trend、Url 属性,作为 KPI 卡片的数据契约,便于魔方 Dashboard 视图渲染。大石头 authored at 2026-09-02 19:03:34
diff --git a/NewLife.YuqueWeb/Widgets/BookCountWidget.cs b/NewLife.YuqueWeb/Widgets/BookCountWidget.cs
index e6ed7ea..6dd49f8 100644
--- a/NewLife.YuqueWeb/Widgets/BookCountWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/BookCountWidget.cs
@@ -14,7 +14,7 @@ public class BookCountWidget : IWidget
var books = Book.GetValids().Count;
var groups = Group.Meta.Count;
- return new
+ return new KpiData
{
Value = books.ToString("n0"),
Trend = $"知识组 {groups} 个",
diff --git a/NewLife.YuqueWeb/Widgets/DocCountWidget.cs b/NewLife.YuqueWeb/Widgets/DocCountWidget.cs
index 53a3131..3edb10d 100644
--- a/NewLife.YuqueWeb/Widgets/DocCountWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/DocCountWidget.cs
@@ -12,7 +12,7 @@ public class DocCountWidget : IWidget
{
var (enable, pub) = Document.GetCounts();
- return new
+ return new KpiData
{
Value = enable.ToString("n0"),
Trend = $"公开 {pub} 篇",
diff --git a/NewLife.YuqueWeb/Widgets/KpiData.cs b/NewLife.YuqueWeb/Widgets/KpiData.cs
new file mode 100644
index 0000000..91d71c2
--- /dev/null
+++ b/NewLife.YuqueWeb/Widgets/KpiData.cs
@@ -0,0 +1,16 @@
+namespace NewLife.YuqueWeb.Widgets;
+
+/// <summary>KPI 小卡数据。工作台 KPI 组件返回的公开数据模型,承载 { Value, Trend, Url } 渲染契约。
+/// 必须是公开类型:匿名类型编译为 internal,业务程序集返回匿名对象时,魔方 Dashboard 视图
+/// (NewLife.Cube.dll)跨程序集 dynamic 绑定会抛 RuntimeBinderException('object' 不含 'Value')</summary>
+public class KpiData
+{
+ /// <summary>主数值</summary>
+ public String Value { get; set; }
+
+ /// <summary>趋势描述。可为简单 HTML 片段</summary>
+ public String Trend { get; set; }
+
+ /// <summary>点击跳转地址</summary>
+ public String Url { get; set; }
+}
diff --git a/NewLife.YuqueWeb/Widgets/NavCountWidget.cs b/NewLife.YuqueWeb/Widgets/NavCountWidget.cs
index b092663..fbb86f4 100644
--- a/NewLife.YuqueWeb/Widgets/NavCountWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/NavCountWidget.cs
@@ -12,7 +12,7 @@ public class NavCountWidget : IWidget
{
var (enable, total) = Nav.GetCounts();
- return new
+ return new KpiData
{
Value = enable.ToString("n0"),
Trend = $"共 {total} 项",
diff --git a/NewLife.YuqueWeb/Widgets/TodayDocsWidget.cs b/NewLife.YuqueWeb/Widgets/TodayDocsWidget.cs
index cf7395d..f55d4e2 100644
--- a/NewLife.YuqueWeb/Widgets/TodayDocsWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/TodayDocsWidget.cs
@@ -12,7 +12,7 @@ public class TodayDocsWidget : IWidget
{
var count = Document.GetTodayCount();
- return new
+ return new KpiData
{
Value = count.ToString(),
Trend = "今日内容更新",
diff --git a/NewLife.YuqueWeb/Widgets/TotalHitsWidget.cs b/NewLife.YuqueWeb/Widgets/TotalHitsWidget.cs
index 80a4b05..47da910 100644
--- a/NewLife.YuqueWeb/Widgets/TotalHitsWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/TotalHitsWidget.cs
@@ -12,7 +12,7 @@ public class TotalHitsWidget : IWidget
{
var (_, hits, localHits) = Document.GetScale();
- return new
+ return new KpiData
{
Value = hits.ToString("n0"),
Trend = $"本站点击 {localHits}",
diff --git a/NewLife.YuqueWeb/Widgets/WordCountWidget.cs b/NewLife.YuqueWeb/Widgets/WordCountWidget.cs
index 116f3da..d85cc17 100644
--- a/NewLife.YuqueWeb/Widgets/WordCountWidget.cs
+++ b/NewLife.YuqueWeb/Widgets/WordCountWidget.cs
@@ -14,7 +14,7 @@ public class WordCountWidget : IWidget
// 超过1万字用万字展示,否则原样
var value = words >= 10_000 ? (words / 10_000d).ToString("0.0") + "万字" : words.ToString("n0");
- return new
+ return new KpiData
{
Value = value,
Trend = "正文字数合计",