zhouwx
2024-07-15 6459f8bff3e568e65e6dc617f22c686cf5a8db44
组卷
已修改10个文件
已添加2个文件
823 ■■■■ 文件已修改
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/onlineEducation/exam.js 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/classHourBatch/components/classHourChange.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/classHourBatch/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/count/index.vue 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/groupExams/components/examChooseStudent.vue 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/groupExams/components/examDialog.vue 138 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/groupExams/components/student.vue 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/groupExams/index.vue 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/offlineEducation/components/recordDialog.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/systemManage/company/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -30,6 +30,7 @@
    "js-base64": "^3.7.5",
    "js-cookie": "3.0.1",
    "jsencrypt": "3.3.1",
    "moment": "^2.30.1",
    "nprogress": "0.2.0",
    "p-limit": "^5.0.0",
    "pinia": "2.0.22",
src/api/onlineEducation/exam.js
@@ -41,47 +41,47 @@
    })
}
//批次与学员关系
//批次下的学员列表(分页)
// export function getBatchStudent(param) {
//     return request({
//         url: '/phase-student/list',
//         method: 'get',
//         params: param
//     })
// }
// // 校验学员是否已存在
// export function checkStudentUnique(data) {
//     return request({
//         url: '/phase-student/checkStudentUnique',
//         method: 'post',
//         data: data
//     })
// }
// // 批量新增学员
// export function batchAddStudent(data) {
//     return request({
//         url: '/phase-student/batchAdd',
//         method: 'post',
//         data: data
//     })
// }
// // 批量删除学员
// export function batchDelStudent(data) {
//     return request({
//         url: '/phase-student/batchDelete',
//         method: 'delete',
//         data: data
//     })
// }
//
// // 批量删除学员
// export function delBatchStu(userId) {
//     return request({
//         url: '/phase-student/' + userId,
//         method: 'delete'
//     })
// }
//试卷与学员关系
//试卷下的学员列表(分页)
export function getExamStudent(param) {
    return request({
        url: '/paper-student/list',
        method: 'get',
        params: param
    })
}
// 校验学员是否已存在
export function checkExamStudentUnique(data) {
    return request({
        url: '/paper-student/checkStudentUnique',
        method: 'post',
        data: data
    })
}
 // 批量新增学员
export function examAddStudent(data) {
    return request({
        url: '/paper-student/batchAdd',
        method: 'post',
        data: data
    })
}
 // 批量删除学员
export function examDelStudent(data) {
    return request({
        url: '/paper-student/batchDelete',
        method: 'delete',
        data: data
    })
}
 // 删除学员
export function delExamStu(userId) {
    return request({
        url: '/paper-student/' + userId,
        method: 'delete'
    })
}
//
// //企业课时变更记录列表(分页)
// export function getCompanyPeriod(param) {
src/router/index.js
@@ -79,6 +79,19 @@
      }
    ]
  },
  {
    path: '/examStu',
    component: Layout,
    redirect: '/examStu',
    children: [
      {
        path: '/examStu',
        component: () => import('@/views/onlineEducation/groupExams/components/student.vue'),
        name: 'ExamStu',
        meta: { title: '考试学员',icon: 'form',  affix: true }
      }
    ]
  },
  // {
  //   path: '',
  //   component: Layout,
src/views/onlineEducation/classHourBatch/components/classHourChange.vue
@@ -9,8 +9,8 @@
      <el-table v-loading="state.loading" :data="state.dataList" :border="true">
        <el-table-column label="创建时间" prop="createTime" align="center"  width="180" />
        <el-table-column label="变动来源" prop="origin" align="center"  />
        <el-table-column label="变动情况" prop="modifyPeriod" align="center"  />
        <el-table-column label="变动后剩余(分)" prop="remainPeriod" align="center"  />
        <el-table-column label="变动情况" prop="modifyPeriodMin" align="center"  />
        <el-table-column label="变动后剩余" prop="remainPeriodMin" align="center"  />
<!--        <el-table-column label="操作" align="center" class-name="small-padding fixed-width"  width="180">-->
<!--          <template #default="scope">-->
<!--            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>-->
@@ -54,7 +54,8 @@
  dataList: [],
  queryParams: {
    pageNum: 1,
    pageSize: 10
    pageSize: 10,
    companyId: null
  },
  total: 0,
  title: ''
@@ -63,13 +64,20 @@
const openDialog = async (value) => {
  state.title = '课时余量变动明细'
  dialogVisible.value = true;
  state.queryParams.companyId = value
  await getList()
}
const getList = async () => {
  state.loading = true
  const res = await getCompanyPeriod(state.queryParams)
  if(res.code == 200){
    state.dataList = res.data.list
    state.dataList = res.data.list.map(item => {
      return {
        ...item,
        modifyPeriodMin: item.modifyPeriod ? (item.modifyPeriod /60).toFixed(2).replace(/\.00$/, '')+'分钟' : '',
        remainPeriodMin: item.remainPeriod ? (item.remainPeriod  /60).toFixed(2).replace(/\.00$/, '')+'分钟' : ''
      }
    })
    state.total = res.data.total
  }else{
    ElMessage.warning(res.message)
src/views/onlineEducation/classHourBatch/index.vue
@@ -74,7 +74,8 @@
  dataList: [],
  isAdmin: false,
  companyName: '',
  remainPeriod: null
  remainPeriod: null,
  companyId: null
});
@@ -89,6 +90,7 @@
    data.remainPeriod = userInfo.remainPeriod ? (userInfo.remainPeriod /60).toFixed(2).replace(/\.00$/, ''):''
    data.isAdmin = false;
    data.companyName = userInfo.companyName
    data.companyId = userInfo.companyId
  }
  await getList()
})
@@ -146,6 +148,6 @@
}
const openDetail = () => {
  classHourRef.value.openDialog()
  classHourRef.value.openDialog(data.companyId)
}
</script>
src/views/onlineEducation/count/index.vue
@@ -1,8 +1,8 @@
<template>
  <div class="app-container">
    <div style="margin-bottom: 10px">
      <el-form style="display: flex">
        <el-form-item label="企业:">
      <el-form style="display: flex;flex-wrap: wrap">
        <el-form-item label="企业:" v-if="state.isAdmin">
          <el-select
              v-model="state.queryParams.companyId"
              style="width: 100%"
@@ -27,7 +27,7 @@
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              value-format="YYYY-MM-DD 00:00:00"
              format="YYYY-MM-DD"
          />
        </el-form-item>
        <el-form-item  style="margin-left: 50px">
@@ -47,15 +47,31 @@
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="state.dataList" :border="true" row-key="id">
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="企业名称" prop="sort" align="center" />
      <el-table-column label="企业编号" prop="sort" align="center" width="80" />
      <el-table-column label="总批次/人数" prop="sort" align="center" width="80" />
      <el-table-column label="三级" prop="sort" align="center" width="80" />
      <el-table-column label="二级" prop="sort" align="center" width="80" />
      <el-table-column label="一级" prop="sort" align="center" width="80" />
      <el-table-column label="考试人次" prop="sort" align="center" width="80" />
      <el-table-column label="合格人次" prop="sort" align="center" width="80" />
      <el-table-column label="考试合格率" prop="sort" align="center" width="80" />
      <el-table-column label="企业名称" prop="companyName" align="center" />
      <el-table-column label="企业编号" prop="companyCode" align="center" />
      <el-table-column label="总批次/人数" prop="sort" align="center"  >
        <template #default="scope">
          <span>{{scope.row.phaseStudentCount && scope.row.phaseCount ? scope.row.phaseCount + '/' +scope.row.phaseStudentCount:''}}</span>
        </template>
      </el-table-column>
      <el-table-column label="三级" prop="sort" align="center" >
        <template #default="scope">
          <span>{{scope.row.level3StudentCount && scope.row.level3PhaseCount ? scope.row.level3PhaseCount+ '/' +scope.row.level3StudentCount:''}}</span>
        </template>
      </el-table-column>
      <el-table-column label="二级" prop="sort" align="center" >
        <template #default="scope">
          <span>{{scope.row.level2StudentCount && scope.row.level2PhaseCount ? scope.row.level2PhaseCount+ '/' +scope.row.level2StudentCount:''}}</span>
        </template>
      </el-table-column>
      <el-table-column label="一级" prop="sort" align="center"  >
        <template #default="scope">
          <span>{{scope.row.level1StudentCount && scope.row.level1PhaseCount ? scope.row.level1PhaseCount+ '/' +scope.row.level1StudentCount:''}}</span>
        </template>
      </el-table-column>
      <el-table-column label="考试人次" prop="paperStudentCount" align="center"  />
      <el-table-column label="合格人次" prop="passStudentCount" align="center"  />
      <el-table-column label="合格率" prop="passRate" align="center"  />
    </el-table>
    <pagination
        v-show="state.total > 0"
@@ -71,10 +87,12 @@
<script setup>
import {getCurrentInstance, onMounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import moment from "moment";
import {delClassification, getClassification} from "@/api/onlineEducation/courseClass";
import {getCompany} from "@/api/onlineEducation/company";
import {getCompanyCount} from "@/api/onlineEducation/count";
import Cookies from "js-cookie";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const areaRef = ref();
@@ -94,11 +112,19 @@
  companyList: [],
  pageNum: 1,
  pageSize: 10,
  isAdmin: false
});
//页面加载
onMounted(() => {
  setDate();
  const userInfo = JSON.parse(Cookies.get('userInfo'))
  console.log("userInfo",userInfo)
  state.isAdmin = userInfo.userType === 0;
  if(state.isAdmin){
  getCompanyList();
  }
  getList();
});
@@ -106,7 +132,14 @@
  loading.value = true;
  const res = await getCompanyCount(state.queryParams);
  if(res.code === 200){
    state.dataList = res.data
    state.dataList = res.data.list.map(item => {
      return {
        ...item,
        passRate: item.passStudentCount!=null && item.paperStudentCount!=null ? (item.passStudentCount / item.paperStudentCount).toFixed(2) *100 + '%': ''
      }
    })
    state.total = res.data.total
  }else{
    ElMessage.warning(res.message)
  }
@@ -150,23 +183,44 @@
    getCompanyList('')
  }, 500)
}
const setDate = () => {
  let isDate = new Date()
  let sTime = `${isDate.getFullYear()}-${isDate.getMonth() + 1}-${isDate.getDate()-7}`
  let eTime = `${isDate.getFullYear()}-${isDate.getMonth() + 1}-${isDate.getDate()}`
  sTime = `${sTime}`
  eTime = `${eTime}`
  searchTime.value = [sTime,eTime];
  state.queryParams.startTime = searchTime.value[0]+' 00:00:00'
  state.queryParams.endTime = searchTime.value[1]+' 00:00:00'
}
const changeTime=(value)=>{
  console.log('11',searchTime.value)
  if(!value){
    state.queryParams.endTime = ""
    state.queryParams.startTime = ""
  }
  searchTime.value[0]=moment(searchTime.value[0]).format('YYYY-MM-DD')
  searchTime.value[1]=moment(searchTime.value[1]).format('YYYY-MM-DD')
}
const searchClick = () => {
  if(searchTime.value && searchTime.value.length>0){
    state.queryParams.startTime = searchTime.value[0]
    state.queryParams.endTime = searchTime.value[1]
    state.queryParams.startTime = searchTime.value[0] + ' 00:00:00'
    state.queryParams.endTime = searchTime.value[1] + ' 00:00:00'
  }
  getList();
}
/** 重置新增的表单以及其他数据  */
function reset() {
  data.queryParams.name = '';
  data.queryParams.pageNum = 1;
  state.queryParams = {
    companyId: '',
    type: null,
    endTime: '',
    startTime: '',
    pageNum: 1,
    pageSize: 10,
  }
  searchTime.value = [];
  getList();
}
src/views/onlineEducation/groupExams/components/examChooseStudent.vue
对比新文件
@@ -0,0 +1,248 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="50%"
        :before-close="handleClose"
    >
      <div style="margin-bottom: 20px">
        <el-checkbox v-model="state.bindBatchStu" @change="changeBind">绑定批次人员</el-checkbox>
        <el-select
            v-if="state.bindBatchStu"
            multiple
            collapse-tags
            collapse-tags-tooltip
            v-model="state.form.phaseIds"
            style="width: 160px;margin: 0 20px"
            v-loadMoreNew:[reselectExam]="handleScroll"
            :popper-class="reselectExam.name"
            class="item-width"
            placeholder="请选择批次"
        >
          <el-option
              v-for="item in state.batchList"
              :key="item.id"
              :label="item.name"
              :value="item.id"
          />
        </el-select>
      </div>
      <!-- 表格数据 -->
      <el-table
          ref="tableRef"
          v-loading="loading"
          :data="state.dataList"
          :border="true"
          :row-key="getRowKey"
          @selection-change="handleSelectionChange"
          v-model="state.selectRowKeys"
      >
        <el-table-column type="selection" :reserve-selection="true" width="55" align="center" />
<!--        <el-table-column label="序号" type="index" align="center" width="80" />-->
        <el-table-column label="工号" prop="empno" align="center" width="60"  />
        <el-table-column label="姓名" prop="name" align="center"  />
        <el-table-column label="性别" prop="sex" align="center" >
          <template #default="scope">
            <span>{{scope.row.sex == 0 ? '男':'女'}}</span>
          </template>
        </el-table-column>
        <el-table-column label="手机号" prop="phone" align="center" width="130"/>
        <el-table-column label="身份证" prop="idNo" align="center" width="200" :show-overflow-tooltip="true"/>
<!--        <el-table-column label="创建人" prop="createBy" align="center"/>-->
        <el-table-column label="工作岗位" prop="post" align="center"/>
        <el-table-column label="职务" prop="duty" align="center"/>
<!--        <el-table-column label="一人一档" prop="duty" align="center" width="120">-->
<!--          <template #default="scope">-->
<!--            <el-button link type="primary">培训考试记录</el-button>-->
<!--          </template>-->
<!--        </el-table-column>-->
      </el-table>
      <pagination
          v-show="state.total> 0"
          :total="state.total"
          v-model:page="state.queryParams.pageNum"
          v-model:limit="state.queryParams.pageSize"
          @pagination="getList"
      />
      <template #footer>
        <span class="dialog-footer">
            <el-button @click="handleClose" size="default">取 消</el-button>
            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {nextTick, reactive, ref, toRefs} from 'vue'
import {ElMessage} from "element-plus";
import Cookies from "js-cookie";
import {
  addQuestionBank,
  checkQuestionBankName,
  editQuestionBank,
  getQuestionBank
} from "@/api/onlineEducation/questionBank";
import {getStudent} from "@/api/onlineEducation/student";
import {batchAddStudent, getBatch} from "@/api/onlineEducation/batch";
import {examAddStudent} from "@/api/onlineEducation/exam";
const dialogVisible = ref(false);
const title = ref("");
const tableRef = ref();
const length = ref()
const emit = defineEmits(["getList"]);
const startUsername = ref('');
const classifyRef = ref(null)
const state = reactive({
  form: {
    paperId: null,
    phaseIds: [],
    studentIds: []
  },
  isAdmin: false,
  total: 0,
  queryParams: {
    pageNum: 1,
    pageSize: 10
  },
  dataList: [],
  totalItems: 0, // 总数据条数,从后端接口获取
  phaseId: null,
  chooseStu: [],
  companyId: null,
  selectRowKeys: [],
  batchList: [],
  batchPageNum: 1, // 当前页码
  batchPageSize: 10, // 每页显示的数量
  hasMoreItemsBatch: null, // 是否还有更多选项
  bindBatchStu: false
})
const loading = ref(false);
const reselectExam = reactive({
  name: 'ExamBatch'
})
const getRowKey = (row) => {
  return row.id
}
const openDialog = async (data) => {
  // state.selectRowKeys = [10,11]
  state.form.paperId = data.queryParams.paperId
  title.value = '学员选择';
  dialogVisible.value = true;
  await getList()
  await loadMoreBatchData()
}
const getList = async () => {
  loading.value = true
  const res = await getStudent(state.queryParams)
  if(res.code == 200){
    state.dataList = res.data.list
    state.total = res.data.total
    // await nextTick(() => {
    //   const currentIds = state.dataList.map(row => row.id)
    //   const selectIds = state.selectRowKeys.filter(id => currentIds.includes(id))
    //   state.dataList.forEach(row => {
    //     if (selectIds.includes(row.id)) {
    //       tableRef.value.toggleRowSelection(row, true);
    //     }
    //   })
    // });
  }else{
    ElMessage.warning(res.message)
  }
  loading.value = false
}
const onSubmit = async () => {
    const res = await examAddStudent(state.form)
    if(res.code === 200){
      ElMessage({
        type: 'success',
        message: '新增成功'
      });
    }else{
      ElMessage.warning(res.message)
    }
    emit("getList")
    reset();
    dialogVisible.value = false;
}
const handleClose = () => {
  reset();
  dialogVisible.value = false;
  emit("getList")
}
const reset = () => {
  state.form = {
    paperId: null,
    phaseIds: [],
    studentIds: []
  }
  state.bindBatchStu = false;
  state.batchList = [];
  state.batchPageNum = 1;
  state.batchPageSize = 10;
  state.hasMoreItemsBatch = null;
  tableRef.value.clearSelection();
  state.dataList = []
  state.total = 0
  state.queryParams ={
    pageNum: 1,
    pageSize: 10
  }
}
const handleSelectionChange = (val) => {
  state.form.studentIds = val.map(item => item.id)
  console.log("选中的行",  state.form.studentIds)
}
const handleScroll = () => {
  if(state.batchPageNum >= state.hasMoreItemsBatch) return
  state.batchPageNum++;
  loadMoreBatchData()
}
const loadMoreBatchData = async () => {
  console.log(' Bank');
  const queryParams = {
    pageNum: state.batchPageNum,
    pageSize: state.batchPageSize,
  }
  const res = await getBatch(queryParams)
  if (res.code == 200) {
    state.hasMoreItemsBatch = res.data.totalPage
    const data = res.data
    state.batchList = state.batchList.concat(data.list)
  }else{
    ElMessage.warning(res.message)
  }
}
const changeBind = (val) => {
  console.log('vv',val)
}
defineExpose({
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
src/views/onlineEducation/groupExams/components/examDialog.vue
@@ -4,6 +4,7 @@
        v-model="dialogVisible"
        width="800px"
        :before-close="handleClose"
        destroy-on-close
    >
      <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" >
          <span style="font-size: 20px;font-weight: 800;margin-left: 20px">考试配置</span>
@@ -28,15 +29,18 @@
        <div  style="display: flex">
          <div class="group">
            <div>
              单选:<el-input style="max-width: 40px"></el-input>&nbsp;题
              单选:<el-input style="max-width: 40px" v-model.trim="state.form.singleNum"></el-input>&nbsp;题
            </div>
            <div style="margin-left: 20px">
              每题:<el-input style="max-width: 40px"></el-input>&nbsp;分
              每题:<el-input style="max-width: 40px" v-model.trim="state.form.singleScore"></el-input>&nbsp;分
            </div>
          </div>
          <div class="group" >
           <div>
             共&nbsp;<span style="max-width: 30px">xxx</span>&nbsp;分
             共&nbsp;
             <span style="max-width: 30px" v-show="state.form.singleNum && state.form.singleScore">{{state.form.singleNum * state.form.singleScore}}</span>
<!--             <span v-else></span>-->
             &nbsp;分
           </div>
            <el-select
                clearable
@@ -54,33 +58,36 @@
                  :value="item.id"
              />
            </el-select>
          <el-radio-group v-model="radio"  >
          <el-radio-group v-model="state.form.singleMethod"  >
            <el-radio :label="1" style="max-width: 30px">随机</el-radio>
            <el-radio :label="2">默认</el-radio>
            <el-radio :label="2">顺序</el-radio>
          </el-radio-group>
          </div>
        </div>
        <div  style="display: flex">
          <div class="group">
            <div>
              多选:<el-input style="max-width: 40px"></el-input>&nbsp;题
              多选:<el-input style="max-width: 40px" v-model="state.form.multiNum"></el-input>&nbsp;题
            </div>
            <div style="margin-left: 20px">
              每题:<el-input style="max-width: 40px"></el-input>&nbsp;分
              每题:<el-input style="max-width: 40px" v-model="state.form.multiScore"></el-input>&nbsp;分
            </div>
          </div>
          <div class="group" >
            <div>
              共&nbsp;<span style="max-width: 30px">xxx</span>&nbsp;分
              共&nbsp;
              <span style="max-width: 30px" v-show="state.form.multiNum && state.form.multiScore">{{state.form.multiNum * state.form.multiScore}}</span>
<!--              <span v-else></span>-->
              &nbsp;分
            </div>
            <el-select
                clearable
                v-model="state.form.singleBankId"
                v-model="state.form.multiBankId"
                style="width: 160px;margin: 0 20px"
                v-loadMoreNew:[reselectSingle]="handleScroll"
                :popper-class="reselectSingle.name"
                class="item-width"
                placeholder="请选择单选题题库"
                placeholder="请选择多选题题库"
            >
              <el-option
                  v-for="item in state.bankListSingle"
@@ -89,33 +96,36 @@
                  :value="item.id"
              />
            </el-select>
            <el-radio-group v-model="radio"  >
            <el-radio-group v-model="state.form.multiMethod"  >
              <el-radio :label="1" style="max-width: 30px">随机</el-radio>
              <el-radio :label="2">默认</el-radio>
              <el-radio :label="2">顺序</el-radio>
            </el-radio-group>
          </div>
        </div>
        <div  style="display: flex">
          <div class="group">
            <div>
              判断:<el-input style="max-width: 40px"></el-input>&nbsp;题
              判断:<el-input style="max-width: 40px" v-model="state.form.judgeNum"></el-input>&nbsp;题
            </div>
            <div style="margin-left: 20px">
              每题:<el-input style="max-width: 40px"></el-input>&nbsp;分
              每题:<el-input style="max-width: 40px" v-model="state.form.judgeScore"></el-input>&nbsp;分
            </div>
          </div>
          <div class="group" >
            <div>
              共&nbsp;<span style="max-width: 30px">xxx</span>&nbsp;分
              共&nbsp;
              <span style="max-width: 30px" v-show="state.form.judgeNum && state.form.judgeScore">{{state.form.judgeNum * state.form.judgeScore}}</span>
<!--              <span v-else></span>-->
              &nbsp;分
            </div>
            <el-select
                clearable
                v-model="state.form.singleBankId"
                v-model="state.form.judgeBankId"
                style="width: 160px;margin: 0 20px"
                v-loadMoreNew:[reselectSingle]="handleScroll"
                :popper-class="reselectSingle.name"
                class="item-width"
                placeholder="请选择单选题题库"
                placeholder="请选择判断题题库"
            >
              <el-option
                  v-for="item in state.bankListSingle"
@@ -124,24 +134,32 @@
                  :value="item.id"
              />
            </el-select>
            <el-radio-group v-model="radio"  >
            <el-radio-group v-model="state.form.judgeMethod"  >
              <el-radio :label="1" style="max-width: 30px">随机</el-radio>
              <el-radio :label="2">默认</el-radio>
              <el-radio :label="2">顺序</el-radio>
            </el-radio-group>
          </div>
        </div>
        <span class="group" style="margin-bottom: 10px">共计:xxx分</span>
        <span class="group" style="margin-bottom: 20px">共计:
          {{state.form.judgeNum * state.form.judgeScore+state.form.multiNum * state.form.multiScore+state.form.singleNum * state.form.singleScore}}
          &nbsp;分</span>
        <div style="display: flex;justify-content: space-between;align-items: center">
          <el-form-item label="合格分数:" prop="">
            <el-input-number v-model="num" :min="1" :max="10" style="margin-right: 10px" /> (大于等于)
          <el-form-item label="合格分数:" prop="passScore">
            <el-input-number v-model="state.form.passScore" :min="1" :max="1000" style="margin-right: 10px" /> (大于等于)
          </el-form-item>
          <el-form-item label="限制时长:" prop="">
            <el-input v-model="num1" style="max-width: 200px" >
          <el-form-item label="限制时长:" prop="limitTime">
            <el-input v-model="state.form.limitTime" style="max-width: 200px"  :disabled="state.form.limited === 0">
              <template #append>分钟</template>
            </el-input>
          </el-form-item>
        </div>
        </div>
        <el-form-item label="是否限制考试时长:" prop="limited" style="margin-left: 60px">
          <el-radio-group v-model="state.form.limited" @change="changeLimit" >
            <el-radio :label="0" style="max-width: 30px">否</el-radio>
            <el-radio :label="1">是</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
@@ -169,7 +187,7 @@
  editQuestionBank,
  getQuestionBank
} from "@/api/onlineEducation/questionBank";
import {checkExamName} from "@/api/onlineEducation/exam";
import {addExam, checkExamName, editExam} from "@/api/onlineEducation/exam";
const dialogVisible = ref(false);
const title = ref("");
@@ -214,11 +232,30 @@
    name: '',
    categoryId: null,
    companyName: '',
    companyId: null
    companyId: null,
    judgeBankId: null,
    judgeMethod: 1,
    judgeNum: null,
    judgeScore: null,
    multiBankId: null,
    multiMethod: 1,
    multiNum: null,
    multiScore: null,
    singleBankId: null,
    singleMethod: 1,
    singleNum: null,
    singleScore: null,
    limited: 1,
    limitTime: null,
    passScore: null
  },
  formRules: {
    name: [{required: true, trigger: "blur", validator: validateName}],
    categoryId: [{required: true, message: '请选择课程分类', trigger: 'blur'}],
    limited: [{required: true, message: '请选择课程分类', trigger: 'blur'}],
    limitTime: [{required: true, message: '请选择课程分类', trigger: 'blur'}],
    passScore: [{required: true, message: '请选择课程分类', trigger: 'blur'}]
  },
  classifyList: [],
  isAdmin: false,
@@ -247,9 +284,7 @@
  }
  title.value = type === 'addFirst' || type === 'add' ? '新增' : type ==='edit' ? '编辑' : '' ;
  if(type === 'edit') {
    state.form.id = value.id
    state.form.name = value.name
    state.form.categoryId = value.categoryId
    state.form = value
    startUsername.value = value.name;
  }else if(type === 'add' && value ){
    state.form.parentId = value.id
@@ -258,18 +293,18 @@
}
const onSubmit = async () => {
  if(state.isAdmin){
    ElMessage({
      type: 'warning',
      message: '管理员暂无权限'
    });
    return;
  }
  // if(state.isAdmin){
  //   ElMessage({
  //     type: 'warning',
  //     message: '管理员暂无权限'
  //   });
  //   return;
  // }
  const valid = await busRef.value.validate();
  if(valid){
    if(title.value === '新增'){
      const {id, ...data} = JSON.parse(JSON.stringify(state.form))
      const res = await addQuestionBank(data)
      const res = await addExam(data)
      if(res.code === 200){
        ElMessage({
          type: 'success',
@@ -284,7 +319,7 @@
      dialogVisible.value = false;
    }else if(title.value === '编辑'){
      const {...data} = JSON.parse(JSON.stringify(state.form))
      const res = await editQuestionBank(data)
      const res = await editExam(data)
      if(res.code === 200){
        ElMessage({
          type: 'success',
@@ -348,7 +383,22 @@
    name: '',
    categoryId: null,
    companyName: '',
    companyId: null
    companyId: null,
    judgeBankId: null,
    judgeMethod: 1,
    judgeNum: null,
    judgeScore: null,
    multiBankId: null,
    multiMethod: 1,
    multiNum: null,
    multiScore: null,
    singleBankId: null,
    singleMethod: 1,
    singleNum: null,
    singleScore: null,
    limited: 0,
    limitTime: null,
    passScore: null
  }
}
const handleScroll = () => {
@@ -372,6 +422,14 @@
    ElMessage.warning(res.message)
  }
}
const changeLimit = (val) => {
  state.form.limitTime = null
  if(val === 0) {
    state.form.limitTime = 0
  }
}
defineExpose({
  openDialog
});
src/views/onlineEducation/groupExams/components/student.vue
对比新文件
@@ -0,0 +1,184 @@
<template>
  <div class="app-container">
    <div style="margin-bottom: 10px;display: flex;align-items: center;justify-content: space-between">
      <el-button
          type="primary"
          plain
          icon="Plus"
          @click="openDialog()"
      >选择学员</el-button>
      <el-button
          type="danger"
          plain
          icon="Delete"
          @click="handleDeleteBatch"
      >批量删除</el-button>
    </div>
    <!-- 表格数据 -->
    <el-table ref="tableRef" v-loading="loading" :data="dataList" :border="true" :row-key="getRowKey"  @selection-change="handleSelectionChange">
      <el-table-column type="selection" :reserve-selection="true" width="55" align="center" />
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="试卷名称" align="center" >
        <template #default="scope">
          <span>{{scope.row.examPaper.name}}</span>
        </template>
      </el-table-column>
      <el-table-column label="学员名称" prop="studentName" align="center" >
        <template #default="scope">
          <span>{{scope.row.student.name}}</span>
        </template>
      </el-table-column>
      <el-table-column label="手机号" prop="studentPhone" align="center" >
        <template #default="scope">
          <span>{{scope.row.student.phone}}</span>
        </template>
      </el-table-column>
      <el-table-column label="成绩" prop="score" align="center"  />
      <el-table-column label="是否合格" prop="passed" align="center"  >
        <template #default="scope">
          <span>{{scope.row.passed === 0 ? '不合格' : '合格'}}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width"  width="180">
        <template #default="scope">
          <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <exam-choose-student ref="dialogRef" @getList=getList></exam-choose-student>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import Cookies from "js-cookie";
import {delQuestionBank, getQuestionBank} from "@/api/onlineEducation/questionBank";
import {batchDelStudent, delBatchStu, getBatchStudent} from "@/api/onlineEducation/batch";
import {useRoute} from 'vue-router'
import {delExamStu, examDelStudent, getExamStudent} from "@/api/onlineEducation/exam";
import ExamChooseStudent from "@/views/onlineEducation/groupExams/components/examChooseStudent.vue";
const route = useRoute()
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const tableRef = ref();
const data = reactive({
  queryParams: {
    paperId: null,
    pageNum: 1,
    pageSize: 10,
  },
  total: 0,
  dataList: [],
  isAdmin: false,
  chooseStu: []
});
const { queryParams, total, dataList } = toRefs(data);
onMounted(async ()=>{
  const userInfo = JSON.parse(Cookies.get('userInfo'))
  console.log("userInfo",userInfo)
  if(userInfo.userType === 0){
    data.isAdmin = true;
  }else {
    data.isAdmin = false;
  }
  const val = JSON.parse(route.query.val)
  // data.queryParams.pageId = val.id
  data.queryParams.paperId = val.id
  await getList()
})
onUnmounted(()=>{
})
const getRowKey = (row) => {
  return row.id
}
const getList = async () => {
  loading.value = true
  const res = await getExamStudent(data.queryParams)
  if(res.code == 200){
    data.dataList = res.data.list
    data.total = res.data.total
  }else{
    ElMessage.warning(res.message)
  }
  loading.value = false
}
const openDialog = () => {
  dialogRef.value.openDialog(data);
}
/** 重置新增的表单以及其他数据  */
function reset() {
  proxy.resetForm("roleRef");
}
const handleSelectionChange = (val) => {
  console.log("选中的行", val)
  data.chooseStu = val.map(item => item.id)
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delExamStu(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
          tableRef.value.clearSelection();
        }else{
          ElMessage.warning(res.message)
        }
      })
}
const handleDeleteBatch = () => {
  if(data.chooseStu && data.chooseStu.length <=0){
    ElMessage.warning('请选择要删除的学员');
    return false;
  }
  ElMessageBox.confirm(
      '确定删除选择的所有数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        // const param = {
        //   phaseStudentIds: data.chooseStu
        // }
        const res = await examDelStudent(data.chooseStu)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
</script>
src/views/onlineEducation/groupExams/index.vue
@@ -14,11 +14,28 @@
      <el-table-column label="企业名称" prop="companyName" align="center"  />
      <el-table-column label="创建账户" prop="createBy" align="center"  />
      <el-table-column label="科目/类别" prop="categoryName" align="center"  />
      <el-table-column label="考生人数" prop="" align="center"  />
      <el-table-column label="合格人数" prop="" align="center"  />
      <el-table-column label="平均分数" prop="" align="center"  />
      <el-table-column label="合格率" prop="" align="center"  />
      <el-table-column label="限制时长" prop="limitTime" align="center"  />
      <el-table-column label="考生人数" prop="" align="center">
        <template #default="scope">
          <span>{{scope.row.paperStudentInfoVO.studentCount}}</span>
        </template>
      </el-table-column>
      <el-table-column label="合格人数" prop="" align="center">
        <template #default="scope">
          <span>{{scope.row.paperStudentInfoVO.passStudentCount}}</span>
        </template>
      </el-table-column>
      <el-table-column label="平均分数" prop="" align="center" >
        <template #default="scope">
          <span>{{scope.row.paperStudentInfoVO.avgScore}}</span>
        </template>
      </el-table-column>
      <el-table-column label="合格率" prop="passRate" align="center" />
      <el-table-column label="限制时长" prop="limitTime" align="center"  >
        <template #default="scope">
          <span v-if="scope.row.limitTime == 0">不限时</span>
          <span v-else>{{scope.row.limitTime}}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width"  width="180">
        <template #default="scope">
          <el-button link type="primary" @click="toStuChoose(scope.row)">学生数据</el-button>
@@ -91,7 +108,13 @@
  loading.value = true
  const res = await getExam(data.queryParams)
  if(res.code == 200){
    data.dataList = res.data.list
    data.dataList = res.data.list.map(item => {
      return {
        ...item,
        passRate: item.paperStudentInfoVO.passStudentCount ===0 && item.paperStudentInfoVO.studentCount ===0  ? '0%': (item.paperStudentInfoVO.passStudentCount / item.paperStudentInfoVO.studentCount).toFixed(2) *100 + '%'
      }
    })
    data.total = res.data.total
  }else{
    ElMessage.warning(res.message)
@@ -128,7 +151,7 @@
}
const toStuChoose = (val) => {
  const v = JSON.stringify(val)
  router.push({ path: "/chooseStu", query: { val: v } });
  router.push({ path: "/examStu", query: { val: v } });
}
const openDetail = () => {
src/views/onlineEducation/offlineEducation/components/recordDialog.vue
@@ -82,7 +82,6 @@
</template>
<script setup>
import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
import InfiniteList from 'vue3-infinite-list';
import { View } from "@element-plus/icons-vue";
import scorllSelect from '@/components/scrollSelect/index.vue'
import {ElMessage, ElMessageBox} from "element-plus";
src/views/onlineEducation/systemManage/company/index.vue
@@ -20,7 +20,7 @@
      <el-table-column label="总课时(分)" prop="totalPeriodMin" align="center"/>
      <el-table-column label="课时变动详情" align="center" class-name="small-padding fixed-width" >
        <template #default="scope">
          <el-button link type="primary">查看详情</el-button>
          <el-button link type="primary" @click="openDetail(scope.row)">查看详情</el-button>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" >
@@ -41,6 +41,7 @@
    />
    <company-dialog ref="dialogRef" @getList=getList></company-dialog>
    <class-hour-change ref="classHourRef" @getList=getList></class-hour-change>
  </div>
</template>
@@ -49,6 +50,7 @@
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/onlineEducation/company";
import companyDialog from "./components/companyDialog.vue";
import classHourChange from '@/views/onlineEducation/classHourBatch/components/classHourChange.vue'
const { proxy } = getCurrentInstance();
const loading = ref(false);
@@ -63,7 +65,7 @@
});
const { queryParams, total, dataList } = toRefs(data);
const classHourRef = ref();
onMounted(()=>{
  getList()
})
@@ -119,4 +121,7 @@
      })
}
const openDetail = (val) => {
  classHourRef.value.openDialog(val.id)
}
</script>