以下两张图是专家库覆盖算法
选取专家类型的HTML结构
<el-cascader placeholder="请选择专家类型" :options="specialtyCategoryOption" :props="{multiple:true,emitPath:false }" collapse-tags clearable v-model="form.expertType" @change="getExpertListByType"/>
第一步 筛选出满足特长的专家
/* 变量介绍
选中的特长类型
type=[1,2,7,8,9,10,]
当前专家库里满足目前选中的特长类型 true则代表有,false则代表是没有,默认为false
typeObj={
1:false,
2:false,
7:false,
8:false,
9:false,
10:false,
}
经过筛选后,满足选中特长类型的专家将保存在改变量中
selectExpertsList =[
{
name:'张三',//专家姓名
specialtyCategory:'1,2,5,6',//该专家所拥有的特长
meetStrengths:[1,2],//满足当前选中的特长
meetStrengthsStr:'1,2',//满足当前选中的特长
agreementStatus:true //该专家没有拒绝过该项目
},.......
]
*/
//选特长时触发
getExpertListByType(type){
this.expertType = type
if(type.length == 0) return //未选中特长时 跳出函数
let typeObj = {}
type.forEach(v=>{
typeObj[v]=false //初始化,所有值都未命中状态
})
//挑出满足条件的专家 且 找出所有专家都没有满足的特长
let selectExpertsList = this.allExperts.filter(val=>{
let useType = val.specialtyCategory.split(',');
let isSatisfy = false
val.meetStrengths = []
val.meetStrengthsStr = ''
if(val.agreementStatus === false ) return false // 如果该专家拒绝过 直接跳过
useType.forEach(v=>{
if((typeObj.hasOwnProperty(v))){
isSatisfy =true //该专家满足所有特长中一个
typeObj[v] = true //命中则置为true
if(val.meetStrengthsStr==''){
val.meetStrengthsStr = v
}else{
val.meetStrengthsStr = val.meetStrengthsStr+','+v
}
val.meetStrengths.push(v)
}
})
return isSatisfy
})
this.selectExpertsList = selectExpertsList
},
将符合选中特长的专家,按命中的特长作分类存在变量combination中
/* 经过整合后combination的结构会是这样的
combination:{
'1,2':[
{
name:'张三',//专家姓名
specialtyCategory:'1,2,5,6',//该专家所拥有的特长
meetStrengths:[1,2],//满足当前选中的特长
meetStrengthsStr:'1,2',//满足当前选中的特长
agreementStatus:true //该专家没有拒绝过该项目
},
{
name:'李四',//专家姓名
specialtyCategory:'1,2,4,6',//该专家所拥有的特长
meetStrengths:[1,2],//满足当前选中的特长
meetStrengthsStr:'1,2',//满足当前选中的特长
agreementStatus:true //该专家没有拒绝过该项目
},
],
'7,8,9':[
{
name:'小明',//专家姓名
specialtyCategory:'5,6,8,9',//该专家所拥有的特长
meetStrengths:[8,9],//满足当前选中的特长
meetStrengthsStr:'8,9',//满足当前选中的特长
agreementStatus:true //该专家没有拒绝过该项目
},
],
'8,9,10':[
{
name:'小明',//专家姓名
specialtyCategory:'3,4,6,8,9,10',//该专家所拥有的特长
meetStrengths:[8,9,10],//满足当前选中的特长
meetStrengthsStr:'8,9,10',//满足当前选中的特长
agreementStatus:true //该专家没有拒绝过该项目
},
],.......
}
*/
//随机抽取专家(有几个专家,抽几个专家) 整合数据
processingData(){
if(this.tix()) return
if(this.reviewForm.passed==="1") return this.$message({ type: "error", message: "评审已经通过(抽选结束)!" });
this.getExpertListByType(this.expertType) //重新获取满足条件的专家
let combination = this.specialityGrouping()//专家按命中的特长类型分组
this.arrange(combination,this.expertNumber,false)
},
//将专家按命中的特长类型分组
specialityGrouping(){
let combination = {}
this.selectExpertsList.forEach(v=>{//将专家按命中的特长类型分组
if(!combination.hasOwnProperty(v.meetStrengthsStr)){
combination[v.meetStrengthsStr] = []
}
combination[v.meetStrengthsStr].push(v)
})
return combination
},
抽取专家方法 : arrange()、combination这是按专家按命中的特长类型分组、num专家数量、isByElection是否是补选
// combination这是按专家按命中的特长类型分组 num专家数量
arrange(combination,num,isByElection){
let key = Object.keys(combination)
let amount = num
let allAmount = num
let keyLen = key.length
if(keyLen < amount ){ //当专家种类少于专家数量时
// diff = amount - keyLen // 多出来的 后续抽取
amount = keyLen
}
// var allArr = [...new Set(key.flatMap(item => item.split(',')))]
// console.log(allArr)
let maxCombinations = this.combinations(key,amount) //得到最大限度满足特长的几
组合
const index = this.getRandomInt(0,maxCombinations.length-1) //随机生成一个整数
let randomCombination = maxCombinations[index] //从maxCombinations中随机获取其中一个组合
let expertList = [] //存放专家的变量
randomCombination.forEach(v=>{//从抽取的组合下,随机抽取专家
let arr = combination[v]//获取该特长的所有专家
const index = this.getRandomInt(0,arr.length-1)
arr[index].teamLeader = 0
expertList.push(arr[index])
})
let currentExpertNum = expertList.length
let diffExpertNum =allAmount - currentExpertNum // 算出还差几个专家数量,差的这
几个就直接抽取,因为expertList变量里存的专家已经最大限度去覆盖了所选的特长。
if(isByElection){//是否是补选
this.selectedExpertsList = this.selectedExpertsList.concat(expertList)
}else{
this.selectedExpertsList = [...expertList]
}
for(let i = 0;i<diffExpertNum;i++){
this.expertDeduplication()//先过滤专家库里的专家
this.randomSelectEvent()//然后再随机抽取专家
}
},
combinations(input, length) {//排列组合 找出满足特长最多的组合
let maxLen = 0;
let maxCombination = [];
function recurse(prefix, remaining, k) {
if (k === 0) {
var newArr = prefix.flatMap(item => item.split(','));
var uniqueArr = [...new Set(newArr)];
if(uniqueArr.length > maxLen){
maxLen = uniqueArr.length
maxCombination = []
maxCombination.push(prefix)
}else if (uniqueArr.length == maxLen){
maxCombination.push(prefix)
}
// result.push(prefix);
return;
}
for (var i = 0; i < remaining.length; i++) {
recurse(prefix.concat(remaining[i]), remaining.slice(i + 1), k - 1);
}
}
for(let i=1;i<=length;i++){
recurse([], input, i);
}
// recurse([], input, length);
return maxCombination;
},
expertDeduplication:去重函数,主要从专家库里过滤掉已选的专家
expertDeduplication(){ //专家库去重操作 已选的专家:selectedExpertsList 专家库:selectExpertsList
let selectExpertsList = this.selectExpertsList
//过滤专家库里已选的专家
this.selectedExpertsList.forEach(item => {
selectExpertsList = selectExpertsList.filter(i=> {
/*
item.name != i.name 过滤已选的专家 不使用id是因为后端接口返回的已选专家的id与专家库里的id不是同一个东西
item.agreementStatus !== false 过滤拒绝的专家
*/
return item.name != i.name //|| item.agreementStatus !== false
})
})
this.selectExpertsList = selectExpertsList//过滤掉已选专家后专家库里的专家
randomSelectEvent : 消息提醒函数
setActiveExperts:随机抽取一个专家
randomSelectEvent(){
if (!this.form.projectName) return this.$message({ type: "error", message: "请选择项目" });
if(this.selectedExpertsList.length>=this.expertNumber) return this.$message({ type: "error", message: "不得超过规定的专家数量!" });
if(!this.selectExpertsList.length) return this.$message({ type: "error", message: "没有可以抽取的专家数据!" });
this.setActiveExperts()
},
setActiveExperts(){
const index = this.getRandomInt(0,this.selectExpertsList.length-1)//生成随机数
if(this.isExistExperts(this.selectExpertsList[index])) return this.randomSelectEvent() //如果存在则重新抽取
this.activeExperts = this.selectExpertsList[index];
let num =0
// if(this.selectedNumber == 0) num = 1 //当选出第一个专家时,默认他为组长
// else num = 0
this.$set(this.activeExperts,'teamLeader',num)
this.selectExpertsList.splice(index,1)
this.selectName = this.activeExperts.name;
this.selectedExpertsList.push(this.activeExperts)
//判断已选取列表是否已存在
isExistExperts(data){
const res = this.selectedExpertsList.find(item=>item.id === data.id)
return !!res
},
// 获取随机数
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
},
在保持原有的专家不变情况下增加专家数量。
<el-button type="primary" style="width: 150px;margin-left: 0px;margin-top: 50px;" @click="byElection" :disabled="this.reviewForm.passed==='1'">补选随机抽取并添加</el-button>
byElection:补选函数
//补选随机抽取专家(缺几个专家,抽几个专家)
byElection(){
if(this.tix()) return
if(this.reviewForm.passed==="1") return this.$message({ type: "error", message: "评审已经通过(抽选结束)!" });
//补选需要过滤已选的专家和拒绝的专家
this.expertDeduplication()
let combination = this.specialityGrouping()
let diffNum = this.expertNumber - this.selectedExpertsList.length
if(diffNum == 0 ) return
this.arrange(combination,diffNum,true)
},