feat: update student experiment report modules

fix/non-blocking-route-navigation
chenyuan 2 months ago
parent 4009e939ff
commit 9299eb644b

@ -56,6 +56,9 @@ export const getUserRank = (params) => {
params params
}) })
} }
export const getCurrentExperimentReport = () => {
return request({ url: '/api/student/experiment-report/current', method: 'get' })
}
// 更新访问次数 // 更新访问次数
export const updateVisitCount = (params) => { export const updateVisitCount = (params) => {
return request({ return request({

@ -1,349 +1,20 @@
<!--
* @Author: qinzhenpen qzp1807@126.com
* @Date: 2025-04-24 15:46:26
* @LastEditors: qinzhenpen qzp1807@126.com
* @LastEditTime: 2025-05-20 18:32:30
* @FilePath: \e-commerce-internet\src\views\test-report\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template> <template>
<div class="test-report app-container"> <div class="test-report app-container">
<div class="report-title"> <h2>实验报告</h2>
<h2><Strong>实验报告</Strong></h2> <el-row class="student-info"><el-col :span="6">姓名:{{ userInfo.name }}</el-col><el-col :span="6">学号:{{ userInfo.username }}</el-col><el-col :span="8">班级:{{ report.teachingClassName || userInfo.className }}</el-col></el-row>
</div> <el-card><template #header><span>实训总成绩:{{ report.totalScore || 0 }} 分</span><span class="rank">班级排名:{{ report.rank || '-' }}</span></template>
<div class="report-centent"> <el-table :data="report.modules || []" border><el-table-column prop="moduleName" label="实训模块"/><el-table-column label="权重"><template #default="{row}">{{ percent(row.weight) }}</template></el-table-column><el-table-column prop="score" label="模块成绩"/><el-table-column label="任务完成"><template #default="{row}">{{ row.completedTaskCount }} / {{ row.totalTaskCount }}</template></el-table-column></el-table>
<el-row class="ptb10">
<el-col :span="15">
<el-row>
<el-col :span="6">
<label>姓名:</label>
<span>{{ userInfo.name }}</span>
</el-col>
<el-col :span="6">
<label>学号:</label>
<span>{{ userInfo.username }}</span>
</el-col>
<el-col :span="11">
<label>班级:</label>
<span>{{ userInfo.className }}</span>
</el-col>
</el-row>
</el-col>
</el-row>
<el-card class="score-card" style="margin-bottom: 20px">
<template #header>
<div class="score-header">
<span class="total-score">实时系统总成绩:{{ totalScore }}分</span>
<span class="class-rank ml20">班级排名:{{ rank || 0 }}</span>
</div>
</template>
<el-table :data="[scores]" border style="width: 100%" header-row-class-name="score-table-header">
<el-table-column v-for="item in scoreItems" :key="item.prop" :prop="item.prop" :label="`${item.label}(${item.weight})`" align="center">
<template #default="{ row }">{{ row[item.prop] }}分</template>
</el-table-column>
</el-table>
<div class="evaluation">
<p>指导建议与评价:{{ scoreresult }}</p>
</div>
</el-card> </el-card>
<el-card class="score-card" style="margin-bottom: 20px"> <el-card class="tasks"><template #header>实训任务明细</template>
<template #header> <el-table :data="report.tasks || []" border><el-table-column prop="taskName" label="任务名称"/><el-table-column prop="moduleName" label="所属模块"/><el-table-column prop="progressStatus" label="完成状态"/><el-table-column prop="aiAssessmentScore" label="AI 评分"><template #default="{row}">{{ row.aiAssessmentScore ?? '-' }}</template></el-table-column></el-table>
<div class="score-header">
<span class="total-score">实训系统访问次数:{{ totalvisitCount }}次</span>
<span class="class-rank ml20">班级排名:{{ rankInfo.visitCountRank || 0 }}</span>
</div>
</template>
<el-table :data="[visitCount]" border style="width: 100%" header-row-class-name="score-table-header">
<el-table-column v-for="item in VisitCountItems" :key="item.prop" :prop="item.prop" :label="item.label" align="center">
<template #default="{ row }">{{ row[item.prop] }}次</template>
</el-table-column>
</el-table>
<div class="evaluation">
<p>指导建议与评价:{{ visitCountresult || "当前暂无有效数据,无法生成评价建议" }}</p>
</div>
</el-card> </el-card>
<el-card class="score-card" style="margin-bottom: 20px">
<template #header>
<div class="score-header">
<span class="total-score">实训系统学习时长:{{ totalTime }}</span>
<span class="class-rank ml20">班级排名:{{ rankInfo.studyTimeRank || 0 }}</span>
</div>
</template>
<el-table :data="[times]" border style="width: 100%" header-row-class-name="score-table-header">
<el-table-column v-for="item in timeItems" :key="item.prop" :prop="item.prop" :label="item.label" align="center">
<template #default="{ row }">{{ row[item.prop] }}分钟</template>
</el-table-column>
</el-table>
<div class="evaluation">
<p>指导建议与评价:{{ timeresult || "当前暂无有效数据,无法生成评价建议" }}</p>
</div>
</el-card>
<el-table
:data="tableData"
border
style="width: 100%"
:header-cell-style="{
color: '#fff',
fontSize: '14px',
backgroundColor: '#02a7f0!important',
}"
>
<el-table-column prop="module" label="实训任务" align="center" />
<el-table-column prop="score" label="任务状态" align="center">
<template #default="{ row }">
<span>已完成</span>
</template>
</el-table-column>
<el-table-column prop="score" label="任务成绩" align="center" />
</el-table>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import * as indexApi from "@/api/index"; import { getCurrentExperimentReport } from '@/api/index';
import useUserStore from "@/store/modules/user"; import useUserStore from '@/store/modules/user';
import { onMounted, ref } from "vue"; const userInfo=useUserStore().userInfo; const report=ref({modules:[],tasks:[]});
const userStore = useUserStore(); const percent=(weight)=>weight==null?'-':`${Number(weight)*100}%`;
const userInfo = userStore.userInfo; onMounted(async()=>{const res=await getCurrentExperimentReport(); report.value=res?.data||{modules:[],tasks:[]};});
const totalScore = ref(0); // 实时系统总成绩
const rank = ref(0); // 班级排名
const scoreItems = ref([
{ prop: "market", label: "市场需求挖掘", weight: "25%", key: "oneScore", key1: "oneWeight" },
{ prop: "design", label: "产品规划", weight: "20%", key: "twoScore", key1: "twoWeight" },
{ prop: "test", label: "产品投放测试", weight: "20%", key: "threeScore", key1: "threeWeight" },
{ prop: "supply", label: "供应渠道管理", weight: "20%", key: "fourScore", key1: "fourWeight" },
{ prop: "evaluate", label: "产品评估与考核", weight: "15%", key: "fiveScore", key1: "fiveWeight" },
]);
const VisitCountItems = ref([
{ prop: "market", label: "市场需求挖掘" },
{ prop: "design", label: "产品规划" },
{ prop: "test", label: "产品投放测试" },
{ prop: "supply", label: "供应渠道管理" },
{ prop: "evaluate", label: "产品评估与考核" },
]);
const timeItems = ref([
{ prop: "market", label: "市场需求挖掘", key: "marketResearchTime" },
{ prop: "design", label: "产品规划", key: "productPlanningTime" },
{ prop: "test", label: "产品投放测试", key: "productLaunchTestTime" },
{ prop: "supply", label: "供应渠道管理", key: "supplyChannelTime" },
{ prop: "evaluate", label: "产品评估与考核", key: "productEvaluationTime" },
]);
const scores = ref({
market: 0,
design: 0,
test: 0,
supply: 0,
evaluate: 0,
});
const visitCount = ref({
market: 0,
design: 0,
test: 0,
supply: 0,
evaluate: 0,
});
const times = ref({
market: 0,
design: 0,
test: 0,
supply: 0,
evaluate: 0,
});
// 时间总和
const totalTime = computed(() => {
let time = 0;
for (let key in times.value) {
time += times.value[key];
}
return time;
});
// 时间总和
const totalvisitCount = computed(() => {
let time = 0;
for (let key in visitCount.value) {
time += visitCount.value[key];
}
return time;
});
const rankInfo = ref({});
const tableData = ref([]);
const scoreresult = ref("");
const visitCountresult = ref("");
const timeresult = ref("");
// 查询实训详情
const getTrainDetail = () => {
indexApi.getStuModuleDetail({ userId: userInfo.userId }).then((res) => {
if (!res.data) return;
tableData.value = res.data;
});
};
// 查询学生权重
const getwight = () => {
indexApi
.getStuWeightScore({
userId: userStore.userInfo.userId,
classId: userStore.userInfo.classId,
schoolId: userStore.userInfo.schoolId,
})
.then((res) => {
if (!res.data) return;
rank.value = res.data?.stuRank;
scoreItems.value.forEach((item) => {
item.weight = res?.data[item.key1] * 100 + "%";
scores.value[item.prop] = res?.data[item.key];
});
for (const key in scores.value) {
totalScore.value += scores.value[key];
}
totalScore.value = totalScore.value.toFixed(2);
scoreresult.value = generateEvaluation(scores.value, scoreItems.value, `第${res.data.stuRank}名`);
});
};
// 查询访问次数
const getAccessCount = () => {
indexApi
.getUserVisitCount({
userId: userStore.userInfo.userId,
schoolId: userStore.userInfo.schoolId,
})
.then((res) => {
if (!res.data) return;
res?.data.forEach((item) => {
let items = VisitCountItems.value.find((i) => i.label == item.project);
if (item) {
visitCount.value[items.prop] = item.visitCount;
}
});
visitCountresult.value = generateVisitAnalysis(visitCount.value, VisitCountItems.value);
});
};
// 查看学习时间
const getStudyTime = () => {
indexApi
.getStudyTime({
userId: userStore.userInfo.userId,
})
.then((res) => {
if (!res.data) return;
timeItems.value.forEach((item) => {
times.value[item.prop] = res?.data[item.key];
});
});
};
// 查询用户排名
const getRank = () => {
indexApi
.getUserRank({
userId: userStore.userInfo.userId,
})
.then((res) => {
if (!res.data) return;
rankInfo.value = res?.data;
timeresult.value = generateTimeAnalysis(times.value, timeItems.value, scores.value, scoreItems.value, `第${rankInfo.value.studyTimeRank}名`);
});
};
function generateEvaluation(scores, scoreItems, rankText = "第1名") {
const entries = scoreItems.map((item) => ({
label: item.label,
score: scores[item.prop],
}));
const allZero = entries.every((e) => !e.score || e.score === 0);
if (allZero) return "当前暂无有效评分数据,无法生成评价建议。";
const maxScore = Math.max(...entries.map((e) => e.score));
const maxItems = entries.filter((e) => e.score === maxScore);
const minScore = Math.min(...entries.map((e) => e.score));
const minItems = entries.filter((e) => e.score === minScore);
const maxText = maxItems.map((e) => `**${e.label}(${e.score}分)**`).join("、");
const minText = minItems.map((e) => `**${e.label}(${e.score}分)**`).join("、");
return `该生总成绩在班级中排名为${rankText}。其中,${maxText}表现突出,体现了良好的能力。然而,在${minText}方面得分相对较低,建议加强相关方面的学习,以提升综合能力。`;
}
function generateVisitAnalysis(visitCounts, visitItems) {
const entries = visitItems.map((item) => ({
label: item.label,
count: visitCounts[item.prop],
}));
const allZero = entries.every((e) => !e.count || e.count === 0);
if (allZero) return "当前暂无任何模块被访问,建议尽快引导用户进入系统进行操作。";
entries.sort((a, b) => b.count - a.count);
const maxCount = entries[0].count;
const minCount = entries[entries.length - 1].count;
const mostVisited = entries.filter((e) => e.count === maxCount);
const leastVisited = entries.filter((e) => e.count === minCount);
const mostText = mostVisited.map((e) => `“${e.label}”(${e.count}次)`).join("、");
const leastText = leastVisited.map((e) => `“${e.label}”(${e.count}次)`).join("、");
let result = `模块使用情况分析如下:其中,${mostText}访问频率最高,说明用户对此类内容关注较多。`;
if (minCount !== maxCount) {
result += ` 相对而言,${leastText}访问次数较少,建议关注其使用引导或内容优化。`;
}
return result;
}
function generateTimeAnalysis(times, timeItems, scores, scoreItems, rankText = "第1名") {
const timeEntries = timeItems.map((item) => ({
prop: item.prop,
label: item.label,
time: times[item.prop] || 0,
score: scores[item.prop] ?? null,
}));
const allZero = timeEntries.every((e) => !e.time || e.time === 0);
if (allZero) return "当前暂无有效学习时长数据,建议尽快进入系统进行学习。";
const maxTime = Math.max(...timeEntries.map((e) => e.time));
const maxTimeItems = timeEntries.filter((e) => e.time === maxTime);
const resultParts = [`该生实训系统学习时长在班级排名为${rankText}。`];
const timeText = maxTimeItems.map((e) => `**${e.label}**学习时长最长`).join(",");
resultParts.push(`其中,${timeText}。`);
maxTimeItems.forEach((item) => {
if (item.score !== null && item.score < 10) {
resultParts.push(`${item.label}的成绩与该模块的学习时长不匹配,建议优化学习策略,提高学习效率。`);
}
});
return resultParts.join("");
}
onMounted(() => {
getTrainDetail();
getwight();
getAccessCount();
getStudyTime();
getRank();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>.student-info{margin:20px 0}.rank{margin-left:24px}.tasks{margin-top:20px}</style>
.test-report {
color: #ffffff;
.report-title {
margin: 2rem 0;
text-align: center;
}
.report-centent {
padding: 0px 10rem;
.el-row {
.el-col {
label {
width: 100px !important;
margin-right: 10px;
}
}
}
:deep(.score-card) {
box-shadow: none !important;
border-color: #02a7f0 !important;
.el-card__header {
background: #02a7f0 !important;
}
.score-header {
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 5px;
color: #ffffff;
}
}
}
}
</style>

@ -0,0 +1 @@
const assert=require('assert'),fs=require('fs'),path=require('path'); const root=path.resolve(__dirname,'..'); const api=fs.readFileSync(path.join(root,'src/api/index.js'),'utf8'); const page=fs.readFileSync(path.join(root,'src/views/test-report/index.vue'),'utf8'); assert(/getCurrentExperimentReport/.test(api)); assert(/experiment-report\/current/.test(api)); assert(/getCurrentExperimentReport/.test(page)); assert(/completedTaskCount/.test(page)&&/aiAssessmentScore/.test(page)); assert(!/getStuWeightScore|getUserVisitCount|getStudyTime|getUserRank|getStuModuleDetail/.test(page)); console.log('student experiment report static checks passed');
Loading…
Cancel
Save