新增课程管理

main
hcj 6 months ago
parent 70c85d086a
commit 146e23af91
  1. 2
      .env.development
  2. 4
      .env.production
  3. 1
      package.json
  4. 94
      src/api/trainService/index.js
  5. 348
      src/components/FileUpload/index.vue
  6. 71
      src/components/vueQr/index.vue
  7. 11
      src/router/index.js
  8. 113
      src/utils/ruoyi.js
  9. 90
      src/views/trainService/attendanceList.vue
  10. 415
      src/views/trainService/courseManage.vue
  11. BIN
      src/views/trainService/imageTemp/1.jpg
  12. BIN
      src/views/trainService/imageTemp/2.jpg
  13. BIN
      src/views/trainService/imageTemp/peixun2.png
  14. BIN
      src/views/trainService/imageTemp/peixun3.png
  15. 96
      src/views/trainService/signInInfoList.vue
  16. 11
      src/views/trainService/signlist.vue
  17. 4
      src/views/trainService/swiperManager.vue

@ -6,6 +6,8 @@ VUE_APP_BASE_API = '/prod-api'
# VUE_APP_BASE_TARGET = 'http://www.bjkeyware.com/test-api'
VUE_APP_BASE_TARGET = 'http://192.168.0.229:9999'
# VUE_APP_BASE_TARGET = 'http://www.bjkeyware.com/prod-api'
# VUE_APP_BASE_TARGET = 'http://192.168.0.129:9999'
# VUE_APP_BASE_TARGET = 'http://172.16.36.180:9999'

@ -5,9 +5,9 @@ ENV = 'production'
VUE_APP_BASE_API = '/prod-api'
# VUE_APP_BASE_API = '/test-api'
VUE_APP_BASE_TARGET = 'https://www.keyitest.cn/prod-api'
# VUE_APP_BASE_TARGET = 'https://www.keyitest.cn/prod-api'
# VUE_APP_BASE_TARGET = 'http://192.168.0.229:9999'
# VUE_APP_BASE_TARGET = 'http://192.168.0.230:9999'
VUE_APP_BASE_TARGET = 'http://192.168.0.229:9999/prod-api'
# VUE_APP_BASE_TARGET = 'http://www.bjkeyware.com/test-api'
# VUE_APP_BASE_TARGET = 'http://www.bjkeyware.com/prod-api'
# VUE_APP_BASE_TARGET = 'http://www.keyitest.cn/prod-api'

@ -60,6 +60,7 @@
"npm": "^10.3.0",
"nprogress": "0.2.0",
"qrcodejs2": "0.0.2",
"qs": "^6.1.2",
"quill": "1.3.7",
"quill-image-drop-module": "^1.0.3",
"quill-image-extend-module": "^1.1.2",

@ -16,7 +16,6 @@ export function addSchool(data) {
url: '/train/school/add',
method: 'post',
data,
})
}
export function updateSchool(data) {
@ -24,8 +23,6 @@ export function updateSchool(data) {
url: '/train/school/update',
method: 'post',
data,
})
}
@ -33,7 +30,6 @@ export function delSchool(id) {
return request({
url: `/train/school/del/?schoolId=${id}`,
method: 'delete',
})
}
@ -235,7 +231,95 @@ export function getSwiperClass() {
export function delteSwiper(id) {
console.log(id);
return request({
url: '/train/admin/banner/'+id,
url: '/train/admin/banner/' + id,
method: 'delete',
})
}
// 获取所有班级
export function getClassList() {
return request({
url: '/train/admin/trainClasses',
method: 'get',
})
}
// 获取所有课程
export function getCourseList(params) {
return request({
url: '/course/admin/findAll',
method: 'get',
params
})
}
// 增加课程
export function addCourseItem(data) {
return request({
url: '/course/admin/add',
method: 'post',
data:JSON.stringify(data)
})
}
// 修改课程
export function putCourseItem(data) {
return request({
url: '/course/admin/update',
method: 'post',
data
})
}
// 删除课程
export function delCourse(id) {
return request({
url: '/course/admin/' + id,
method: 'delete',
})
}
// 根据课程ID获取改课程下所有签到用户信息
export function getSigninInfo(id) {
return request({
url: '/course/admin/singin/' + id,
method: 'get',
})
}
// 删除签到信息
export function delSigninItem(id) {
return request({
url: '/course/admin/singin/' + id,
method: 'delete',
})
}
// 考勤删除
export function delAttendance(id) {
return request({
url: '/course/admin/singin/class/' + id,
method: 'delete',
})
}
// 获取考勤信息表
export function getAttendanceList(id) {
return request({
url: '/course/admin/singin/class/' + id,
method: 'get',
})
}
// 导出考勤表
export function exportAttendance(id) {
return request({
url: '/course/admin/importSignIn/class/' + id,
method: 'get',
responseType: 'blob'
})
}
// 导出所有签到信息
export function exportSignIn(id) {
return request({
url: '/course/admin/importSignIn/course/' + id,
method: 'get',
responseType: 'blob'
})
}

@ -1,179 +1,217 @@
<template>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="1"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="upload"
>
<!-- 上传按钮 -->
<el-button size="mini" type="primary">选取文件</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
</el-upload>
<!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in list">
<el-link :href="file.url" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
</li>
</transition-group>
</div>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="upload"
>
<!-- 上传按钮 -->
<el-button size="mini" type="primary">选取文件</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
</el-upload>
<!-- 文件列表 -->
<transition-group
class="upload-file-list el-upload-list el-upload-list--text"
name="el-fade-in-linear"
tag="ul"
>
<li
:key="file.uid + ''"
class="el-upload-list__item ele-upload-list__item-content"
v-for="(file, index) in fileList"
>
<el-link :href="file.url" :underline="false" target="_blank">
<span class="el-icon-document"> {{ file.fileName }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
</li>
</transition-group>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import { getToken } from '@/utils/auth'
export default {
props: {
//
value: [String, Object, Array],
// (MB)
fileSize: {
type: Number,
default: 5,
},
// , ['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
},
//
isShowTip: {
type: Boolean,
default: true
}
},
data() {
return {
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", //
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: [],
};
},
computed: {
//
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
props: {
//
value: [String, Object, Array],
// (MB)
fileSize: {
type: Number,
default: 5,
},
// , ['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ['doc', 'xls', 'ppt', 'txt', 'pdf'],
},
//
isShowTip: {
type: Boolean,
default: true,
},
limit: {
type: Number,
default: 1,
},
},
//
list() {
let temp = 1;
if (this.value) {
//
const list = Array.isArray(this.value) ? this.value : [this.value];
//
return list.map((item) => {
if (typeof item === "string") {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
this.fileList = [];
return [];
}
},
},
methods: {
//
handleBeforeUpload(file) {
//
if (this.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
this.$message.error(`请上传${this.fileType.join("/")}格式文件!`);
return false;
}
}
//
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
data() {
return {
uploadFileUrl: process.env.VUE_APP_BASE_API + '/common/upload', //
headers: {
Authorization: 'Bearer ' + getToken(),
},
fileList: [],
}
}
return true;
},
//
handleExceed() {
this.$message.error(`只允许上传单个文件`);
computed: {
//
showTip() {
return this.isShowTip && (this.fileType || this.fileSize)
},
// //
// list() {
// let temp = 1;
// if (this.value) {
// console.log(this.value);
// //
// const list = Array.isArray(this.value) ? this.value : [this.value];
// //
// return list.map((item) => {
// if (typeof item === "string") {
// item = { name: item, url: item };
// }
// item.uid = item.uid || new Date().getTime() + temp++;
// return item;
// });
// } else {
// this.fileList = [];
// return [];
// }
// },
},
//
handleUploadError(err) {
this.$message.error("上传失败, 请重试");
methods: {
//
handleBeforeUpload(file) {
//
if (this.fileType) {
let fileExtension = ''
if (file.name.lastIndexOf('.') > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true
if (fileExtension && fileExtension.indexOf(type) > -1) return true
return false
})
if (!isTypeOk) {
this.$message.error(`请上传${this.fileType.join('/')}格式文件!`)
return false
}
}
//
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize
if (!isLt) {
this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`)
return false
}
}
return true
},
//
handleExceed() {
this.$message.error(`只允许上传${this.limit}个文件`)
},
fileInit(val) {
console.log(this.fileList);
if (val) {
this.fileList = val
} else {
this.fileList = this.value
}
},
//
handleUploadError(err) {
this.$message.error('上传失败, 请重试')
},
//
handleUploadSuccess(res, file) {
const temp = {
filePath: res.fileName,
fileName: file.name,
}
// console.log(temp);
this.fileList.push(temp)
this.$message.success('上传成功')
this.$emit('input', this.fileList)
},
//
handleDelete(index) {
this.fileList.splice(index, 1)
this.$emit('input', this.fileList)
},
//
getFileName(name) {
if (name.lastIndexOf('/') > -1) {
return name.slice(name.lastIndexOf('/') + 1).toLowerCase()
} else {
return ''
}
},
reset() {
console.log('reset')
this.fileList = []
},
},
//
handleUploadSuccess(res, file) {
this.$message.success("上传成功");
this.$emit("input", res.url);
created() {
this.fileList = this.value
// console.log(this.fileList)
},
//
handleDelete(index) {
this.fileList.splice(index, 1);
this.$emit("input", '');
},
//
getFileName(name) {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
} else {
return "";
}
}
},
created() {
this.fileList = this.list;
},
};
}
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
margin-right: 10px;
}
</style>

@ -1,7 +1,12 @@
<template>
<div class="qrWrapper">
<vueQr class="qr" :text="JSON.stringify(signInInfo)" :callback="test"></vueQr>
<span>有效时间5min</span>
<vueQr class="qr" :text="JSON.stringify(signInInfo)" :callback="test" @click="caleImage" ref="vueQr"> </vueQr>
<div @click="caleImage" class="qrWra"></div>
<div>点击二维码可放大</div>
<div v-if="toggle" @click="toggle = !toggle" class="bigQr">
<!-- <div class="closeImage" @click="toggle = !toggle">X</div> -->
<img width="400px" height="400px" class="imgInfo" :src="qrImage" alt="" />
</div>
</div>
</template>
@ -9,24 +14,47 @@
import vueQr from 'vue-qr'
export default {
props: {
signInInfo: Object
signInInfo: String,
},
components: {
vueQr
vueQr,
},
computed: {},
created() {
},
data() {
return {
countDown: 300,
toggle: false,
openTime: '',
qrImage: '',
}
},
computed: {
countDownShow() {
return (5 * 60 * 1000 - this.countDown / (60 * 1000)).toFixed(1)
},
},
methods: {
test(a, b) {
console.log("扫码成功");
}
}
this.qrImage = a
},
caleImage() {
this.toggle = !this.toggle
},
},
}
</script>
<style scoped>
.closeImage {
position: absolute;
right: 30px;
top: 10px;
font-size: 30px;
color: #fff;
cursor: pointer;
}
.qrWrapper {
width: 100%;
height: 200px;
@ -40,4 +68,33 @@ export default {
height: 200px;
width: 200px;
}
.bqr {
width: 600px;
height: 600px;
}
.scle {
color: aqua;
cursor: pointer;
}
.bigQr {
position: fixed;
z-index: 3;
width: 100%;
height: 100%;
top: 0;
display: flex;
background-color: rgba(17, 17, 17, 0.63);
}
.qrWra {
width: 200px;
height: 200px;
position: absolute;
top: 70px;
z-index: 2;
}
.imgInfo{
margin-left: 50%;
transform: translateX(-50%);
margin-top: 100px;
}
</style>

@ -122,7 +122,7 @@ export const constantRoutes = [
component: (resolve) => require(['@/views/system/user/tester/c2ct'], resolve),
name: 'tester',
meta: { title: '测试资质认证', icon: 'user' }
} ,
},
{
path: 'message',
component: (resolve) => require(['@/views/my/message'], resolve),
@ -157,6 +157,7 @@ export const constantRoutes = [
}
]
},
{
path: '/test',
component: Layout,
@ -246,6 +247,14 @@ export const constantRoutes = [
}
]
},
// {
// path: '/signInInfoList',
// component: (resolve) => require(['@/views/trainService/courseManageCom/signInInfoList.vue'], resolve),
// name: 'signInList',
// meta: { title: '签到详情', icon: '' }
// },
{
path: '/job',
component: Layout,

@ -105,39 +105,42 @@ export function download(fileName) {
}
// 通用下载方法
export function downloadHttp(filePath) {
window.location.href = baseURL + "/common/download/http?filePath=" + encodeURI(filePath) + "&delete=" + false;
// window.location.href = baseURL + "/common/download/http?filePath=" + encodeURI(filePath) + "&delete=" + false;
window.location.href = baseURL + encodeURI(filePath);
}
// 通用下载方法
export function downloadFast(filePath, loading) {
// window.location.href = baseURL + "/common/download/fast?filePath=" + encodeURI(filePath) + "&delete=" + false;
var url = baseURL + "/common/download/fast?filePath=" + encodeURI(filePath) + "&delete=" + false;
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(res => {
try {
const aLink = document.createElement('a')
var blob = new Blob([res.data], {type: 'application/octet-stream'})
// //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
var contentDisposition = decodeURI(res.headers['content-disposition'])
var result = patt.exec(contentDisposition)
var fileName = result[1]
fileName = fileName.replace(/\"/g, '')
aLink.href = URL.createObjectURL(blob)
aLink.setAttribute('download', fileName) // 设置下载文件名称
document.body.appendChild(aLink)
aLink.click()
document.body.removeChild(aLink);
loading.close();
} catch (e) {
alert("文件异常,请联系管理员");
loading.close();
}
})
// window.location.href = baseURL + "/common/download/fast?filePath=" + encodeURI(filePath) + "&delete=" + false;
// var url = baseURL + "/common/download/fast?filePath=" + encodeURI(filePath) + "&delete=" + false;
// var url = baseURL + encodeURI(filePath);
var url = baseURL + filePath;
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(res => {
try {
const aLink = document.createElement('a')
var blob = new Blob([res.data], { type: 'application/octet-stream' })
// //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
var contentDisposition = decodeURI(res.headers['content-disposition'])
var result = patt.exec(contentDisposition)
var fileName = result[1]
fileName = fileName.replace(/\"/g, '')
aLink.href = URL.createObjectURL(blob)
aLink.setAttribute('download', fileName) // 设置下载文件名称
document.body.appendChild(aLink)
aLink.click()
document.body.removeChild(aLink);
loading.close();
} catch (e) {
alert("文件异常,请联系管理员");
loading.close();
}
})
}
// 字符串格式化(%s )
@ -214,36 +217,36 @@ export function handleTree(data, id, parentId, children) {
}
// 任务类型
export function findLabelValueByProp(dic, value, propl,label) {
export function findLabelValueByProp(dic, value, propl, label) {
if (!value) return ""
const valueArr = (value+'').split(',')
const valueArr = (value + '').split(',')
propl = propl ? propl : 'value';
label = label ? label : 'label';
let resultArr = []
if (validatenull(dic)) return value
valueArr.forEach((it,i) => {
const index = dic.findIndex(item=>item[propl] == valueArr[i])
if (index != -1) {
resultArr.push(dic[index][label])
}
valueArr.forEach((it, i) => {
const index = dic.findIndex(item => item[propl] == valueArr[i])
if (index != -1) {
resultArr.push(dic[index][label])
}
})
return resultArr.join(',')
}
export function validatenull(val) {
if (typeof val === 'boolean') {
return false
}
if (typeof val === 'number') {
return false
}
if (val instanceof Array) {
if (val.length === 0) return true
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true
} else {
if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') return true
return false
}
return false
}
}
export function validatenull(val) {
if (typeof val === 'boolean') {
return false
}
if (typeof val === 'number') {
return false
}
if (val instanceof Array) {
if (val.length === 0) return true
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true
} else {
if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') return true
return false
}
return false
}

@ -0,0 +1,90 @@
<template>
<div class="singInWrapper">
<div class="expBtn">
<el-button icon="el-icon-download" type="warning" size="small" style="margin-bottom:20px" @click="excelExport"
>导出为excel</el-button
>
</div>
<el-table v-loading="loading" :data="signinList">
<el-table-column label="序号" type="index" />
<el-table-column label="学生姓名" prop="studentName" align="center" />
<el-table-column label="学号" prop="studentCode" align="center" />
<el-table-column label="手机号" prop="mobile" align="center"> </el-table-column>
<!-- <el-table-column label="签到总次数" prop="createTime" align="center"></el-table-column> -->
<el-table-column label="实际签到次数" prop="signCount" align="center"></el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="signIninit"
/>
</div>
</template>
<script>
import { getAttendanceList, delAttendance, exportAttendance } from '@/api/trainService/index'
import { saveAs } from 'file-saver'
export default {
created() {
const query = this.$route.query
this.name = query.name
this.id = query.id
this.signIninit()
},
props: {},
data() {
return {
name: '',
id: '',
loading: false,
total: 0,
signinList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
},
}
},
methods: {
async signIninit() {
const res = await getAttendanceList(this.id)
this.total = res.total
this.signinList = res.rows
},
async excelExport() {
const res = await exportAttendance(this.id)
const blob = new Blob([res])
saveAs(blob, this.name + '考勤表.xlsx')
},
toDelSignInItem(params) {
this.$confirm('是否确认删除', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(function () {
return delAttendance(params.signId)
})
.then(() => {
this.signIninit()
this.$message.success('删除成功')
})
},
},
}
</script>
<style lang="scss" scoped>
.singInWrapper {
padding: 20px;
.expBtn {
display: flex;
margin: 5px;
justify-content: space-between;
}
}
</style>

@ -1,38 +1,52 @@
<template>
<div class="container" style="padding: 30px">
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px"
style="display: flex;margin-left: 5px">
<div style="margin-top: 5px;margin-bottom:20px;margin-right: 5px">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="option({}, 'add')">新增课程</el-button>
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="68px"
style="display: flex; margin-left: 5px"
>
<div style="margin-top: 5px; margin-bottom: 20px; margin-right: 5px">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="option({}, 'add')"
>新增课程</el-button
>
</div>
<!-- <el-form-item label="学校名称" prop="schoolName">
<el-select v-model="queryParams.schoolName">
<el-option v-for="it in schoolList" :key="it.schoolId" :label="it.schoolName"
:value="it.schoolName"></el-option>
</el-select>
<el-form-item label="课程" prop="courseId">
<el-input v-model="queryParams.courseName"></el-input>
</el-form-item>
<el-form-item label="班级名称" prop="courseId">
<el-input v-model="queryParams.className"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="getList">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
<el-form-item label="班级名称" prop="trainClassName">
<el-input v-model="queryParams.trainClassName" placeholder="请输入班级名称" clearable size="small" />
</el-form-item> -->
</el-form>
<el-table v-loading="loading" :data="courseList">
<el-table-column label="序号" align="center" type="index" />
<el-table-column label="课堂名称" align="center" prop="courseName" />
<el-table-column label="所属班级" align="center" prop="className">
<template slot-scope="scope">
{{ scope.row.classType }}
</template>
</el-table-column>
<el-table-column label="课程链接" align="center">
<el-table-column label="所属班级" align="center" prop="className" />
<el-table-column label="课程名称" align="center" prop="courseName" />
<el-table-column label="任课老师" align="center" prop="instructor" />
<el-table-column label="上课时间" align="center" prop="courseTime" />
<el-table-column label="课程资料" align="center">
<template slot-scope="scope">
{{ scope.row.courseLink }}
<div
v-for="name in scope.row.attachments"
:key="name.fileId"
@click="downLoadSource(name)"
class="sourceItem"
>
{{ name.fileName }}
</div>
</template>
</el-table-column>
<el-table-column label="课程资料" align="center" prop="courseSource" />
<el-table-column label="签到情况" align="center">
<template slot-scope="scope">
{{ scope.row.signIn }} / 70
<el-button size="mini" type="text" @click="option(scope.row, 'siginList')">{{
scope.row.signIn
}}</el-button>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
@ -43,93 +57,144 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog class="diaform" :title="title" :visible.sync="visibleopen" :close-on-click-modal="false"
width="700px" append-to-body @close="colsedia">
<div v-if="title == '签到码'">
<vueQr :signInInfo="signInInfo"></vueQr>
<el-dialog
class="diaform"
:title="title"
:visible.sync="visibleopen"
:close-on-click-modal="false"
append-to-body
width="700px"
@open="openDia"
@close="closeDia"
>
<div v-if="optionType == 'signIn'">
<vueQr :signInInfo="JSON.stringify(signInInfo)"></vueQr>
</div>
<el-form v-else :model="swiperInfo" :rules="rules" ref="form" label-width="150px" text-align:center>
<el-form-item label="所属班级" prop="filePath">
<el-select v-model="swiperInfo.entityId" placeholder="请选择班级" clearable>
<el-option v-for="item in classList" :key="item.train_class_id" :label="item.train_class_name"
:value="item.train_class_id"></el-option>
<el-form v-else :model="courseForm" :rules="rules" ref="form" label-width="150px" text-align:center>
<el-form-item label="所属班级" prop="classId">
<el-select v-model="courseForm.classId" placeholder="请选择班级" clearable>
<el-option
v-for="item in classList"
:key="item.trainClassId"
:label="item.trainClassName"
:value="item.trainClassId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="课程名称" prop="fileName">
<el-input v-model="swiperInfo.fileName"></el-input>
<el-form-item label="课程名称" prop="courseName">
<el-input class="courseName" v-model="courseForm.courseName"></el-input>
</el-form-item>
<el-form-item label="课堂老师" prop="fileType">
<el-radio-group v-model="swiperInfo.fileType">
<el-radio label="pc"></el-radio>
<el-radio label="app"></el-radio>
</el-radio-group>
<el-form-item label="课堂老师" prop="instructor">
<el-select v-model="courseForm.instructor" placeholder="请选择老师" clearable>
<el-option
v-for="item in teacherList"
:key="item.talent_id"
:label="item.name"
:value="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="上课时间" prop="courseTime">
<el-date-picker
v-model="courseForm.courseTime"
type="datetime"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间"
>
</el-date-picker>
</el-form-item>
<el-form-item label="课程资料">
<el-form-item label="课程资料" prop="attachments">
<div>
<ImageUpload ref="imgupload" v-model="swiperInfo.filePath" listtype='picture-card'
class="widthFix" fileName="publicize" :limit="1" />
<fileUpload
ref="fileUpload"
v-model="courseForm.attachments"
listtype="picture-card"
:fileType="['rar']"
class="widthFix"
fileName="publicize"
:limit="3"
/>
</div>
</el-form-item>
<el-form-item label="课时">
<el-select v-model="swiperInfo.entityId" placeholder="请选择班级" clearable>
<el-option v-for="item in classList" :key="item.train_class_id" :label="item.train_class_name"
:value="item.train_class_id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="课时链接">
<el-select v-model="swiperInfo.entityId" placeholder="请选择班级" clearable>
<el-option v-for="item in classList" :key="item.train_class_id" :label="item.train_class_name"
:value="item.train_class_id"></el-option>
</el-select>
</el-form-item>
<el-form-item style="margin-top: 80px; text-align: center; margin-left: -150px">
<el-button type="primary" style="width: 135px; font-size: 16px; height: 39px; line-height: 11px"
@click="submitto('confirm')">确认</el-button>
<el-button style="width: 135px; font-size: 16px; height: 39px; line-height: 11px; margin-left: 30px"
@click="colsedia">取消</el-button>
<el-button
type="primary"
style="width: 135px; font-size: 16px; height: 39px; line-height: 11px"
@click="submitto('confirm')"
>确认</el-button
>
<el-button
style="width: 135px; font-size: 16px; height: 39px; line-height: 11px; margin-left: 30px"
@click="closeDia"
>取消</el-button
>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import { getSwiperInfo, addSwiperInfo, getSwiperClass, delteSwiper } from '@/api/trainService/index'
import ImageUpload from '@/components/ImgUpload/index.vue'
import {
getCourseList,
addCourseItem,
putCourseItem,
delCourse,
listTeacherAll,
getClassList,
} from '@/api/trainService/index'
import fileUpload from '@/components/FileUpload/index.vue'
import { encodeChinese } from '@/utils/encodeChinese'
import vueQr from '@/components/vueQr/index.vue'
import { deepClone } from '@/utils'
const titleMap = {
"motify": "修改",
"delete": "删除",
"signIn": "签到码",
"add": "新增"
motify: '修改',
delete: '删除',
signIn: '签到码',
add: '新增',
siginList: '签到情况',
attendance: '考勤',
}
const courseRule = {
courseName: [{ required: true, message: '请输入课程名称', trigger: 'blur' }],
classId: [{ required: true, message: '请选择班级', trigger: 'change' }],
instructor: [{ required: true, message: '请选择任课老师', trigger: 'change' }],
courseTime: [{ required: true, trigger: 'change', message: '请输入上课时间' }],
}
export default {
name: 'peixunbaoming',
components: { ImageUpload, vueQr },
components: { fileUpload, vueQr },
data() {
return {
imgAction: process.env.VUE_APP_BASE_API + '/upload',
loading: false,
id: '',
currentCourse: {},
optionType: '',
//
signInInfo: {},
courseList: [
{
'courseName': 1,
'className': 1,
'classType': 1,
'courseLink': 1,
'courseSource': 1,
'signIn': 1
}
courseId: '',
className: '',
courseName: '',
courseTime: '',
instructor: '',
signIn: '',
attachments: [],
},
],
//
total: 0,
@ -137,112 +202,156 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
courseName: '',
className: '',
},
swiperInfo: {
fileName: 1,
entityId: '',
fileType: '',
filePath: []
courseForm: {
classId: '',
courseName: '',
instructor: '',
attachments: [],
courseTime: '',
},
//
classList: [],
title: '新增图片',
//
teacherList: [],
title: '',
visibleopen: false,
rules: {
filePath: [
{ required: true, message: '请上传图片', trigger: 'blur' },
],
fileName: [
{ required: true, trigger: 'blur', message: "请输入排序" },
{ pattern: /^\d+$/, message: "请输入正整数", trigger: "blur" }
],
fileType: [
{ required: true, message: '请选择类型', trigger: 'change' },
],
},
currentImage: '',
rules: courseRule,
}
},
methods: {
async getList() {
const res = await getSwiperInfo()
this.imageList = res.data
const classRes = await getSwiperClass()
console.log(classRes);
this.classList = classRes.data
const res = await getCourseList(this.queryParams)
this.total = res.total
this.courseList = res.rows
},
addCourse() {
this.title = '新增图片'
this.visibleopen = true
},
previewImage(params) {
this.title = '图片预览'
console.log(params);
this.currentImage = process.env.VUE_APP_BASE_API + params.filePath
this.visibleopen = true
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
courseName: '',
className: '',
}
this.getList()
},
//
async modify(row) {
this.title = '修改图片'
this.visibleopen = true
this.form = row
downLoadSource(params) {
const url = params.filePath
console.log(url)
if (!url) {
return
}
downloadHttp(url)
},
async option(row, type) {
this.optionType = type
this.currentCourse = row
this.title = titleMap[type]
if (type === 'motify') {
} else if (type === 'delete') {
} else if (type === 'add') {
} else if (type === 'signIn') {
this.generateSignIn(row)
switch (type) {
case 'motify':
this.courseForm = deepClone(row)
this.initDialog()
break
case 'delete':
this.deleteRow(row)
return
case 'add':
this.initDialog()
break
case 'signIn':
this.generateSignIn(row)
break
case 'siginList':
// this.id = row.courseId
this.$router.push({
path: '/signInInfoList',
query: {
name: row.className,
id: row.courseId,
},
})
return
default:
break
}
this.visibleopen = true
},
//
submitto() {
console.log(this.courseForm)
this.$refs['form'].validate((valid) => {
if (valid) {
this.submit()
} else {
console.log('error submit!!');
return false;
console.log('error submit!!')
return false
}
})
},
async submit() {
const data = deepClone(this.swiperInfo)
data.filePath = data.filePath[data.filePath.length - 1].toString()
data.entityId = data.entityId * 1
const res = await addSwiperInfo(data)
if (res.code == 200) {
this.$message.success('新增成功')
this.colsedia()
this.getList()
const data = deepClone(this.courseForm)
if (this.courseForm.courseId) {
const res = await putCourseItem(data)
res.code == 200 && this.$message.success('修改成功')
} else {
const res = await addCourseItem(data)
res.code == 200 && this.$message.success('新增成功')
}
this.closeDia()
this.getList()
},
//
colsedia() {
this.$refs['form'].resetFields()
this.swiperInfo = {
fileName: '',
entityId: '',
fileType: '',
filePath: []
// dialog init
async initDialog() {
const classRes = await getClassList()
const teacherRes = await listTeacherAll()
this.teacherList = teacherRes
this.classList = classRes.data
},
openDia() {
if (this.optionType === 'motify') {
console.log('motify');
this.$refs.fileUpload?.fileInit(this.courseForm.attachments)
}
this.$refs.imgupload.clearImages()
this.currentImage = ""
console.log('dialog open ~~~')
},
//
closeDia() {
this.$refs['form']?.resetFields()
Object.keys(this.courseForm).forEach((item, index) => {
if (Array.isArray(this.courseForm[item])) {
} else {
this.courseForm[item] = ''
}
})
this.$refs.fileUpload?.reset()
this.visibleopen = false
},
//
generateSignIn(row) {
const time = new Date().getTime().toString()
this.signInInfo = { 'time': time, ...row }
this.signInInfo['courseId'] = row.courseId
this.signInInfo['className'] = encodeChinese(row.className)
this.signInInfo['courseName'] = encodeChinese(row.courseName)
this.title = '签到码'
this.visibleopen = true
},
/**
* 删除签到信息
*/
deleteRow(row) {
this.$confirm('是否确认删除课程', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(function () {
return delCourse(row.courseId)
})
.then(() => {
this.getList()
this.$message.success('删除成功')
})
},
},
created() {
@ -251,8 +360,18 @@ export default {
}
</script>
<style lang="scss" scoped>
.el-upload-list--picture-card .el-upload-list__item {
width: 100% !important;
<style lang="scss">
.courseName {
width: 220px;
}
.sourceItem {
color: #1890ff;
cursor: pointer;
text-decoration: underline;
}
.sourceItem:hover {
color: #0e4272;
cursor: pointer;
text-decoration: underline;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 KiB

@ -0,0 +1,96 @@
<template>
<div class="singInWrapper">
<div class="expBtn">
<el-button icon="el-icon-download" type="warning" size="small" style="margin-bottom:20px" @click="excelExport"
>导出为excel</el-button
>
</div>
<el-table v-loading="loading" :data="signinList">
<el-table-column label="序号" type="index" />
<el-table-column label="学生姓名" prop="studenName" align="center" />
<el-table-column label="学号" prop="studenCode" align="center" />
<el-table-column label="手机号" prop="mobile" align="center"> </el-table-column>
<el-table-column label="时间" prop="createTime" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="delSignInItem(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="signIninit"
/>
</div>
</template>
<script>
import { getSigninInfo, delSigninItem, exportSignIn } from '@/api/trainService/index'
import { saveAs } from 'file-saver'
export default {
created() {
const query = this.$route.query
this.name = query.name
this.id = query.id
this.signIninit()
},
props: {},
data() {
return {
name: '',
id: '',
loading: false,
total: 0,
signinList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
},
}
},
methods: {
async signIninit() {
console.log(this.id)
const res = await getSigninInfo(this.id)
this.total = res.total
this.signinList = res.rows
},
async excelExport() {
const res = await exportSignIn(this.id)
const blob = new Blob([res])
saveAs(blob, this.name + '签到记录.xlsx')
//this.$route.query.name
// this.download(res)
},
delSignInItem(params) {
this.$confirm('是否确认删除', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(function () {
return delSigninItem(params.signId)
})
.then(() => {
this.signIninit()
this.$message.success('删除成功')
})
},
},
}
</script>
<style lang="scss" scoped>
.singInWrapper {
padding: 20px;
.expBtn {
display: flex;
justify-content: space-between;
margin: 5px;
}
}
</style>

@ -110,6 +110,7 @@
>
<el-button size="mini" type="text" @click="modify(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click="delrow(scope.row)">删除</el-button>
<el-button size="mini" type="text" @click="option(scope.row)">考勤</el-button>
</template>
</el-table-column>
</el-table>
@ -458,6 +459,16 @@ export default {
})
}
},
option(row) {
console.log(row);
this.$router.push({
path: '/attendanceList',
query: {
name: row.trainClassName,
id: row.trainClassId,
},
})
},
async delrow(row) {
this.$confirm('是否确认删除培训班?', '提示', {
confirmButtonText: '确定',

@ -63,8 +63,8 @@
<el-form-item label="班级">
<el-select v-model="swiperInfo.entityId" placeholder="请选择班级" clearable>
<el-option v-for="item in classList" :key="item.train_class_id" :label="item.train_class_name"
:value="item.train_class_id"></el-option>
<el-option v-for="item in classList" :key="item.trainClassId" :label="item.trainClassName"
:value="item.trainClassId"></el-option>
</el-select>
</el-form-item>

Loading…
Cancel
Save