该例子可以根据填写的班级总人数、男女比例进行随机分班,支持Excel的学生信息表格导入和分班结果Excel的导出和展示。
其中导入的Excel只需要这样填写即可:
效果如下图所示:
因为我是用Vue脚手架写的,我就直接放核心代码。你们放在脚手架里直接就可以运行出结果啦~需要引用一下elementui、xlsx、file-saver即可。
template部分:
<template>
<div class="mainBox">
<div class="btnDIv">
<el-button type="primary" @click="divisionFun">分班</el-button>
<el-button type="primary" @click="exportFun">导出</el-button>
</div>
<div class="tableDiv">
<div class="elTable">
<el-table
:data="tableData"
border
stripe
:height="heightTable"
id="exportTab">
<el-table-column
type="index"
width="50"
label="序号"
align="center">
</el-table-column>
<el-table-column
prop="姓名"
label="姓名"
align="center">
</el-table-column>
<el-table-column
prop="报名号"
label="报名号"
align="center">
</el-table-column>
<el-table-column
prop="性别"
label="性别"
align="center">
</el-table-column>
<el-table-column
prop="住址"
label="住址"
align="center">
</el-table-column>
<el-table-column
prop="年龄"
label="年龄"
align="center">
</el-table-column>
<el-table-column
prop="是否适龄"
label="是否适龄"
align="center">
</el-table-column>
<el-table-column
prop="是否满足入学条件"
label="是否满足入学条件"
align="center">
</el-table-column>
<el-table-column
prop="班级"
label="班级"
align="center">
<template slot-scope="scope">
<span style="color: red;font-weight: bold;">{{scope.row['班级']}}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!--弹窗-->
<el-dialog
title="提示"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px">
<el-form-item label="班级总人数:" prop="totalNum">
<el-input-number
v-model="ruleForm.totalNum"
:min="1"
:max="1000"
style="width:calc(100% - 40px);
margin-right: 10px;">
</el-input-number>
<span>人</span>
</el-form-item>
<el-form-item label="男生比例:" prop="male">
<el-input-number
v-model="ruleForm.male"
:min="0"
:max="100"
style="width:calc(100% - 40px);
margin-right: 10px;"
@input="maleFun">
</el-input-number>
<span>%</span>
</el-form-item>
<el-form-item label="女生比例:" prop="famale">
<el-input-number
v-model="ruleForm.famale"
:min="0"
:max="100"
style="width:calc(100% - 40px); margin-right: 10px;"
@input="famaleFun">
</el-input-number>
<span>%</span>
</el-form-item>
<el-form-item label="上传Excel:" prop="fileList">
<el-upload
action="###"
multiple
accept=".xls, .xlsx"
:file-list="ruleForm.fileList"
:show-file-list="true"
:before-upload="beforeAvatarUpload"
:auto-upload="false"
:on-change="onChange">
<el-button type="primary">上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传.xls, .xlsx文件</div>
</el-upload>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFun('ruleForm')">分 班</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
js部分:
<script>
// 在vue中导入XLSX
import * as XLSX from 'xlsx'
import FileSaver from 'file-saver'
export default {
data(){
return{
tableData: [],
heightTable: '100%',
//弹窗
dialogVisible: false,
rules: {
totalNum: [
{ required: true, message: '请输入班级总人数', trigger: 'blur' }
],
fileList: [
{ required: true, message: '请上传Excel', trigger: 'change' }
],
male: [
{ required: true, message: '请输入男生比例', trigger: 'blur' }
],
famale: [
{ required: true, message: '请输入女生比例', trigger: 'blur' }
],
},
wb: {},
ruleForm: {
totalNum: 30,
male: 50,
famale: 50,
fileList: []
}
}
},
methods: {
divisionFun(){
//分班
this.dialogVisible = true;
},
exportFun(){
//导出
/* generate workbook object from table */
var xlsxParam = { raw: true } // 导出的内容只做解析,不进行格式转换
var table = document.querySelector('#exportTab').cloneNode(true)
if(table.querySelector('.el-table__fixed')) table.removeChild(table.querySelector('.el-table__fixed')) //这里是双下划线
var wb = XLSX.utils.table_to_book(table, xlsxParam)
/* get binary string as output */
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: true, type: 'array' })
try {
FileSaver.saveAs(new Blob([wbout], { type: 'application/octet-stream' }), '分班表.xlsx')
} catch (e) {
if (typeof console !== 'undefined') {
console.log(e, wbout)
}
}
return wbout;
},
//弹窗
handleClose(){
this.dialogVisible = false;
this.$set(this.ruleForm, 'fileList', []);
},
maleFun(val){
this.ruleForm.famale = 100 - val;
},
famaleFun(val){
this.ruleForm.male = 100 - val;
},
beforeAvatarUpload(file) {
let extension = file.name.substring(file.name.lastIndexOf(".") + 1);
let bool = true;
if (extension !== "xlsx" && extension !== "xls") {
bool = false;
this.$message.error('上传文件只能是.xls, .xlsx格式!');
}
return bool;
},
onChange(file, fileList){
if (fileList.length > 0) {
this.ruleForm.fileList = [fileList[fileList.length - 1]];
}
},
dispFile(file) {
return new Promise(function(resolve, reject) {
const reader = new FileReader()
reader.onload = function(e) {
const data = e.target.result
this.wb = XLSX.read(data, {
type: 'binary'
})
const result = []
this.wb.SheetNames.forEach((sheetName) => {
result.push({
sheetName: sheetName,
sheet: XLSX.utils.sheet_to_json(this.wb.Sheets[sheetName])
})
})
resolve(result)
}
reader.readAsBinaryString(file.raw)
})
},
submitFun(formName){
this.$refs[formName].validate((valid) => {
if (valid) {
let file = this.ruleForm.fileList[0];
this.dispFile(file).then(async(tabJson) => {
let data = [];
if (tabJson && tabJson.length > 0) {
data = tabJson[0].sheet;
}
data = await this.dispData(data);
this.tableData = data;
this.handleClose();
})
this.dialogVisible = false
} else {
return false;
}
});
},
async dispData(data){
//分班规矩:依据一定的男女比例、班级总人数进行分班
let newData = [];
let maleData = [];//男生
let famaleData = [];//女生
let totalNum = this.ruleForm.totalNum;//班级总人数
let male = this.ruleForm.male;//男生比例
let famale = this.ruleForm.famale;//女生比例
//第一步:区分男女
for(let i = 0; i < data.length; i++){
if(data[i]['性别'] === "男"){
maleData.push(data[i]);
}else if(data[i]['性别'] === "女"){
famaleData.push(data[i]);
}
}
//第二步:班级总数
let classTotal = Math.ceil(data.length / totalNum);
//第三步:区分班级-男生女生人数不足时相互补充
for(let j = 0; j < classTotal; j++){
let maleNum = Math.ceil(totalNum * (male / 100));//男生人数
let famaleNum = Math.ceil(totalNum * (famale / 100));//女生人数
let maleDifference = 0;//差值
if(maleData.length < maleNum){
maleDifference = maleNum - maleData.length;
if(famaleData.length - famaleNum > 0){
let differenceDataOne = famaleData.splice(0, maleDifference);
maleData = [...maleData, ...differenceDataOne];
}
}
newData = this.dispSexFun(maleNum, maleData, j+1, newData);//随机男生
let famaleDifference = 0;//差值
if(famaleData.length < famaleNum){
famaleDifference = famaleNum - famaleData.length;
if(maleData.length > 0){
let differenceDataTwo = maleData.splice(0, famaleDifference);
famaleData = [...famaleData, ...differenceDataTwo];
}
}
newData = this.dispSexFun(famaleNum, famaleData, j+1, newData);//随机女生
}
return newData;
},
dispSexFun(totalNum, sexData, className, newData){
//totalNum:总人数 sexData:男/女数组 className: 班级序号 newData:最后拆分得到的新数组
for(let m = 0; m < totalNum; m++){
let index = this.radomFun(0, sexData.length);
let newObj = sexData[index];
if(newObj){
newObj['班级'] = className + "班";
newData.push(newObj);
sexData.splice(index, 1);
}
}
return newData;
},
radomFun(min, max) {
//产生不重复的随机数
let key = Math.floor((Math.random() * (max-min)) + min);
return key;
}
}
}
</script>
css部分:
<style lang="scss">
.mainBox{
width: calc(100% - 40px);
height: calc(100% - 40px);
background: #eee;
padding: 20px;
.btnDIv{
display: flex;
}
.tableDiv{
height: calc(100% - 65px);
margin-top: 10px;
.elTable{
padding: 20px;
background: #fff;
border-radius: 10px;
height: calc(100% - 40px);
}
}
}
</style>