|
@@ -69,21 +69,11 @@
|
|
></el-input-number>
|
|
></el-input-number>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="教学大纲">
|
|
|
|
|
|
+ <el-form-item v-if="courseInfo.outlineFilePath" 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
|
|
>
|
|
>
|
|
- <upload-button
|
|
|
|
- btn-content="上传文件"
|
|
|
|
- btn-icon="icon icon-import"
|
|
|
|
- :disabled="uploading || !courseInfo.id"
|
|
|
|
- :upload-data="{ id: courseInfo.id }"
|
|
|
|
- :upload-url="uploadUrl"
|
|
|
|
- :format="importFileTypes"
|
|
|
|
- @valid-error="validError"
|
|
|
|
- @upload-success="uploadSuccess"
|
|
|
|
- ></upload-button>
|
|
|
|
</div>
|
|
</div>
|
|
<el-input
|
|
<el-input
|
|
v-if="formModel.syllabus"
|
|
v-if="formModel.syllabus"
|
|
@@ -126,7 +116,27 @@
|
|
<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">
|
|
- <!-- {{ output }} -->
|
|
|
|
|
|
+ <template v-if="aiThinkingResult">
|
|
|
|
+ <div
|
|
|
|
+ class="sse-thinking-title"
|
|
|
|
+ @click="thinkVisible = !thinkVisible"
|
|
|
|
+ >
|
|
|
|
+ <template v-if="thinking">
|
|
|
|
+ <i class="el-icon-loading"></i>
|
|
|
|
+ <span>深度思考中...</span>
|
|
|
|
+ </template>
|
|
|
|
+ <template v-else>
|
|
|
|
+ <svg-btn name="think"></svg-btn>
|
|
|
|
+ <span>已深度思考(用时:{{ thinkDuration }}秒)</span>
|
|
|
|
+ <i v-if="thinkVisible" class="el-icon el-icon-arrow-down"></i>
|
|
|
|
+ <i v-else class="el-icon el-icon-arrow-down"></i>
|
|
|
|
+ </template>
|
|
|
|
+ </div>
|
|
|
|
+ <div v-show="thinkVisible" class="sse-thinking-container">
|
|
|
|
+ <sse-result-view :output="aiThinkingResult"></sse-result-view>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
<sse-result-view :output="aiResult"></sse-result-view>
|
|
<sse-result-view :output="aiResult"></sse-result-view>
|
|
</div>
|
|
</div>
|
|
<div class="action-buttons">
|
|
<div class="action-buttons">
|
|
@@ -147,8 +157,6 @@ import { BASE_QUESTION_TYPES } from "@/constants/constants";
|
|
import { createParser } from "eventsource-parser";
|
|
import { createParser } from "eventsource-parser";
|
|
import PropertyTreeSelect from "../PropertyTreeSelect.vue";
|
|
import PropertyTreeSelect from "../PropertyTreeSelect.vue";
|
|
import SseResultView from "./SseResultView.vue";
|
|
import SseResultView from "./SseResultView.vue";
|
|
-import UploadButton from "@/components/UploadButton.vue";
|
|
|
|
-import { QUESTION_API } from "@/constants/constants";
|
|
|
|
import {
|
|
import {
|
|
sourceDetailPageListApi,
|
|
sourceDetailPageListApi,
|
|
aiBuildQuestionApi,
|
|
aiBuildQuestionApi,
|
|
@@ -162,7 +170,6 @@ export default {
|
|
name: "AiQuestionCreateDialog",
|
|
name: "AiQuestionCreateDialog",
|
|
components: {
|
|
components: {
|
|
PropertyTreeSelect,
|
|
PropertyTreeSelect,
|
|
- UploadButton,
|
|
|
|
SseResultView,
|
|
SseResultView,
|
|
},
|
|
},
|
|
props: {
|
|
props: {
|
|
@@ -206,14 +213,13 @@ export default {
|
|
},
|
|
},
|
|
],
|
|
],
|
|
},
|
|
},
|
|
- // upload
|
|
|
|
- uploading: false,
|
|
|
|
- importFileTypes: ["pdf"],
|
|
|
|
- uploadUrl: `${QUESTION_API}/course/outline/upload`,
|
|
|
|
- showIframeDialog: false,
|
|
|
|
// ai question result
|
|
// ai question result
|
|
taskId: "",
|
|
taskId: "",
|
|
aiResult: "",
|
|
aiResult: "",
|
|
|
|
+ aiThinkingResult: "",
|
|
|
|
+ thinking: false,
|
|
|
|
+ thinkVisible: true,
|
|
|
|
+ thinkDuration: "",
|
|
loading: false,
|
|
loading: false,
|
|
controller: null,
|
|
controller: null,
|
|
parser: null,
|
|
parser: null,
|
|
@@ -226,14 +232,6 @@ export default {
|
|
);
|
|
);
|
|
},
|
|
},
|
|
},
|
|
},
|
|
- watch: {
|
|
|
|
- cancel() {
|
|
|
|
- this.modalIsShow = false;
|
|
|
|
- },
|
|
|
|
- open() {
|
|
|
|
- this.modalIsShow = true;
|
|
|
|
- },
|
|
|
|
- },
|
|
|
|
methods: {
|
|
methods: {
|
|
close() {
|
|
close() {
|
|
this.modalIsShow = false;
|
|
this.modalIsShow = false;
|
|
@@ -283,17 +281,12 @@ export default {
|
|
},
|
|
},
|
|
handleClose() {
|
|
handleClose() {
|
|
this.aiResult = "";
|
|
this.aiResult = "";
|
|
|
|
+ this.aiThinkingResult = "";
|
|
this.taskId = "";
|
|
this.taskId = "";
|
|
|
|
+ this.thinking = false;
|
|
|
|
+ this.thinkDuration = "";
|
|
this.stopStream();
|
|
this.stopStream();
|
|
},
|
|
},
|
|
- validError(error) {
|
|
|
|
- this.$message.error(error.message);
|
|
|
|
- },
|
|
|
|
- uploadSuccess(response) {
|
|
|
|
- console.log(response);
|
|
|
|
- // TODO:
|
|
|
|
- this.$message.success("上传成功!");
|
|
|
|
- },
|
|
|
|
setAuth(config) {
|
|
setAuth(config) {
|
|
const headers = {};
|
|
const headers = {};
|
|
let userSession = sessionStorage.getItem("user");
|
|
let userSession = sessionStorage.getItem("user");
|
|
@@ -323,6 +316,9 @@ export default {
|
|
|
|
|
|
this.stopStream(); // 清理前一个流
|
|
this.stopStream(); // 清理前一个流
|
|
this.aiResult = "";
|
|
this.aiResult = "";
|
|
|
|
+ this.aiThinkingResult = "";
|
|
|
|
+ this.thinkDuration = "";
|
|
|
|
+ this.thinking = false;
|
|
this.taskId = "";
|
|
this.taskId = "";
|
|
this.loading = true;
|
|
this.loading = true;
|
|
|
|
|
|
@@ -347,10 +343,12 @@ export default {
|
|
signal: (this.controller = new AbortController()).signal, // Ensures a new controller for each call
|
|
signal: (this.controller = new AbortController()).signal, // Ensures a new controller for each call
|
|
}
|
|
}
|
|
);
|
|
);
|
|
|
|
+ const startThinkingTime = Date.now();
|
|
|
|
+ this.thinking = true;
|
|
|
|
|
|
const onEvent = (event) => {
|
|
const onEvent = (event) => {
|
|
if (event.data === "[DONE]") {
|
|
if (event.data === "[DONE]") {
|
|
- console.log(this.aiResult);
|
|
|
|
|
|
+ // console.log(this.aiResult);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
try {
|
|
@@ -363,9 +361,23 @@ export default {
|
|
parsed.choices[0] &&
|
|
parsed.choices[0] &&
|
|
parsed.choices[0].delta
|
|
parsed.choices[0].delta
|
|
) {
|
|
) {
|
|
- const content = parsed.choices[0].delta.content;
|
|
|
|
- // console.log(content);
|
|
|
|
- if (typeof content === "string") {
|
|
|
|
|
|
+ const { content, reasoning_content } = parsed.choices[0].delta;
|
|
|
|
+
|
|
|
|
+ // 处理思考过程内容
|
|
|
|
+ if (reasoning_content) {
|
|
|
|
+ requestAnimationFrame(() => {
|
|
|
|
+ this.aiThinkingResult += reasoning_content;
|
|
|
|
+ });
|
|
|
|
+ return;
|
|
|
|
+ } else {
|
|
|
|
+ this.thinking = false;
|
|
|
|
+ this.thinkDuration = Math.round(
|
|
|
|
+ (Date.now() - startThinkingTime) / 1000
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 处理生成结果内容
|
|
|
|
+ if (content) {
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
this.aiResult += content;
|
|
this.aiResult += content;
|
|
});
|
|
});
|
|
@@ -381,6 +393,7 @@ export default {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
const onError = (error) => {
|
|
const onError = (error) => {
|
|
|
|
+ this.thinking = false;
|
|
console.error("Error parsing event:", error);
|
|
console.error("Error parsing event:", error);
|
|
};
|
|
};
|
|
|
|
|
|
@@ -401,6 +414,7 @@ export default {
|
|
}
|
|
}
|
|
} catch (err) {
|
|
} catch (err) {
|
|
console.error("流式处理错误:", err);
|
|
console.error("流式处理错误:", err);
|
|
|
|
+ this.thinking = false;
|
|
} finally {
|
|
} finally {
|
|
this.loading = false;
|
|
this.loading = false;
|
|
this.controller = null; // Ensure controller is reset
|
|
this.controller = null; // Ensure controller is reset
|
|
@@ -427,6 +441,8 @@ export default {
|
|
},
|
|
},
|
|
clearPreview() {
|
|
clearPreview() {
|
|
this.aiResult = "";
|
|
this.aiResult = "";
|
|
|
|
+ this.aiThinkingResult = "";
|
|
|
|
+ this.taskId = "";
|
|
},
|
|
},
|
|
},
|
|
},
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
@@ -434,3 +450,11 @@ export default {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|
|
|
|
+
|
|
|
|
+<style>
|
|
|
|
+.thinking-container {
|
|
|
|
+ background-color: #f8f9fa;
|
|
|
|
+ border-left: 3px solid #409eff;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+}
|
|
|
|
+</style>
|