merge: score center class access and teacher scoring

# Conflicts:
#	src/api/teacher.js
#	src/views/teacherEnd/score/index.vue
bugfix/ai-assessment-evidence-rescore
chenyuan 1 month ago
commit e86d97e91c

@ -635,6 +635,12 @@ export function getOwnedTeachingClasses() {
method: "get", method: "get",
}); });
} }
export function getAccessibleTeachingClasses() {
return request({
url: "/api/weight/accessibleTeachingClasses",
method: "get",
});
}
export function getTeachingClassWeight(teachingClassId) { export function getTeachingClassWeight(teachingClassId) {
return request({ return request({
url: "/api/weight/selectTeachingClassWeight", url: "/api/weight/selectTeachingClassWeight",
@ -693,7 +699,7 @@ export function getScoreDetail(params) {
params, params,
}); });
} }
// 保存老师对学生的人工评分 // 保存班级创建教师对学生的人工评分
export function saveTeacherScore(params) { export function saveTeacherScore(params) {
return request({ return request({
url: "/api/weight/saveTeacherScore", url: "/api/weight/saveTeacherScore",

@ -28,14 +28,18 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="班级:"> <el-form-item label="班级:">
<el-select v-model="scoreParams.classId" placeholder="" clearable @change="getList"> <el-select v-model="scoreParams.classId" placeholder="请选择班级" @change="handleClassChange">
<el-option v-for="school in teachingClassList" :key="school.schoolClassId" :label="school.className" :value="school.schoolClassId" /> <el-option v-for="school in teachingClassList" :key="school.schoolClassId" :label="school.className" :value="school.schoolClassId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="exportExcel">导出</el-button> <el-button type="primary" :disabled="!scoreParams.classId" @click="exportExcel">导出</el-button>
<el-button type="success" @click="scoreweight">权重设置</el-button> <span :title="classActionHint">
<el-button type="success" @click="updateScore">成绩更新</el-button> <el-button type="success" :disabled="!canManageSelectedClass" @click="scoreweight">权重设置</el-button>
</span>
<span :title="classActionHint">
<el-button type="success" :disabled="!canManageSelectedClass" @click="updateScore">成绩更新</el-button>
</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="table-info1"> <div class="table-info1">
@ -48,7 +52,7 @@
<el-table-column prop="score" label="参考成绩" align="center" /> <el-table-column prop="score" label="参考成绩" align="center" />
<el-table-column label="老师评分" align="center" min-width="190"> <el-table-column label="老师评分" align="center" min-width="190">
<template #default="{ row }"> <template #default="{ row }">
<div class="teacher-score-editor"> <div v-if="canManageSelectedClass" class="teacher-score-editor">
<el-input-number <el-input-number
v-model="row.teacherScoreDraft" v-model="row.teacherScoreDraft"
:min="0" :min="0"
@ -65,6 +69,10 @@
@click="saveTeacherScore(row)" @click="saveTeacherScore(row)"
>保存</el-button> >保存</el-button>
</div> </div>
<div v-else class="teacher-score-readonly">
<strong>{{ formatTeacherScore(row.teacherScore) }}</strong>
<small>只读</small>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="实训进度" align="center"> <el-table-column label="实训进度" align="center">
@ -163,9 +171,12 @@ import * as indexApi from "@/api/teacher";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
import useUserStore from "@/store/modules/user"; import useUserStore from "@/store/modules/user";
const userStore = useUserStore(); const userStore = useUserStore();
const currentUserId = computed(() => String(userStore.userInfo.userId || ""));
const workingCapitalList = ref([]); const workingCapitalList = ref([]);
const loading = ref(false); const loading = ref(false);
const savingScoreUserId = ref(""); const savingScoreUserId = ref("");
const classList = ref([]);
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING"));
const scoreParams = ref({ const scoreParams = ref({
classId: "", classId: "",
index: 1, index: 1,
@ -183,6 +194,17 @@ const WeightStart = ref(false);
// 权重 // 权重
const workingCapitalTotal = ref(0); const workingCapitalTotal = ref(0);
const scoreWeightObj = ref({}); const scoreWeightObj = ref({});
const selectedTeachingClass = computed(() => teachingClassList.value.find(
(item) => item.schoolClassId === scoreParams.value.classId
));
const canManageSelectedClass = computed(() =>
Boolean(selectedTeachingClass.value)
&& String(selectedTeachingClass.value.createdBy || "") === currentUserId.value
);
const classActionHint = computed(() => canManageSelectedClass.value
? ""
: "权重设置和成绩更新仅限班级创建老师操作"
);
const formatTrainingProgress = (row) => { const formatTrainingProgress = (row) => {
if (row.trainingProgress === null || row.trainingProgress === undefined) return "--"; if (row.trainingProgress === null || row.trainingProgress === undefined) return "--";
const percentage = Number(row.trainingProgress); const percentage = Number(row.trainingProgress);
@ -214,6 +236,11 @@ const buildScoreQuery = () => {
}; };
}; };
const getList = () => { const getList = () => {
if (!scoreParams.value.classId) {
workingCapitalList.value = [];
workingCapitalTotal.value = 0;
return;
}
loading.value = true; loading.value = true;
indexApi.getScoreList(buildScoreQuery()).then((res) => { indexApi.getScoreList(buildScoreQuery()).then((res) => {
workingCapitalList.value = (res.data.list || []).map((row) => ({ workingCapitalList.value = (res.data.list || []).map((row) => ({
@ -223,8 +250,7 @@ const getList = () => {
: Number(row.teacherScore), : Number(row.teacherScore),
})); }));
workingCapitalTotal.value = res.data.total; workingCapitalTotal.value = res.data.total;
loading.value = false; }).finally(() => (loading.value = false));
});
}; };
const scoreweight = () => { const scoreweight = () => {
if (!scoreParams.value.classId) { if (!scoreParams.value.classId) {
@ -257,17 +283,32 @@ const workingCapitalWeightHandle = () => {
} }
}); });
}; };
const classList = ref([]); const getClassList = async () => {
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING")); try {
const getClassList = () => { const res = await indexApi.getAccessibleTeachingClasses();
indexApi.getOwnedTeachingClasses().then((res) => { classList.value = res.data || [];
classList.value = res.data;
if (scoreParams.value.classId && !teachingClassList.value.some((item) => item.schoolClassId === scoreParams.value.classId)) { if (scoreParams.value.classId && !teachingClassList.value.some((item) => item.schoolClassId === scoreParams.value.classId)) {
scoreParams.value.classId = ""; scoreParams.value.classId = "";
} }
}); if (!scoreParams.value.classId) {
const ownClass = teachingClassList.value.find((item) => String(item.createdBy || "") === currentUserId.value);
scoreParams.value.classId = (ownClass || teachingClassList.value[0])?.schoolClassId || "";
}
getList();
} catch (error) {
workingCapitalList.value = [];
workingCapitalTotal.value = 0;
}
};
const handleClassChange = () => {
scoreParams.value.index = 1;
getList();
}; };
const exportExcel = () => { const exportExcel = () => {
if (!scoreParams.value.classId) {
proxy.$modal.msgWarning("请选择教学班后再导出成绩");
return;
}
const { index, size, ...query } = buildScoreQuery(); const { index, size, ...query } = buildScoreQuery();
indexApi.exportScoreList(query).then((res) => { indexApi.exportScoreList(query).then((res) => {
// 打开怎么是空的 // 打开怎么是空的
@ -282,7 +323,7 @@ const exportExcel = () => {
}; };
const getClassScoreDetail = (row) => { const getClassScoreDetail = (row) => {
if (!scoreParams.value.classId) { if (!scoreParams.value.classId) {
proxy.$modal.msgWarning("请选择自己的教学班后再查看成绩详情"); proxy.$modal.msgWarning("请选择教学班后再查看成绩详情");
return; return;
} }
scoreDetailStudent.value = { scoreDetailStudent.value = {
@ -335,7 +376,6 @@ const updateScore = () => {
}; };
// 拿到当前路由信息 // 拿到当前路由信息
onMounted(() => { onMounted(() => {
getList();
getClassList(); getClassList();
}); });
</script> </script>
@ -380,4 +420,23 @@ onMounted(() => {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
} }
.teacher-score-readonly {
display: inline-flex;
align-items: center;
gap: 8px;
strong {
color: #63dcff;
font-size: 16px;
font-variant-numeric: tabular-nums;
}
small {
padding: 2px 7px;
color: rgba(190, 218, 231, 0.68);
border: 1px solid rgba(96, 172, 207, 0.28);
border-radius: 999px;
}
}
</style> </style>

@ -0,0 +1,21 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const root = path.resolve(__dirname, "..");
const page = fs.readFileSync(path.join(root, "src/views/teacherEnd/score/index.vue"), "utf8");
const api = fs.readFileSync(path.join(root, "src/api/teacher.js"), "utf8");
assert.match(page, /label="老师评分"/);
assert.match(page, /teacherScoreDraft/);
assert.match(page, /canManageSelectedClass/);
assert.match(page, /createdBy/);
assert.match(page, /saveTeacherScore/);
assert.match(page, /老师评分必须为0至100之间的数值/);
assert.match(page, /ownClass \|\| teachingClassList\.value\[0\]/);
assert.match(page, /getAccessibleTeachingClasses/);
assert.doesNotMatch(page, /onMounted\(\(\) => \{\s*getList\(\);\s*getClassList/);
assert.match(api, /url: "\/api\/weight\/saveTeacherScore"/);
assert.match(api, /url: "\/api\/weight\/accessibleTeachingClasses"/);
console.log("score center class access and teacher score contract passed");

@ -8,7 +8,7 @@ const api = fs.readFileSync('src/api/teacher.js', 'utf8');
assert(api.includes('/api/weight/ownedTeachingClasses')); assert(api.includes('/api/weight/ownedTeachingClasses'));
assert(api.includes('/api/weight/selectTeachingClassWeight')); assert(api.includes('/api/weight/selectTeachingClassWeight'));
assert(api.includes('/api/weight/updateTeachingClassWeight')); assert(api.includes('/api/weight/updateTeachingClassWeight'));
assert(page.includes('getOwnedTeachingClasses')); assert(page.includes('getAccessibleTeachingClasses'));
assert(page.includes('互联网产品开发基础认知')); assert(page.includes('互联网产品开发基础认知'));
assert(page.includes('市场洞察与需求分析')); assert(page.includes('市场洞察与需求分析'));
assert(page.includes('产品规划与设计')); assert(page.includes('产品规划与设计'));

Loading…
Cancel
Save