|
|
|
|
|
<!--
|
|
|
|
|
|
* @Author: qinzhenpen qzp1807@126.com
|
|
|
|
|
|
* @Date: 2024-10-31 14:59:14
|
|
|
|
|
|
* @LastEditors: qinzhenpen qzp1807@126.com
|
|
|
|
|
|
* @LastEditTime: 2025-05-16 18:25:56
|
|
|
|
|
|
* @FilePath: \corporate-finance\src\views\teacher\working-capital.vue
|
|
|
|
|
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
|
|
|
-->
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-container score" v-loading="loading">
|
|
|
|
|
|
<el-form :inline="true" class="demo-form-inline">
|
|
|
|
|
|
<el-form-item label="用户名:">
|
|
|
|
|
|
<el-input v-model="scoreParams.name" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>
|
|
|
|
|
|
<el-button type="primary" link color="#51bcb5" @click="getList">
|
|
|
|
|
|
<!-- <img src="@/assets/images/tea搜索.png" alt="" /> -->
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="学号:">
|
|
|
|
|
|
<el-input v-model="scoreParams.studentId" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>
|
|
|
|
|
|
<el-button type="primary" link color="#51bcb5" @click="getList">
|
|
|
|
|
|
<!-- <img src="@/assets/images/tea搜索.png" alt="" /> -->
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="班级:">
|
|
|
|
|
|
<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-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" :disabled="!scoreParams.classId" @click="exportExcel">导出</el-button>
|
|
|
|
|
|
<span :title="classActionHint">
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<div class="table-info1">
|
|
|
|
|
|
<div class="table-hide">
|
|
|
|
|
|
<el-table :data="workingCapitalList" stripe border style="width: 100%">
|
|
|
|
|
|
<el-table-column prop="stuRank" label="排名" align="center" width="120" />
|
|
|
|
|
|
<el-table-column prop="name" label="用户名" align="center" />
|
|
|
|
|
|
<el-table-column prop="username" label="学号" align="center" />
|
|
|
|
|
|
<el-table-column prop="className" label="班级" align="center" />
|
|
|
|
|
|
<el-table-column prop="score" label="参考成绩" align="center" />
|
|
|
|
|
|
<el-table-column label="老师评分" align="center" min-width="130">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<div class="teacher-score-readonly">
|
|
|
|
|
|
<strong>{{ formatTeacherScore(row.teacherScore) }}</strong>
|
|
|
|
|
|
<small>自动汇总</small>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="实训进度" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<span :title="trainingProgressTitle(row)">
|
|
|
|
|
|
已完成 {{ row.completedTaskCount || 0 }}/{{ row.totalTaskCount || 0 }}
|
|
|
|
|
|
<small v-if="row.trainingProgress !== null && row.trainingProgress !== undefined">({{ formatTrainingProgress(row) }})</small>
|
|
|
|
|
|
<small v-else>(--)</small>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="成绩详情" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-button link type="success" @click="getClassScoreDetail(row)">查看详情</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<pagination v-show="workingCapitalTotal > 0" :total="workingCapitalTotal" v-model:page="scoreParams.index" v-model:limit="scoreParams.size" @pagination="getList" />
|
|
|
|
|
|
<el-dialog v-model="WeightStart" title="权重设置" width="500" class="F_dialog teacher-dialog">
|
|
|
|
|
|
<el-form ref="ruleFormRef" :model="scoreWeightObj" class="demo-form" :rules="rules">
|
|
|
|
|
|
<el-form-item label="互联网产品开发基础认知:" prop="foundationWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.foundationWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="市场洞察与需求分析:" prop="marketInsightWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.marketInsightWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="产品规划与设计:" prop="planningDesignWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.planningDesignWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="产品开发与测试验证:" prop="developmentValidationWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.developmentValidationWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="产品上线与运营推广:" prop="launchOperationWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.launchOperationWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="综合实训:" prop="comprehensiveTrainingWeight" label-width="200">
|
|
|
|
|
|
<el-input v-model.number="scoreWeightObj.comprehensiveTrainingWeight" placeholder="" clearable>
|
|
|
|
|
|
<template #suffix>%</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<div class="dialog-footer" style="text-align: center">
|
|
|
|
|
|
<el-button type="primary" @click="workingCapitalWeightHandle">确定</el-button>
|
|
|
|
|
|
<el-button @click="WeightStart = false">取消</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
<el-dialog v-model="scoreDetailsStart" title="任务评阅" width="60%" class="F_dialog teacher-dialog">
|
|
|
|
|
|
<div class="score-detail-summary">
|
|
|
|
|
|
<div class="score-detail-summary__item">
|
|
|
|
|
|
<span>学生姓名</span>
|
|
|
|
|
|
<strong>{{ scoreDetailStudent.name || "--" }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="score-detail-summary__item">
|
|
|
|
|
|
<span>AI评测总分</span>
|
|
|
|
|
|
<strong>{{ formatScore(scoreDetailStudent.aiTotalScore) }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="score-detail-summary__item">
|
|
|
|
|
|
<span>老师评分</span>
|
|
|
|
|
|
<strong>{{ formatTeacherScore(scoreDetailStudent.teacherScore) }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="tableData"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
height="400"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#02a7f0!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="taskName" label="实训任务" align="center" />
|
|
|
|
|
|
<el-table-column prop="progressStatus" label="任务状态" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<span>{{ row.progressStatus === "COMPLETED" ? "已完成" : row.progressStatus === "IN_PROGRESS" ? "进行中" : "未开始" }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="aiAssessmentScore" label="AI评测成绩" align="center" />
|
|
|
|
|
|
<el-table-column label="教师评分" align="center" min-width="190">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<div v-if="canManageSelectedClass && canScoreTask(row)" class="task-score-editor">
|
|
|
|
|
|
<el-input-number
|
|
|
|
|
|
v-model="row.teacherScoreDraft"
|
|
|
|
|
|
:min="0"
|
|
|
|
|
|
:max="100"
|
|
|
|
|
|
:precision="0"
|
|
|
|
|
|
:step="1"
|
|
|
|
|
|
:controls="false"
|
|
|
|
|
|
placeholder="0-100"
|
|
|
|
|
|
:aria-label="`给${row.taskName}评分`"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-button type="primary" size="small" :loading="savingTaskId === row.taskId" @click="saveTaskTeacherScore(row)">保存</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span v-else>{{ formatTeacherScore(row.teacherScore) }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<pagination
|
|
|
|
|
|
v-show="taskDetailTotal > 0"
|
|
|
|
|
|
:total="taskDetailTotal"
|
|
|
|
|
|
v-model:page="taskDetailPagination.index"
|
|
|
|
|
|
v-model:limit="taskDetailPagination.size"
|
|
|
|
|
|
@pagination="getClassScoreDetail"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import * as indexApi from "@/api/teacher";
|
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
const currentUserId = computed(() => String(userStore.userInfo.userId || ""));
|
|
|
|
|
|
const workingCapitalList = ref([]);
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
const savingTaskId = ref("");
|
|
|
|
|
|
const classList = ref([]);
|
|
|
|
|
|
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING"));
|
|
|
|
|
|
const scoreParams = ref({
|
|
|
|
|
|
classId: "",
|
|
|
|
|
|
index: 1,
|
|
|
|
|
|
size: 10,
|
|
|
|
|
|
schoolId: userStore.userInfo.schoolId,
|
|
|
|
|
|
});
|
|
|
|
|
|
const scoreDetailsStart = ref(false);
|
|
|
|
|
|
const tableData = ref([]);
|
|
|
|
|
|
const taskDetailRows = ref([]);
|
|
|
|
|
|
const taskDetailTotal = ref(0);
|
|
|
|
|
|
const taskDetailPagination = ref({ index: 1, size: 10 });
|
|
|
|
|
|
const scoreDetailStudent = ref({
|
|
|
|
|
|
name: "",
|
|
|
|
|
|
aiTotalScore: null,
|
|
|
|
|
|
teacherScore: null,
|
|
|
|
|
|
});
|
|
|
|
|
|
const WeightStart = ref(false);
|
|
|
|
|
|
// 权重
|
|
|
|
|
|
const workingCapitalTotal = ref(0);
|
|
|
|
|
|
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) => {
|
|
|
|
|
|
if (row.trainingProgress === null || row.trainingProgress === undefined) return "--";
|
|
|
|
|
|
const percentage = Number(row.trainingProgress);
|
|
|
|
|
|
if (!Number.isFinite(percentage)) return "--";
|
|
|
|
|
|
return `${percentage.toFixed(2).replace(/\.00$/, "")}%`;
|
|
|
|
|
|
};
|
|
|
|
|
|
const trainingProgressTitle = (row) => {
|
|
|
|
|
|
if (row.trainingProgress === null || row.trainingProgress === undefined) return "";
|
|
|
|
|
|
return `已完成 ${row.completedTaskCount || 0}/${row.totalTaskCount || 0} 项`;
|
|
|
|
|
|
};
|
|
|
|
|
|
const formatScore = (score) => {
|
|
|
|
|
|
if (score === null || score === undefined || score === "") return "--";
|
|
|
|
|
|
const value = Number(score);
|
|
|
|
|
|
return Number.isFinite(value) ? value.toFixed(2).replace(/\.00$/, "") : "--";
|
|
|
|
|
|
};
|
|
|
|
|
|
const formatTeacherScore = (score) => score === null || score === undefined || score === ""
|
|
|
|
|
|
? "待评阅"
|
|
|
|
|
|
: formatScore(score);
|
|
|
|
|
|
const normalizeTaskScoreRow = (row) => ({
|
|
|
|
|
|
...row,
|
|
|
|
|
|
teacherScoreDraft: row.teacherScore === null || row.teacherScore === undefined
|
|
|
|
|
|
? null
|
|
|
|
|
|
: Number(row.teacherScore),
|
|
|
|
|
|
});
|
|
|
|
|
|
const weightFields = ["foundationWeight", "marketInsightWeight", "planningDesignWeight", "developmentValidationWeight", "launchOperationWeight", "comprehensiveTrainingWeight"];
|
|
|
|
|
|
const rules = Object.fromEntries(weightFields.map((field) => [field, [
|
|
|
|
|
|
{ required: true, message: "请输入权重", trigger: "blur" },
|
|
|
|
|
|
{ type: "number", message: "权重必须为数字值" },
|
|
|
|
|
|
]]));
|
|
|
|
|
|
const buildScoreQuery = () => {
|
|
|
|
|
|
const { studentId, ...query } = scoreParams.value;
|
|
|
|
|
|
return {
|
|
|
|
|
|
...query,
|
|
|
|
|
|
userName: studentId,
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
const getList = () => {
|
|
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
|
|
workingCapitalList.value = [];
|
|
|
|
|
|
workingCapitalTotal.value = 0;
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
return indexApi.getScoreList(buildScoreQuery()).then((res) => {
|
|
|
|
|
|
workingCapitalList.value = res.data.list || [];
|
|
|
|
|
|
workingCapitalTotal.value = res.data.total;
|
|
|
|
|
|
}).finally(() => (loading.value = false));
|
|
|
|
|
|
};
|
|
|
|
|
|
const scoreweight = () => {
|
|
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请选择自己的教学班后再设置权重");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
WeightStart.value = true;
|
|
|
|
|
|
indexApi.getTeachingClassWeight(scoreParams.value.classId).then((res) => {
|
|
|
|
|
|
scoreWeightObj.value = res.data;
|
|
|
|
|
|
weightFields.forEach((key) => (scoreWeightObj.value[key] = Number(scoreWeightObj.value[key]) * 100));
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
const workingCapitalWeightHandle = () => {
|
|
|
|
|
|
proxy.$refs.ruleFormRef.validate((valid) => {
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
let params = {};
|
|
|
|
|
|
let sum = 0;
|
|
|
|
|
|
weightFields.forEach((key) => {
|
|
|
|
|
|
params[key] = Number(scoreWeightObj.value[key]) / 100;
|
|
|
|
|
|
sum += params[key];
|
|
|
|
|
|
});
|
|
|
|
|
|
if (Math.abs(sum - 1) > 0.000001) {
|
|
|
|
|
|
proxy.$message.error("权重合计为100%");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
indexApi.updateTeachingClassWeight(scoreParams.value.classId, params).then((res) => {
|
|
|
|
|
|
proxy.$message.success("权重设置成功");
|
|
|
|
|
|
WeightStart.value = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
const getClassList = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await indexApi.getAccessibleTeachingClasses();
|
|
|
|
|
|
classList.value = res.data || [];
|
|
|
|
|
|
if (scoreParams.value.classId && !teachingClassList.value.some((item) => item.schoolClassId === 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 = () => {
|
|
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请选择教学班后再导出成绩");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const { index, size, ...query } = buildScoreQuery();
|
|
|
|
|
|
indexApi.exportScoreList(query).then((res) => {
|
|
|
|
|
|
// 打开怎么是空的
|
|
|
|
|
|
const blob = new Blob([res], { type: "application/vnd.ms-excel" });
|
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
|
|
const link = document.createElement("a");
|
|
|
|
|
|
link.href = url;
|
|
|
|
|
|
link.download = "成绩表.xlsx";
|
|
|
|
|
|
link.click();
|
|
|
|
|
|
URL.revokeObjectURL(url);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
const refreshScoreDetail = () => {
|
|
|
|
|
|
if (!scoreDetailStudent.value.userId || !scoreParams.value.classId) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
return indexApi.getScoreDetail({
|
|
|
|
|
|
userId: scoreDetailStudent.value.userId,
|
|
|
|
|
|
classId: scoreParams.value.classId,
|
|
|
|
|
|
...taskDetailPagination.value,
|
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
|
const responseData = res.data || [];
|
|
|
|
|
|
if (Array.isArray(responseData)) {
|
|
|
|
|
|
taskDetailRows.value = responseData.map(normalizeTaskScoreRow);
|
|
|
|
|
|
taskDetailTotal.value = taskDetailRows.value.length;
|
|
|
|
|
|
const start = (taskDetailPagination.value.index - 1) * taskDetailPagination.value.size;
|
|
|
|
|
|
tableData.value = taskDetailRows.value.slice(start, start + taskDetailPagination.value.size);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
tableData.value = (responseData.list || []).map(normalizeTaskScoreRow);
|
|
|
|
|
|
taskDetailTotal.value = Number(responseData.total) || tableData.value.length;
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
const getClassScoreDetail = (row) => {
|
|
|
|
|
|
if (row?.userId) {
|
|
|
|
|
|
taskDetailPagination.value.index = 1;
|
|
|
|
|
|
scoreDetailStudent.value = {
|
|
|
|
|
|
userId: row.userId,
|
|
|
|
|
|
name: row.name,
|
|
|
|
|
|
aiTotalScore: row.score,
|
|
|
|
|
|
teacherScore: row.teacherScore,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!scoreParams.value.classId || !scoreDetailStudent.value.userId) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请选择教学班后再查看成绩详情");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
refreshScoreDetail().then(() => (scoreDetailsStart.value = true));
|
|
|
|
|
|
};
|
|
|
|
|
|
const canScoreTask = (row) => Boolean(row.submitted) || row.progressStatus === "COMPLETED";
|
|
|
|
|
|
const saveTaskTeacherScore = (row) => {
|
|
|
|
|
|
if (row.teacherScoreDraft === null || row.teacherScoreDraft === undefined || row.teacherScoreDraft === "") {
|
|
|
|
|
|
proxy.$message.error("请输入教师评分");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const score = Number(row.teacherScoreDraft);
|
|
|
|
|
|
if (!Number.isInteger(score) || score < 0 || score > 100) {
|
|
|
|
|
|
proxy.$message.error("教师评分必须为0至100之间的整数");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
savingTaskId.value = row.taskId;
|
|
|
|
|
|
indexApi.saveTaskTeacherScore({
|
|
|
|
|
|
classId: scoreParams.value.classId,
|
|
|
|
|
|
studentUserId: scoreDetailStudent.value.userId,
|
|
|
|
|
|
taskId: row.taskId,
|
|
|
|
|
|
teacherScore: score,
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
row.teacherScore = score;
|
|
|
|
|
|
row.teacherScoreDraft = score;
|
|
|
|
|
|
proxy.$message.success("任务评分保存成功");
|
|
|
|
|
|
return Promise.all([refreshScoreDetail(), getList()]);
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
const latest = workingCapitalList.value.find((item) => item.userId === scoreDetailStudent.value.userId);
|
|
|
|
|
|
if (latest) {
|
|
|
|
|
|
scoreDetailStudent.value.teacherScore = latest.teacherScore;
|
|
|
|
|
|
}
|
|
|
|
|
|
}).finally(() => (savingTaskId.value = ""));
|
|
|
|
|
|
};
|
|
|
|
|
|
// 更新成绩
|
|
|
|
|
|
const updateScore = () => {
|
|
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请选择自己的教学班后再更新成绩");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
const params = { schoolId: userStore.userInfo.schoolId };
|
|
|
|
|
|
if (scoreParams.value.classId) {
|
|
|
|
|
|
params.teachingClassId = scoreParams.value.classId;
|
|
|
|
|
|
}
|
|
|
|
|
|
indexApi.doRankOne(params).then((res) => {
|
|
|
|
|
|
proxy.$message.success("成绩更新成功");
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
getList();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
// 拿到当前路由信息
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
getClassList();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.score {
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
height: calc(100vh - 70px);
|
|
|
|
|
|
}
|
|
|
|
|
|
.task-score-editor {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input-number) {
|
|
|
|
|
|
width: 104px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.score-detail-summary {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.score-detail-summary__item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
padding: 14px 18px;
|
|
|
|
|
|
background: rgba(2, 167, 240, 0.08);
|
|
|
|
|
|
border: 1px solid rgba(2, 167, 240, 0.3);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
|
color: #7f9cab;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
strong {
|
|
|
|
|
|
color: #02a7f0;
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
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>
|