zhouwx
2024-07-05 c293e8f2c2c8f1eae95b0255a8745456963511d2
线下登记、统计
已修改4个文件
已添加4个文件
741 ■■■■■ 文件已修改
src/api/onlineEducation/count.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/onlineEducation/examRecord.js 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/directive.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/directivesNew.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/count/index.vue 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/offlineEducation/components/recordDialog.vue 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/onlineEducation/offlineEducation/index.vue 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/onlineEducation/count.js
对比新文件
@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getCompanyCount(params) {
    return request({
        url: '/statistic/companyStatistic',
        method: 'get',
        params: params
    })
}
src/api/onlineEducation/examRecord.js
对比新文件
@@ -0,0 +1,35 @@
import request from '@/utils/request'
export function getRecord(param) {
    return request({
        url: '/exam-record/list',
        method: 'get',
        params: param
    })
}
export function addRecord(data) {
    return request({
        url: '/exam-record',
        method: 'post',
        data: data
    })
}
export function editRecord(params) {
    return request({
        url: `/exam-record`,
        method: 'put',
        data: params
    })
}
export function delRecord(userId) {
    return request({
        url: '/exam-record/' + userId,
        method: 'delete'
    })
}
src/main.js
@@ -51,6 +51,7 @@
import { Boot } from '@wangeditor/editor'
import attachmentModule from '@wangeditor/plugin-upload-attachment'
import loadMore from '@/utils/selectLoadMoreDirective'
import loadMoreNew from '@/utils/directive'
import "video.js/dist/video-js.css"
Boot.registerModule(attachmentModule)
const app = createApp(App)
@@ -83,6 +84,8 @@
app.use(preReClick)
app.component('svg-icon', SvgIcon)
app.directive('loadMore',loadMore)
app.directive('loadMoreNew',loadMoreNew)
// app.use(Directives)
directive(app)
// 使用element-plus 并且设置全局的大小
src/utils/directive.ts
@@ -1,6 +1,6 @@
import { Directive, DirectiveBinding } from 'vue'
const loadMore: Directive = {
const loadMoreNew: Directive = {
    beforeMount(el: any, binding: DirectiveBinding) {
        let arg = binding.arg as any
        if (!arg) return
@@ -23,4 +23,4 @@
        }
    }
}
export default loadMore
export default loadMoreNew
src/utils/directivesNew.js
对比新文件
@@ -0,0 +1,48 @@
// // import Vue from 'vue'
// //
// // export default () => {
// //     Vue.directive('selectScroll', {
// //         bind (el, binding) {
// //             //  如上图,我通过v-if来控制了两个select框,当没有binding.arg这个参数时,我只能监听到企业类型下的select框,所以,我通过传参控制了监听的哪个select框
// //             var className = '.' + binding.arg
// //             el.className = binding.arg
// //             // 获取滚动页面DOM
// //             const SCROLL_DOM = el.querySelector(`${className} .el-select-dropdown .el-select-dropdown__wrap`)
// //             // const SCROLL_DOM = el.querySelector(“.el-select-dropdown .el-select-dropdown__wrap“)
// //             SCROLL_DOM.addEventListener('scroll', function () {
// //                 // 当前的滚动位置 减去  上一次的滚动位置
// //                 // 如果为true则代表向上滚动,false代表向下滚动
// //                 const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
// //                 // 如果已达到指定位置则触发
// //                 if (CONDITION) {
// //                     // 将滚动行为告诉组件
// //                     binding.value()
// //                 }
// //             })
// //         }
// //     })
// // }
//
// import { Directive, DirectiveBinding } from 'vue'
//
// const selectScroll: Directive = {
//     beforeMount(el: any, binding: DirectiveBinding) {
//         //  如上图,我通过v-if来控制了两个select框,当没有binding.arg这个参数时,我只能监听到企业类型下的select框,所以,我通过传参控制了监听的哪个select框
//         const className = '.' + binding.arg;
//         el.className = binding.arg
//         // 获取滚动页面DOM
//         const SCROLL_DOM = el.querySelector(`${className} .el-select-dropdown .el-select-dropdown__wrap`)
//         // const SCROLL_DOM = el.querySelector(“.el-select-dropdown .el-select-dropdown__wrap“)
//         SCROLL_DOM.addEventListener('scroll', function () {
//             // 当前的滚动位置 减去  上一次的滚动位置
//             // 如果为true则代表向上滚动,false代表向下滚动
//             const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
//             // 如果已达到指定位置则触发
//             if (CONDITION) {
//                 // 将滚动行为告诉组件
//                 binding.value()
//             }
//         })
//     },
// }
// export default selectScroll
src/views/onlineEducation/count/index.vue
@@ -1,12 +1,174 @@
<template>
<div>数据统计</div>
  <div class="app-container">
    <div style="margin-bottom: 10px">
      <el-form style="display: flex">
        <el-form-item label="企业:">
          <el-select
              v-model="state.queryParams.companyId"
              style="width: 100%"
              v-loadMore="loadMore"
              class="m-2"
              placeholder="请选择所属企业"
              popper-class="more_select_dropdown"
          >
            <el-option
                v-for="item in state.companyList"
                :key="item.id"
                :label="item.name"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="时间范围:" style="margin-left: 20px">
          <el-date-picker
              v-model="searchTime"
              type="daterange"
              @change="changeTime"
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              value-format="YYYY-MM-DD 00:00:00"
          />
        </el-form-item>
        <el-form-item  style="margin-left: 50px">
          <el-radio-group v-model="state.queryParams.type">
            <el-radio :label="1">线上教育</el-radio>
            <el-radio :label="2">线下教育</el-radio>
            <el-radio :label="null">全部</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" style="margin-left: 30px" @click="searchClick">查询</el-button>
          <el-button plain @click="reset">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 表格数据 -->
    <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>
    <pagination
        v-show="state.total > 0"
        :total="state.total"
        v-model:page="state.queryParams.pageNum"
        v-model:limit="state.queryParams.pageSize"
        @pagination="getList"
    />
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delClassification, getClassification} from "@/api/onlineEducation/courseClass";
import {getCompany} from "@/api/onlineEducation/company";
import {getCompanyCount} from "@/api/onlineEducation/count";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const areaRef = ref();
const searchTime = ref([]);
const state = reactive({
  queryParams: {
    companyId: '',
    type: null,
    endTime: '',
    startTime: '',
    pageNum: 1,
    pageSize: 10,
  },
  total: 0,
  dataList: [
  ],
  companyList: [],
  pageNum: 1,
  pageSize: 10,
});
//页面加载
onMounted(() => {
  getCompanyList();
  getList();
});
const getList = async () => {
  loading.value = true;
  const res = await getCompanyCount(state.queryParams);
  if(res.code === 200){
    state.dataList = res.data
  }else{
    ElMessage.warning(res.message)
  }
  loading.value = false;
}
const finshed = ref(false)
const getCompanyList = async (type)=>{
  if (type === 'open' && state.pageNum !== 1) {
  } else {
    const queryParams = {
      pageNum: state.pageNum,
      pageSize: state.pageSize,
    }
    const res = await getCompany(queryParams)
    if (res.code == 200) {
      if (res.data.pageNum === state.pageNum) {
        finshed.value = false;
        if (state.pageNum == 1) {
          state.companyList = res.data.list
        } else {
          state.companyList = state.companyList.concat(res.data.list)
        }
      } else {
        finshed.value = true;
      }
    } else {
      ElMessage.warning(res.message)
    }
    console.log("state.companyList",state.companyList)
  }
}
//触底函数
const loadMore = () => {
  console.log(' 触底了');
  // 防抖处理
  setTimeout(() => {
    if (finshed.value) return //值为true,则代表没有数据了
    state.pageNum += 1
    getCompanyList('')
  }, 500)
}
const changeTime=(value)=>{
  if(!value){
    state.queryParams.endTime = ""
    state.queryParams.startTime = ""
  }
}
const searchClick = () => {
  if(searchTime.value && searchTime.value.length>0){
    state.queryParams.startTime = searchTime.value[0]
    state.queryParams.endTime = searchTime.value[1]
  }
  getList();
}
/** 重置新增的表单以及其他数据  */
function reset() {
  data.queryParams.name = '';
  data.queryParams.pageNum = 1;
  getList();
}
</script>
<style scoped lang="scss">
</style>
src/views/onlineEducation/offlineEducation/components/recordDialog.vue
对比新文件
@@ -0,0 +1,333 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="state.title"
        width="550px"
        :before-close="handleClose"
    >
      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="180px" >
        <el-form-item label="企业名称:"  prop="companyName" >
          <el-select
              v-if="state.isAdmin"
              v-model="state.form.companyName"
              style="width: 100%"
              v-loadMoreNew:[reselect]="handleScroll"
              :popper-class="reselect.name"
              @change="selectCompany"
              class="item-width"
          >
            <el-option
                v-for="item in state.companyList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
          <el-input v-else v-model.trim="state.form.companyName" disabled ></el-input>
        </el-form-item>
        <el-form-item label="计划名称:"  prop="planName" >
          <el-input v-model.trim="state.form.planName" placeholder="请输入计划名称"></el-input>
        </el-form-item>
        <el-form-item label="学员:"  prop="studentName" >
          <el-select
              v-model="state.form.studentName"
              style="width: 100%"
              v-loadMoreNew:[reselectStu]="handleScrollStu"
              :popper-class="reselectStu.name"
              class="m-2"
              @change="selectValue"
              placeholder="请选择学生"
              popper-class="more_select_dropdown"
          >
            <el-option
                v-for="item in state.studentList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="课程名称:"  prop="courseName" >
          <el-input v-model.trim="state.form.courseName" :disabled="disabled" placeholder="请输入课程名称" ></el-input>
        </el-form-item>
        <el-form-item label="培训等级:"  prop="level" >
          <el-input v-model.trim="state.form.level" :disabled="disabled" placeholder="请输入培训等级" ></el-input>
        </el-form-item>
        <el-form-item label="要求课时(分):"  prop="period" >
          <el-input v-model.trim="state.form.period" :disabled="disabled" placeholder="请输入要求课时(分)" ></el-input>
        </el-form-item>
        <el-form-item label="实际课时(分):"  prop="actualPeriod" >
          <el-input v-model.trim="state.form.actualPeriod" :disabled="disabled" placeholder="请输入实际课时(分)" ></el-input>
        </el-form-item>
        <el-form-item label="考试成绩:"  prop="score" >
          <el-input v-model.trim="state.form.score" :disabled="disabled" placeholder="请输入考试成绩" ></el-input>
        </el-form-item>
        <el-form-item label="是否合格:"  prop="passed" >
          <el-radio-group v-model="state.form.passed"  :disabled="disabled">
            <el-radio :label="0">不合格</el-radio>
            <el-radio :label="1">合格</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer v-if="state.title !='查看'">
        <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 {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";
import {verifyPhone, verifyPwd, verifyUsername} from "@/utils/validate";
import { checkUserName, checkPhone } from "@/api/login"
import {getStudent, resetPwd} from "@/api/onlineEducation/student"
import {Base64} from "js-base64"
import Cookies from "js-cookie";
import {addStudent, checkStuIdNo, checkStuPhone, editStudent} from "@/api/onlineEducation/student";
import {addRecord, editRecord} from "@/api/onlineEducation/examRecord";
import {getCompany} from "@/api/onlineEducation/company";
const emit = defineEmits(["getList"]);
const dialogVisible = ref(false)
const superRef = ref(null)
const scrollRef = ref(null)
const reselect = reactive({
  name: 'company'
})
const reselectStu = reactive({
  name: 'student'
})
const state = reactive({
  title: '',
  form: {
    id: null,
    companyName: '',
    planName: '',
    studentId: '',
    level: '',
    period: '',
    actualPeriod: '',
    score: null,
    passed: 0,
    studentName: '',
    courseName: '',
    companyId: null
  },
  formRules:{
    companyName: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
    planName: [{ required: true, message: '请输入计划名称', trigger: 'blur' }],
    studentName: [{ required: true, message: '请选择学员', trigger: 'blur' }],
    level: [{ required: true, message: '请输入培训等级', trigger: 'blur' }],
    period: [{ required: true, message: '请输入要求课时(分)', trigger: 'blur' }],
    actualPeriod: [{ required: true, message: '实际课时(分)', trigger: 'blur' }],
    score: [{ required: true, message: '请输入考试成绩', trigger: 'blur' }],
  },
  isAdmin: false,
  studentList: [],
  stuPageNum: 1, // 当前页码
  stuPageSize: 10, // 每页显示的数量
  hasMoreStu: null, // 是否还有更多选项
  pageNum: 1,
  pageSize: 10,
  companyList: [],
  companyPageNum: 1, // 当前页码
  companyPageSize: 10, // 每页显示的数量
  hasMoreItems: null, // 是否还有更多选项
})
onMounted(() => {
});
const disabled = ref(false);
const openDialog = async (type, value) => {
  await loadMoreStuData();
  if(state.isAdmin){
    await loadMoreCompanyData();
  }
  const userInfo = JSON.parse(Cookies.get('userInfo'))
  console.log("userInfo",userInfo)
  if(userInfo.userType === 0){
    state.isAdmin = true;
    state.form.userType = 0;
  }else {
    state.isAdmin = false;
    state.form.companyId = userInfo.companyId;
    state.form.companyName = userInfo.companyName;
    state.form.userType = 1;
  }
  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : type ==='pwd' ? '修改密码' : '查看' ;
  if(type === 'edit' || type === 'view') {
    if( type === 'view'){
      disabled.value = true;
    }
    state.form = value
    // state.form.studentName = value.company.name;
    console.log("ba",state.form)
  }
  if(type == 'pwd'){
    state.form.id = value.id
  }
  dialogVisible.value = true
}
const onSubmit = async () => {
  const valid = await superRef.value.validate();
  if(valid){
    if(state.title == '新增'){
      const {id,...data} = state.form
      const res = await addRecord(data)
      if(res.code == 200){
        ElMessage.success(res.message)
        emit('getList')
        handleClose()
        dialogVisible.value = false;
      }else{
        ElMessage.warning(res.message)
      }
    }else if(state.title == '编辑'){
      const {id, name, phone, sex, companyId, empno, post, duty, idNo} = state.form
      const data = {id, name, phone, sex, companyId, empno, post, duty, idNo}
      const res = await editRecord(data)
      if(res.code == 200){
        ElMessage.success(res.message)
        emit('getList')
        handleClose()
      }else{
        ElMessage.warning(res.message)
      }
    }else{
      const {id,password} = state.form
      const data = {id,password}
      data.password = Base64.encode(data.password)
      const res = await resetPwd(data)
      if(res.code == 200){
        ElMessage.success(res.message)
        emit('getList')
        handleClose()
      }else{
        ElMessage.warning(res.message)
      }
    }
  }
}
const handleClose = () => {
  state.form = {
    id: null,
    companyName: '',
    planName: '',
    studentId: '',
    level: '',
    period: '',
    actualPeriod: '',
    score: null,
    passed: 0,
    studentName: '',
    courseName: '',
    companyId: null
  }
  state.companyPageNum = 1;
  state.companyPageSize = 10;
  state.companyList = [];
  state.stuPageNum = 1;
  state.stuPageSize = 10;
  state.studentList = [];
  superRef.value.clearValidate();
  superRef.value.resetFields()
  dialogVisible.value = false;
}
const selectValue = (val) => {
  state.studentList.forEach(item => {
    if(item.name === val){
      state.form.studentId = item.id
    }
  })
}
const selectCompany = (val) => {
  state.companyList.forEach(item => {
    if(item.name === val){
      state.form.companyId = item.id
    }
  })
}
const handleScrollStu = () => {
  console.log(' student',state.hasMoreStu);
  if(state.stuPageNum >= state.hasMoreStu) return
  state.stuPageNum++;
  loadMoreStuData()
}
const loadMoreStuData = async () => {
  const queryParams = {
    pageNum: state.stuPageNum,
    pageSize: state.stuPageSize,
  }
  const res = await getStudent(queryParams)
  if (res.code == 200) {
    state.hasMoreStu = res.data.totalPage
    const data = res.data
    state.studentList = state.studentList.concat(data.list)
  }else{
    ElMessage.warning(res.message)
  }
}
const handleScroll = () => {
  console.log(' Company',state.hasMoreItems);
  if(state.companyPageNum >= state.hasMoreItems) return
    state.companyPageNum++;
    loadMoreCompanyData()
}
const loadMoreCompanyData = async () => {
  const queryParams = {
    pageNum: state.companyPageNum,
    pageSize: state.companyPageSize,
  }
  const res = await getCompany(queryParams)
  if (res.code == 200) {
    state.hasMoreItems = res.data.totalPage
    const data = res.data
    state.companyList = state.companyList.concat(data.list)
  }else{
    ElMessage.warning(res.message)
  }
}
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/offlineEducation/index.vue
@@ -1,12 +1,131 @@
<template>
<div>线下教育登记</div>
  <div class="app-container">
    <div style="margin-bottom: 10px">
      <el-button
          type="primary"
          @click="openDialog('add',{})"
      >新增登记</el-button>
      <el-button
          type="primary"
          plain
      >批量导入</el-button>
    </div>
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="dataList" :border="true">
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="企业名称" prop="companyName" align="center"  />
      <el-table-column label="计划名称" prop="planName" align="center"  />
      <el-table-column label="学员姓名" prop="studentName" 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="idNo" align="center" width="200" :show-overflow-tooltip="true"/>
      <el-table-column label="课程名称" prop="courseName" align="center"/>
      <el-table-column label="培训等级" prop="level" align="center"/>
      <el-table-column label="要求课时(分)" prop="period" align="center"/>
      <el-table-column label="实际课时(分)" prop="actualPeriod" align="center"/>
      <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>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <record-dialog ref="dialogRef" @getList=getList></record-dialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/onlineEducation/company";
import recordDialog from "./components/recordDialog.vue"
import {delUser, getUser} from "@/api/onlineEducation/user";
import Cookies from "js-cookie";
import {delStudent, getStudent} from "@/api/onlineEducation/student";
import {getRecord} from "@/api/onlineEducation/examRecord";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
  },
  total: 0,
  dataList: [],
  isAdmin: false
});
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;
  }
  await getList()
})
onUnmounted(()=>{
})
const getList = async () => {
  loading.value = true
  const res = await getRecord(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 = (type, value) => {
  dialogRef.value.openDialog(type, value);
}
/** 重置新增的表单以及其他数据  */
function reset() {
  proxy.resetForm("roleRef");
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delStudent(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
</script>
<style scoped lang="scss">
</style>