试用前走查的修改:报错看得见、学生找得到作业、运行区和设计器的若干体验问题
CI/CD / test (push) Canceled after 0s
CI/CD / deploy (push) Canceled after 0s

- 运行区:编译错误、运行异常、控制台输出和保存的图片移到窗体下面,不再被实训台挤成 0 高;缩放和放大按钮移到桌面上方,不压窗体的关闭按钮;放大后也能停止程序;点停止后清掉窗体
- 运行异常:常见 .NET 异常配中文说明,显示出错的文件、行号和方法,点击跳到代码
- 运行中的下拉列表挂到页面最外层,能伸出窗体和分组框(小窗体也能选到 COM4、COM5)
- 学生登录后进"我的作业";任务面板从工具栏下面开始,不挡导航;记住上次做的实验,从数据库页面返回、恢复数据时默认用它
- 代码页和设计页左栏都占 3/5,文件标签放不下时换行;实验选择框加宽;关闭粘性滚动
- 设计器:双击生成的事件方法带缩进;双击工具箱放进选中控件所在的容器并错开位置;新控件比容器宽时按比例缩小;无边框窗体不画标题栏
- 中文界面:代码编辑器(查找替换等)、属性窗口的颜色选项
- SQL:DATEADD、DATEDIFF、DATEPART 的日期单位(day、hour)解析成 IdentifierLiteral 时报"不支持",补上
- 项目 8 的预置环境数据改成建库那天之前的 7 天;p6-t3 评分项改为"按日期查询环境数据"
- 没有 AI 建议时不显示"全部采纳 AI 建议"和相关说明;导出 CSV 只给纯数字的文字加制表符

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-27 08:49:42 +08:00
1 parent da623fa648
commit f6024dc113
30 files changed
+537 -255

No files matched your search

+2 -1
View File
@@ -802,9 +802,10 @@ internal sealed class Translator
}
}
/// <summary>DATEADD(day, ...) 的第一个参数是关键字,解析成列名</summary>
/// <summary>DATEADD(day, ...) 的第一个参数是关键字:ScriptDom 解析成 IdentifierLiteral(旧版本是列名)</summary>
private static string DatePartName(ScalarExpression expression) => expression switch
{
IdentifierLiteral i => i.Value,
ColumnReferenceExpression c => c.MultiPartIdentifier.Identifiers[^1].Value,
StringLiteral s => s.Value,
_ => throw SqlException.NotSupported("这种 datepart 写法"),
+46 -4
View File
@@ -134,12 +134,54 @@ public static class SimHost
public static void ReportException(Exception ex)
{
while (ex is TargetInvocationException { InnerException: not null } wrapper) ex = wrapper.InnerException!;
Send(new JsonObject
var type = ex.GetType().FullName;
// 仿真库自己抛的异常信息已经是中文;.NET 自带的是英文,常见的几种配上中文说明,原文放在括号里
var message = HasChinese(ex.Message) ? $"{ex.Message}({type})"
: ExplainException(ex) is { } explained ? $"{explained}({type}: {ex.Message})"
: $"{type}: {ex.Message}";
var frame = StudentFrame(ex);
var result = new JsonObject
{
["type"] = "exception",
["message"] = $"{ex.GetType().FullName}: {ex.Message}",
["detail"] = StudentStackTrace(ex),
});
["message"] = message,
["detail"] = frame is { } f ? $"出错位置:{f.File} 第 {f.Line} 行({f.Method} 方法)" : StudentStackTrace(ex),
};
if (frame is { } at)
{
result["file"] = at.File;
result["line"] = at.Line;
}
Send(result);
}
private static bool HasChinese(string text) => text.Any(c => c >= '\u4e00' && c <= '\u9fff');
private static string? ExplainException(Exception ex) => ex switch
{
FormatException => "文字转换成数字或日期时格式不对,比如文本框是空的,或者里面不是数字",
NullReferenceException => "用到的对象是 null:变量还没有赋值,或者对象、窗体还没有 new 出来",
IndexOutOfRangeException => "数组的下标超出了范围:下标从 0 开始,最大是长度减 1",
ArgumentOutOfRangeException => "下标或参数超出了范围,比如列表、字符串的下标太大,或者下拉框还没有选中",
DivideByZeroException => "整数除以了 0",
OverflowException => "数字太大或太小,超出了这个类型能表示的范围",
InvalidCastException => "类型转换失败,这个对象不能转换成要求的类型",
KeyNotFoundException => "字典里没有这个键",
FileNotFoundException => "找不到这个文件",
DirectoryNotFoundException => "找不到这个文件夹",
_ => null,
};
/// <summary>堆栈里离出错点最近的一行学生代码:文件名、行号、方法名</summary>
private static (string File, int Line, string Method)? StudentFrame(Exception ex)
{
foreach (var frame in new System.Diagnostics.StackTrace(ex, true).GetFrames())
{
var file = frame.GetFileName();
var line = frame.GetFileLineNumber();
if (file == null || line <= 0 || file.Contains("/runtime/Sim.")) continue;
return (Path.GetFileName(file), line, frame.GetMethod()?.Name ?? "");
}
return null;
}
/// <summary>只保留学生代码的堆栈行,去掉仿真库内部的调用</summary>