diff --git a/src/api/AI.js b/src/api/AI.js
index 2fa83f9..24895f9 100644
--- a/src/api/AI.js
+++ b/src/api/AI.js
@@ -217,10 +217,13 @@ export function uploadResourceFile(data) {
}
// 学情分析
export function getLearningAnalysis(params) {
- let { stuNeed,stuData}=params
+ const { stuNeed, stuData } = params
return request({
- url: import.meta.env.VITE_APP_AI_API+'learnAnalysis/getStuPersonLearnAnalysis?stuNeed='+stuNeed,
+ url: import.meta.env.VITE_APP_AI_API + 'learnAnalysis/getStuPersonLearnAnalysis',
method: 'POST',
- data:stuData
+ params: { stuNeed },
+ data: stuData,
+ // AI 服务使用独立鉴权,不能携带本系统的登录令牌。
+ headers: { isToken: false }
})
-}
\ No newline at end of file
+}
diff --git a/src/views/analysis-of-students/analysis.vue b/src/views/analysis-of-students/analysis.vue
index 740d3f2..174562b 100644
--- a/src/views/analysis-of-students/analysis.vue
+++ b/src/views/analysis-of-students/analysis.vue
@@ -12,7 +12,8 @@
此处为AI根据学情数据进行的智能分析,从个人成绩和实训时长角度给出分析结果,仅供参考
-
+
+
{{ chid.name }}
@@ -57,39 +58,62 @@ const dialogue = ref([
},
]);
const scoreList = ref([]);
-const getScoreList = () => {
- if (roleId.value == 4) {
- indexApi.getStuModuleDetail({ userId: userStore.userInfo.userId }).then((res) => {
- scoreList.value = res.data;
- proxy.$cache.session.setJSON("scoreList", scoreList.value);
- });
- } else {
- scoreApi
- .getScoreList({
+const scoreLoaded = ref(false);
+const hasScoreData = ref(false);
+const hasTeacherScoreData = (row) => {
+ return Number(row.completedTaskCount || 0) > 0 || moduleMap.some(({ key }) => row[`${key}Score`] !== null && row[`${key}Score`] !== undefined);
+};
+const getScoreList = async () => {
+ scoreLoaded.value = false;
+ scoreList.value = [];
+ hasScoreData.value = false;
+ proxy.$cache.session.remove("scoreList");
+ try {
+ if (roleId.value == 4) {
+ const res = await indexApi.getStuModuleDetail({ userId: userStore.userInfo.userId });
+ scoreList.value = Array.isArray(res?.data) ? res.data : [];
+ hasScoreData.value = scoreList.value.length > 0;
+ } else {
+ const res = await scoreApi.getScoreList({
index: 1,
size: 1000,
schoolId: userStore.userInfo.schoolId,
- })
- .then((res) => {
- const transformedData = res.data.list.map((item) => ({
- name: item.name,
- className: item.className,
- schoolId: item.schoolId,
- scores: moduleMap.map(({ key, name }) => ({
- moduleName: name,
- score: item[`${key}Score`] || 0,
- // 总成绩
- totalScore: item.score || 0,
- // 总排名
- totalRank: item.stuRank || null,
- })),
- }));
- proxy.$cache.session.setJSON("scoreList", transformedData);
});
+ const rows = Array.isArray(res?.data?.list) ? res.data.list : [];
+ hasScoreData.value = rows.some(hasTeacherScoreData);
+ scoreList.value = rows.map((item) => ({
+ name: item.name,
+ className: item.className,
+ schoolId: item.schoolId,
+ scores: moduleMap.map(({ key, name }) => ({
+ moduleName: name,
+ score: item[`${key}Score`] || 0,
+ // 总成绩
+ totalScore: item.score || 0,
+ // 总排名
+ totalRank: item.stuRank || null,
+ })),
+ }));
+ }
+ if (hasScoreData.value) {
+ proxy.$cache.session.setJSON("scoreList", scoreList.value);
+ } else {
+ proxy.$message.warning("暂无成绩数据,暂时无法进行AI学情分析");
+ }
+ } catch (error) {
+ proxy.$message.error("成绩数据加载失败,请稍后重试");
+ } finally {
+ scoreLoaded.value = true;
}
};
// 分析
const analysis = (val) => {
+ if (!scoreLoaded.value) {
+ return proxy.$message.warning("成绩数据正在加载,请稍候");
+ }
+ if (!hasScoreData.value) {
+ return proxy.$message.warning("暂无成绩数据,暂时无法进行AI学情分析");
+ }
proxy.$cache.session.setJSON("msg", val.msg);
router.push("/ai/learning-situation-dialogue");
};
@@ -165,6 +189,13 @@ onMounted(() => {
background: linear-gradient(-90deg, #1160ff 0%, #1994ff 100%);
}
}
+ .empty-score {
+ height: 100%;
+ --el-empty-description-margin-top: 16px;
+ :deep(.el-empty__description p) {
+ color: #ffffff;
+ }
+ }
.brief-introduction {
display: flex;
justify-content: center;
@@ -176,4 +207,4 @@ onMounted(() => {
}
}
}
-
\ No newline at end of file
+
diff --git a/src/views/analysis-of-students/dialogue.vue b/src/views/analysis-of-students/dialogue.vue
index 3f42f89..337de72 100644
--- a/src/views/analysis-of-students/dialogue.vue
+++ b/src/views/analysis-of-students/dialogue.vue
@@ -159,16 +159,20 @@ const gInterrupt = () => {
}
};
// 获取分析
-const getAnalysis = () => {
+const getAnalysis = async () => {
loading.value = true;
- AI.getLearningAnalysis({ stuNeed: proxy.$cache.session.getJSON("msg"), stuData: proxy.$cache.session.getJSON("scoreList") }).then((res) => {
+ try {
+ const res = await AI.getLearningAnalysis({ stuNeed: proxy.$cache.session.getJSON("msg"), stuData: proxy.$cache.session.getJSON("scoreList") });
messages.value.push({
id: Date.now(),
role: "assistant",
content: res,
});
+ } catch (error) {
+ proxy.$message.error("AI学情分析暂时不可用,请稍后重试");
+ } finally {
loading.value = false;
- });
+ }
};
// 计算高度的函数
const updateHeight = () => {
@@ -425,4 +429,4 @@ onUnmounted(() => {
.el-scrollbar {
--el-scrollbar-bg-color: transparent !important;
}
-
\ No newline at end of file
+
diff --git a/tests/ai-learning-analysis.static.test.cjs b/tests/ai-learning-analysis.static.test.cjs
new file mode 100644
index 0000000..8c26a49
--- /dev/null
+++ b/tests/ai-learning-analysis.static.test.cjs
@@ -0,0 +1,29 @@
+const fs = require("node:fs");
+const path = require("node:path");
+const test = require("node:test");
+const assert = require("node:assert/strict");
+
+const root = path.resolve(__dirname, "..");
+const apiSource = fs.readFileSync(path.join(root, "src/api/AI.js"), "utf8");
+const analysisSource = fs.readFileSync(path.join(root, "src/views/analysis-of-students/analysis.vue"), "utf8");
+const dialogueSource = fs.readFileSync(path.join(root, "src/views/analysis-of-students/dialogue.vue"), "utf8");
+
+test("learning analysis request does not send the commerce login token", () => {
+ const requestBlock = apiSource.match(/export function getLearningAnalysis[\s\S]*?\n}/)?.[0] || "";
+ assert.match(requestBlock, /headers:\s*\{\s*isToken:\s*false\s*}/);
+ assert.match(requestBlock, /params:\s*\{\s*stuNeed\s*}/);
+});
+
+test("learning analysis page handles empty and failed score data", () => {
+ assert.match(analysisSource, /暂无成绩数据,暂时无法进行AI学情分析/);
+ assert.match(analysisSource, /proxy\.\$cache\.session\.remove\("scoreList"\)/);
+ assert.match(analysisSource, /rows\.some\(hasTeacherScoreData\)/);
+ assert.match(analysisSource, /completedTaskCount/);
+ assert.match(analysisSource, /if \(!hasScoreData\.value\)/);
+ assert.match(analysisSource, /finally\s*\{\s*scoreLoaded\.value = true/);
+});
+
+test("AI analysis failure always closes the loading overlay", () => {
+ assert.match(dialogueSource, /AI学情分析暂时不可用,请稍后重试/);
+ assert.match(dialogueSource, /finally\s*\{\s*loading\.value = false/);
+});