鲁洪霞 2024-03-22 15:02:41 +08:00
parent 3213c25bc1
commit 7b110927c4
8 changed files with 1095 additions and 2 deletions

2
.env
View File

@ -1,4 +1,4 @@
VITE_BASE_URL=http://192.168.0.49:8093/ VITE_BASE_URL=http://192.168.0.55:8093/
VITE_PROXY=/api/ VITE_PROXY=/api/
VITE_FILE_URL=https://file.zcloudchina.com/YTHFile VITE_FILE_URL=https://file.zcloudchina.com/YTHFile
VITE_TEMPLATE_URL=https://qaaq.qhdsafety.com/file/ VITE_TEMPLATE_URL=https://qaaq.qhdsafety.com/file/

2
package-lock.json generated
View File

@ -19,7 +19,7 @@
"axios": "^1.6.3", "axios": "^1.6.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"element-plus": "^2.4.4", "element-plus": "^2.6.1",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",

View File

@ -46,6 +46,33 @@ export const getClassManagementStudentExamRecordsList = (params) =>
post("/stageexam/list", params); // 班级管理学员考试记录列表 post("/stageexam/list", params); // 班级管理学员考试记录列表
export const getClassManagementStudentExamRecordsView = (params) => export const getClassManagementStudentExamRecordsView = (params) =>
post("/stageexam/findExam", params); // 班级管理学员考试记录查看 post("/stageexam/findExam", params); // 班级管理学员考试记录查看
export const getTrainingProgressList = (params) =>
post("/student/studentList", params); // 查询进度列表
export const getTrainingProgressListExportStudentRecords = (params) =>
post("/student/derivedRecord", params); // 导出学员信息
export const getExamdraftList = (params) =>
post("/stageexampaperCache/list", params); // 试卷草稿管理列表
export const setpaperQuestionCacheQuestionsAdd = (params) =>
post("/paperQuestionCache/add", params); // 草稿试卷管理试题新增
export const setpaperQuestionCacheQuestionsEdit = (params) =>
post("/paperQuestionCache/edit", params); // 草稿试卷管理试题修改
export const setpaperQuestionCacheDelete = (params) =>
post("/paperQuestionCache/delete", params); // 草稿试卷管理试题删除
export const setstageexampaperCacheEdit = (params) =>
post("/stageexampaperCache/edit", params); // 草稿试卷管理保存修改
export const setstageexampaperCacheDelete = (params) =>
post("/stageexampaperCache/delete", params); // 草稿试卷管理保存修改
export const setstageexampaperCacheformal = (params) =>
post("/stageexampaperCache/convertStageexam", params); // 草稿试卷管理保存修改
export const getClassManagementCurriculumList = (params) => export const getClassManagementCurriculumList = (params) =>
post("/classCurriculum/list", params); // 班级管理课程列表 post("/classCurriculum/list", params); // 班级管理课程列表
export const setClassManagementCurriculumDelete = (params) => export const setClassManagementCurriculumDelete = (params) =>

View File

@ -24,3 +24,9 @@ export const getExamPaperManagementView = (params) =>
post("/stageexampaperinput/goEdit", params); // 试卷管理查看 post("/stageexampaperinput/goEdit", params); // 试卷管理查看
export const getExamPaperManagementTestQuestions = (params) => export const getExamPaperManagementTestQuestions = (params) =>
post("/paperQuestion/listForInherit", params); // 试卷管理试题 post("/paperQuestion/listForInherit", params); // 试卷管理试题
export const getpaperQuestionCacheQuestions = (params) =>
post("/paperQuestionCache/list", params); // 试卷草稿管理试题
export const getStagexampaperCacheView = (params) =>
post("/stageexampaperCache/goEdit", params); // 试卷管理查看

View File

@ -0,0 +1,248 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'add' ? '新增' : '修改'"
@close="fnClose"
@open="fnQuestionTypeChange"
>
<el-form ref="formRef" :rules="rules" :model="form" label-width="110px">
<el-form-item label="试题类型" prop="QUESTIONTYPE">
<el-select v-model="form.QUESTIONTYPE" @change="fnQuestionTypeChange">
<el-option
v-for="item in questionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="题干" prop="QUESTIONDRY">
<el-input
v-model="form.QUESTIONDRY"
type="textarea"
:autosize="{ minRows: 3 }"
/>
</el-form-item>
<template v-if="form.QUESTIONTYPE !== '3' && form.QUESTIONTYPE !== '4'">
<el-form-item label="选项A" prop="OPTIONA">
<el-input v-model="form.OPTIONA" />
</el-form-item>
<el-form-item label="选项B" prop="OPTIONB">
<el-input v-model="form.OPTIONB" />
</el-form-item>
<el-form-item label="选项C" prop="OPTIONC">
<el-input v-model="form.OPTIONC" />
</el-form-item>
<el-form-item label="选项D" prop="OPTIOND">
<el-input v-model="form.OPTIOND" />
</el-form-item>
</template>
<el-form-item label="答案" prop="ANSWER">
<el-select v-model="form.ANSWER">
<el-option
v-for="item in answerOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="分值" prop="SCORE">
<el-input-number v-model="form.SCORE" />
</el-form-item>
<el-form-item label="试题标签" prop="LABEL_TYPE">
<el-select v-model="form.LABEL_TYPE">
<el-option
v-for="item in testQuestionLabels"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
<el-form-item label="关联课件名称" prop="VIDEOCOURSEWARE_ID">
<el-select-v2
v-model="form.VIDEOCOURSEWARE_ID"
:options="associatedCoursewareName"
filterable
:props="{ label: 'COURSEWARENAME', value: 'VIDEOCOURSEWARE_ID' }"
/>
</el-form-item>
<el-form-item label="答案解析" prop="DESCR">
<el-input
v-model="form.DESCR"
type="textarea"
:autosize="{ minRows: 3 }"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="fnClose"></el-button>
<el-button type="primary" @click="fnSubmit"></el-button>
</template>
</el-dialog>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import { nextTick, ref } from "vue";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import { ElMessage } from "element-plus";
import { layoutFnGetTestQuestionLabels } from "@/assets/js/data_dictionary.js";
import {
getAssociatedCoursewareNameList,
setpaperQuestionCacheQuestionsAdd,
setpaperQuestionCacheQuestionsEdit,
} from "@/request/training_process_management.js";
import { cloneDeep } from "lodash-es";
const questionTypeOptions = [
{ value: "1", label: "单选题" },
{ value: "2", label: "多选题" },
{ value: "3", label: "判断题" },
];
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
form: {
type: Object,
required: true,
default: () => ({}),
},
type: {
type: String,
required: true,
default: "",
},
id: {
type: String,
required: true,
default: "",
},
isInherit: {
type: Boolean,
required: true,
default: false,
},
});
const emits = defineEmits([
"update:visible",
"update:form",
"get-data",
"confirm",
]);
const { visible, form } = useVModels(props, emits);
const formRef = ref(null);
const answerOptions = ref([]);
const associatedCoursewareName = ref([]);
const rules = {
QUESTIONTYPE: [
{ required: true, message: "请选择试题类型", trigger: "change" },
],
QUESTIONDRY: [{ required: true, message: "请输入题干", trigger: "blur" }],
OPTIONA: [{ required: true, message: "请输入选项A", trigger: "blur" }],
OPTIONB: [{ required: true, message: "请输入选项B", trigger: "blur" }],
OPTIONC: [{ required: true, message: "请输入选项C", trigger: "blur" }],
OPTIOND: [{ required: true, message: "请输入选项D", trigger: "blur" }],
ANSWER: [{ required: true, message: "请选择答案", trigger: "change" }],
SCORE: [{ required: true, message: "请输入分值", trigger: "blur" }],
LABEL_TYPE: [
{ required: true, message: "请选择试题标签", trigger: "change" },
],
VIDEOCOURSEWARE_ID: [
{ required: true, message: "请选择关联课件名称", trigger: "change" },
],
};
const testQuestionLabels = await layoutFnGetTestQuestionLabels();
const fnGetAssociatedCoursewareNameList = async () => {
const resData = await getAssociatedCoursewareNameList();
associatedCoursewareName.value = resData.CourseWareNameList;
};
fnGetAssociatedCoursewareNameList();
const fnQuestionTypeChange = async () => {
await nextTick();
if (form.value.QUESTIONTYPE === "1") {
answerOptions.value = [
{ value: "A", label: "A" },
{ value: "B", label: "B" },
{ value: "C", label: "C" },
{ value: "D", label: "D" },
];
} else if (form.value.QUESTIONTYPE === "2") {
answerOptions.value = [
{ value: "AB", label: "AB" },
{ value: "AC", label: "AC" },
{ value: "AD", label: "AD" },
{ value: "BC", label: "BC" },
{ value: "BD", label: "BD" },
{ value: "CD", label: "CD" },
{ value: "ABC", label: "ABC" },
{ value: "ABD", label: "ABD" },
{ value: "ACD", label: "ACD" },
{ value: "BCD", label: "BCD" },
{ value: "ABCD", label: "ABCD" },
];
} else if (form.value.QUESTIONTYPE === "3") {
answerOptions.value = [
{ value: "A", label: "对" },
{ value: "B", label: "错" },
];
} else {
answerOptions.value = [];
}
};
const fnClose = () => {
formRef.value.resetFields();
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
if (form.value.QUESTIONTYPE === "3") {
form.value.OPTIONA = "对";
form.value.OPTIONB = "错";
form.value.OPTIONC = "";
form.value.OPTIOND = "";
}
let arr = [];
if (form.value.QUESTIONTYPE === "3") {
arr = ["#" + form.value.OPTIONA + "#", "#" + form.value.OPTIONB + "#"];
} else if (form.value.QUESTIONTYPE !== "4") {
arr = [
"#" + form.value.OPTIONA + "#",
"#" + form.value.OPTIONB + "#",
"#" + form.value.OPTIONC + "#",
"#" + form.value.OPTIOND + "#",
];
}
const s = arr.join(",") + ",";
for (let i = 0; i < arr.length - 1; i++) {
if (s.replace(arr[i] + ",", "").indexOf(arr[i]) > -1) {
ElMessage.warning("试题答案重复:" + arr[i].split("#").join(""));
return;
}
}
if (!props.isInherit) {
props.type === "add"
? await setpaperQuestionCacheQuestionsAdd({
...form.value,
STAGEEXAMPAPERINPUT_ID: props.id,
})
: await setpaperQuestionCacheQuestionsEdit({ ...form.value });
emits("get-data");
} else {
emits("confirm", cloneDeep(form.value));
}
ElMessage.success("操作成功");
fnClose();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,297 @@
<template>
<layout-card>
<el-divider content-position="left">试卷基本信息</el-divider>
<el-form
ref="formRef"
:model="data.form"
:rules="rules"
label-width="100px"
>
<el-row>
<el-col :span="24">
<el-form-item label="试卷名称" prop="EXAMNAME">
<el-input v-model="data.form.EXAMNAME" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总分数" prop="EXAMSCORE">
<el-input-number v-model="data.form.EXAMSCORE" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合格分数" prop="PASSSCORE">
<el-input-number v-model="data.form.PASSSCORE" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div v-if="type !== 'add'">
<el-divider content-position="left">试卷题目信息</el-divider>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnGetData"
>
<el-row>
<el-col :span="6">
<el-form-item label="题目内容" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="题目类型" prop="QUESTIONTYPE">
<el-select v-model="searchForm.QUESTIONTYPE">
<el-option
v-for="item in questionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnGetData">
重置
</el-button>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px" class="end">
<el-button type="primary" @click="fnAddOrEdit({}, '')">
新增试题
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="items mt-20 p-20">
<div
v-for="(item, index) in list"
:key="item.PAPER_QUESTION_ID"
class="item ptb-20"
>
<div class="mt-10">
{{ index + 1 }}.
<span v-if="item.QUESTIONTYPE === '1'"> () </span>
<span v-if="item.QUESTIONTYPE === '2'"> () </span>
<span v-if="item.QUESTIONTYPE === '3'"> () </span>
{{ item.QUESTIONDRY }}
<span class="ml-10">(题目分值{{ item.SCORE }})</span>
</div>
<div class="mt-10 ml-30">
<el-radio-group
v-if="item.QUESTIONTYPE === '1'"
disabled
:model-value="item.ANSWER"
>
<el-radio label="A">A.{{ item.OPTIONA }}</el-radio>
<el-radio label="B">B.{{ item.OPTIONB }}</el-radio>
<el-radio label="C">C.{{ item.OPTIONC }}</el-radio>
<el-radio label="D">D.{{ item.OPTIOND }}</el-radio>
</el-radio-group>
<el-checkbox-group
v-if="item.QUESTIONTYPE === '2'"
disabled
:model-value="item.ANSWER?.split('')"
>
<el-checkbox label="A">A.{{ item.OPTIONA }}</el-checkbox>
<el-checkbox label="B">B.{{ item.OPTIONB }}</el-checkbox>
<el-checkbox label="C">C.{{ item.OPTIONC }}</el-checkbox>
<el-checkbox label="D">D.{{ item.OPTIOND }}</el-checkbox>
</el-checkbox-group>
<el-radio-group
v-if="item.QUESTIONTYPE === '3'"
disabled
:model-value="item.ANSWER"
>
<el-radio label="A">A.{{ item.OPTIONA }}</el-radio>
<el-radio label="B">B.{{ item.OPTIONB }}</el-radio>
</el-radio-group>
</div>
<div class="flex">
<div>
<div class="mt-10">答案{{ item.ANSWER }}</div>
<div class="mt-10">答案解析{{ item.DESCR }}</div>
<div class="mt-10">关联课件名称{{ item.COURSEWARENAME }}</div>
</div>
<div class="tr">
<el-button @click="fnAddOrEdit(item, index)">编辑</el-button>
<el-button @click="fnDelete(item.PAPER_QUESTION_ID, index)">
删除
</el-button>
</div>
</div>
</div>
</div>
</div>
<div class="mt-10 tc">
<el-button type="primary" @click="fnSubmit"> </el-button>
</div>
<add-test-questions
:id="STAGEEXAMPAPERINPUT_ID"
v-model:visible="data.addOrEditDialog.visible"
v-model:form="data.addOrEditDialog.form"
:type="data.addOrEditDialog.type"
:is-inherit="type === 'inherit'"
@get-data="fnGetDataInfo"
@confirm="fnAddTestQuestionsConfirm"
/>
</layout-card>
</template>
<script setup>
import { nextTick, reactive, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import {
setpaperQuestionCacheDelete,
setstageexampaperCacheEdit,
} from "@/request/training_process_management.js";
import {
getpaperQuestionCacheQuestions,
getStagexampaperCacheView,
} from "@/request/training_resource_management.js";
import useListData from "@/assets/js/useListData.js";
import { cloneDeep, sumBy } from "lodash-es";
import AddTestQuestions from "./components/add_test_questions.vue";
const route = useRoute();
const router = useRouter();
const { type, STAGEEXAMPAPERINPUT_ID } = route.query;
const questionTypeOptions = [
{ value: "1", label: "单选题" },
{ value: "2", label: "多选题" },
{ value: "3", label: "判断题" },
];
const rules = {
EXAMNAME: [{ required: true, message: "请输入试卷名称", trigger: "blur" }],
EXAMSCORE: [{ required: true, message: "请输入总分数", trigger: "blur" }],
PASSSCORE: [{ required: true, message: "请输入合格分数", trigger: "blur" }],
fileList: [{ required: true, message: "请上传试题", trigger: "change" }],
};
const formRef = ref(null);
const data = reactive({
form: {
EXAMNAME: "",
EXAMSCORE: 0,
PASSSCORE: 0,
fileList: [],
},
addOrEditDialog: {
visible: false,
form: {
QUESTIONTYPE: "",
QUESTIONDRY: "",
OPTIONA: "",
OPTIONB: "",
OPTIONC: "",
OPTIOND: "",
ANSWER: "",
SCORE: 0,
LABEL_TYPE: "",
VIDEOCOURSEWARE_ID: "",
},
type: "",
index: "",
},
});
const { list, searchForm, fnGetData } = useListData(
getpaperQuestionCacheQuestions,
{
otherParams: { STAGEEXAMPAPERINPUT_ID },
immediate: false,
usePagination: false,
}
);
const fnGetDataInfo = async () => {
if (!STAGEEXAMPAPERINPUT_ID) return;
const resData = await getStagexampaperCacheView({ STAGEEXAMPAPERINPUT_ID });
resData.pd.EXAMSCORE = +resData.pd.EXAMSCORE;
resData.pd.PASSSCORE = +resData.pd.PASSSCORE;
data.form = resData.pd;
fnGetData();
};
fnGetDataInfo();
const fnAddOrEdit = async (row, index) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.type = row.PAPER_QUESTION_ID ? "edit" : "add";
data.addOrEditDialog.index = index;
if (row.PAPER_QUESTION_ID) {
data.addOrEditDialog.form = cloneDeep(row);
data.addOrEditDialog.form.SCORE = +row.SCORE;
}
};
const fnAddTestQuestionsConfirm = (value) => {
if (data.addOrEditDialog.type === "add") list.value.push(value);
else list.value.splice(data.addOrEditDialog.index, 1, value);
data.form.EXAMSCORE = sumBy(list.value, (item) => +item.SCORE);
};
const fnDelete = debounce(
1000,
async (PAPER_QUESTION_ID) => {
await ElMessageBox.confirm("确定要删除吗?", {
type: "warning",
});
await setpaperQuestionCacheDelete({
PAPER_QUESTION_ID,
STAGEEXAMPAPERINPUT_ID,
});
ElMessage.success("删除成功");
await fnGetDataInfo();
},
{ atBegin: true }
);
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
if (data.form.PASSSCORE > data.form.EXAMSCORE) {
ElMessage.warning("合格分数不能大于总分数");
return;
}
await setstageexampaperCacheEdit({
...data.form,
});
ElMessage.success("保存成功");
router.back();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss">
.items {
border: 1px solid var(--el-border-color);
.item {
border-bottom: 1px dashed #ebeef5;
&:first-child {
padding-top: 0;
}
&:last-child {
border-bottom: none;
}
}
}
.flex {
display: flex;
align-items: center;
div {
flex: 1;
}
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="6">
<el-form-item label="试卷名称:">
<el-input v-model="searchForm.KEYWORDS" placeholder="搜索" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="创建时间" prop="dates">
<el-date-picker
v-model="searchForm.dates"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="满分:">
<el-input v-model="searchForm.EXAMSCORE" placeholder="满分" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPaginationTransfer">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<layout-table
ref="tableRef"
v-model:pagination="pagination"
:data="list"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="60" fixed="left">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column prop="EXAMNAME" label="试卷名称" />
<el-table-column prop="CREATTIME" label="上传时间" align="center" />
<el-table-column prop="EXAMSCORE" label="满分" align="center" />
<el-table-column label="试卷类型" width="150">
<template #default="{ row }">
{{ translationStatus(row.SOURCETYPE, TypeOfTestPaperList) }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="text"
@click="
router.push({
path: '/training_process_management/examdraft_management/edit',
query: {
STAGEEXAMPAPERINPUT_ID: row.STAGEEXAMPAPERINPUT_ID,
},
})
"
>编辑</el-button
>
<el-button type="text" @click="fnDelete(row.STAGEEXAMPAPERINPUT_ID)"
>删除</el-button
>
<el-button
type="text"
@click="convertStageexam(row.STAGEEXAMPAPERINPUT_ID, '0')"
>转成正式试卷</el-button
>
</template>
</el-table-column>
</layout-table>
</layout-card>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import { useRouter } from "vue-router";
import {
getExamdraftList,
setstageexampaperCacheDelete,
setstageexampaperCacheformal,
} from "@/request/training_process_management.js";
import { serialNumber, translationStatus } from "@/assets/js/utils.js";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
const router = useRouter();
const TypeOfTestPaperList = [
{ ID: "1", NAME: "平台试卷" },
{ ID: "2", NAME: "自建试卷" },
];
const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } =
useListData(getExamdraftList);
const fnGetDataTransfer = () => {
fnGetData({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnDelete = debounce(
1000,
async (STAGEEXAMPAPERINPUT_ID) => {
await ElMessageBox.confirm("确定要删除吗?", {
type: "warning",
});
await setstageexampaperCacheDelete({ STAGEEXAMPAPERINPUT_ID });
ElMessage.success("删除成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
const convertStageexam = debounce(
1000,
async (STAGEEXAMPAPERINPUT_ID) => {
await ElMessageBox.confirm("确定要转成正式试卷吗?", {
type: "warning",
});
await setstageexampaperCacheformal({ STAGEEXAMPAPERINPUT_ID });
ElMessage.success("转成正式试卷成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,362 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="6">
<el-form-item label="学员姓名" prop="STUDENT_NAME">
<el-input v-model="searchForm.STUDENT_NAME" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="班级编码" prop="CODE">
<el-input v-model="searchForm.CODE" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="班级名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开始时间" prop="TIME">
<el-date-picker
v-model="searchForm.TIME"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束时间" prop="TIME1">
<el-date-picker
v-model="searchForm.TIME1"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="入班时间" prop="TIME2">
<el-date-picker
v-model="searchForm.TIME2"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学习状态" prop="STUDYSTATE">
<el-select v-model="searchForm.STUDYSTATE">
<el-option
v-for="item in studyStateList"
:key="item.ID"
:value="item.ID"
:label="item.NAME"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="考试状态" prop="STAGEEXAMSTATE">
<el-select v-model="searchForm.STAGEEXAMSTATE">
<el-option
v-for="item in stageExamStateList"
:key="item.ID"
:value="item.ID"
:label="item.NAME"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPaginationTransfer">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<div class="table_btn">
<el-button type="primary" @click="fnStudentRecords"
>导出学员信息</el-button
>
</div>
<layout-table
ref="tableRef"
v-model:pagination="pagination"
:data="list"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="60" fixed="left">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
label="学员姓名"
prop="STUDENT_NAME"
show-overflow-tooltip
fixed="left"
/>
<el-table-column
label="身份证号"
prop="USER_ID_CARD"
show-overflow-tooltip
fixed="left"
width="150"
/>
<el-table-column
label="班级名称"
prop="CLASS_NAME"
show-overflow-tooltip
width="150"
/>
<el-table-column
label="班级编码"
prop="CODE"
show-overflow-tooltip
width="150"
/>
<el-table-column
label="部门"
prop="DEPARTMENT_NAME"
show-overflow-tooltip
width="200"
/>
<el-table-column
label="工种"
prop="POST_NAME"
show-overflow-tooltip
width="150"
/>
<el-table-column
show-overflow-tooltip
prop="START_TIME"
label="培训开始时间"
width="200"
/>
<el-table-column
show-overflow-tooltip
prop="END_TIME"
label="培训结束时间"
align="center"
width="100"
/>
<el-table-column
show-overflow-tooltip
prop="CREATTIME"
label="入班时间"
align="center"
width="100"
/>
<el-table-column
show-overflow-tooltip
prop="CURRICULUMNAME"
label="培训课程"
align="center"
width="200"
/>
<el-table-column
show-overflow-tooltip
prop="ALL_CLASSHOUR"
label="要求学时"
align="center"
width="100"
/>
<el-table-column
show-overflow-tooltip
prop="COMPLETE_CLASSHOUR"
label="已完成学时"
align="center"
width="100"
/>
<el-table-column label="学习状态" width="150">
<template #default="{ row }">
{{ translationStatus(row.STUDYSTATE, studyStateList) }}
</template>
</el-table-column>
<el-table-column label="考试状态" width="150">
<template #default="{ row }">
{{ translationStatus(row.STAGEEXAMSTATE, stageExamStateList) }}
</template>
</el-table-column>
<el-table-column prop="EXAMSCORE" label="考试分数" width="100" />
<el-table-column label="任务状态" width="150">
<template #default="{ row }">
{{ translationStatus(row.STATE, taskStatusList) }}
</template>
</el-table-column>
</layout-table>
</layout-card>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import { serialNumber, translationStatus } from "@/assets/js/utils.js";
import {
getTrainingProgressListExportStudentRecords,
getTrainingProgressList,
} from "@/request/training_process_management.js";
import { ElMessage, ElMessageBox } from "element-plus";
import * as XLSX from "xlsx";
const studyStateList = [
{ ID: "0", NAME: "未学习" },
{ ID: "1", NAME: "学习中" },
{ ID: "2", NAME: "已学完" },
{ ID: "3", NAME: "已完成" },
{ ID: "4", NAME: "未完成" },
{ ID: "5", NAME: "待评估" },
{ ID: "6", NAME: "评估未合格" },
];
const stageExamStateList = [
{ ID: "0", NAME: "不考试" },
{ ID: "1", NAME: "待考试" },
{ ID: "2", NAME: "考试未通过" },
{ ID: "3", NAME: "考试通过" },
{ ID: "4", NAME: "未参加" },
];
const taskStatusList = [
{ ID: "1", NAME: "未申请" },
{ ID: "4", NAME: "待开班" },
{ ID: "5", NAME: "培训中" },
{ ID: "6", NAME: "培训结束" },
];
const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } =
useListData(getTrainingProgressList);
const fnGetDataTransfer = () => {
fnGetData({
STARTTIME: searchForm.value.TIME?.[0],
ENDTIME: searchForm.value.TIME?.[1],
OVERSTARTTIME: searchForm.value.TIME1?.[0],
OVERENDTIME: searchForm.value.TIME1?.[1],
CREATESTARTTIME: searchForm.value.TIME2?.[0],
CREATENDTIME: searchForm.value.TIME2?.[1],
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
STARTTIME: searchForm.value.TIME?.[0],
ENDTIME: searchForm.value.TIME?.[1],
OVERSTARTTIME: searchForm.value.TIME1?.[0],
OVERENDTIME: searchForm.value.TIME1?.[1],
CREATESTARTTIME: searchForm.value.TIME2?.[0],
CREATENDTIME: searchForm.value.TIME2?.[1],
});
};
const fnStudentRecords = async () => {
await ElMessageBox.confirm("确定要导出查询出来所有的学员信息?", {
type: "warning",
});
const resData = await getTrainingProgressListExportStudentRecords({
...searchForm.value,
STARTTIME: searchForm.value.TIME?.[0],
ENDTIME: searchForm.value.TIME?.[1],
OVERSTARTTIME: searchForm.value.TIME1?.[0],
OVERENDTIME: searchForm.value.TIME1?.[1],
CREATESTARTTIME: searchForm.value.TIME2?.[0],
CREATENDTIME: searchForm.value.TIME2?.[1],
});
if (resData.varList.length > 0) fnExportStudentRecords(resData.varList);
else ElMessage.warning("没有学员信息");
};
const fnExportStudentRecords = (list) => {
const tableData = [
[
"序号",
"学员姓名",
"身份证号",
"班级名称",
"班级编码",
"部门",
"工种",
"培训开始时间",
"培训结束时间",
"入班时间",
"培训课程",
"要求学时",
"已完成学时",
"学习状态",
"考试状态",
"考试分数",
"任务状态",
],
];
list.forEach((item, index) => {
for (let i = 0; i < studyStateList.length; i++) {
if (studyStateList[i].ID === item.STUDYSTATE) {
item.STUDYSTATE = studyStateList[i].NAME;
break;
}
}
for (let i = 0; i < stageExamStateList.length; i++) {
if (stageExamStateList[i].ID === item.STAGEEXAMSTATE) {
item.STAGEEXAMSTATE = stageExamStateList[i].NAME;
break;
}
}
for (let i = 0; i < taskStatusList.length; i++) {
if (taskStatusList[i].ID === item.STATE) {
item.STATE = taskStatusList[i].NAME;
break;
}
}
if (item.COMPLETE_CLASSHOUR === "0.0") item.COMPLETE_CLASSHOUR = "0";
if (item.ALL_CLASSHOUR === "0.0") item.ALL_CLASSHOUR = "0";
tableData.push([
index + 1,
item.STUDENT_NAME,
item.USER_ID_CARD,
item.CLASS_NAME,
item.CODE,
item.DEPARTMENT_NAME,
item.POST_NAME,
item.START_TIME,
item.END_TIME,
item.CREATTIME,
item.CURRICULUMNAME,
item.ALL_CLASSHOUR,
item.COMPLETE_CLASSHOUR,
item.STUDYSTATE,
item.STAGEEXAMSTATE,
item.EXAMSCORE,
item.STATE,
]);
});
const ws = XLSX.utils.aoa_to_sheet(tableData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "学员信息统计");
XLSX.writeFile(wb, "学员信息统计表.xlsx");
ElMessage.success("导出成功");
};
</script>
<style scoped lang="scss">
.table_btn {
margin-bottom: 20px;
}
</style>