试用前走查的修改:报错看得见、学生找得到作业、运行区和设计器的若干体验问题
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

+37 -7
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
// 按控件类型渲染一个 WinForms 控件及其子控件。
// 用户操作只通过 act() 上报,由运行器按真实 WinForms 的顺序触发事件。
import { computed, inject, provide, ref, type CSSProperties } from 'vue'
import { computed, inject, onBeforeUnmount, provide, ref, watch, type CSSProperties } from 'vue'
import type { ControlNode, UserAction } from '@iot/shared'
import { AMBIENT_BACK_COLOR, AMBIENT_BACK_TYPES, boxCss, DEFAULT_BACK_COLOR, displayText } from './format'
import { backgroundCss, imageSources, RUN_IMAGES, srcset, type ImageValue } from './images'
@@ -49,10 +49,12 @@ const passwordChar = computed(() => {
return ch && ch !== '\0' ? ch : ''
})
// ComboBox
// ComboBox:下拉列表挂到页面最外层(和真实 WinForms 一样可以伸出窗体),按控件在屏幕上的位置和缩放摆放
const dropDownOpen = ref(false)
const items = computed(() => (p.value.Items as string[] | undefined) ?? [])
const editable = computed(() => p.value.DropDownStyle !== 'DropDownList')
const combo = ref<HTMLElement>()
const listStyle = ref<CSSProperties>({})
function openDropDown() {
if (!enabled.value) return
@@ -60,11 +62,37 @@ function openDropDown() {
dropDownOpen.value = false
return
}
const el = combo.value
if (!el) return
const rect = el.getBoundingClientRect()
listStyle.value = {
left: `${rect.left}px`,
top: `${rect.bottom}px`,
width: `${el.offsetWidth}px`,
transform: `scale(${rect.width / el.offsetWidth || 1})`,
font: getComputedStyle(el).font,
}
// 先触发 Click(教材在 Click 里填充串口列表),列表随后按最新的 Items 显示
act({ action: 'dropDown' })
dropDownOpen.value = true
}
/** 点列表以外的地方、滚动页面时收起列表 */
function closeOnOutside(event: Event) {
const target = event.target as Node
if (combo.value?.contains(target) || (target instanceof Element && target.closest('.wf-combobox-list'))) return
dropDownOpen.value = false
}
watch(dropDownOpen, (open) => {
const method = open ? 'addEventListener' : 'removeEventListener'
window[method]('mousedown', closeOnOutside, true)
window[method]('scroll', closeOnOutside, true)
})
onBeforeUnmount(() => {
window.removeEventListener('mousedown', closeOnOutside, true)
window.removeEventListener('scroll', closeOnOutside, true)
})
function selectItem(index: number) {
dropDownOpen.value = false
act({ action: 'select', index })
@@ -130,7 +158,7 @@ const itemHeight = computed(() => `${(p.value.ItemHeight as number | undefined)
</div>
<!-- ComboBox -->
<div v-else-if="node.type === 'ComboBox'" class="wf-control wf-combobox" :style="style">
<div v-else-if="node.type === 'ComboBox'" ref="combo" class="wf-control wf-combobox" :style="style">
<input
:value="String(p.Text ?? '')"
:readonly="!editable"
@@ -139,10 +167,12 @@ const itemHeight = computed(() => `${(p.value.ItemHeight as number | undefined)
@input="act({ action: 'text', value: ($event.target as HTMLInputElement).value })"
/>
<span class="wf-combobox-arrow" @mousedown.prevent="openDropDown">▾</span>
<ul v-if="dropDownOpen" class="wf-combobox-list">
<li v-for="(item, index) in items" :key="index" @mousedown.prevent="selectItem(index)">{{ item }}</li>
<li v-if="items.length === 0" class="wf-combobox-empty">&nbsp;</li>
</ul>
<Teleport to="body">
<ul v-if="dropDownOpen" class="wf-combobox-list" :style="listStyle">
<li v-for="(item, index) in items" :key="index" @mousedown.prevent="selectItem(index)">{{ item }}</li>
<li v-if="items.length === 0" class="wf-combobox-empty">&nbsp;</li>
</ul>
</Teleport>
</div>
<!-- ListBox:点空白处只触发 Click,点某一项先选中再触发 Click -->