千问设计蓝图 + Trae 交付成品:WPF 通信监控控制台诞生记

千问设计蓝图 + Trae 交付成品:WPF 通信监控控制台诞生记

项目:BridgeServer(通信监控控制台)|技术栈:ASP.NET Core MVC(net8.0)+ Bootstrap5 + jQuery
日期:2026-08-13


写在前面

这篇博客记录的是一次让我自己都感到意外的开发体验:我先让千问(通义千问)把模糊需求具象化为 5 界面蓝图,再把蓝图丢给 Trae,几分钟内得到完整可运行的 ASP.NET Core MVC 程序。全程我只做「传话筒 + 决策者」,代码一行没写。下面是完整实录。

本文是开发过程的真实记录。文中「我」指代作者(人),「千问」「Trae」分别代表两个 AI 助手。所有代码、截图均为本次实际产出。


一、蓝图的诞生:和千问的两轮对话

第一轮:模板化回答,不行

需求其实很简单:写一个 Web 程序读写本地 WPF 程序的数据,能通信。但我特别强调了一点——界面不能是 TextBox+Button 的敷衍货。我给它设定了三个 UI 架构师角色和一堆约束(全局布局 / 至少 3 个核心页面 / 工业风配色 / 防呆反馈机制)。结果它回了我一套「B 端 UI 架构师 Skill」——全是抽象术语和话术模板,还教我「你可以这样对 Trae 说:……」,就是没告诉我界面长什么样。

我问的是「程序需要哪些界面、每个界面长什么样」,它给我的是「怎么用话术让别的 AI 去做」。

我较真回去的一句话

你写的这个并不是我想要的,这个程序难道只有一个界面吗,每个界面的功能是什么,假设有 5 个界面,每个界面都是一个 TextBox 和一个按钮吗,我需要的是界面长什么样

「难道每个界面都是一个 TextBox 和一个 Button 吗?」——这句话是整个故事的转折点。把它砸回去之后,对方才从「模板话术」档位切换到了「真出蓝图」档位。

第二轮:道歉,交出高质量蓝图

它立刻道歉:「抱歉,我之前的描述确实过于抽象了,没有让你直观地看到界面的『长相』」。然后给出了一份具象化的蓝图——6 个部分(1 个全局框架 + 5 个核心界面),每个界面都用「长相」+「功能」两段描述,把布局结构、组件类型、交互方式全写死了。最后还加了一条 视觉与交互规范,把「专业感间距」「Loading 态」「Toast 反馈」写了进去。

这份蓝图,就是我接下来交给 Trae 的原始输入。它不是我自己写的——是我较真追问之后,对方才认真写出来的。跟 AI 打交道这件事,有时候你得把要求说死,它才会出好东西。


二、需求蓝图:我交给 Trae 的原始描述

千问最终产出的蓝图共 6 段:1 个全局框架 + 5 个核心界面。每段都用「长相」+「功能」双段式描述,把布局结构、组件类型、交互方式写死,并加了一条严禁出现「一个 TextBox + 一个 Button」的简陋布局的禁用条款。

完整蓝图原文见文末附录。


三、Trae 的第一反应:不是急着写代码,而是先问

把这份蓝图原文复制粘贴给 Trae 后,我以为它会立刻开始 dotnet new 建项目、啪啪啪写代码。结果它做的第一件事,是先摸清现状(这个项目其实已经存在,是从 Blazor 模板改造成 MVC 的半成品,有一堆占位页),然后反问我三个关键决策

这三个问题问得相当到位,都是「不问清楚就会返工」的架构级决策:

问题 Trae 给出的选项 我的选择 为什么
项目名/命名空间当前是 BridgeServer,要改吗? 保留 / 改 TangdaoConsole / 改 BridgeConsole 保留 BridgeServer 只改 Controller 和 View 命名即可,改动最小
蓝图里反复提到「WPF 通信」,数据来源怎么处理? 先用 Mock 假数据 / 真接 WPF 程序 先用 Mock 假数据 先把 UI 和交互做完整,WPF 真实对接留作接口预留
5 个界面的视觉主题用哪种? 科技深色 / 保留小米橙 / 专业浅色蓝灰 科技深色主题 工业监控大屏风格,专业感强

这三个决策一锁,后面的实现路径就清晰了。我后来回想,这一步其实是整个流程里最值钱的一步——它决定了后面所有代码的方向,避免了「写完一半发现方向错了」的灾难。

小结:好的 AI 助手不会接到任务就闷头干,它会先确认方向。如果 Trae 上来就写代码、不问问题,反而要警惕——它可能在用「看起来很忙」掩盖「没想清楚」。


四、架构设计:Trae 给出的方案

确认决策后,Trae 进入了「计划模式」,先用一个子代理(它管这叫 Plan agent)通读了整个项目,确认了「6 个 Controller 几乎全是占位页」这个现状,然后给出了一份完整的设计方案。我挑核心的说:

4.1 命名映射:旧瓶换新酒

项目本身(BridgeServer 命名空间、csproj、slnx)不动,只把 Controller 和 View 的命名从「文件处理工具」语义改成「监控控制台」语义:

旧 Controller 新 Controller 对应蓝图界面
ConvertController DashboardController 设备总览仪表盘
CodecController ConfigurationController 参数配置中心
FormatController DataMonitorController 实时数据监控台
GenerateController CommandController 指令下发控制台
TextProcessController HistoryController 历史报警与日志

旧的 5 个 Controller 和对应 View 全删,新建 5 个。HomeController.Index 的重定向目标从 Convert 改到 Dashboard

4.2 全局框架:Flexbox 实现 T 型布局

布局选了 Flexbox 而非 CSS Grid。理由很实在:T 型布局是「顶部横条 + 下方左右分栏」,Flex 表达更直观,主工作区独立滚动好控制。

┌─────────────────────────────────────────────┐
│ 状态栏(品牌名 │ 时钟 │ 通信灯+用户名) 52px │
├──────────┬──────────────────────────────────┤
│ 侧边栏   │                                  │
│ 220px    │      主工作区(独立滚动)         │
│ 5个导航  │      @RenderBody()               │
│          │                                  │
└──────────┴──────────────────────────────────┘

状态栏的时钟用 JS 每秒更新(服务端只渲染初始值避免首屏空白),通信灯每 2 秒轮询一次 /Api/System/Status

4.3 视觉主题:保留 Bootstrap5 + 变量覆盖

这是个很聪明的决策。它没有弃用 Bootstrap 从零手写 CSS(那样工作量翻 2-3 倍),而是用 Bootstrap 5.3 原生的 data-bs-theme="dark" 深色模式做基底,再通过覆盖 --bs-* CSS 变量注入青蓝色板。这样栅格、表单、表格、模态框开箱即用,深色主题只需 ~100 行 CSS 微调。

核心色板:

:root[data-bs-theme="dark"] {
  /* 背景层级(由深到浅) */
  --bg-base: #0a0e1a;      /* 主工作区底色 */
  --bg-sidebar: #0d1320;   /* 侧边栏 */
  --bg-surface: #111827;   /* 卡片 */
  --bg-elevated: #1a2332;  /* 表头/悬浮 */
  /* 强调色(青蓝高亮) */
  --accent-primary: #00d4ff;
  --accent-glow: rgba(0, 212, 255, 0.35);
  /* 状态色 */
  --status-success: #00ff88;
  --status-error: #ff4757;
  --status-warning: #ffa502;
}

4.4 MockDataService:单例 + 线程安全

数据层是一个 MockDataService 单例,用 lock 保护。设计上很注意「为将来真实对接 WPF 留口子」——所有方法都是独立的,将来真实对接时只需替换方法实现,前端零改动

Mock 数据怎么体现「实时变化」?这是我觉得设计得最巧的地方:

  • Dashboard 6 指标:每个用 Queue<double> 维护最近 20 个点的滑动窗口,每次被调用取末值 ±5% 随机扰动追加
  • DataMonitor:每行通道值每次调用 ±3% 扰动
  • Command 执行GetCommandProgress 每次被前端轮询调用时推进进度,不用后台线程(避免线程清理问题)
  • History:构造函数预生成 200 条日志(过去 7 天随机分布),查询时 LINQ 过滤 + Skip/Take 分页
  • 通信状态灯:默认绿灯,加 5% 随机断连概率,演示红灯告警效果

五、实现过程:四个阶段一气呵成

方案确认后,AI 按四个阶段推进,每阶段都用了「并行写多个独立文件」的策略来提速。

阶段 1:基础设施层(4 个文件并行)

最先搞定的是地基:全局样式、布局、脚本、启动配置。

  • [wwwroot/css/site.css](file:///d:/TraeProject/BridgeServer/BridgeServer/wwwroot/css/site.css) — 整文件重写为科技深色主题,包含全局框架、卡片、表格、表单、按钮(含 Loading 态)、Toast 全套样式
  • [Views/Shared/_Layout.cshtml](file:///d:/TraeProject/BridgeServer/BridgeServer/Views/Shared/_Layout.cshtml) — T 型布局骨架,状态栏 + 侧边栏 + 主工作区
  • [wwwroot/js/site.js](file:///d:/TraeProject/BridgeServer/BridgeServer/wwwroot/js/site.js) — 全局工具:showToast / showLoading / fetchJson / startClock / startConnPoll / PollerManager(防 setInterval 泄漏)
  • [Program.cs](file:///d:/TraeProject/BridgeServer/BridgeServer/Program.cs) — 注册 MockDataService 单例 + 末尾加 fallback 重定向

Program.cs 里这个兜底路由我觉得是点睛之笔:

// 兜底:未匹配的旧路由(如 /Convert)重定向到 Dashboard,避免 404
app.MapFallback("/{*path}", context =>
{
    context.Response.Redirect("/Dashboard");
    return Task.CompletedTask;
});

因为旧的 /Convert/Codec 等路由全删了,如果有收藏夹或外链会 404。加这一行,所有未匹配路径都跳到 Dashboard,体验干净。

阶段 2:数据层(6 个 DTO 文件 + 1 个服务)

按界面分子目录建 DTO,每个界面一个文件(C# 一个文件可放多个类):

  • Models/Dashboard/DashboardCardDto.cs
  • Models/Configuration/ConfigurationDto.cs(含 ConfigGroupDtoConfigItemDto
  • Models/DataMonitor/MonitorRowDto.cs(含 MonitorDetailDto
  • Models/Command/CommandDto.cs(含 CommandProgressDtoLogLineDto
  • Models/History/HistoryLogDto.cs(含 HistoryFilterDtoPagedResultDto<T>
  • Models/System/SystemStatusDto.cs

然后是核心的 [Services/MockDataService.cs](file:///d:/TraeProject/BridgeServer/BridgeServer/Services/MockDataService.cs),前面 4.4 节已介绍。

阶段 3:Controller 层(删旧 + 新建并行)

一次性删掉 5 个旧 Controller + 15 个旧 View 文件,同时并行创建 7 个新 Controller。每个 Controller 都很薄——页面 Action 返回 View,数据 Action 返回 JsonResult

public class DashboardController : Controller
{
    private readonly MockDataService _mock;
    public DashboardController(MockDataService mock) { _mock = mock; }

    public IActionResult Index() { return View(); }

    // 卡片数据(前端每 3 秒轮询)
    public IActionResult GetCards() { return Json(_mock.GetDashboardCards()); }
}

特别注意那个 ApiSystemController——专门给状态栏通信灯轮询用,单独抽出来不污染业务 Controller,且用特性路由 [Route("Api/System")] 挂在 /Api/System/Status

阶段 4:View 层(5 个界面,由简到繁)

这是最能体现蓝图落地的一步。每个 View 都是 HTML 骨架 + 末尾内联 JS(用 _Layout@RenderSection("Scripts"))。每个页面脚本开头都有一行 PollerManager.clearAll() 清理上一页的轮询,避免 setInterval 累积泄漏——这个细节很专业。


六、关键技术点解析

这一节挑几个我认为有技术含量的点展开。

6.1 Dashboard 趋势折线:手绘 SVG 而非图表库

这个决策让我意识到,引入第三方库之前先想想「最小可行方案」是什么。200KB 的 Chart.js 和 40 行原生 JS,后者胜出。

蓝图要求每个卡片有「小型趋势折线图」。Trae 选了手绘 SVG,而不是引入 Chart.js(~200KB)或 ApexCharts(~400KB)。理由很充分:

  • 每卡片只需 20 个点的小折线,<polyline> 用 JS 生成 points 即可
  • 无需引入大依赖,深色描边色用 stroke="var(--accent-primary)" 完全可控
  • 多卡片场景 SVG 性能远优于 Canvas 图表库
  • 后续若需复杂交互(tooltip/缩放)再切换 Chart.js,容器 class 已预留

核心绘制函数就几十行(宽高与卡片内 SVG 容器的 CSS 尺寸匹配,Mock 场景下无需响应式):

function drawTrend(svgEl, points, max, min) {
    if (!svgEl || !points || points.length === 0) return;
    var w = 200, h = 40, pad = 4;
    var range = (max - min) || 1;
    var step = (w - pad * 2) / (points.length - 1);
    var coords = points.map(function (v, i) {
        var x = pad + i * step;
        var y = h - pad - ((v - min) / range) * (h - pad * 2);
        return x.toFixed(1) + ',' + y.toFixed(1);
    }).join(' ');
    // ...赋给 <polyline>,加 drop-shadow 发光
}

实测 6 个折线全部渲染,每个 20 个点,效果带青蓝辉光,符合工业大屏审美。

6.2 轮询管理器 PollerManager:防止 setInterval 泄漏

这是个「不写不会出 bug,写了才知有多坑」的细节——单页应用切换路由时,旧定时器如果不清理,CPU 会静默飙升。

Dashboard 每 3 秒轮询、DataMonitor 每 2-3 秒轮询、Command 每 0.5 秒轮询……如果不清理,切换页面时旧的 setInterval 还在跑。Trae 封装了一个 PollerManager

var PollerManager = {
    _timers: {},
    register: function (key, fn, ms) { this.clear(key); this._timers[key] = setInterval(fn, ms); },
    clear: function (key) { /* clearInterval + delete */ },
    clearAll: function () { /* 清理所有 */ }
};

每个页面脚本开头 PollerManager.clearAll(),进入新页面前先清旧页面的轮询。这是有工程经验的处理方式。

6.3 配置保存:合并而非覆盖

这个坑很隐蔽——前端 DTO 和后端 DTO 字段不对齐时,直接覆盖会丢元数据。「合并更新」是更安全的姿势。

参数配置中心的「保存」有个小陷阱:前端表单只回传 key/value,但后端 ConfigItemDto 还有 labeloptionsmin/max 等元数据。如果直接用前端 DTO 覆盖,下次渲染就丢失 label 了。

Trae 的处理是合并更新——只更新 Value,保留其他元数据:

public bool SaveConfiguration(ConfigurationDto dto)
{
    lock (_lock)
    {
        foreach (var newGroup in dto.Groups)
        {
            var existGroup = _configuration.Groups.FirstOrDefault(g => g.Key == newGroup.Key);
            if (existGroup == null) continue;
            foreach (var newItem in newGroup.Items)
            {
                var existItem = existGroup.Items.FirstOrDefault(i => i.Key == newItem.Key);
                if (existItem != null) existItem.Value = newItem.Value;  // 只更新值
            }
        }
        return true;
    }
}

6.4 历史分页:后端分页而非前端

日志系统迟早会涨到数万条。前端一次性拉全量再分页,首屏会卡死。后端分页是这类场景的标配。

History 预生成 200 条,Trae 选了后端分页Where().Skip().Take() 返回当前页 + 总数)。前端只持有当前页数据,翻页重新 fetch,首页/末页按钮自动禁用。

6.5 Command 进度:前端轮询驱动推进

后台线程推进进度看似直观,但线程清理、生命周期管理都是隐患。让「轮询本身」驱动进度推进,是个反直觉但极简的方案。

指令执行的进度条,Trae 没有用后台线程(Task.Run 推进进度会有线程清理问题),而是让 GetCommandProgress 每次被前端轮询调用时推进 10%。前端每 500ms 轮询一次,进度 0→10→20…→100,推进节奏天然由轮询驱动,无后台线程依赖。


七、验证与成果

7.1 编译验证

$ dotnet build
已成功生成。
    0 个警告
    0 个错误

干净利落。

7.2 逐页验证结果

启动 dotnet run,浏览器打开 http://localhost:5046,首页自动跳转 /Dashboard。逐页验证:

界面 验证项
全局 左侧 5 导航可切换;时钟每秒跳动;通信灯绿显示「(模拟)」
Dashboard 6 卡片数据渲染(温度49.1℃/电压356.4V/转速2952.9RPM…);6 个 SVG 折线每个 20 点
Configuration 2 分组(电机/传感器);7 数字输入 + 3 开关 + 2 下拉;保存/恢复按钮齐全
DataMonitor 8 行数据;6 排序表头;16 行按钮(8×2);自动刷新开关
Command 点击指令→进度条 0→60%→100%;日志滚动 12 行;成功 Toast
History 20 条/页;分页 1/10;筛选+查询+翻页均可用
路由 fallback 访问旧路径 /Convert 自动跳 /Dashboard

7.3 成果截图

5 个界面的实际运行截图(科技深色主题,青蓝高亮):

界面一:设备总览仪表盘

设备总览仪表盘

6 个数据卡片栅格排列,每个含超大青蓝数值、迷你折线图(带辉光)和趋势百分比。

界面二:参数配置中心

参数配置中心

电机参数组、传感器阈值组两个带边框卡片,数值调节器/开关/下拉混排,底部双按钮。

界面三:实时数据监控台

实时数据监控台

数据表格占满工作区,斑马纹 + 排序表头 + 行内「刷新/详情」图标按钮。

界面四:指令下发控制台(执行中)

指令下发控制台

左右分栏:左侧快捷按钮 + 指令列表;右侧进度条 + 日志滚动区(分级别着色)。

界面五:历史报警与日志

历史报警与日志

顶部筛选区(日期范围 + 级别 + 关键字 + 查询),底部数据表格 + 分页器。


八、整个过程的时间感

我特意留意了一下整个流程的节奏,大致是这样的:

  1. 敷衍 → 我较真追问 → 出蓝图:约 10 分钟,核心产出是那份道歉后重写的 6 段式蓝图
  2. 蓝图输入:把蓝图原样贴进 Trae
  3. 现状摸排 + 三个决策反问:Trae 先读项目、再问命名/数据源/主题三个问题
  4. 进入计划模式:子代理通读全项目,产出含命名映射表、CSS 方案、Mock 设计的完整方案
  5. 批准方案后执行:四个阶段并行推进——基础设施 4 文件 → 数据层 7 文件 → Controller 7 个 → View 5 个
  6. 编译:通过(0 错误 0 警告)
  7. 运行验证:浏览器逐页点一遍,全部通过

从「我开始聊需求」到「看到 5 个能跑的界面」,整个过程大约30 分钟。从把蓝图贴进 Trae 到交付成品,没有一次返工——我以为至少得来回改几轮样式、修几个 bug,结果一次过。


九、几点感悟

写完这篇复盘,我有几个挺深的感触,分享出来:

1. 蓝图越具体,AI 越不会「应付」

千问在蓝图里写死了「严禁一个 TextBox + 一个 Button 的简陋布局」,并给每个界面定义了具体的「长相」和「功能」。结果 Trae 交付的每个界面都是真正有结构感的布局。AI 不是不会做复杂布局,是它需要你明确表达「我要复杂的」。如果你只说「做个页面」,它默认给你最简方案——那不是它偷懒,是它在按「最稳妥」的方式回应模糊指令。而把一句「难道每个界面都是一个 TextBox 和一个 Button 吗?」砸回去之后,对方立刻从「话术模板」档位切换到了「真出蓝图」档位——跟 AI 沟通,较真比客气有用。

2. 好的 AI 协作是「对话」而非「指令」

这次最值钱的一步不是写代码,而是 Trae 反问我的那三个决策。如果我当初没被问,直接让它「做」,它大概率会自己拍板(比如选保留小米橙主题、选 WebSocket),后面我看了不满意再返工。「先确认方向再动手」这件事,AI 比很多人做得还好——因为它不会嫌问问题烦。

3. 那些我没要求、但 Trae 主动加的细节

有几个地方 Trae 是主动做的,不在蓝图里,但都很专业:

  • PollerManager 防 setInterval 泄漏
  • MapFallback 兜底路由防 404
  • Mock 通信灯 5% 随机断连演示告警
  • 配置保存用合并而非覆盖(保住 label 元数据)
  • SVG 折线带 drop-shadow 辉光

这些细节是「有经验的工程师才会想到」的,Trae 主动补上了。这是让我觉得「它比我厉害」的地方——不是它写得快,是它想得周全。

4. Mock 数据不是「假数据」,是「接口契约」

这次 Mock 数据层的设计让我意识到一件事:好的 Mock 不是随便编点假数据,而是把将来真实对接的接口契约先定下来。所有 GetDashboardCards()QueryHistory(filter) 这些方法签名,将来真实对接 WPF 时直接换成真实实现就行,前端一行不改。这让「先用 Mock 跑通 UI」和「将来真实对接」变成了平滑过渡,而不是推倒重来。


十、结语

这次开发让我对「AI 辅助编程」有了新的认识。以前我总觉得 AI 是「打字快的实习生」——你说一句它写一句,错了你改。但这次协作,一个出蓝图、一个写代码,更像「会先问清楚需求、会自己挑技术方案、还会主动补细节的合作者」。

后来我想通了:我的价值不在「写代码」,在「定义清楚要做什么」。蓝图是千问出的,三个决策是我拍的,验收标准是我定的。它们把执行部分加速了,但「做什么」和「做成什么样」还是我说了算。

这大概就是 AI 时代开发者的新定位——从「码农」变成「产品定义者 + 验收者」。会写需求、会做决策、会判断好坏,比会敲代码重要得多。

如果你想自己跑一遍,启动方式:

cd d:\TraeProject\BridgeServer\BridgeServer
dotnet run
# 浏览器打开 http://localhost:5046

完整项目结构见仓库 README,核心改动是 5 个业务 Controller + 1 个 ApiSystemController + MockDataService 单例 + 5 个 Index.cshtml 视图,外加 _Layout.cshtml(T 型布局)和 site.css(科技深色主题)。

项目源码:d:\TraeProject\BridgeServer
博客配图:d:\TraeProject\BridgeServer\docs\images\


写于 2026-08-13,一次让我对 AI 编程改观的开发实录。


附录:完整蓝图原文(供参考)

这份蓝图是对方在第一轮敷衍、被我较真追问后,第二轮道歉重写的版本。我原文复制粘贴给了 Trae。

1. 全局框架布局(Global Shell)

  • 长相:采用经典的「左侧边栏导航 + 顶部状态栏 + 右侧主工作区」的 T 型布局。
  • 功能:左侧边栏提供 5 个界面的切换菜单;顶部状态栏始终悬浮,显示当前时间、WPF 通信状态指示灯(绿/红)及当前登录用户。

2. 界面一:设备总览仪表盘(Dashboard)

  • 长相:采用 卡片式栅格布局。页面由 4-6 个矩形的「数据状态卡片」组成(类似智能家居控制面板)。
  • 功能:用于只读展示 WPF 中的核心实时数据。每个卡片内包含:标题(如「当前温度」)、超大字号的核心数值、以及一个小型的趋势折线图。数据通过 WebSocket 或定时轮询自动刷新,无需任何手动输入框。

3. 界面二:参数配置中心(Configuration)

  • 长相:采用 分组表单布局(Grouped Form)。摒弃单一输入框,将参数按业务分组(如「电机参数组」「传感器阈值组」),每组使用带边框的卡片包裹。
  • 功能:用于向 WPF 批量写入配置。包含多种控件:数值调节器(InputNumber)、开关(Switch)、下拉选择器(Select)。底部配备「保存配置」和「恢复默认」两个大按钮。

4. 界面三:实时数据监控台(Data Monitor)

  • 长相:采用 数据表格布局(DataGrid / Table)。占据整个主工作区。
  • 功能:用于查看 WPF 内存中的大量结构化数据(如传感器阵列状态、队列数据)。表头支持排序,表格支持斑马纹显示。每行的最右侧提供「手动刷新」和「详情查看」图标按钮。

5. 界面四:指令下发控制台(Command Console)

  • 长相:采用 左右分栏布局。左侧为「指令列表/快捷操作面板」,右侧为「指令执行详情与反馈区」。
  • 功能:用于触发 WPF 中的特定动作(如:启动测试、清空缓存、导出日志)。左侧提供多个不同样式的操作按钮,右侧展示执行进度条、日志滚动输出或成功/失败的弹窗反馈。

6. 界面五:历史报警与日志(History Logs)

  • 长相:采用 顶部筛选区 + 底部列表区 的布局。
  • 功能:展示从 WPF 同步过来的历史数据或报警记录。顶部包含:时间范围选择器(Date Picker)、状态下拉框、关键字搜索框和「查询」按钮。底部是带有分页控件(Pagination)的数据表格。

视觉与交互规范

  • 专业感:所有输入和展示控件必须对齐,使用合理的间距(Margin/Padding)。
  • 状态反馈:所有涉及向 WPF 写入数据的按钮,在点击后必须显示 Loading 状态,并在成功后给出全局消息提示(Toast)。
posted @ 2026-08-14 01:08  孤沉  阅读(18)  评论(0)    收藏  举报