|
@@ -69,7 +69,10 @@
|
|
></el-input-number>
|
|
></el-input-number>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item v-if="courseInfo.outlineFilePath" label="教学大纲">
|
|
|
|
|
|
+ <el-form-item
|
|
|
|
+ v-if="courseInfo.outlineFilePath && courseOutlineParsed"
|
|
|
|
+ label="教学大纲"
|
|
|
|
+ >
|
|
<div class="box-justify">
|
|
<div class="box-justify">
|
|
<el-checkbox v-model="formModel.syllabus"
|
|
<el-checkbox v-model="formModel.syllabus"
|
|
>教学大纲pdf</el-checkbox
|
|
>教学大纲pdf</el-checkbox
|
|
@@ -88,6 +91,7 @@
|
|
<property-tree-select
|
|
<property-tree-select
|
|
v-model="formModel.propertyIdList"
|
|
v-model="formModel.propertyIdList"
|
|
:course-id="courseInfo.id"
|
|
:course-id="courseInfo.id"
|
|
|
|
+ :disabled="loading"
|
|
:style="{ width: '100%' }"
|
|
:style="{ width: '100%' }"
|
|
></property-tree-select>
|
|
></property-tree-select>
|
|
<el-input
|
|
<el-input
|
|
@@ -115,14 +119,14 @@
|
|
|
|
|
|
<div class="right-panel">
|
|
<div class="right-panel">
|
|
<div class="sse-output-title">试题预览</div>
|
|
<div class="sse-output-title">试题预览</div>
|
|
- <div class="sse-output-container">
|
|
|
|
|
|
+ <div class="sse-output-container" ref="sseOuiputContainerRef">
|
|
<template v-if="aiThinkingResult">
|
|
<template v-if="aiThinkingResult">
|
|
<div
|
|
<div
|
|
class="sse-thinking-title"
|
|
class="sse-thinking-title"
|
|
@click="thinkVisible = !thinkVisible"
|
|
@click="thinkVisible = !thinkVisible"
|
|
>
|
|
>
|
|
<template v-if="thinking">
|
|
<template v-if="thinking">
|
|
- <i class="el-icon-loading"></i>
|
|
|
|
|
|
+ <i class="el-icon-loading" style="margin-right: 6px"></i>
|
|
<span>深度思考中...</span>
|
|
<span>深度思考中...</span>
|
|
</template>
|
|
</template>
|
|
<template v-else>
|
|
<template v-else>
|
|
@@ -163,6 +167,8 @@ import {
|
|
aiBuildQuestionSaveApi,
|
|
aiBuildQuestionSaveApi,
|
|
} from "../../api";
|
|
} from "../../api";
|
|
|
|
|
|
|
|
+import { courseOutlineParsedCheckApi } from "@/modules/questions/api";
|
|
|
|
+
|
|
import { fetchTime } from "@/plugins/syncServerTime";
|
|
import { fetchTime } from "@/plugins/syncServerTime";
|
|
import { getAuthorization } from "@/plugins/crypto";
|
|
import { getAuthorization } from "@/plugins/crypto";
|
|
|
|
|
|
@@ -213,6 +219,7 @@ export default {
|
|
},
|
|
},
|
|
],
|
|
],
|
|
},
|
|
},
|
|
|
|
+ courseOutlineParsed: false,
|
|
// ai question result
|
|
// ai question result
|
|
taskId: "",
|
|
taskId: "",
|
|
aiResult: "",
|
|
aiResult: "",
|
|
@@ -233,12 +240,26 @@ export default {
|
|
},
|
|
},
|
|
},
|
|
},
|
|
methods: {
|
|
methods: {
|
|
|
|
+ scrollToBottom() {
|
|
|
|
+ const container = this.$refs.sseOuiputContainerRef;
|
|
|
|
+ if (container) {
|
|
|
|
+ container.scrollTop = container.scrollHeight;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
close() {
|
|
close() {
|
|
this.modalIsShow = false;
|
|
this.modalIsShow = false;
|
|
},
|
|
},
|
|
open() {
|
|
open() {
|
|
this.modalIsShow = true;
|
|
this.modalIsShow = true;
|
|
},
|
|
},
|
|
|
|
+ async checkCourseOutlineParsed() {
|
|
|
|
+ if (!this.courseInfo.id || !this.courseInfo.outlineFilePath) return;
|
|
|
|
+ const res = await courseOutlineParsedCheckApi(this.courseInfo.id).catch(
|
|
|
|
+ () => {}
|
|
|
|
+ );
|
|
|
|
+ if (!res) return;
|
|
|
|
+ this.courseOutlineParsed = res.data;
|
|
|
|
+ },
|
|
async getQuestionTypes() {
|
|
async getQuestionTypes() {
|
|
if (!this.courseInfo.id) return;
|
|
if (!this.courseInfo.id) return;
|
|
const res = await sourceDetailPageListApi({
|
|
const res = await sourceDetailPageListApi({
|
|
@@ -278,6 +299,7 @@ export default {
|
|
this.formModel = this.getInitForm();
|
|
this.formModel = this.getInitForm();
|
|
this.getQuestionTypes();
|
|
this.getQuestionTypes();
|
|
this.getInitForm();
|
|
this.getInitForm();
|
|
|
|
+ this.checkCourseOutlineParsed();
|
|
},
|
|
},
|
|
handleClose() {
|
|
handleClose() {
|
|
this.aiResult = "";
|
|
this.aiResult = "";
|
|
@@ -285,6 +307,7 @@ export default {
|
|
this.taskId = "";
|
|
this.taskId = "";
|
|
this.thinking = false;
|
|
this.thinking = false;
|
|
this.thinkDuration = "";
|
|
this.thinkDuration = "";
|
|
|
|
+ this.courseOutlineParsed = false;
|
|
this.stopStream();
|
|
this.stopStream();
|
|
},
|
|
},
|
|
setAuth(config) {
|
|
setAuth(config) {
|
|
@@ -318,11 +341,18 @@ export default {
|
|
this.aiResult = "";
|
|
this.aiResult = "";
|
|
this.aiThinkingResult = "";
|
|
this.aiThinkingResult = "";
|
|
this.thinkDuration = "";
|
|
this.thinkDuration = "";
|
|
- this.thinking = false;
|
|
|
|
this.taskId = "";
|
|
this.taskId = "";
|
|
this.loading = true;
|
|
this.loading = true;
|
|
|
|
|
|
|
|
+ // 设置60秒超时
|
|
|
|
+ const timeoutId = setTimeout(() => {
|
|
|
|
+ this.controller.abort();
|
|
|
|
+ this.$message.error("请求超时!");
|
|
|
|
+ this.loading = false;
|
|
|
|
+ }, 60000);
|
|
|
|
+
|
|
try {
|
|
try {
|
|
|
|
+ this.controller = new AbortController();
|
|
const res = await aiBuildQuestionApi(
|
|
const res = await aiBuildQuestionApi(
|
|
{
|
|
{
|
|
...this.formModel,
|
|
...this.formModel,
|
|
@@ -340,9 +370,12 @@ export default {
|
|
url: "/api/uq_basic/ai/question/stream/build",
|
|
url: "/api/uq_basic/ai/question/stream/build",
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
- signal: (this.controller = new AbortController()).signal, // Ensures a new controller for each call
|
|
|
|
|
|
+ signal: this.controller.signal, // 使用AbortController的signal
|
|
}
|
|
}
|
|
);
|
|
);
|
|
|
|
+
|
|
|
|
+ // 请求成功后清除超时定时器
|
|
|
|
+ clearTimeout(timeoutId);
|
|
const startThinkingTime = Date.now();
|
|
const startThinkingTime = Date.now();
|
|
this.thinking = true;
|
|
this.thinking = true;
|
|
|
|
|
|
@@ -355,7 +388,9 @@ export default {
|
|
const parsed = JSON.parse(event.data);
|
|
const parsed = JSON.parse(event.data);
|
|
if (!this.taskId && parsed.taskId) {
|
|
if (!this.taskId && parsed.taskId) {
|
|
this.taskId = parsed.taskId;
|
|
this.taskId = parsed.taskId;
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
+
|
|
if (
|
|
if (
|
|
parsed.choices &&
|
|
parsed.choices &&
|
|
parsed.choices[0] &&
|
|
parsed.choices[0] &&
|
|
@@ -363,25 +398,28 @@ export default {
|
|
) {
|
|
) {
|
|
const { content, reasoning_content } = parsed.choices[0].delta;
|
|
const { content, reasoning_content } = parsed.choices[0].delta;
|
|
|
|
|
|
- // 处理思考过程内容
|
|
|
|
- if (reasoning_content) {
|
|
|
|
- requestAnimationFrame(() => {
|
|
|
|
- this.aiThinkingResult += reasoning_content;
|
|
|
|
- });
|
|
|
|
- return;
|
|
|
|
- } else {
|
|
|
|
|
|
+ // 只要content不为null,则表示思考结束,开始处理生成结果内容
|
|
|
|
+ if (content !== null) {
|
|
this.thinking = false;
|
|
this.thinking = false;
|
|
- this.thinkDuration = Math.round(
|
|
|
|
- (Date.now() - startThinkingTime) / 1000
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
|
|
|
|
- // 处理生成结果内容
|
|
|
|
- if (content) {
|
|
|
|
|
|
+ if (!this.thinkDuration) {
|
|
|
|
+ this.thinkDuration = Math.round(
|
|
|
|
+ (Date.now() - startThinkingTime) / 1000
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
- this.aiResult += content;
|
|
|
|
|
|
+ this.aiResult += content || "";
|
|
|
|
+ this.scrollToBottom();
|
|
});
|
|
});
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 处理思考过程内容
|
|
|
|
+ requestAnimationFrame(() => {
|
|
|
|
+ this.aiThinkingResult += reasoning_content || "";
|
|
|
|
+ this.scrollToBottom();
|
|
|
|
+ });
|
|
}
|
|
}
|
|
} catch (e) {
|
|
} catch (e) {
|
|
console.error(
|
|
console.error(
|
|
@@ -415,6 +453,8 @@ export default {
|
|
} catch (err) {
|
|
} catch (err) {
|
|
console.error("流式处理错误:", err);
|
|
console.error("流式处理错误:", err);
|
|
this.thinking = false;
|
|
this.thinking = false;
|
|
|
|
+ // 请求出错时清除超时定时器
|
|
|
|
+ clearTimeout(timeoutId);
|
|
} finally {
|
|
} finally {
|
|
this.loading = false;
|
|
this.loading = false;
|
|
this.controller = null; // Ensure controller is reset
|
|
this.controller = null; // Ensure controller is reset
|