导出 VS 工程:实验页工具栏的"导出 EXE"改成下拉按钮"导出 ▾",里面是"导出 EXE"和"导出 VS 工程"。导出的 zip 是教材那样的 VS 2012 窗体应用工程(.sln + .csproj,.NET Framework 4.5),在机房电脑上解压、双击 .sln 用 VS 打开接着改,按 F5 运行。导出前照机房环境编译一遍要放进 zip 的代码:C# 5(VS 2012 的编译器)、真机版设备类库,有错不导出;新版 C# 写法报 CS8026,按功能名给出改法(字符串插值改 string.Format、?. 改先判断 null、=> 改大括号等 12 种)。导出 EXE 仍按最新 C#。平台的 Properties/Resources.Designer.cs 换成 VS 生成的写法和 Resources.resx,图片放 Resources\;代码里按文件名用的素材(ring.mp3)复制到输出目录,运行时在 bin\Debug\Resources\;每个窗体带 .resx(媒体播放器 OcxState 用教材默认状态),SubType、DependentUpon 和 VS 一样;补上 AssemblyInfo、Settings、App.config。教材 dll 全放 lib\,只引用代码直接用到的(和教材工程一样),只带这些编译不过时引用全部;用到书上的 ledHelper.Close() 时带上 LedHelperCompat.cs。文本文件用带 BOM 的 UTF-8 和 CRLF(VS 2012 打开不带 BOM 的文件会把中文当 GBK);zip 文件名用 GBK,另附 Info-ZIP Unicode 文件名字段(0x7075),中文 Windows 自带解压和 7-Zip 都显示正确(ZipWriter.cs)。23 个示例都能导出,导出的工程在 mono 的 msbuild 里全部编译通过、输出目录和教材 bin\Debug 一样、程序能启动;VS 2012 还要在机房实测。接口 POST /api/export-project,代码有错时 422。export-exe.ts 改名 export.ts(服务器和网页各一个)。使用说明、设计方案、README、部署说明同步
Deploy / deploy (push) Failing after 1m8s
Deploy / deploy (push) Failing after 1m8s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c3ce31fe1
commit
2aa267839f
17 files changed
+1209
-80
No files matched your search
@@ -1,18 +1,23 @@
|
||||
// 导出 EXE:服务器照教材工程(真机版设备类库)编译,下载一个 exe,在机房电脑上双击就能控制真的实训台。
|
||||
// 导出:服务器照教材工程(真机版设备类库)编译,下载到机房电脑上用。
|
||||
// EXE:一个 exe,双击就能控制真的实训台;VS 工程:教材那样的 VS 2012 工程(zip),用 VS 打开接着改。
|
||||
import type { Diagnostic, SourceFile } from '@iot/shared'
|
||||
import { apiResponse, ApiError } from '../api'
|
||||
import { projectNamespace } from '../designer/templates'
|
||||
|
||||
export type ExportKind = 'exe' | 'project'
|
||||
|
||||
const EXT: Record<ExportKind, string> = { exe: 'exe', project: 'zip' }
|
||||
|
||||
/** 下载成功返回 null;代码有错误返回报错(照真机版类库编译,和 VS 里一样);其他问题抛出 ApiError */
|
||||
export async function downloadExe(files: SourceFile[]): Promise<Diagnostic[] | null> {
|
||||
export async function downloadExport(kind: ExportKind, files: SourceFile[]): Promise<Diagnostic[] | null> {
|
||||
let response: Response
|
||||
try {
|
||||
response = await apiResponse('POST', '/api/export-exe', { files, name: projectNamespace(files) })
|
||||
response = await apiResponse('POST', `/api/export-${kind}`, { files, name: projectNamespace(files) })
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError && error.status === 422 && Array.isArray(error.data.diagnostics)) return error.data.diagnostics as Diagnostic[]
|
||||
throw error
|
||||
}
|
||||
const name = fileName(response.headers.get('Content-Disposition')) ?? `${projectNamespace(files)}.exe`
|
||||
const name = fileName(response.headers.get('Content-Disposition')) ?? `${projectNamespace(files)}.${EXT[kind]}`
|
||||
const url = URL.createObjectURL(await response.blob())
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
@@ -168,16 +168,24 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
|
||||
<figcaption>数据库页面:左边是你的库和表(点表名编辑数据),右边写 SQL、按 F5 执行,下面是结果</figcaption>
|
||||
</figure>
|
||||
|
||||
<h3>8. 导出 EXE,在机房电脑上运行</h3>
|
||||
<h3>8. 导出 EXE 或 VS 工程,在机房电脑上运行</h3>
|
||||
<ul>
|
||||
<li>
|
||||
点工具栏的<b>导出 EXE</b>,下载一个
|
||||
点工具栏的<b>导出</b>→<b>导出 EXE</b>,下载一个
|
||||
exe(名字是工程名,比如“教室情景模式.exe”)。拷到接着实训台的机房电脑上双击运行,选对串口、点打开,就能控制<b>真的</b>设备。
|
||||
</li>
|
||||
<li>导出时照机房电脑上的设备类库编译,和在 VS 里编译一样。代码有错误时弹出报错,点它跳到出错的地方。</li>
|
||||
<li>设备类库已经装在 exe 里,不用另外拷 dll。项目 7 寝室的音乐第一次运行时会放到 exe 旁边的 Resources 文件夹。</li>
|
||||
<li>项目 6、8 的程序连的是机房电脑上真的 SQL Server,那台电脑上要有书上建的库。</li>
|
||||
<li>第一次运行时 Windows 可能提示“Windows 已保护你的电脑”:点<b>更多信息</b>→<b>仍要运行</b>。</li>
|
||||
<li>
|
||||
想在机房的 VS 里接着改,点<b>导出</b>→<b>导出 VS 工程</b>,下载一个 zip。解压后双击里面的 .sln 文件用 VS 打开,按 F5
|
||||
运行。工程和书上的一样:设备类库在 lib 文件夹里,图片、声音在 Resources 文件夹里。
|
||||
</li>
|
||||
<li>
|
||||
机房的 VS 2012 只认 C# 5 的写法。导出工程时照它检查,用了新写法(比如
|
||||
<code>$"温度{t}"</code>)会弹出报错和改法,改好再导出。
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -4,7 +4,21 @@
|
||||
// 老师运行学生提交的程序(/teacher/submissions/:id/run,改动只用于试运行,不保存)。
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { NButton, NInput, NModal, NPopconfirm, NPopover, NSelect, NTab, NTabs, NTag, useMessage, type SelectGroupOption } from 'naive-ui'
|
||||
import {
|
||||
NButton,
|
||||
NDropdown,
|
||||
NInput,
|
||||
NModal,
|
||||
NPopconfirm,
|
||||
NPopover,
|
||||
NSelect,
|
||||
NTab,
|
||||
NTabs,
|
||||
NTag,
|
||||
useMessage,
|
||||
type DropdownOption,
|
||||
type SelectGroupOption,
|
||||
} from 'naive-ui'
|
||||
import type { Assignment, Diagnostic, Review, SourceFile, Submission } from '@iot/shared'
|
||||
import FormDesigner from '../designer/FormDesigner.vue'
|
||||
import { createDesignerHistory } from '../designer/history'
|
||||
@@ -25,7 +39,7 @@ import CodeEditor from '../editor/CodeEditor.vue'
|
||||
import { checkProject } from '../editor/language'
|
||||
import { api } from '../api'
|
||||
import { currentUser, logout } from '../auth'
|
||||
import { downloadExe } from '../editor/export-exe'
|
||||
import { downloadExport, type ExportKind } from '../editor/export'
|
||||
import { deleteWork, loadWork, sampleWorkKey, saveWork } from '../editor/works'
|
||||
import RunArea from '../run/RunArea.vue'
|
||||
import { createRunSession } from '../run/session'
|
||||
@@ -479,22 +493,37 @@ async function showDiagnostic(file: string, line: number, column: number) {
|
||||
editorRef.value?.reveal(file, line, column)
|
||||
}
|
||||
|
||||
// ---------- 导出 EXE:照真机版类库编译,在机房电脑上双击运行 ----------
|
||||
// ---------- 导出:照真机版类库编译,EXE 在机房电脑上双击运行,VS 工程用机房的 VS 2012 打开 ----------
|
||||
const exporting = ref(false)
|
||||
/** 导出时的编译报错(照真机版类库编译,和平台上运行时可能不同) */
|
||||
const exportErrors = ref<Diagnostic[]>([])
|
||||
const exportErrorsOpen = ref(false)
|
||||
const exportKind = ref<ExportKind>('exe')
|
||||
|
||||
async function exportExe() {
|
||||
const EXPORT_OPTIONS: DropdownOption[] = [
|
||||
{ key: 'exe', label: '导出 EXE', props: { title: '编译成 Windows 程序(.exe),在机房电脑上双击运行,控制真的实训台' } },
|
||||
{ key: 'project', label: '导出 VS 工程', props: { title: '打包成 VS 2012 工程(.zip),在机房电脑上解压后用 VS 打开,接着改代码、按 F5 运行' } },
|
||||
]
|
||||
|
||||
const EXPORT_DONE: Record<ExportKind, string> = {
|
||||
exe: '已导出,在机房电脑上双击运行(要接真的实训台)',
|
||||
project: '已导出,解压后双击 .sln 文件用 VS 打开,按 F5 运行(要接真的实训台)',
|
||||
}
|
||||
|
||||
async function exportProgram(kind: ExportKind) {
|
||||
if (exporting.value) return
|
||||
exporting.value = true
|
||||
exportKind.value = kind
|
||||
try {
|
||||
const errors = await downloadExe(files.value.map((f) => ({ ...f })))
|
||||
const errors = await downloadExport(
|
||||
kind,
|
||||
files.value.map((f) => ({ ...f })),
|
||||
)
|
||||
if (errors) {
|
||||
exportErrors.value = errors
|
||||
exportErrorsOpen.value = true
|
||||
} else {
|
||||
message.success('已导出,在机房电脑上双击运行(要接真的实训台)')
|
||||
message.success(EXPORT_DONE[kind])
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : String(error))
|
||||
@@ -630,9 +659,9 @@ onBeforeUnmount(() => clearTimeout(rerunTimer))
|
||||
<NButton v-if="taskPanel && !viewing" size="small" title="任务说明、自动检查哪些功能;可以自己先检查一遍(不计分)" @click="taskOpen = !taskOpen">
|
||||
任务
|
||||
</NButton>
|
||||
<NButton size="small" :loading="exporting" title="编译成 Windows 程序(.exe),在机房电脑上双击运行,控制真的实训台" @click="exportExe">
|
||||
导出 EXE
|
||||
</NButton>
|
||||
<NDropdown trigger="click" :options="EXPORT_OPTIONS" :disabled="exporting" @select="(key: ExportKind) => exportProgram(key)">
|
||||
<NButton size="small" :loading="exporting" title="导出到机房电脑上用:EXE 双击运行,VS 工程用 VS 打开">导出 ▾</NButton>
|
||||
</NDropdown>
|
||||
<NPopconfirm v-if="modified && !viewing" positive-text="恢复" negative-text="取消" @positive-click="restoreOriginal">
|
||||
<template #trigger>
|
||||
<NButton quaternary size="small">{{ assignmentId ? '恢复到作业起点' : '恢复原始代码' }}</NButton>
|
||||
@@ -657,7 +686,10 @@ onBeforeUnmount(() => clearTimeout(rerunTimer))
|
||||
</header>
|
||||
|
||||
<NModal v-model:show="exportErrorsOpen" preset="card" title="代码有错误,不能导出" style="width: 640px; max-width: calc(100vw - 32px)">
|
||||
<p class="export-errors-note">导出时照机房电脑上的设备类库编译,和在 VS 里编译一样。点击报错跳到出错的代码。</p>
|
||||
<p v-if="exportKind === 'project'" class="export-errors-note">
|
||||
导出时照机房电脑上的 VS 2012 编译(只支持 C# 5,用真机版设备类库)。点击报错跳到出错的代码。
|
||||
</p>
|
||||
<p v-else class="export-errors-note">导出时照机房电脑上的设备类库编译,和在 VS 里编译一样。点击报错跳到出错的代码。</p>
|
||||
<div v-for="(d, i) in exportErrors.slice(0, 20)" :key="i" class="export-error" @click="jumpToExportError(d)">
|
||||
{{ d.file }}({{ d.line }},{{ d.column }}) {{ d.code }}:{{ d.message }}
|
||||
<div v-if="d.hint" class="hint">提示:{{ d.hint }}</div>
|
||||
|
||||
Reference in new issue
Block a user