feat: unify student AI learning and assessment panel

dev-QQq
chenyuan 4 weeks ago
parent 01af770d8e
commit b6be9d0eac

@ -6,13 +6,14 @@
AI助学
</h3>
<p class="ai-instruction">点击参考提示词可自动带入问题AI 将结合当前实训内容给出建议</p>
<div class="ai-output">
<div class="output-glow"></div>
<p>{{ displayAssistMessage }}</p>
<div v-if="referenceText" class="reference-block">
<button v-if="referenceText" type="button" class="reference-block" @click="useReferencePrompt">
<strong>{{ referenceTitle }}</strong>
<span v-html="referenceText"></span>
</div>
</button>
</div>
<div class="ai-input-shell">
@ -29,7 +30,7 @@
AI助评
</h3>
<div class="ai-output">
<div class="ai-output ai-output--review">
<div class="output-glow"></div>
<p>{{ displayEvalMessage }}</p>
<div v-if="displayScoreVisible" class="score-badge">
@ -37,18 +38,17 @@
</div>
</div>
<div class="ai-input-shell">
<textarea v-model="reviewQuestion" placeholder="请求AI助评智能打分"></textarea>
<button type="button" class="send-btn" aria-label="AI" @click="requestEvaluation">
<el-icon><Top /></el-icon>
</button>
</div>
<el-button class="assessment-button" :loading="assessmentLoading" :disabled="!currentTaskKey" @click="requestEvaluation">AI</el-button>
</section>
</aside>
</template>
<script setup>
import { Reading, Star, Top } from "@element-plus/icons-vue";
import { getAiTrainingEvaluation, requestAiTrainingAssessment, requestAiTrainingHelp } from "@/api/aiTrainingEvaluation";
import { normalizeEvaluationReports } from "./trainingAiEvaluationReport";
import { resolveTrainingTaskKey } from "@/views/training/taskKeyMap";
import { buildReferencePrompt } from "./studentTrainingAiPanel";
const props = defineProps({
studyTip: {
@ -83,6 +83,7 @@ const props = defineProps({
type: Array,
default: () => [],
},
taskKey: { type: String, default: "" },
});
const emit = defineEmits(["request-assist", "request-evaluation"]);
@ -93,27 +94,56 @@ const assistMessage = ref("");
const internalEvalMessage = ref("");
const internalScore = ref("--");
const internalScoreVisible = ref(false);
const assessmentLoading = ref(false);
const evaluation = ref({});
const route = useRoute();
const currentTaskKey = computed(() => resolveTrainingTaskKey(route, props.taskKey));
const reports = computed(() => normalizeEvaluationReports(evaluation.value));
const displayAssistMessage = computed(() => assistMessage.value || props.studyTip);
const displayScoreVisible = computed(() => props.scoreVisible || internalScoreVisible.value);
const displayFinalScore = computed(() => (props.scoreVisible ? props.finalScore : internalScore.value));
const displayScoreVisible = computed(() => props.scoreVisible || internalScoreVisible.value || reports.value.score !== null);
const displayFinalScore = computed(() => (props.scoreVisible ? props.finalScore : reports.value.score ?? internalScore.value));
const displayEvalMessage = computed(() => {
if (props.scoreVisible) return props.evalMessage;
return internalEvalMessage.value || props.evalMessage;
return reports.value.assessmentReport || internalEvalMessage.value || props.evalMessage;
});
function requestAssist() {
onMounted(loadEvaluation);
watch(currentTaskKey, loadEvaluation);
async function loadEvaluation() {
if (!currentTaskKey.value) return;
try { evaluation.value = (await getAiTrainingEvaluation(currentTaskKey.value))?.data || {}; } catch (error) { evaluation.value = {}; }
}
function useReferencePrompt() {
assistQuestion.value = buildReferencePrompt(props.referenceText);
}
async function requestAssist() {
emit("request-assist", assistQuestion.value);
assistMessage.value = assistQuestion.value
? `围绕“${assistQuestion.value}”,建议先对照任务目标检查信息是否完整,再补充数据依据和分析结论。`
: props.assistText;
if (currentTaskKey.value) {
try {
await requestAiTrainingHelp(currentTaskKey.value);
await loadEvaluation();
assistMessage.value = reports.value.helpReport || props.assistText;
return;
} catch (error) {}
}
assistMessage.value = assistQuestion.value ? `围绕“${assistQuestion.value}”,建议先对照任务目标检查信息是否完整,再补充数据依据和分析结论。` : props.assistText;
}
function requestEvaluation() {
async function requestEvaluation() {
emit("request-evaluation", reviewQuestion.value);
internalScoreVisible.value = true;
internalScore.value = 88;
internalEvalMessage.value = "已完成初步评估:内容结构基本完整,建议继续补充真实数据来源、分析过程和可执行结论。";
if (!currentTaskKey.value) return;
assessmentLoading.value = true;
try {
await requestAiTrainingAssessment(currentTaskKey.value);
await loadEvaluation();
internalScoreVisible.value = reports.value.score !== null;
internalScore.value = reports.value.score ?? "--";
internalEvalMessage.value = reports.value.assessmentReport || props.evalMessage;
} finally { assessmentLoading.value = false; }
}
</script>
@ -194,6 +224,10 @@ function requestEvaluation() {
margin: 0;
}
}
.ai-instruction { margin: -4px 0 12px; color: #9fcbe2; font-size: 13px; line-height: 1.6; }
.reference-block { width: 100%; border: 0; background: transparent; padding: 14px 0 0; text-align: left; cursor: pointer; }
.reference-block:hover strong { color: #77eaff; }
.assessment-button { width: 100%; margin-top: 14px; min-height: 44px; font-weight: 800; }
.output-glow {
position: absolute;

@ -0,0 +1,4 @@
export function buildReferencePrompt(referenceText) {
const text = String(referenceText || "").trim();
return text ? `请根据以下参考提示词协助我完成当前任务:\n${text}` : "";
}

@ -155,9 +155,8 @@
</main>
<div class="training-ai-tools">
<TrainingAiEvaluation :task-key="pageKey" :has-any-answer="hasAnyAnswer" />
<TrainingAiSidebar
:task-key="pageKey"
:study-tip="`当前任务建议:围绕“${page.title}”先明确产品对象,再按任务流程逐步补全分析内容。`"
:progress-items="progressItems"
/>
@ -170,7 +169,6 @@ import { Download, Upload } from "@element-plus/icons-vue";
import { getTrainingTaskByKey } from "@/api/trainingTask";
import { getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile } from "@/api/studentTrainingAnswer";
import { getStudentTrainingPage } from "./studentTrainingPages";
import TrainingAiEvaluation from "@/views/components/TrainingAiEvaluation.vue";
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
import { isStudentDemo } from "@/utils/studentDemo";

@ -0,0 +1,10 @@
import assert from "node:assert/strict";
import { buildReferencePrompt } from "../src/views/components/studentTrainingAiPanel.js";
assert.equal(
buildReferencePrompt("先从案例中拆出核心层、有形层、延伸层。"),
"请根据以下参考提示词协助我完成当前任务:\n先从案例中拆出核心层、有形层、延伸层。"
);
assert.equal(buildReferencePrompt(""), "");
console.log("student training AI panel contract passed");
Loading…
Cancel
Save