feat: export AI teaching plans as Word

bugfix/ai-assessment-evidence-rescore
陈沅 1 month ago
parent cbab3d95be
commit f2b31bf553

@ -17,9 +17,9 @@
<img src="@/assets/images/ai.webp" alt="" />
</div>
<MdPreview :editorId="id" :modelValue="item.content" :codeFoldable="false" theme="dark" codeTheme="a11y" />
<div class="copy">
<el-tooltip class="box-item" effect="dark" content="复制" placement="top">
<svg-icon icon-class="copy" v-copyText="item.content" v-copyText:callback="copyTextSuccess" />
<div v-if="item.role !== 'user'" class="answer-export">
<el-tooltip class="box-item" effect="dark" content="导出教案" placement="top">
<svg-icon icon-class="导出" @click.stop="exportTeachingPlan(item)" />
</el-tooltip>
</div>
</div>
@ -47,6 +47,7 @@ import { getAssetsFile } from "@/utils/index.js";
import * as AI from "@/api/AI.js";
import "md-editor-v3/lib/style.css";
import * as XLSX from "xlsx";
import { marked } from "marked";
import useUserStore from "@/store/modules/user";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@ -146,9 +147,31 @@ const scrollToBottom = () => {
scrollEl.scrollTop = scrollEl.scrollHeight;
}
};
const copyTextSuccess = () => {
proxy.$modal.msgSuccess("复制成功");
};
function exportTeachingPlan(item) {
if (!item?.content?.trim()) {
proxy.$message.warning("暂无可导出的教案内容");
return;
}
const documentHtml = `<!DOCTYPE html><html><head><meta charset="utf-8"><style>
@page { size: A4; margin: 20mm; }
body { color: #1f2937; font-family: "Microsoft YaHei", Arial, sans-serif; font-size: 11pt; line-height: 1.7; }
h1 { margin: 0 0 20px; color: #0f4c81; font-size: 20pt; }
h2, h3 { color: #0f4c81; }
table { width: 100%; margin: 14px 0; border-collapse: collapse; table-layout: fixed; }
th, td { padding: 8px 10px; border: 1px solid #8ca7c1; vertical-align: top; word-break: break-word; }
th { color: #102a43; background: #dceeff; font-weight: 700; }
pre { padding: 12px; overflow-wrap: break-word; white-space: pre-wrap; background: #f4f7fa; }
img { max-width: 100%; }
</style></head><body><h1>AI 实训教案</h1>${marked.parse(item.content, { gfm: true, breaks: true })}</body></html>`;
const blob = new Blob(["\ufeff", documentHtml], { type: "application/msword;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `AI实训教案-${new Date().toISOString().slice(0, 10)}.doc`;
link.click();
URL.revokeObjectURL(url);
proxy.$modal.msgSuccess("教案已导出");
}
// 中断对话
const gInterrupt = () => {
if (abortController) {
@ -373,7 +396,7 @@ onUnmounted(() => {
gap: 1rem;
position: relative;
padding-right: 10px;
.copy {
.answer-export {
position: absolute;
top: 5px;
right: 23px;
@ -385,7 +408,7 @@ onUnmounted(() => {
}
}
&:hover {
.copy {
.answer-export {
display: block;
cursor: pointer;
}
@ -408,7 +431,7 @@ onUnmounted(() => {
.qustionCon {
position: relative;
padding-left: 10px;
.copy {
.answer-export {
position: absolute;
top: 5px;
left: 8px;
@ -420,7 +443,7 @@ onUnmounted(() => {
}
}
&:hover {
.copy {
.answer-export {
display: block;
cursor: pointer;
}

@ -0,0 +1,18 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const page = fs.readFileSync(
path.resolve(__dirname, "../src/views/analysis-of-students/dialogue.vue"),
"utf8",
);
assert.match(page, /content="导出教案"/);
assert.match(page, /icon-class="导出"/);
assert.doesNotMatch(page, /v-copyText="item\.content"/);
assert.match(page, /function exportTeachingPlan\(item\)/);
assert.match(page, /marked\.parse\(item\.content/);
assert.match(page, /table-layout:\s*fixed/);
assert.match(page, /th, td\s*\{[^}]*word-break:\s*break-word/);
console.log("AI lesson plan export static checks passed");
Loading…
Cancel
Save