<template> <div> <el-card shadow="hover" class="mgb20"> <div> <div class="flex-center mgb20"> <p class="hr_tag"></p> <span>学生情况分析</span> </div> <div> <div class="stat"> <div class="item"> <div class="chart" id="chart1"></div> </div> <div class="item"> <div class="chart" id="chart2"></div> </div> <div class="item"> <div class="chart" id="chart3"></div> </div> </div> </div> </div> </el-card> <el-card shadow="hover"> <div class="flex-between mgb20"> <div> <el-button type="primary" size="small" v-auth round @click="addStudent">新增学生</el-button> <el-button type="primary" size="small" v-auth round @click="batchImport">批量导入</el-button> <el-button type="primary" size="small" v-auth round @click="delAllSelection">批量删除</el-button> </div> <div> <el-input placeholder="请输入学生姓名/学号/学校名称" prefix-icon="el-icon-search" style="width: 260px" v-model="keyword" clearable></el-input> </div> </div> <el-table :data="studentData" class="table" ref="table" stripe header-align="center" @selection-change="handleSelectionChange" :row-key="getRowKeys"> <el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column> <el-table-column type="index" width="100" label="序号" align="center"> </el-table-column> <el-table-column prop="account" label="账号" align="center"> </el-table-column> <el-table-column prop="schoolName" label="院校" align="center"></el-table-column> <el-table-column prop="workNumber" label="学号" align="center"> </el-table-column> <el-table-column prop="userName" label="学生姓名" align="center"> </el-table-column> <el-table-column prop="className" label="班级" align="center"></el-table-column> <el-table-column prop="countries" label="账号角色" align="center"> <template slot-scope="scope"> {{roleStatus(scope.row.roleId)}} </template> </el-table-column> <el-table-column prop="loginNumber" label="登录次数" align="center"> </el-table-column> <el-table-column prop="lastLoginTime" label="上次登录时间" align="center"> </el-table-column> <el-table-column label="操作" align="center" width="300"> <template slot-scope="scope"> <el-button type="text" v-auth @click="editStudent(scope.row,true)">查看</el-button> <el-button type="text" v-auth @click="editStudent(scope.row,false)">编辑</el-button> <el-button type="text" v-auth @click="resetPassword(scope.row)">重置密码</el-button> <el-button type="text" v-auth @click="handleDelete(scope.row)">删除</el-button> <el-switch v-model="scope.row.disableAccount" :active-value="0" :inactive-value="1" style="margin: 0 5px" @change="switchOff($event,scope.row,scope.$index)" v-auth="'学生管理:禁用'" ></el-switch> </template> </el-table-column> </el-table> <div class="pagination"> <el-pagination background @current-change="currentChange" :current-page="pageNo" layout="total, prev, pager, next" :total="totals"> </el-pagination> </div> </el-card> <el-dialog :title="isDetail ? '查看学生' : (isAdd ? '新增学生' : '编辑学生')" :visible.sync="studentVisible" width="30%" center @close="closeStudent" class="dialog" :close-on-click-modal="false"> <el-form ref="form" :model="form" :rules="rules" :disabled="isDetail" label-width="100px"> <el-form-item prop="account" label="账号"> <el-input v-model="form.account" placeholder="请输入学生账号" @change="accountChange"></el-input> </el-form-item> <el-form-item prop="userName" label="学生姓名"> <el-input v-model="form.userName" placeholder="请输入学生姓名"></el-input> </el-form-item> <el-form-item prop="roleId" label="账号角色"> 学生 </el-form-item> <el-form-item prop="workNumber" label="学生学号"> <el-input v-model="form.workNumber" placeholder="" @change="worknumberChange"></el-input> </el-form-item> <el-form-item prop="phone" label="手机号"> <el-input v-model="form.phone" placeholder="可用于登录平台,以及找回密码" maxlength="11"></el-input> </el-form-item> <el-form-item prop="email" label="邮箱"> <el-input v-model="form.email" placeholder="可用于登录平台,以及找回密码"></el-input> </el-form-item> <el-form-item prop="schoolAppellationId" label="所在院校"> <el-select v-model="form.schoolAppellationId" placeholder="默认为当前院校(可修改)" filterable @change="worknumberChange"> <el-option v-for="(item,index) in schoolList" :key="index" :label="item.schoolName" :value="item.schoolId"></el-option> </el-select> </el-form-item> <el-form-item prop="uniqueIdentificationAccount" label="唯一标识"> <el-input disabled v-model="form.uniqueIdentificationAccount"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer" v-if="!isDetail"> <el-button @click="studentVisible = false">取消</el-button> <el-button type="primary" @click="saveData">确 定</el-button> </span> </el-dialog> <!-- 批量导入 --> <el-dialog title="批量导入" :visible.sync="importVisible" width="24%" center :close-on-click-modal="false"> <div style="text-align: center"> <div style="margin-bottom: 10px;"><el-button type="primary" @click="downLoad">模板下载<i class="el-icon-download el-icon--right"></i></el-button></div> <el-upload accept=".xls,.xlsx" :on-remove="handleRemove" :on-error="uploadError" :on-success="uploadSuccess" :before-remove="beforeRemove" :limit="1" :on-exceed="handleExceed" :action="this.api.uploadFile" :file-list="uploadList" name="file" > <el-button type="primary" class="ml20">上传文件<i class="el-icon-upload2 el-icon--right"></i></el-button> </el-upload> <el-link v-if="uploadFaild" type="primary" @click="showFaild">部分数据导入失败,查看失败原因</el-link> </div> <span slot="footer" class="dialog-footer"> <el-button @click="importVisible = false">取 消</el-button> <el-button type="primary" @click="uploadSure">确 定</el-button> </span> </el-dialog> <el-dialog title="重置密码" :visible.sync="passwordVisible" :close-on-click-modal="false" @close="closePassword" width="30%"> <el-form ref="passwordForm" :model="form" label-width="60px"> <el-form-item label="原密码"> <el-input type="password" v-model="passwordForm.password" placeholder="请输入原密码"></el-input> </el-form-item> <el-form-item label="新密码"> <el-input type="password" v-model="passwordForm.newPassword" placeholder="请输入新密码" @keyup.enter.native="editPassword"></el-input> </el-form-item> <el-form-item label="新密码"> <el-input type="password" v-model="passwordForm.reNewPassword" placeholder="请确认新密码" @keyup.enter.native="editPassword"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="passwordVisible = false">取 消</el-button> <el-button type="primary" @click="editPassword">确 定</el-button> </span> </el-dialog> </div> </template> <script> import echarts from 'echarts' export default { name: 'dashboard', data() { return { isDetail: false, keyword: '', schoolId: 2105, form: { userName: '', account: '', phone: '', uniqueIdentificationAccount: '', workNumber: '', email: '', account: '', roleId: 4, schoolId: 2105, schoolAppellationId: '' }, rules: { account: [ { required: true, message: '请输入账号', trigger: 'blur' }, { pattern: /^[a-zA-Z0-9_-]{1,16}$/, message: '请输入正确的账号', trigger: 'blur' } ], userName: [ { required: true, message: '请输入学生姓名', trigger: 'blur' } ], schoolAppellationId: [ { required: true, message: '请选择所在院校', trigger: 'change' } ], // uniqueIdentificationAccount: [ // { required: true, message: '请输入唯一标识', trigger: 'blur' }, // ], workNumber: [ { required: true, message: '请输入学生学号', trigger: 'blur' } ], phone: [ // { required: true, message: '请输入职工手机号', trigger: 'blur' }, { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' } ], email: [ // { required: true, message: '请输入邮箱', trigger: 'blur' }, { pattern: /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/, message: '请输入正确的邮箱', trigger: 'blur' } ] }, studentData:[], pageNo: 1, pageSize: 10, totals: 0, multipleSelection: [], uploadList: [], importVisible: false, isAdd: true, studentVisible: false, accountReapeat: false, originalAccount: '', phoneRepeat: false, workNumberRepeat: false, originalWorkNumber: '', isDetail: false, resetVisible: false, passwordVisible: false, passwordForm: { password: '', newPassword: '', reNewPassword: '' }, schoolList: [], uploadFaild: false, token: '', accountMsg: '' }; }, mounted() { this.form.schoolAppellationId = 2105 this.getData() this.getSchoolData() this.getStat() }, watch: { keyword: function(val) { clearTimeout(this.searchTimer) this.searchTimer = setTimeout(() => { this.getData() },500) } }, methods: { getData() { let data = { schoolId: '', seachContent: this.encodeString(this.keyword), page: this.pageNo, size: this.pageSize } this.$get(this.api.queryStudent,data).then(res => { this.studentData = res.data.list this.totals = res.data.totalCount }).catch(res => {}); }, getStat() { this.$get(this.api.queryStudentData).then(res => { let stat1 = [] res.data['学生分布情况'].map(n => { stat1.push({ name: n.schoolName, value: n.schoolNumber }) }) let myChart = echarts.init(document.getElementById('chart1')) myChart.setOption({ title: { text: '实验学生的学校分布情况' }, tooltip: { trigger: 'item' }, legend: { top: 'bottom' }, series: [ { name: '学校分布情况', type: 'pie', center: ['50%', '47%'], top: 30, bottom: 50, // radius: ['40%', '70%'], avoidLabelOverlap: false, // roseType: 'area', itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 }, // emphasis: { // label: { // show: true, // fontSize: '20', // fontWeight: 'bold' // } // }, labelLine: { show: false }, data: stat1 } ] }) let schoolList = [] let frequencyList = [] res.data['学生平均登录情况'].map(n => { schoolList.push(n.schoolName) frequencyList.push(n.studentLoginNumber) }) let myChart1 = echarts.init(document.getElementById('chart2')) myChart1.setOption({ title: { text: '实验学生平均登录次数' }, tooltip: { trigger: 'item' }, xAxis: { type: 'category', data: schoolList }, yAxis: { type: 'value' }, series: [{ data: frequencyList, type: 'bar', smooth: true, color: ['#8191fd'] }] }) let schoolList1 = [] let timeList = [] res.data['学生实验平均时长'].map(n => { schoolList1.push(n.schoolName) timeList.push(n.studentAvgTime) }) let myChart2 = echarts.init(document.getElementById('chart3')) myChart2.setOption({ title: { text: '实验学生的平均实验时间情况', }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: '3%', right: '10%', bottom: '3%', containLabel: true }, xAxis: { name: '小时', type: 'value', boundaryGap: [0, 0.01] }, yAxis: { type: 'category', data: schoolList1 }, series: [ { name: '平均实验时间:', type: 'bar', data: timeList, color: ['#9278ff'] }, ] }) }).catch(res => {}); }, saveData() { this.$refs.form.validate((valid) => { if (valid) { if(this.accountReapeat) return this.$message.warning(this.accountMsg) if(this.workNumberRepeat) return this.$message.warning('该学生学号已存在') if(this.phoneRepeat) return this.$message.warning('该手机号已存在') if(this.form.studentId) { this.$put(this.api.updateStudent,this.form).then(res => { if(res.errmessage == 'success') { this.$message.success('提交成功!'); this.studentVisible = false this.getData() this.getStat() }else{ this.$message.error(res.message); } }).catch(res => {}); }else{ this.form.uniqueIdentificationAccount = new Date().getTime() this.$post(this.api.addStudent,this.form).then(res => { if(res.errmessage == 'success') { this.$message.success('提交成功!'); this.studentVisible = false this.getData() this.getStat() }else{ this.$message.error(res.message); } }).catch(res => {}); } }else{ return false; } }) }, async accountChange(){ if(this.form.account !== this.originalAccount){ let res = await this.$get(this.api.queryAccountIsExist, { account: this.encodeString(this.form.account), schoolId: this.schoolId }); if(res.message.user.length){ let roleId = res.message.user[0].roleId this.accountMsg = roleId == 4 ? '该账号已存在' : (roleId.includes(',') ? '该账号已绑定老师和管理员' : `该账号已绑定${this.roleStatus(roleId)}`) this.$message.warning(this.accountMsg) this.accountReapeat = true }else{ this.accountReapeat = false } }else{ this.accountReapeat = false } }, async worknumberChange(){ // if(this.form.workNumber !== this.originalWorkNumber){ let res = await this.$get(this.api.queryWorkNumberIsExist, { workNumber: this.encodeString(this.form.workNumber), schoolAppellationId: this.form.schoolAppellationId }); if(res.status != 200){ this.$message.warning('该学生学号已存在'); this.workNumberRepeat = true }else{ this.workNumberRepeat = false } // }else{ // this.workNumberRepeat = false // } }, async phoneChange(){ let res = await this.$get(this.api.queryPhone, { phone: this.form.phone }); if(res.message.length != 0){ this.$message.warning('该手机号已存在'); this.phoneRepeat = true }else{ this.phoneRepeat = false } }, batchImport(){ this.importVisible = true this.uploadList = [] this.uploadFaild = false }, // 获取学校/客户名称 getSchool(){ this.clearcity() this.getSchoolData() this.pageNo = 1 this.getData() }, getSchoolData(){ let data = { schoolName: '', provinceId: '', cityId: '' } this.$get(this.api.querySchool,data).then(res => { this.schoolList = res.message }).catch(res => {}); }, closeStudent(){ this.isDetail = false this.$refs.form.clearValidate() this.form = { schoolId: 2105, userName: '', account: '', phone: '', uniqueIdentificationAccount: '', workNumber: '', email: '', account: '', roleId: 4, schoolAppellationId: 2105 } }, currentChange(val) { this.pageNo = val; this.getData(); }, addStudent(){ this.isAdd = true this.studentVisible = true }, editStudent(row,isDetail){ this.isAdd = false this.isDetail = isDetail this.studentVisible = true this.form = Object.assign({},row) this.originalWorkNumber = row.workNumber this.originalAccount = row.account this.$nextTick(() => { this.$refs.form.clearValidate() }) }, downLoad(){ location.href = this.api.download }, handleDelete(row) { this.$confirm('确定要删除吗?', '提示', { type: 'warning' }) .then(() => { this.$post(this.api.daleteStudent,[row.studentId]).then(res => { this.$message.success('删除成功'); this.getData() this.getStat() }).catch(res => {}); }) .catch(() => {}); }, getRowKeys(row) { return row.userId; }, handleSelectionChange(val) { this.multipleSelection = val; }, delAllSelection() { if(this.multipleSelection.length != ''){ let newArr = this.multipleSelection let delList = newArr.map(item => { return item.studentId }) this.$confirm('确定要删除选中用户吗?', '提示', { type: 'warning' }) .then(() => { let data = delList this.$post(this.api.daleteStudent,data).then(res => { this.$refs.table.clearSelection() this.$message.success('删除成功'); this.getData() this.getStat() }).catch(res => {}); }).catch(() => {}); }else{ this.$message.error('请先选择数据 !'); } }, resetPassword(row){ this.$confirm(`重置后的密码为:${this.$config.initialPassword},确定重置?`, '提示', { }).then(() => { this.$put(this.api.reSetPassword,[row.userId]).then(res => { if(res.errmessage == 'success') this.$message.success('重置成功') }).catch(res => {}); }).catch(() => { }); }, switchOff(val,row,index) { let data = { studentId: row.studentId, disableAccount: row.disableAccount ? 0 : 1 } this.$put(this.api.disableAccount,data) .then(res => {}) .catch(err => {}); }, closePassword() { this.passwordForm = { password: '', newPassword: '', reNewPassword: '' } }, editPassword(){ if(!this.passwordForm.password) return this.$message.warning('请输入原密码') if(!this.passwordForm.newPassword) return this.$message.warning('请输入新密码') if(!this.passwordForm.reNewPassword) return this.$message.warning('请确认新密码') if(this.passwordForm.newPassword.length < 6 || this.passwordForm.reNewPassword.length < 6) return this.$message.warning('请输入6位数以上的密码') if(this.passwordForm.newPassword !== this.passwordForm.reNewPassword) return this.$message.warning('输入的新密码不一致,请重新确认') if(this.passwordForm.password === this.passwordForm.newPassword) return this.$message.warning('原密码跟新密码不能一致') let data = this.passwordForm data.userid = this.userId this.$put(this.api.reSetPassword,data) .then(res => { if(res.errmessage == 'success'){ this.$message.success('更换成功') this.passwordVisible = false } }) .catch(err => { console.log(err); }); }, // 上传文件 handleExceed(files, fileList) { this.$message.warning( `当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!` ); }, showFaild(){ location.href = `${this.api.export_failure}?token=${this.token}` }, uploadSuccess(res, file, fileList) { this.uploadFaild = false if(res.errmessage == 'success'){ if(res.data.token){ this.token = res.data.token this.uploadFaild = true }else{ this.$message.success('上传成功') } }else{ res.message ? this.$message.error(res.message) : this.$message.error('上传失败,请检查数据') } }, uploadError(err, file, fileList) { this.$message({ message: "上传出错,请重试!", type: "error", center: true }); }, beforeRemove(file, fileList) { return this.$confirm(`确定移除 ${file.name}?`); }, handleRemove(file, fileList) { this.uploadList = fileList this.uploadFaild = false }, uploadSure(){ this.importVisible = false this.pageNo = 1 this.keyword = '' this.getData() }, } }; </script> <style lang="scss" scoped> .mag{ margin-right: 20px; } .no-mb /deep/.el-form-item{ margin-bottom: 0; } /deep/.el-row{ padding: 0 !important; margin-bottom: 0; } .stat{ display: flex; .item{ flex: 1; padding: 15px; margin-right: 20px; box-shadow: 1px 1px 3px 1px rgba(146,120,255,.3); box-sizing: border-box; &:last-child{ margin-right: 0; } .chart{ height: 300px; } } } </style>