From 61a32e900dfdd494914f069d95ab370bc0935629 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 26 Oct 2026 21:39:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E4=B8=8B=E6=8B=89=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=8C=89=E9=A1=B9=E7=9B=AE=E5=88=86=E7=BB=84=EF=BC=9B?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF=E5=9B=BE=E6=9B=B4=E6=96=B0=E5=88=B0=E5=B7=B2?= =?UTF-8?q?=E4=B8=8A=E7=BA=BF=E3=80=81=E5=BE=85=E4=B8=80=E4=B8=AA=E7=8F=AD?= =?UTF-8?q?=E8=AF=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/设计方案.md | 8 ++++--- web/src/pages/DatabasePage.vue | 3 ++- web/src/pages/WorkbenchPage.vue | 17 ++++++++++---- web/src/samples.ts | 34 ++++++++++++++++++++++++++++ web/src/teacher/AssignmentDialog.vue | 19 ++++++++++++---- 5 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 web/src/samples.ts diff --git a/docs/设计方案.md b/docs/设计方案.md index ba98a58..0046c3e 100644 --- a/docs/设计方案.md +++ b/docs/设计方案.md @@ -235,9 +235,9 @@ IOT/ ```mermaid flowchart TB P0["阶段 0 · 技术验证(已完成 2026-10-22)
教材项目 2 任务 1 原样运行;实测编译、延迟、内存"] - P1["阶段 1 · MVP(进行中,约 3 个月)
虚拟实训台 ✔、代码编辑器 ✔、窗体设计器 ✔、多窗体 ✔
教材示例:项目 1–3 全部 ✔,项目 4–5 中平台已支持的 3 个任务 ✔
部署(沙箱、Docker Compose、GitHub Actions)✔"] - P2["阶段 2 · 教学闭环(进行中,约 2 个月)
账号与班级 ✔、作品保存在服务器 ✔、自动评分 ✔、布置与提交作业 ✔
评阅(看代码、运行、老师评分、评语)✔、成绩表与导出 ✔;一个班试用"] - P3["阶段 3 · 扩展(进行中)
教材项目 4–8:摄像头 ✔、线程 ✔、SQLite 模拟数据库 ✔、虚拟时钟 ✔、图表 ✔"] + P1["阶段 1 · MVP(已完成 2026-10-22)
虚拟实训台 ✔、代码编辑器 ✔、窗体设计器 ✔、多窗体 ✔
教材示例:项目 1–3 全部 ✔,项目 4–5 中平台已支持的 3 个任务 ✔
部署(沙箱、Docker Compose、GitHub Actions)✔"] + P2["阶段 2 · 教学闭环(功能已完成,待一个班试用)
账号与班级 ✔、作品保存在服务器 ✔、自动评分 ✔、布置与提交作业 ✔
评阅(看代码、运行、老师评分、评语)✔、成绩表与导出 ✔
一个班试用(2026-10-26 已部署上线,待试用)"] + P3["阶段 3 · 扩展(功能已完成 2026-10-23)
教材项目 4–8:摄像头 ✔、线程 ✔、SQLite 模拟数据库 ✔、虚拟时钟 ✔、图表 ✔
教材 8 个项目共 23 个示例 ✔、任务说明 ✔"] P4["阶段 4 · 进阶(之后)
校园街景场景、故障注入题库、导出 VS 工程"] P0 -->|"关口:链路跑通,延迟和资源占用可接受"| P1 P1 -->|"关口:教材项目 1–3 能在平台上完整做完"| P2 @@ -247,6 +247,8 @@ flowchart TB 并行事项:尽早办理 ICP 备案(需数周)。 +**当前状态(2026-10-26)**:第 1–3 阶段的功能已全部完成,平台已部署到线上服务器(GitHub Actions 自动部署)。下一步是第 2 阶段的关口:带一个班试用,按试用中暴露的问题修改,之后再考虑第 4 阶段。 + **第 0 阶段结果(2026-10-22,已完成)**:教材项目 2 任务 1 的书上代码一字未改,在网页中完整运行:选串口、打开、推送文字到 LED 屏,未打开串口时弹出 "Error:发送失败"。本机实测(16 核、28 GB): | 指标 | 结果 | diff --git a/web/src/pages/DatabasePage.vue b/web/src/pages/DatabasePage.vue index 98d3da6..87205ab 100644 --- a/web/src/pages/DatabasePage.vue +++ b/web/src/pages/DatabasePage.vue @@ -10,6 +10,7 @@ import EditTab from '../database/EditTab.vue' import ObjectExplorer, { type ExplorerAction } from '../database/ObjectExplorer.vue' import QueryTab from '../database/QueryTab.vue' import { execute, loadExplorer, quoteName, type ExplorerDatabase, type ExplorerTable } from '../database/sql' +import { renderSampleTag, sampleOptions } from '../samples' type Tab = | { key: string; kind: 'query'; title: string; database: string; sql?: string; run?: boolean } @@ -217,7 +218,7 @@ onMounted(refresh) - +

会用实验的初始数据替换实验用到的数据库(如"环境数据表"),你在这些库里做的改动会丢失;其他数据库不受影响。

▶ 运行 ■ 停止 diff --git a/web/src/samples.ts b/web/src/samples.ts new file mode 100644 index 0000000..75f7527 --- /dev/null +++ b/web/src/samples.ts @@ -0,0 +1,34 @@ +// 实验下拉菜单:按教材的项目分组,组名是项目名,组里每项只写"任务N …";选中后框里显示完整标题 +import { h } from 'vue' +import type { SelectGroupOption, SelectOption, SelectRenderTag } from 'naive-ui' + +/** 教材 8 个项目的名称(去掉"智慧校园系统——"前缀) */ +const PROJECTS: Record = { + 1: 'C#入门基础', + 2: '环境监测模块', + 3: '智慧路灯控制模块', + 4: '入侵信息监测模块', + 5: '灾情信息监测模块', + 6: '数据存储模块', + 7: '情景控制模块', + 8: '智慧校园管理平台', +} + +/** 实验标题形如"项目3 任务2 实现人体红外传感器和路灯联动",按项目分组;不是这种格式的放进"其他" */ +export function sampleOptions(list: { id: string; title: string }[]): SelectGroupOption[] { + const groups = new Map() + for (const s of list) { + const m = /^项目(\d+)\s*(.*)$/.exec(s.title) + const key = m ? m[1]! : '其他' + let group = groups.get(key) + if (!group) { + group = { type: 'group', key, label: m ? `项目${key} ${PROJECTS[key] ?? ''}`.trim() : '其他', children: [] } + groups.set(key, group) + } + group.children.push({ label: m ? m[2] : s.title, value: s.id, title: s.title }) + } + return [...groups.values()] +} + +/** 选择框里显示完整标题(带项目号),下拉列表里仍是简短的"任务N …" */ +export const renderSampleTag: SelectRenderTag = ({ option }) => h('span', { title: option.title as string }, option.title as string) diff --git a/web/src/teacher/AssignmentDialog.vue b/web/src/teacher/AssignmentDialog.vue index 181e1fd..0e24ce7 100644 --- a/web/src/teacher/AssignmentDialog.vue +++ b/web/src/teacher/AssignmentDialog.vue @@ -7,6 +7,7 @@ import { NButton, NDatePicker, NInput, NInputNumber, NModal, NRadio, NRadioGroup import type { Assignment, Scoring, SourceFile, StartMode } from '@iot/shared' import { api } from '../api' import { assignmentTemplate } from '../designer/templates' +import { renderSampleTag, sampleOptions } from '../samples' const props = defineProps<{ show: boolean; classId: number; editing: Assignment | null }>() const emit = defineEmits<{ 'update:show': [show: boolean]; saved: [] }>() @@ -18,7 +19,8 @@ const DEFAULT_MANUAL = [ { title: '变量与控件命名规范', points: 10 }, ] -const samples = ref<{ label: string; value: string }[]>([]) +const samples = ref<{ id: string; title: string }[]>([]) +const sampleGroups = computed(() => sampleOptions(samples.value)) const sampleId = ref(null) const title = ref('') const startMode = ref('ui') @@ -42,8 +44,7 @@ watch( async (show) => { if (!show) return if (samples.value.length === 0) { - const list = await api<{ id: string; title: string }[]>('GET', '/api/samples') - samples.value = list.map((s) => ({ label: s.title, value: s.id })) + samples.value = await api<{ id: string; title: string }[]>('GET', '/api/samples') } const e = props.editing if (e) { @@ -74,7 +75,7 @@ watch( async function chooseSample(id: string) { sampleId.value = id - title.value = samples.value.find((s) => s.value === id)?.label ?? '' + title.value = samples.value.find((s) => s.id === id)?.title ?? '' void loadSampleTask(id).then(() => { if (sampleId.value === id) task.value = sampleTask.value }) @@ -128,7 +129,15 @@ async function save() { >
- +