|
|
@@ -60,16 +60,10 @@
|
|
|
<span style="font-weight: bold; font-size: larger">
|
|
|
全部测试
|
|
|
</span>
|
|
|
-<!-- <div class="course-type-selector">-->
|
|
|
-<!-- <el-select v-model="examType" class="m-2" placeholder="测试类型">-->
|
|
|
-<!-- <el-option-->
|
|
|
-<!-- v-for="item in examTypeOptions"-->
|
|
|
-<!-- :key="item.value"-->
|
|
|
-<!-- :label="item.label"-->
|
|
|
-<!-- :value="item.value"-->
|
|
|
-<!-- />-->
|
|
|
-<!-- </el-select>-->
|
|
|
-<!-- </div>-->
|
|
|
+ <el-button class="ml-16" plain type="primary"
|
|
|
+ @click="dialogExamTitle='新增测试';dialogExamVisible=true;switchExamDisabled=false;">
|
|
|
+ 新建测试
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
<el-tabs v-model="activeTestPaneName">
|
|
|
@@ -96,6 +90,8 @@
|
|
|
</el-tabs>
|
|
|
</el-card>
|
|
|
</el-col>
|
|
|
+
|
|
|
+<!-- 创建门户课程-->
|
|
|
<el-dialog v-model="dialogVisible" :before-close="handleFormCancel" :width="700" :title="dialogTitle">
|
|
|
<el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
|
|
|
<el-form-item label="课程名称" prop="name">
|
|
|
@@ -171,6 +167,140 @@
|
|
|
<el-button @click="handleFormCancel">取消</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+
|
|
|
+<!-- 创建Coder考试-->
|
|
|
+ <el-dialog v-model="dialogExamVisible" :before-close="handleExamFormCancel" :width="1200" :title="dialogExamTitle">
|
|
|
+ <el-form ref="examFormRef" :model="examForm" :rules="examFormRules" label-width="150px">
|
|
|
+ <el-form-item label="测试种类" prop="type">
|
|
|
+ <el-select v-model="examForm.examType" class="m-2" placeholder="请选择种类">
|
|
|
+ <el-option
|
|
|
+ v-for="item in examCategoryOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="所属课程" prop="course">
|
|
|
+ <el-select v-model="examForm.course" class="m-2" placeholder="请选择课程">
|
|
|
+ <el-option
|
|
|
+ v-for="item in courseOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="测试名称" prop="name">
|
|
|
+ <el-input class="course-form-item" v-model="examForm.name" placeholder="请输入课程名称"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="测试时间" prop="range">
|
|
|
+ <el-date-picker v-model="examForm.range" range-separator="至"
|
|
|
+ start-placeholder="开始时间" end-placeholder="结束时间" type="datetimerange">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="允许学生查看控制台">
|
|
|
+ <el-switch v-model="examForm.isConsoleOutputShownForStu" :disabled="switchExamDisabled" active-text="开启" inactive-text="不开启"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="启用代码分析">
|
|
|
+ <el-switch v-model="examForm.isCodeAnalysisNeeded" :disabled="switchExamDisabled" active-text="开启" inactive-text="不开启"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="isCoder" label="题目列表">
|
|
|
+ <el-button type="text" @click="showProblemDialog">选择题目</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="$store.state.problem.cart.length">
|
|
|
+ <el-collapse accordion>
|
|
|
+ <el-collapse-item
|
|
|
+ v-for="problem in $store.state.problem.cart"
|
|
|
+ :key="problem.id"
|
|
|
+ :name="problem.id"
|
|
|
+ >
|
|
|
+ <template #title>
|
|
|
+ <span>{{ problem.name }}</span>
|
|
|
+ <el-tag class="problem-type">{{ problem.type }}</el-tag>
|
|
|
+ </template>
|
|
|
+ <b>题目描述:</b>
|
|
|
+ <span class="problem-description">{{ problem.description }}</span>
|
|
|
+ <div class="remove-button">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="mini"
|
|
|
+ @click="$store.commit('removeProblem', problem)"
|
|
|
+ >移除该题</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div style="margin: auto;width: fit-content">
|
|
|
+ <el-button type="primary" @click="handleExamCreate">确定</el-button>
|
|
|
+ <el-button @click="handleExamFormCancel">取消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+
|
|
|
+<!-- 展示Coder题目列表-->
|
|
|
+ <el-dialog v-model="dialogProblemVisible" :width="1200">
|
|
|
+ <el-input v-model="dialogProblemName"
|
|
|
+ style="width:200px;"
|
|
|
+ placeholder="搜索"
|
|
|
+ />
|
|
|
+ <el-select v-model="dialogProblemTypeStr" style="width: 300px" @change="fetchProblemData(1)">
|
|
|
+ <el-option v-for="item in problemTypeOptions" :key="item.value" :value="item.value">
|
|
|
+ {{item.label}}
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ <el-table :data="problemList" >
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template #default="props">
|
|
|
+ <div>{{ props.row.description }}</div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="name" label="名称"></el-table-column>
|
|
|
+ <el-table-column label="类型">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ problemTypeToTagContent(scope.row.type) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="上传时间">
|
|
|
+ <template #default="scope">
|
|
|
+ <span>{{ formatTimestamp(scope.row.createdAt)}}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="mini"
|
|
|
+ @click="checkDetails"
|
|
|
+ >详情</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-if="removeEnable(scope.row)"
|
|
|
+ type="danger"
|
|
|
+ size="mini"
|
|
|
+ @click="$store.commit('removeProblem', scope.row)"
|
|
|
+ >移出试题清单</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-else-if="addEnable(scope.row)"
|
|
|
+ type="success"
|
|
|
+ size="mini"
|
|
|
+ @click="$store.commit('addProblem', scope.row)"
|
|
|
+ >加入试题清单</el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ layout="prev, pager, next"
|
|
|
+ :total="problemPageInfo.total"
|
|
|
+ @current-change="changePage"
|
|
|
+ />
|
|
|
+ </el-dialog>
|
|
|
</el-row>
|
|
|
</template>
|
|
|
|
|
|
@@ -180,6 +310,8 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
import client from "@/utils/oss";
|
|
|
import { Back, Document } from "@element-plus/icons-vue";
|
|
|
import TestCard from "@/components/TestCard";
|
|
|
+import {debounce} from "@/utils/debounce";
|
|
|
+import {formatTimestamp, problemTypeOptions, problemTypeToTagContent} from "@/utils/coder-utils";
|
|
|
|
|
|
export default {
|
|
|
name: "ManagePage",
|
|
|
@@ -231,7 +363,9 @@ export default {
|
|
|
activeIndex: "1",
|
|
|
activeTestPaneName: "eval",
|
|
|
dialogVisible: false,
|
|
|
+ dialogExamVisible: false,
|
|
|
dialogTitle: "新增课程",
|
|
|
+ dialogExamTitle: "新增测试",
|
|
|
form: {
|
|
|
id: null,
|
|
|
name: "",
|
|
|
@@ -250,6 +384,14 @@ export default {
|
|
|
coderLink: false,
|
|
|
evalLink: false,
|
|
|
},
|
|
|
+ examForm: {
|
|
|
+ examType: "coder",
|
|
|
+ course: "",
|
|
|
+ name: "",
|
|
|
+ range: [],
|
|
|
+ isCodeAnalysisNeeded: false,
|
|
|
+ isConsoleOutputShownForStu: false
|
|
|
+ },
|
|
|
formRules: {
|
|
|
name: {
|
|
|
required: true, validator: validateName, trigger: "blur",
|
|
|
@@ -273,7 +415,25 @@ export default {
|
|
|
required: true, validator: validatePrice, trigger: "blur",
|
|
|
},
|
|
|
},
|
|
|
+ examFormRules: {
|
|
|
+ // type: {
|
|
|
+ // required: true, message: "请选择测试种类",
|
|
|
+ // },
|
|
|
+ name: {
|
|
|
+ required: true, message: "请输入测试名称", trigger: "blur",
|
|
|
+ },
|
|
|
+ course: {
|
|
|
+ required: true, message: "请选择测试名称",
|
|
|
+ },
|
|
|
+ range: {
|
|
|
+ required: true, message: "请选择测试时间段", trigger: "blur",
|
|
|
+ }
|
|
|
+ },
|
|
|
+ coderCourses: [],
|
|
|
+ courseOptions: [],
|
|
|
+
|
|
|
switchDisabled: false,
|
|
|
+ switchExamDisabled: false,
|
|
|
//
|
|
|
myCourseList: [],
|
|
|
courseType: "all",
|
|
|
@@ -292,6 +452,17 @@ export default {
|
|
|
{ value: "start", label: "正在进行" },
|
|
|
{ value: "end", label: "已结束" },
|
|
|
],
|
|
|
+ examCategoryOptions: [
|
|
|
+ { value: "eval", label: "评测题"},
|
|
|
+ { value: "coder", label: "代码题"},
|
|
|
+ ],
|
|
|
+ dialogProblemVisible: false,
|
|
|
+ dialogProblemPage: 1,
|
|
|
+ dialogProblemLimit: 10,
|
|
|
+ dialogProblemTypeStr: "",
|
|
|
+ dialogProblemName: "",
|
|
|
+ problemList: [],
|
|
|
+ problemPageInfo: {}
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -302,6 +473,12 @@ export default {
|
|
|
if (this.courseType === "end") return this.myCourseList.filter(i => this.state(i) === "FINISHED");
|
|
|
else return this.myCourseList;
|
|
|
},
|
|
|
+ isCoder() {
|
|
|
+ return this.examForm.examType === "coder";
|
|
|
+ },
|
|
|
+ problemTypeOptions() {
|
|
|
+ return problemTypeOptions(true);
|
|
|
+ }
|
|
|
},
|
|
|
mounted() {
|
|
|
this.$apis.getTeacherCourses().then(res => {
|
|
|
@@ -313,8 +490,26 @@ export default {
|
|
|
this.$apis.getTeacherCoderExamsGroupByCourse().then(res => {
|
|
|
this.coderExamList = res.data.data;
|
|
|
});
|
|
|
+ this.$apis.getAllCoderCourses().then(_res => {
|
|
|
+ this.coderCourses = _res.data.res;
|
|
|
+ this.courseOptions = this.coderCourses.map(i => {
|
|
|
+ return {
|
|
|
+ value: i.id,
|
|
|
+ label: i.name
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ //TODO 防抖!
|
|
|
+ dialogProblemName() {
|
|
|
+ debounce(this.fetchProblemData, 500);
|
|
|
+ }
|
|
|
},
|
|
|
+
|
|
|
methods: {
|
|
|
+ formatTimestamp,
|
|
|
+ problemTypeToTagContent,
|
|
|
state(courseVO) {
|
|
|
const now = new Date();
|
|
|
if (new Date(courseVO.endTime) < now) {
|
|
|
@@ -366,7 +561,8 @@ export default {
|
|
|
function createOrUpdateCourse() {
|
|
|
console.log(_this.form);
|
|
|
if (_this.form.id === null) {
|
|
|
- _this.$apis.createCourse({
|
|
|
+ console.log("进入创建");
|
|
|
+ let data = {
|
|
|
name: _this.form.name,
|
|
|
teacher: {
|
|
|
id: _this.$store.state.user.id,
|
|
|
@@ -382,8 +578,11 @@ export default {
|
|
|
price: Number(_this.form.price),
|
|
|
coderId: _this.form.coderLink ? -1 : null,
|
|
|
evalId: _this.form.evalLink ? -1 : null,
|
|
|
- })
|
|
|
+ };
|
|
|
+ console.log(data);
|
|
|
+ _this.$apis.createCourse(data)
|
|
|
.then((res) => {
|
|
|
+ console.log(res);
|
|
|
_this.myCourseList.push(res.data.data);
|
|
|
_this.dialogVisible = false;
|
|
|
_this.clearForm();
|
|
|
@@ -423,7 +622,7 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- handleFormCancel() {
|
|
|
+ handleFormCancel(done) {
|
|
|
ElMessageBox.confirm("是否确认退出?退出后将不保存表格信息。", "提示", {
|
|
|
confirmButtonText: "确认退出",
|
|
|
cancelButtonText: "我再想想",
|
|
|
@@ -433,11 +632,89 @@ export default {
|
|
|
.then(() => {
|
|
|
this.dialogVisible = false;
|
|
|
this.clearForm();
|
|
|
+ // Zhurui修改,关闭dialog
|
|
|
+ done();
|
|
|
})
|
|
|
.catch((err) => {
|
|
|
console.log(err);
|
|
|
});
|
|
|
},
|
|
|
+ handleExamCreate(){
|
|
|
+ const form = this.$refs.examFormRef;
|
|
|
+ form.validate((valid) => {
|
|
|
+ if (valid) {
|
|
|
+ console.log("validate 成功");
|
|
|
+ this.createCoderExam();
|
|
|
+ } else {
|
|
|
+ console.log("validate 失败了");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ createCoderExam(){
|
|
|
+ //创建Coder考试
|
|
|
+ this.$apis.createCoderExam({
|
|
|
+ course: this.examForm.course,
|
|
|
+ name: this.examForm.name,
|
|
|
+ startAt: this.examForm.range[0].getTime()/1000,
|
|
|
+ endAt: this.examForm.range[1].getTime()/1000,
|
|
|
+ problems: this.$store.state.problem.cart.map((item) => item.id),
|
|
|
+ isConsoleOutputShownForStu: this.examForm.isConsoleOutputShownForStu
|
|
|
+ }).then(_res => {
|
|
|
+ this.$store.commit("clearProblem");
|
|
|
+ ElMessage.success("创建测试成功!");
|
|
|
+ this.dialogExamVisible = false;
|
|
|
+ }).catch(_err => {
|
|
|
+ console.log(_err);
|
|
|
+ ElMessage.warning("创建测试失败!");
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleExamFormCancel(done) {
|
|
|
+ //关闭dialog的提示
|
|
|
+ ElMessageBox.confirm("是否确认退出?退出后将不保存表格信息。", "提示", {
|
|
|
+ confirmButtonText: "确认退出",
|
|
|
+ cancelButtonText: "我再想想",
|
|
|
+ type: "warning",
|
|
|
+ center: true,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ this.dialogExamVisible = false;
|
|
|
+ // this.clearForm();
|
|
|
+ //TODO 清除表格信息
|
|
|
+ done();
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log(err);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ showProblemDialog(){ //打开弹框,获取初始数据
|
|
|
+ this.fetchProblemData();
|
|
|
+ this.dialogProblemVisible = true;
|
|
|
+ },
|
|
|
+ // handleProblemChange(){
|
|
|
+ // debounce(fetchProblemData,1000);
|
|
|
+ // },
|
|
|
+ checkDetails(){
|
|
|
+ ElMessage.warning("暂不支持查看详情!");
|
|
|
+ },
|
|
|
+ fetchProblemData(page=1) { // 按需获得Coder题目
|
|
|
+ this.$apis.retrievePageProblem(page, this.dialogProblemLimit, this.dialogProblemTypeStr, this.dialogProblemName)
|
|
|
+ .then(_res => {
|
|
|
+ this.problemPageInfo = _res.data.page;
|
|
|
+ this.problemList = _res.data.res;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ removeEnable(problem) { //是否可以移除题目
|
|
|
+ return this.$store.state.problem.cart.length && this.$store.state.problem.cart
|
|
|
+ .map((item) => item.id)
|
|
|
+ .includes(problem.id);
|
|
|
+ },
|
|
|
+ addEnable(problem) { //是否可以添加题目
|
|
|
+ return this.$store.getters.isTeacher && problem.modifiedAt;
|
|
|
+ },
|
|
|
+ changePage(val){
|
|
|
+ this.fetchProblemData(val);
|
|
|
+ },
|
|
|
clearForm() {
|
|
|
this.form = {
|
|
|
id: null,
|