从中台复制竞赛模块

Branch_d40a2540
yujialong 2 years ago
parent 7bc3f3313c
commit 7f861789d1
  1. 114
      src/api/index.js
  2. 48
      src/const/match.js
  3. 15
      src/layouts/navbar/index.vue
  4. 41
      src/pages/assessment/list/index.vue
  5. 913
      src/pages/match/add/index.vue
  6. 228
      src/pages/match/add/set.vue
  7. 824
      src/pages/match/add/step1.vue
  8. 427
      src/pages/match/add/step2.vue
  9. 290
      src/pages/match/add/step3.vue
  10. 53
      src/pages/match/add/step4.vue
  11. 39
      src/pages/match/list/index.vue
  12. 63
      src/pages/match/manage/index.vue
  13. 109
      src/pages/match/manage/matchArch.vue
  14. 381
      src/pages/match/manage/matchArchList.vue
  15. 562
      src/pages/match/manage/matchInfo.vue
  16. 29
      src/pages/match/manage/matchProgress.vue
  17. 476
      src/pages/match/manage/matchRank.vue
  18. 370
      src/pages/match/manage/matchReport.vue
  19. 392
      src/pages/match/manage/matchSignup.vue
  20. 15
      src/pages/match/manage/notice.vue
  21. 17
      src/pages/match/manage/noticeDetail.vue
  22. 8
      src/pages/match/preview/index.vue
  23. 14
      src/pages/project/list/index.vue
  24. 7
      src/pages/screen/index.vue
  25. 2
      src/pages/student/list/index.vue
  26. 4
      src/setting.js

@ -2,7 +2,7 @@ import Setting from "@/setting";
const host = Setting.apiBaseURL const host = Setting.apiBaseURL
const uploadURL = Setting.upload.apiURL; const uploadURL = Setting.upload.apiURL;
const host1 = "https://www.occupationlab.com:8080/" const host1 = "https://www.occupationlab.com:8080/"
const host2 = 'http://192.168.31.137:9000/' const host2 = 'http://192.168.31.51:9000/'
export default { export default {
queryToken: `liuwanr/userInfo/queryToken`, queryToken: `liuwanr/userInfo/queryToken`,
@ -113,39 +113,93 @@ export default {
// 赛事 // 赛事
contestPageConditionQueryByOccupationlab: `occupationlab/occupationlab/enterprise/match/contest/contestPageConditionQueryByOccupationlab`, competitionPageConditionQueryByOccupationlab: `competition/competition/management/competitionPageConditionQueryByOccupationlab`,
addContest: `occupationlab/occupationlab/enterprise/match/contest/addContest`, addCompetition: `competition/competition/management/addCompetition`,
editContest: `occupationlab/occupationlab/enterprise/match/contest/editContest`, batchDeleteCompetition: `competition/competition/management/batchDeleteCompetition`,
deleteContest: `occupationlab/occupationlab/enterprise/match/contest/deleteContest`, checkTheStatusOfTheCompetition: `competition/competition/management/checkTheStatusOfTheCompetition`,
getContest: `occupationlab/occupationlab/enterprise/match/contest/getContest`, disabledEventsCompetition: `competition/competition/management/disabledEvents`,
deleteAnnex: `occupationlab/occupationlab/contest/annex/delete`, editCompetition: `competition/competition/management/editCompetition`,
saveAnnex: `occupationlab/occupationlab/contest/annex/save`, getCompetition: `competition/competition/management/getCompetition`,
getSchoolsByProvince: `nakadai/nakadai/school/getSchoolsByProvince`, getProjectAssessmentByCompetition: `occupationlab/occupationlab/projectManage/getProjectAssessmentByCompetition`,
disabledEvents: `occupationlab/occupationlab/enterprise/match/contest/disabledEvents`, publishCompetition: `competition/competition/management/publishCompetition`,
batchDeleteContest: `occupationlab/occupationlab/enterprise/match/contest/batchDeleteContest`, detailsOfCompetitionStage: `${host1}competition/competition/management/detailsOfCompetitionStage`,
entryInformation: `competition/competition/team/entryInformation`,
getCustomerOrder: `nakadai/nakadai/valueModuleManagement/getCustomerOrder`,
curriculumList: `nakadai/nakadai/curriculum/schoolCourse`,
queryCustomer: `nakadai/nakadai/customer/queryCustomer`,
// 赛事内容
addCompetitionContent: `competition/competition/content/addCompetitionContent`,
editCompetitionContent: `competition/competition/content/editCompetitionContent`,
getCompetitionContent: `competition/competition/content/getCompetitionContent`,
// 赛事附件
delAnnex: `competition/competition/announcementAnnex/delete`,
saveAnnex: `competition/competition/announcementAnnex/save`,
updateAnnex: `competition/competition/announcementAnnex/update`,
viewAttachments: `competition/competition/announcementAnnex/viewAttachments`,
// 赛事规则
addCompetitionRule: `competition/competition/rule/addCompetitionRule`,
editCompetitionRule: `competition/competition/rule/editCompetitionRule`,
getCompetitionRule: `competition/competition/rule/getCompetitionRule`,
queryCompetitionStageBySetupId: `competition/competition/stage/queryCompetitionStageBySetupId`,
// 竞赛进展 // 竞赛进展
addContestProgress: `occupationlab/occupationlab/enterprise/match/contest-progress/addContestProgress`, addCompetitionProgress: `competition/competition/progress/addCompetitionProgress`,
deleteContestProgress: `occupationlab/occupationlab/enterprise/match/contest-progress/deleteContestProgress`, deleteCompetitionProgress: `competition/competition/progress/deleteCompetitionProgress`,
getContestProgress: `occupationlab/occupationlab/enterprise/match/contest-progress/getContestProgress`, editCompetitionProgress: `competition/competition/progress/editCompetitionProgress`,
editContestProgress: `occupationlab/occupationlab/enterprise/match/contest-progress/editContestProgress`, getCompetitionProgress: `competition/competition/progress/getCompetitionProgress`,
// 报名人员 // 报名人员
addApplicant: `occupationlab/occupationlab/enterprise/match/applicant/addApplicant`, addCompetitionRegistration: `competition/competition/registration/addCompetitionRegistration`,
disableContests: `occupationlab/occupationlab/enterprise/match/applicant/disableContests`, cancelRegistration: `competition/competition/registration/cancelRegistration`,
excelExport: `${host}occupationlab/occupationlab/enterprise/match/applicant/excelExport`, disableContests: `competition/competition/registration/disableContests`,
queryApplicantByCondition: `occupationlab/occupationlab/enterprise/match/applicant/queryApplicantByCondition`, disableRegistration: `competition/competition/registration/disableRegistration`,
disableApplicant: `occupationlab/occupationlab/enterprise/match/applicant/disableApplicant`, excelExport: `competition/competition/registration/excelExport`,
exportDataInBatches: `${host}occupationlab/occupationlab/enterprise/match/applicant/exportDataInBatches`, exportDataInBatches: `competition/competition/registration/exportDataInBatches`,
queryRegistrationByCondition: `competition/competition/registration/queryRegistrationByCondition`,
searchTeam: `competition/competition/team/searchTeam`,
joinCompetitionTeam: `competition/competition/team/joinCompetitionTeam`,
addCompetitionTeam: `competition/competition/team/addCompetitionTeam`,
studentAccountApplication: `users/users/register/studentAccountApplication`,
checkPhoneOrEmailExist: `users/users/userInfo/checkPhoneOrEmailExist`,
editCompetitionTeam: `competition/competition/team/editCompetitionTeam`,
addAnAdvisor: `competition/competition/teamInstructor/addAnAdvisor`,
deleteAnAdvisor: `competition/competition/teamInstructor/deleteAnAdvisor`,
captainOfTransfer: `competition/competition/team/captainOfTransfer`,
removeTheLine: `competition/competition/team/removeTheLine`,
stageSelectParticipants: `competition/competition/teamParticipant/stageSelectParticipants`,
updateUser: `users/users/userInfo/updateUser`,
enquireAboutSchoolStudents: `users/users/userAccount/enquireAboutSchoolStudents`,
// 赛事公告 // 赛事公告
addAnnouncement: `occupationlab/occupationlab/contest/announcement/addAnnouncement`, addAnnouncement: `competition/competition/announcement/addAnnouncement`,
amendmentAnnouncement: `occupationlab/occupationlab/contest/announcement/amendmentAnnouncement`, amendmentAnnouncement: `competition/competition/announcement/amendmentAnnouncement`,
deleteAnnouncement: `occupationlab/occupationlab/contest/announcement/deleteAnnouncement`, deleteAnnouncement: `competition/competition/announcement/deleteAnnouncement`,
disableAnnouncement: `occupationlab/occupationlab/contest/announcement/disableAnnouncement`, disableAnnouncement: `competition/competition/announcement/disableAnnouncement`,
queryAnnouncementByContestId: `occupationlab/occupationlab/contest/announcement/queryAnnouncementByContestId`, queryAnnouncementByCompetitionId: `competition/competition/announcement/queryAnnouncementByCompetitionId`,
queryAnnouncementDetails: `occupationlab/occupationlab/contest/announcement/queryAnnouncementDetails`, queryAnnouncementDetails: `competition/competition/announcement/queryAnnouncementDetails`,
deleteAnnouncementAnnex: `occupationlab/occupationlab/contestAnnouncementAnnex/delete`, saveAnnouncementAnnex: `competition/competition/announcementAnnex/save`,
saveAnnouncementAnnex: `occupationlab/occupationlab/contestAnnouncementAnnex/save`, // 赛事成绩
batchDeleteContestGrade: `${host1}competition/competition/performance/batchDeleteContestGrade`,
batchImportGrades: `${host1}competition/competition/performance/batchImportGrades`,
exportExperimentalResultsInBatch: `${host1}competition/competition/performance/exportExperimentalResultsInBatch`,
performanceExportFailure: `${host1}competition/competition/performance/exportFailure`,
rankExportFailure: `${host1}competition/competition/rank/exportFailure`,
stageGradeManagementList: `${host1}competition/competition/performance/stageGradeManagementList`,
stageRaceRanking: `${host1}competition/competition/rank/stageRaceRanking`,
overallStandingsInThePointsRace: `${host1}competition/competition/rank/overallStandingsInThePointsRace`,
batchImportRanking: `${host1}competition/competition/rank/batchImportRanking`,
publishRanking: `${host1}competition/competition/rank/publishRanking`,
stageTeamScoreDetails: `${host1}competition/competition/rank/stageTeamScoreDetails`,
multipleStageTeamScoreDetails: `${host1}competition/competition/rank/multipleStageTeamScoreDetails`,
detailsOfTotalTeamScores: `${host1}competition/competition/rank/detailsOfTotalTeamScores`,
cancelRanking: `${host1}competition/competition/rank/cancelRanking`,
reportDetail: `${host1}occupationlab/occupationlab/achievement/reportDetail`,
deleteLastPublication: `${host1}competition/competition/rank/deleteLastPublication`,
whetherToPublish: `${host1}competition/competition/rank/whetherToPublish`,
manuallyRankTheUploadList: `${host1}competition/competition/rank/manuallyRankTheUploadList`,
queryPublicationSource: `${host1}competition/competition/rank/queryPublicationSource`,
releaseVerification: `${host1}competition/competition/rank/releaseVerification`,
gradeImport: `${host2}template/赛事成绩导入模板.xlsx`,
rankImportTeam: `${host2}template/赛事排名导入模板(团队赛).xlsx`,
rankImportPerson: `${host2}template/赛事排名导入模板(个人赛).xlsx`,
// 栏目管理 // 栏目管理
addColumn: `occupationlab/occupationlab/information/column/addColumn`, addColumn: `occupationlab/occupationlab/information/column/addColumn`,
deleteColumn: `occupationlab/occupationlab/information/column/deleteColumn`, deleteColumn: `occupationlab/occupationlab/information/column/deleteColumn`,

@ -0,0 +1,48 @@
/**
* 大赛配置
* */
export default {
rules: [
{
id: 0,
name: '积分赛'
},
{
id: 1,
name: '淘汰赛'
},
{
id: 2,
name: '单项赛'
}
],
methods: [
{
id: 0,
name: '实操'
},
{
id: 1,
name: '理论'
},
{
id: 2,
name: '线下'
}
],
teamCalculationMethods: [
{
id: 0,
name: '最高分'
},
{
id: 1,
name: '平均分'
},
{
id: 2,
name: '总分'
}
],
}

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-menu class="sidebar-el-menu" :default-active="active" background-color="#324157" text-color="#bfcbd9" active-text-color="#9278FF" unique-opened mode="horizontal" @select="menuSelect"> <el-menu v-if="menuRefresh" class="sidebar-el-menu" :default-active="active" background-color="#324157" text-color="#bfcbd9" active-text-color="#9278FF" unique-opened mode="horizontal" @select="menuSelect">
<template v-for="item in menus"> <template v-for="item in menus">
<template v-if="item.subs"> <template v-if="item.subs">
<el-submenu :index="item.index" :key="item.index"> <el-submenu :index="item.index" :key="item.index">
@ -99,7 +99,8 @@ export default {
achievement: ["experiment", "experimentVir", "experimentTeach", "addexperiment", "addexperimentoptions", "showExperiment", "showExperimentoption", "showExperimentoptions"], achievement: ["experiment", "experimentVir", "experimentTeach", "addexperiment", "addexperimentoptions", "showExperiment", "showExperimentoption", "showExperimentoptions"],
project: ["addproject", "program", "programOption", "programOptions"], project: ["addproject", "program", "programOption", "programOptions"],
backstage: ["report"] backstage: ["report"]
} },
menuRefresh: 1
}; };
}, },
computed: { computed: {
@ -155,7 +156,15 @@ export default {
menuSelect(path) { menuSelect(path) {
this.setTabsName('1') this.setTabsName('1')
this.setColumnId('') this.setColumnId('')
path === '/screen' ? window.open(this.$router.resolve(path).href) : this.$router.push(path) if (path === '/screen') {
let arr=this.$route.path.split("/");
let name = `/${arr[1]}/list`
this.active = name;
window.open(this.$router.resolve(path).href)
location.reload()
} else {
this.$router.push(path)
}
} }
} }
}; };

@ -187,8 +187,6 @@ export default {
multipleSelection: [], // multipleSelection: [], //
listLoading:false,// listLoading:false,//
ticker: null, // ticker: null, //
sss:1,
datassdata:0
}; };
}, },
computed: { computed: {
@ -226,11 +224,10 @@ export default {
mounted() { mounted() {
// //
this.$once("hook:beforeDestroy", function() { this.$once("hook:beforeDestroy", function() {
this.sss = 0 clearInterval(this.ticker);
// clearInterval(this.ticker); this.ticker = null;
// this.ticker = null;
}); });
this.getData(); this.getData(1);
this.getschoolCourse(); this.getschoolCourse();
}, },
methods: { methods: {
@ -238,11 +235,8 @@ export default {
this.form.month = '6' this.form.month = '6'
}, },
beginTimer() { beginTimer() {
clearInterval(this.ticker)
this.ticker = setInterval(() => { this.ticker = setInterval(() => {
if(this.sss == 0){
clearInterval(this.ticker);
this.ticker = null;
}else{
for (let i = 0; i < this.listData.length; i++) { for (let i = 0; i < this.listData.length; i++) {
const item = this.listData[i]; const item = this.listData[i];
if (item.countDown > 0) { if (item.countDown > 0) {
@ -256,10 +250,8 @@ export default {
// item.status = 2; // item.status = 2;
} }
} }
item.show = true;
this.$set(this.listData, i, item); this.$set(this.listData, i, item);
} }
}
}, 1000); }, 1000);
}, },
timeFilter(countDown) { timeFilter(countDown) {
@ -275,11 +267,8 @@ export default {
return "00:00:00"; return "00:00:00";
} }
}, },
getData() { getData(counddown) {
this.datassdata = this.datassdata+1
this.listLoading = true; this.listLoading = true;
this.listData.splice(0);
this.sss = 0
let data = { let data = {
...this.form, ...this.form,
keyWord: this.keyWord, keyWord: this.keyWord,
@ -291,7 +280,6 @@ export default {
this.listData = res.list; this.listData = res.list;
this.total = res.total; this.total = res.total;
this.listData.forEach(i => { this.listData.forEach(i => {
i.show = false;
if (i.status == 2) { // if (i.status == 2) { //
i.countDown = 0; i.countDown = 0;
} else { } else {
@ -314,11 +302,7 @@ export default {
} }
} }
}); });
this.sss = 1; counddown && this.beginTimer()
if(this.datassdata == 1){
this.beginTimer()
}
// setTimeout(,3000)
this.listLoading = false; this.listLoading = false;
}).catch(err => { }).catch(err => {
this.listLoading = false; this.listLoading = false;
@ -327,7 +311,6 @@ export default {
initData() { initData() {
this.$refs.table.clearSelection(); this.$refs.table.clearSelection();
this.pageNum = 1; this.pageNum = 1;
this.sss = 0
this.getData(); this.getData();
}, },
getschoolCourse() { // getschoolCourse() { //
@ -349,8 +332,7 @@ export default {
start(row) { start(row) {
this.$post(`${this.api.enableAssessment}?id=${row.id}`).then(res => { this.$post(`${this.api.enableAssessment}?id=${row.id}`).then(res => {
util.successMsg("启动成功!"); util.successMsg("启动成功!");
this.sss = 0 this.getData(1);
this.getData();
}).catch(err => { }).catch(err => {
console.log(err); console.log(err);
}); });
@ -366,7 +348,6 @@ export default {
}; };
this.$post(`${this.api.collectPaper}?id=${row.id}`).then(res => { this.$post(`${this.api.collectPaper}?id=${row.id}`).then(res => {
util.successMsg("提前结束成功!"); util.successMsg("提前结束成功!");
this.sss = 0
this.getData(); this.getData();
}).catch(err => { }).catch(err => {
console.log(err); console.log(err);
@ -380,7 +361,6 @@ export default {
}).then(() => { }).then(() => {
this.$post(this.api.deleteAssessment, [row.id]).then(res => { this.$post(this.api.deleteAssessment, [row.id]).then(res => {
util.successMsg("删除成功"); util.successMsg("删除成功");
this.sss = 0
this.getData(); this.getData();
}).catch(res => { }).catch(res => {
}); });
@ -400,7 +380,6 @@ export default {
this.multipleSelection = []; this.multipleSelection = [];
this.$refs.table.clearSelection(); this.$refs.table.clearSelection();
util.successMsg("删除成功"); util.successMsg("删除成功");
this.sss = 0
this.getData(); this.getData();
}).catch(res => { }).catch(res => {
}); });
@ -416,15 +395,9 @@ export default {
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val; this.multipleSelection = val;
}, },
onSearch() {
this.pageNum = 1;
this.sss = 0
this.getData();
},
handleCurrentChange(val) { handleCurrentChange(val) {
this.pageNum = val this.pageNum = val
this.$router.push(`list?page=${val}`) this.$router.push(`list?page=${val}`)
this.sss = 0
this.getData(); this.getData();
}, },
transferTime(date) { transferTime(date) {

@ -1,600 +1,158 @@
<template> <template>
<div> <div>
<el-card v-if="!form.id" shadow="hover" class="m-b-20"> <el-card v-if="!id" shadow="hover" class="m-b-20">
<div class="flex-between"> <div class="flex-between">
<el-page-header @back="back" :content="'创建赛事'"></el-page-header> <el-page-header @back="back" :content="'创建赛事'"></el-page-header>
</div> </div>
</el-card> </el-card>
<el-card v-if="step !== 4" shadow="hover" class="m-b-20" style="position: relative;margin-top: 20px">
<template v-if="step === 1 || (id && !editing)">
<div class="p-title">大赛发布类型</div>
<el-form label-width="100px" label-suffix=":" size="small" :disabled="!editing && id != ''">
<el-form-item label="请选择类型">
<el-radio v-model="releaseType" :label="0">仅发布信息</el-radio>
<el-radio v-model="releaseType" :label="1">设置完整比赛</el-radio>
</el-form-item>
</el-form>
<el-button v-if="!editing && id" class="edit" type="primary" @click="editing = 1">编辑</el-button>
</template>
<ul :class="['steps', {pointer: !editing && id}]">
<li :class="{active: step === 1,done: step > 1}" @click="toStep(1)">
<span class="circle">1</span>
<p class="text">大赛信息填写</p>
</li>
<template v-if="releaseType">
<li :class="{active: step === 2,done: step > 2}" @click="toStep(2)">
<span class="circle circle2">2</span>
<p class="text">赛程与规则设置</p>
</li>
<li :class="{active: step === 3,done: step > 3}" @click="toStep(3)">
<span class="circle circle3">3</span>
<p class="text">比赛内容设置</p>
</li>
</template>
<li :class="{done: step > 3}">
<span class="circle circle4">{{ releaseType ? 4 : 2 }}</span>
<p class="text">发布{{ !editing && id ? '成功' : '' }}</p>
</li>
</ul>
</el-card>
<div class="page"> <div class="page">
<div class="page-content"> <div class="page-content">
<el-form label-width="170px" label-suffix=":" size="small"> <step1 v-show="step === 1" ref="step1" :editing.sync="editing" @next="next" />
<el-form-item label="竞赛封面(选填)"> <step2 v-if="step === 2" ref="step2" :editing.sync="editing" :setupId.sync="setupId" @next="next" />
<el-upload <step3 v-if="step === 3" ref="step3" :editing.sync="editing" :setupId.sync="setupId" :competitionId.sync="competitionId" @next="next" />
class="avatar-uploader" <step4 v-if="step === 4" />
accept=".jpg,.png,.jpeg,.gif"
:on-remove="handleRemove" <div v-if="step !== 4 && showBtns" class="btns">
:on-error="uploadError" <!-- 处于编辑状态(列表点编辑按钮进来默认是查看状态不可编辑点了编辑按钮才可编辑)或者新增才显示这几个按钮 -->
:on-success="uploadSuccess" <template v-if="editing || !id">
:before-remove="beforeRemove" <el-button v-if="!publishStatus" @click="save(0)">保存{{ releaseType ? '草稿' : '' }}</el-button>
:limit="1" <el-button v-if="step === 2 || step === 3" type="primary" @click="prev">上一步</el-button>
:on-exceed="handleExceed" <el-button v-if="!releaseType || (releaseType && step === 3)" type="primary" @click="save(1)">发布</el-button>
:action="this.api.fileupload" <el-button v-else type="primary" @click="save(id ? 1 : 0, 2)">保存并下一步</el-button>
:headers="headers" </template>
name="file"
>
<img v-if="form.coverUrl" :src="form.coverUrl" class="avatar">
<div class="uploader-default" v-else>
<i class="el-icon-plus"></i>
<p>上传封面</p>
</div>
<div slot="tip" class="el-upload__tip">
<p>展示宽度为220高度140JPG/PNG/GIF3MB以内</p>
</div>
</el-upload>
</el-form-item>
<el-form-item label="竞赛封面长图(选填)">
<el-upload
class="avatar-uploader avatar-uploader-lg"
accept=".jpg,.png,.jpeg,.gif"
:on-remove="handleLgRemove"
:on-error="uploadError"
:on-success="uploadLgSuccess"
:before-remove="beforeRemove"
:limit="1" :on-exceed="handleExceed"
:action="this.api.fileupload"
:headers="headers"
name="file"
>
<img v-if="form.carouselUrl" :src="form.carouselUrl" class="avatar-lg">
<div class="uploader-default" v-else>
<i class="el-icon-plus"></i>
<p>上传封面</p>
</div>
<div slot="tip" class="el-upload__tip">
<p>展示宽度为1920高度300JPG/PNG/GIF3MB以内</p>
</div>
</el-upload>
</el-form-item>
<el-form-item label="比赛范围">
<div>
<el-radio v-model="form.competitionScope" :label="0">本校内</el-radio>
</div>
<div>
<el-radio v-model="form.competitionScope" :label="1">全平台</el-radio>
</div>
<div>
<el-radio v-model="form.competitionScope" :label="2">指定区域院校</el-radio>
<el-button v-if="form.competitionScope === 2" type="primary" size="mini" @click="showRange">选择院校</el-button>
<span style="margin-left: 20px">{{ rangeName }}</span>
</div>
</el-form-item>
<el-form-item label="竞赛名称">
<div class="d-inline-block">
<el-input placeholder="请输入竞赛名称" v-model="form.name" clearable></el-input>
</div>
</el-form-item>
<el-form-item label="主办方">
<div class="inline-input">
<div class="input-wrap" v-for="(item,index) in sponsorList" :key="index">
<el-input placeholder="主办方名称" v-model="sponsorList[index]"></el-input>
<i v-if="sponsorList.length > 1" class="remove" @click="delSponsor(index)"></i>
<button v-if="index == 0" class="add-btn" @click="addSponsor">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</div>
</div>
</el-form-item>
<el-form-item label="承办方(选填)">
<div class="inline-input">
<div class="input-wrap" v-for="(item,index) in undertakerList" :key="index">
<el-input placeholder="承办方名称" v-model="undertakerList[index]"></el-input>
<i v-if="undertakerList.length > 1" class="remove" @click="delOrganizer(index)"></i>
<button v-if="index == 0" class="add-btn" @click="addOrganizer">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</div>
</div>
<button v-if="!undertakerList.length" class="add-btn" @click="addOrganizer">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</el-form-item>
<el-form-item label="报名时间">
<el-date-picker v-model="signupTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
<el-form-item label="竞赛时间">
<el-date-picker v-model="playTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
<el-form-item label="竞赛详情">
<quill :border="true" v-model="form.description" :height="400" />
</el-form-item>
<el-form-item label="附件">
<el-upload
:on-remove="handleAnnexRemove"
:on-error="uploadError"
:before-upload="beforeUpload"
:on-success="uploadAnnexSuccess"
:limit="5"
:on-exceed="handleExceedAnnex"
:action="this.api.fileupload"
:headers="headers"
:file-list="fileList"
name="file"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
<p>支持扩展名.rar .zip .doc .docx .pdf .jpg...</p>
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button v-if="!form.id" @click="save(0)">保存</el-button>
<el-button type="primary" @click="save(1)" v-auth="'/match/list:管理:大赛详情:发布'">发布</el-button>
<el-button type="danger" @click="preview">预览</el-button> <el-button type="danger" @click="preview">预览</el-button>
</el-form-item> <el-button @click="back">取消</el-button>
</el-form>
</div> </div>
</div> </div>
<el-dialog title="请勾选院校" :visible.sync="rangeVisible" width="580px" custom-class="range-dia" :close-on-click-modal="false">
<div class="range-wrap">
<el-cascader
ref="range"
class="range-cas"
key="range"
v-model="range"
:props="props"
:show-all-levels="false"
clearable
filterable
:before-filter="beforeFilter"
:options="rangeList"
@change="rangeChange"
@visible-change="rangeViChange"
@input.native="rangeSearch"></el-cascader>
<el-tag
v-for="(tag, i) in rangeChecked"
:key="tag.value"
class="range-check"
closable
:disable-transitions="false"
@close="val => closeRange(i)">
{{tag.label}}
</el-tag>
</div> </div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="rangeVisible = false"> </el-button>
<el-button size="small" type="primary" @click="rangeSubmit"> </el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import util from "@/libs/util"; import util from "@/libs/util";
import quill from "@/components/quill"; import quill from "@/components/quill";
import Setting from "@/setting"; import step1 from './step1'
import step2 from './step2'
import step3 from './step3'
import step4 from './step4'
import { Loading } from 'element-ui'
export default { export default {
name: "add", name: "add",
data() { data() {
const that = this
return { return {
headers: { id: this.$route.query.id || '',
token: util.local.get(Setting.tokenKey)
},
form: {
id: this.$route.query.id,
platformSource: 1, // (0:1)
isOpen: 0, // (0 1 0)
name: '',
sponsor: '',
undertaker: '',
competitionScope: 0, // (0: 1: 2:)
contestRangeList: [], //
contestAnnexList: [], //
coverUrl: '',
carouselUrl: '',
publishStatus: 0, publishStatus: 0,
signUpStartTime: '', competitionId: '',
signUpEndTime: '', step: 1,
playStartTime: '',
playEndTime: '',
description: '',
},
fileName: '',
signupTime: '',
playTime: '',
sponsorList: [""],
undertakerList: [""],
fileList: [],
pickerOptions: {
disabledDate: time => {
return time.getTime() < new Date().getTime() - 86400000;
}
},
rangeVisible: false,
range: [],
rangeInit: [],
rangeName: '',
rangeChecked: [],
schools: [],
rangeList: [],
//
props: {
multiple: true,
checkStrictly: true,
lazy: true,
lazyLoad (node, resolve) {
//
const input = document.querySelector('.el-cascader__search-input')
const checked = that.rangeChecked
if (input && input.value.trim()) {
const val = input.value.trim()
return that.schools.filter(e => e.label.includes(val))
}
const { level, value } = node
//
if (!level) {
that.$get(that.api.queryProvince).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.provinceId
e.label = e.provinceName
e.children = []
e.disabled = !!checked.find(n => n.provinceId == e.provinceId && !n.cityId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else if (level === 1) {
//
that.$get(that.api.queryCity, {
provinceId: value
}).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.cityId
e.label = e.cityName
e.children = []
e.disabled = !!checked.find(n => n.cityId == e.cityId && n.provinceId == e.provinceId && !n.schoolId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else if (level === 2) {
//
that.$get(that.api.getSchoolsByProvince, {
provinceId: node.data.provinceId,
cityId: value,
schoolName: ''
}).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.schoolId
e.label = e.schoolName
e.leaf = true
e.disabled = !!checked.find(n => n.schoolId == e.schoolId && n.cityId == e.cityId && n.provinceId == e.provinceId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else {
resolve([])
}
}
},
submiting: false, submiting: false,
updateTime: 0, updateTime: 0,
setupId: '',
releaseType: 0,
editing: this.$route.query.id ? 0 : 1,
showBtns: true,
loadIns: null
}; };
}, },
components: { components: {
quill quill,
}, step1,
watch: { step2,
// , step3,
form: { step4
handler(){
this.updateTime++
},
deep:true
},
signupTime: function(val) {
const { form } = this
if (val) {
form.signUpStartTime = val[0];
form.signUpEndTime = val[1];
} else {
form.signUpStartTime = ''
form.signUpEndTime = ''
}
},
playTime: function(val) {
const { form } = this
if (val) {
form.playStartTime = val[0]
form.playEndTime = val[1]
} else {
form.playStartTime = ''
form.playEndTime = ''
}
}
}, },
mounted() { mounted() {
this.getData()
this.getSchool()
},
methods: {
getData() {
const { id } = this.form
id && this.$post(`${this.api.getContest}?contestId=${id}`).then(({ contest }) => {
this.signupTime = [contest.signUpStartTime, contest.signUpEndTime]
this.playTime = [contest.playStartTime, contest.playEndTime]
this.sponsorList = contest.sponsor.split(",")
this.undertakerList = contest.undertaker.split(",")
//
const fileList = contest.contestAnnexList
if (fileList) {
const files = []
fileList.map(e => {
files.push({
name: e.fileName,
url: e.filePath
})
})
this.fileList = files
} else {
contest.contestAnnexList = []
}
//
const ranges = contest.contestRangeList
if (ranges) {
const range = []
ranges.map(e => {
const item = [+e.provinceId]
e.cityId && item.push(+e.cityId)
e.schoolId && item.push(+e.schoolId)
range.push(item)
})
this.range = range
}
// name
const rangeName = contest.contestRangeRespList
if (rangeName) {
const range = []
rangeName.map(e => {
range.push(e.type ? (e.cityName || e.provinceName) : e.schoolName)
})
this.rangeName = range.join(',')
}
this.form = contest
}).catch(err => {})
},
//
showRange() {
this.rangeVisible = true
},
//
rangeChange(val, e) {
const checked = this.$refs.range.getCheckedNodes()
const name = []
const { rangeChecked } = this
checked.map(e => {
rangeChecked.find(n => n.value === e.value && n.label == e.label) || name.push(e.data) // push
})
this.rangeChecked.push(...name)
},
//
rangeViChange(e) {
//
if (e) {
this.rangeList = []
}
}, },
// methods: {
getSchool() { // loading
this.$get(this.api.querySchoolData).then(({ list }) => { showLoad() {
const result = [] this.loadIns = Loading.service()
list.map(e => {
e.value = e.schoolId
e.label = e.schoolName
e.leaf = true
result.push(e)
})
this.schools = result
}).catch(res => {})
},
//
beforeFilter() {
return false
}, },
// // loading
rangeSearch(el) { hideLoad() {
const val = el.target.value.trim() this.loadIns.close()
const checked = this.rangeChecked
if (!val) {
this.rangeList = []
return false
}
clearTimeout(this.rangeTimer)
this.rangeTimer = setTimeout(() => {
let result = this.schools.filter(e => {
if (e.label.includes(val)) {
e.disabled = !!checked.find(n => n.schoolId == e.schoolId)
return e
}
}) //
this.rangeList = result
}, 100)
}, },
closeRange(i) { //
this.rangeChecked.splice(i, 1) save(status, next = 0) {
this.$refs['step' + this.step].save(status, next, this.releaseType)
}, },
// //
rangeSubmit() { prev() {
// id //
const data = [] const { updateTime } = this.$refs['step' + this.step]
const checked = this.rangeChecked console.log("🚀 ~ file: index.vue:142 ~ back ~ updateTime", updateTime)
checked.map(e => { if (updateTime) {
data.push({ this.$confirm(`编辑的内容未保存,是否保存?`, '提示', {
provinceId: e.provinceId || '', type: 'warning'
cityId: e.cityId || '', }).then(() => {
schoolId: e.schoolId || '', this.save(0)
type: e.schoolId ? 0 : 1 }).catch(() => {
}) this.step--
})
this.form.contestRangeList = data
// name
const name = []
checked.map(e => {
name.push(e.label)
}) })
this.rangeName = name.join('、')
this.rangeVisible = false
},
handleExceed(files, fileList) {
util.warningMsg(`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`);
},
handleExceedAnnex(files, fileList) {
util.warningMsg(`当前限制选择 5 个文件,如需更换,请删除一个文件再重新选择!`);
},
uploadSuccess(res) {
const { coverUrl } = this.form
coverUrl && this.$del(`${this.api.fileDeletion}?keys=${coverUrl}`).then(res => {}).catch(res => {})
this.form.coverUrl = res.data.filesResult.fileUrl
},
uploadLgSuccess(res) {
const { carouselUrl } = this.form
carouselUrl && this.$del(`${this.api.fileDeletion}?keys=${carouselUrl}`).then(res => {}).catch(res => {})
this.form.carouselUrl = res.data.filesResult.fileUrl
},
//
uploadAnnexSuccess(res) {
const file = res.data.filesResult
const { id } = this.form
const data = {
contestId: id || '',
fileName: this.fileName,
filePath: file.fileUrl || file.fileId
}
this.form.contestAnnexList.push(data)
//
id && this.$post(this.api.saveAnnex, data).then(res => {}).catch(res => {})
},
//
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 10
if (!isLt2M) util.warningMsg('请上传小于10MB的附件!')
if (isLt2M) {
this.fileName = file.name
return true
} else { } else {
return false this.step--
} }
}, },
uploadError(err, file, fileList) { //
this.$message({ next(next, setupId, competitionId) {
message: "上传出错,请重试!", if (!next) {
type: "error", this.$router.push(`/match?page=${this.$store.state.matchPage}`)
center: true } else if (next === 2) {
}) if (setupId) this.setupId = setupId
}, if (competitionId) {
beforeRemove(file, fileList) { this.$router.push('add?id=' + competitionId)
return this.$confirm(`确定移除 ${file.name}`); this.id = competitionId
}, this.competitionId = competitionId
handleRemove(file, fileList) {
this.$del(`${this.api.fileDeletion}?keys=${this.form.coverUrl}`).then(res => {
this.form.coverUrl = ''
}).catch(res => {})
},
handleLgRemove(file, fileList) {
this.$del(`${this.api.fileDeletion}?keys=${this.form.carouselUrl}`).then(res => {
this.form.carouselUrl = ''
}).catch(res => {})
},
handleAnnexRemove(file, fileList) {
const { url, name } = file
url && this.$del(`${this.api.fileDeletion}?keys=${url}`).then(res => {}).catch(res => {})
const id = this.form.contestAnnexList.find(e => e.fileName === name).id
id && this.$post(`${this.api.deleteAnnex}?id=${id}`).then(res => {}).catch(res => {})
},
//
previewFile(item) {
const { filePath } = item
const suffix = filePath.substr(filePath.lastIndexOf('.') + 1)
window.open((util.isDoc(suffix) || suffix === 'pdf' ? 'https://view.officeapps.live.com/op/view.aspx?src=' : '') + item.filePath)
},
//
download(item) {
util.downloadFile(item.fileName, item.filePath)
},
//
save(status, frame) {
if (this.submiting) return false;
const { form } = this
form.sponsor = this.sponsorList.filter(d => d).join();
form.undertaker = this.undertakerList.filter(d => d).join();
if (form.competitionScope == 2 && !form.contestRangeList.length) return util.warningMsg('请选择区域、院校')
if (!form.name) return util.warningMsg("请填写竞赛名称");
if (status == 1) {
if (!form.sponsor) return util.warningMsg("请填写主办方");
if (!form.signUpStartTime) return util.warningMsg("请选择报名时间");
} }
let now = new Date().getTime(); this.step++
let signUpStartTime = new Date(form.signUpStartTime).getTime();
let signUpEndTime = new Date(form.signUpEndTime).getTime();
let playStartTime = new Date(form.playStartTime).getTime();
// if (signUpStartTime && now > signUpStartTime) return util.warningMsg("");
if (!form.playStartTime && status == 1) return util.warningMsg("请选择竞赛时间");
if (playStartTime && playStartTime < signUpEndTime) return util.warningMsg("竞赛时间不能早于报名结束时间");
if (!form.description && status == 1) return util.warningMsg("请填写竞赛详情");
this.submiting = true
form.publishStatus = status
form.isOpen = status ? 0 : 1 //
if (this.form.id) {
this.$post(this.api.editContest, form).then(res => {
this.updateTime = 1
this.submiting = false;
util.successMsg("修改成功");
frame || this.$router.push(`/match?page=${this.$store.state.matchPage}`) // frame1
})
.catch(err => {
this.submiting = false;
});
} else {
this.$post(this.api.addContest, form).then(res => {
this.submiting = false;
util.successMsg("创建成功");
frame || this.$router.push(`/match?page=${this.$store.state.matchPage}`) // frame1
})
.catch(err => {
this.submiting = false;
});
} }
}, },
//
toStep(i) {
if (this.id && !this.editing) this.step = i
},
// //
preview() { preview() {
util.local.set('match', this.form) util.local.set('match', this.$refs.step1.form)
window.open(this.$router.resolve('preview').href) window.open(this.$router.resolve('/matchPreview').href)
}, },
back() { back() {
const updateTime = this.updateTime
const { id } = this.form
// //
if ((id && updateTime > 1) || (!id && updateTime)) { const { updateTime } = this.$refs['step' + this.step]
console.log("🚀 ~ file: index.vue:142 ~ back ~ updateTime", updateTime)
if (this.step < 4 && updateTime) {
this.$confirm(`编辑的内容未保存,是否保存?`, '提示', { this.$confirm(`编辑的内容未保存,是否保存?`, '提示', {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
@ -606,231 +164,88 @@ export default {
this.backPage() this.backPage()
} }
}, },
backPage(){ backPage() {
this.$router.back() this.$router.push(`/match?page=${this.$store.state.matchPage}&platformSource=${this.$store.state.platformSource}`)
},
addSponsor() {
this.sponsorList.push("");
},
delSponsor(index) {
this.sponsorList.splice(index, 1);
},
addOrganizer() {
this.undertakerList.push("");
},
delOrganizer(index) {
this.undertakerList.splice(index, 1);
} }
} }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
$upload-width: 220px; .edit {
$upload-height: 140px; position: absolute;
$upload-lg-height: 150px; top: 30px;
/deep/ .avatar-uploader { right: 30px;
.el-upload {
position: relative;
width: $upload-width;
height: $upload-height;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
overflow: hidden;
&:hover {
border-color: #cb221c;
}
.uploader-default {
display: flex;
height: $upload-height;
flex-direction: column;
justify-content: center;
text-align: center;
background: rgba(0, 0, 0, 0.04);
i {
font-size: 20px;
font-weight: bold;
color: #8c939d;
}
p {
margin-top: 10px;
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
line-height: 1;
}
}
}
&.avatar-uploader-lg {
.el-upload {
width: 100%;
max-width: 960px;
height: $upload-lg-height;
.uploader-default {
height: $upload-lg-height;
}
}
}
.avatar {
display: block;
width: $upload-width;
height: $upload-height;
}
.avatar-lg {
display: block;
width: 100%;
height: $upload-lg-height;
}
.el-upload__tip {
margin-top: 0;
p {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
line-height: 1;
&:first-child {
margin-bottom: 5px;
}
}
}
}
/deep/ .d-inline-block {
width: 216px;
.el-select, .el-input {
width: 100%;
}
} }
.el-steps {
.inline-input { justify-content: center;
.input-wrap {
display: flex;
align-items: center;
margin-bottom: 10px;
.el-input {
display: inline-block;
width: 216px;
margin-right: 8px;
}
.remove {
width: 16px;
height: 16px;
background: url("../../../assets/img/close.png") 0 0/cover no-repeat;
cursor: pointer;
}
}
.add-btn {
margin-left: 32px;
}
} }
.steps {
.add-btn {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; &.pointer {
width: 216px; li {
line-height: 32px;
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
background-color: transparent;
border: 1px dashed rgba(0, 0, 0, 0.15);
border-radius: 4px;
cursor: pointer; cursor: pointer;
i {
margin-right: 8px;
font-size: 14px;
font-weight: bold;
}
}
/deep/.preview-dia {
.el-dialog__body {
padding: 0 0 20px;
background-color: #F3F6FA;
} }
}
.match {
.banner{
width: 100%;
height: 350px;
color: #fff;
background-size: 100% 350px;
background-repeat: no-repeat;
box-sizing: border-box;
} }
.match-inner { li {
width: 1000px; position: relative;
min-height: calc(100vh - 465px); margin-right: 100px;
padding: 30px 40px 20px; text-align: center;
margin: 40px auto 0;
background-color: #fff;
box-sizing: border-box;
} }
.l-title{ .circle {
display: flex; display: inline-flex;
justify-content: center;
align-items: center; align-items: center;
margin-bottom: 12px; width: 45px;
margin: 0 auto 10px;
line-height: 35px;
font-size: 18px; font-size: 18px;
color: #333; color: #333;
img{ background: #f9f9f9;
margin-right: 5px; border: 5px solid #e1e1e1;
} border-radius: 50%;
&:after {
content: '';
position: absolute;
left: 64px;
width: 146px;
height: 3px;
background: #e1e1e1;
}
}
.active {
.circle {
color: #fff;
border-color: #459ffb;
background: #007EFF;
} }
.title{ .text {
width: 67%; color: #007EFF;
margin: 0 auto;
font-size: 28px;
text-align: center;
color: #0B1D30;
} }
.info .meta{
padding: 16px 0;
font-size: 12px;
color: #999;
text-align: center;
} }
.texts { .done {
margin-bottom: 30px; .circle {
font-size: 14px; color: #fff;
line-height: 1.6; background: #9c86ff;
text-indent: 2em; border-color: #bbacff;
overflow: hidden; &:after {
/deep/img{ background: #bbacff;
max-width: 100%;
} }
} }
.files { .text {
li { color: #9178ff;
display: flex;
align-items: center;
margin: 10px 0;
} }
.file-name {
margin-right: 10px;
} }
.circle2:after {
left: 71px;
width: 147px;
} }
} .circle4:after {
.range-check {
display: inline-block;
margin: 0 0 10px 10px;
}
/deep/.range-cas {
.el-tag {
display: none; display: none;
} }
} }
.btns {
text-align: center;
}
</style> </style>

@ -0,0 +1,228 @@
<template>
<div>
<el-card shadow="hover" class="m-b-20">
<div>
<p class="m-b-20">比赛名称</p>
{{ form.stageName }}
</div>
</el-card>
<el-card shadow="hover" class="m-b-20">
<div>
<p class="m-b-20">比赛时间</p>
<div class="date-inputs">
起止时间
<div style="display: inline-flex;align-items: center;">
<el-date-picker
v-model="form.time"
type="datetimerange"
range-separator="~"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
@change="timeChange">
</el-date-picker>
<el-alert
style="width: auto;padding: 4px 16px;margin-left: 10px;"
:title="'(请设置在 ' + step1.playStartTime + ' ~ ' + step1.playEndTime + '间)'"
type="error"
:closable="false"
effect="dark">
</el-alert>
</div>
</div>
</div>
</el-card>
<el-card shadow="hover" class="mgr20 m-b-20">
<div>
<p class="m-b-20">课程</p>
<div class="inline-input">
<el-select v-model="form.cid" @change="courseChange">
<el-option
v-for="item in curriculumList"
:key="item.cid"
:label="item.curriculumName"
:value="item.cid">
</el-option>
</el-select>
</div>
</div>
</el-card>
<el-card shadow="hover" class="m-b-20">
<div class="flex-between m-b-20">
<span>实训项目</span>
<div style="display: inline-flex;">
<div>
<el-input placeholder="请输入项目名称" prefix-icon="el-icon-search" v-model.trim="keyword" clearable></el-input>
</div>
<el-button style="margin-left: 5px" type="primary" round>自定义实验项目</el-button>
</div>
</div>
<!-- 实训项目表格 -->
<el-table :data="projects" class="table" stripe header-align="center">
<el-table-column width="60" label="选择" align="center">
<template slot-scope="scope">
<el-radio v-model="form.projectId" :label="scope.row.projectId">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="auth" label="项目权限" align="center">
<template slot-scope="scope">
{{ permissionsKeys[scope.row.permissions] }}
</template>
</el-table-column>
<!-- <el-table-column prop="createUser" label="创建人" align="center"></el-table-column> -->
<el-table-column prop="founder" label="创建人" align="center">
<template slot-scope="scope">
{{ scope.row.createUser }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center"></el-table-column>
<!-- <el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="text" @click="showProject(scope.row)">查看</el-button>
</template>
</el-table-column> -->
</el-table>
<div class="pagination">
<el-pagination
background
:page-size="pageSize"
@current-change="handleCurrentChange"
layout="total,prev, pager, next"
:total="total"
></el-pagination>
</div>
</el-card>
<div style="text-align: center">
<el-button @click="back">返回</el-button>
<el-button type="primary" @click="save">保存</el-button>
</div>
</div>
</template>
<script>
import util from "@/libs/util";
export default {
props: ['form', 'step1'],
data() {
return {
curriculumList: [],
keyword: '',
projects: [],
page: 1,
pageSize: 5,
total: 0,
sysId: '',
permissionsKeys: ['练习', '考核', '竞赛'],
timeInvalid: false
};
},
watch: {
// ,
form: {
handler(){
this.updateTime++
},
deep:true
},
keyword: function(val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.initData();
}, 500);
}
},
mounted() {
this.getCourse()
},
methods: {
//
getCourse() {
this.$get(this.api.curriculumList).then(({ data }) => {
const list = data
this.curriculumList = data
if (list.length) this.form.cid = list[0].cid
this.courseChange()
}).catch(err => {});
},
//
courseChange() {
const { sysId, sysName } = this.curriculumList.find(e => e.cid == this.form.cid)
this.form.sysName = sysName
this.sysId = sysId
this.getProject()
},
//
getProject() {
this.$post(this.api.getProjectAssessmentByCompetition, {
pageNum: this.page,
pageSize: this.pageSize,
cid: this.form.cid,
projectName: this.keyword,
systemId: this.sysId,
permissions: 2
}).then(({ data }) => {
this.projects = data.records
this.total = data.total
}).catch(err => {});
},
initData() {
this.page = 1;
this.getProject();
},
handleCurrentChange(val) {
this.page = val;
this.getProject();
},
//
timeChange(val) {
if (val.length) {
const startTime = new Date(val[0])
const endTime = new Date(val[1])
const { playStartTime, playEndTime } = this.step1
if (startTime < new Date(playStartTime) || endTime > new Date(playEndTime)) {
this.timeInvalid = true
return util.warningMsg('设置的阶段比赛时间必须要在第一步设置的竞赛时间范围内,请重新设置。')
}
this.timeInvalid = false
const { form, curStep } = this.$parent
for (const i in form) {
//
if (i !== curStep) {
const time1 = new Date(form[i].startTime)
const time2 = new Date(form[i].endTime)
if ((startTime >= time1 && startTime <= time2) || (endTime >= time1 && endTime <= time2)) {
util.warningMsg('请注意,所设置的时间与已设置的阶段时间重合。')
break
}
}
}
}
},
//
save() {
const { form } = this
if (!form.time.length) return util.warningMsg('请选择比赛时间')
if (this.timeInvalid) return util.warningMsg('设置的阶段比赛时间必须要在第一步设置的竞赛时间范围内,请重新设置。')
if (!form.cid) return util.warningMsg('请选择课程')
if (!form.projectId) return util.warningMsg('请选择项目')
const { systemId, projectName } = this.projects.find(e => e.projectId == form.projectId)
form.systemId = systemId
form.projectName = projectName
form.startTime = form.time[0]
form.endTime = form.time[1]
this.$emit('hideSet', this.form)
},
//
back() {
this.$emit('hideSet')
}
}
};
</script>
<style scoped lang="scss">
</style>

@ -0,0 +1,824 @@
<template>
<div>
<div class="page">
<div class="page-content">
<div class="p-title">大赛信息</div>
<el-form label-width="170px" label-suffix=":" size="small" :disabled="!editing && form.id !== ''">
<el-form-item label="竞赛封面(选填)">
<el-upload
class="avatar-uploader"
accept=".jpg,.png,.jpeg,.gif"
:on-remove="handleRemove"
:on-error="uploadError"
:on-success="uploadSuccess"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:action="this.api.fileupload"
:headers="headers"
name="file"
>
<img v-if="form.coverUrl" :src="form.coverUrl" class="avatar">
<div class="uploader-default" v-else>
<i class="el-icon-plus"></i>
<p>上传封面</p>
</div>
<div slot="tip" class="el-upload__tip">
<p>展示宽度为220高度140JPG/PNG/GIF3MB以内</p>
</div>
</el-upload>
</el-form-item>
<el-form-item label="竞赛封面长图(选填)">
<el-upload
class="avatar-uploader avatar-uploader-lg"
accept=".jpg,.png,.jpeg,.gif"
:on-remove="handleLgRemove"
:on-error="uploadError"
:on-success="uploadLgSuccess"
:before-remove="beforeRemove"
:limit="1" :on-exceed="handleExceed"
:action="this.api.fileupload"
:headers="headers"
name="file"
>
<img v-if="form.carouselUrl" :src="form.carouselUrl" class="avatar-lg">
<div class="uploader-default" v-else>
<i class="el-icon-plus"></i>
<p>上传封面</p>
</div>
<div slot="tip" class="el-upload__tip">
<p>展示宽度为1920高度300JPG/PNG/GIF3MB以内</p>
</div>
</el-upload>
</el-form-item>
<el-form-item class="req" label="竞赛名称">
<div class="d-inline-block">
<el-input placeholder="请输入竞赛名称" v-model="form.name" clearable></el-input>
</div>
</el-form-item>
<el-form-item class="req" label="主办方">
<div class="inline-input">
<div class="input-wrap" v-for="(item,index) in sponsorList" :key="index">
<el-input placeholder="主办方名称" v-model="sponsorList[index]"></el-input>
<i v-if="sponsorList.length > 1" class="remove" @click="delSponsor(index)"></i>
<button v-if="index == 0" class="add-btn" type="button" :disabled="!editing && form.id !== ''" @click="addSponsor">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</div>
</div>
</el-form-item>
<el-form-item label="承办方(选填)">
<div class="inline-input">
<div class="input-wrap" v-for="(item,index) in undertakerList" :key="index">
<el-input placeholder="承办方名称" v-model="undertakerList[index]"></el-input>
<i v-if="undertakerList.length > 1" class="remove" @click="delOrganizer(index)"></i>
<button v-if="index == 0" class="add-btn" type="button" :disabled="!editing && form.id !== ''" @click="addOrganizer">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</div>
</div>
<button v-if="!undertakerList.length" class="add-btn" type="button" @click="addOrganizer">
<i class="el-icon-plus"></i>
<span>添加</span>
</button>
</el-form-item>
<el-form-item class="req" label="报名时间">
<el-date-picker v-model="signupTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
<el-form-item class="req" label="竞赛时间">
<el-date-picker v-model="playTime" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="pickerOptions"></el-date-picker>
</el-form-item>
<el-form-item class="req" label="比赛范围">
<div>
<el-radio v-model="form.competitionScope" :label="0">本校内</el-radio>
</div>
<div>
<el-radio v-model="form.competitionScope" :label="1">全平台</el-radio>
</div>
<div>
<el-radio v-model="form.competitionScope" :label="2">指定区域院校</el-radio>
<el-button v-if="form.competitionScope === 2" type="primary" size="mini" @click="showRange">选择院校</el-button>
<span style="margin-left: 20px">{{ rangeName }}</span>
</div>
</el-form-item>
<el-form-item class="req" label="竞赛类型">
<el-radio v-model="form.completeCompetitionSetup.competitionType" :label="0">个人赛</el-radio>
<el-radio v-model="form.completeCompetitionSetup.competitionType" :label="1">团队赛</el-radio>
</el-form-item>
<el-form-item class="req" v-if="!form.completeCompetitionSetup.competitionType" label="报名人数上限">
<div class="input-center">
<el-input placeholder="请输入人数" v-model.number="form.completeCompetitionSetup.quantityLimit" type="number"></el-input>
</div>
</el-form-item>
<template v-if="form.completeCompetitionSetup.competitionType">
<el-form-item class="req" label="报名团队数上限">
<div class="input-center">
<el-input placeholder="请输入团队数" v-model.number="form.completeCompetitionSetup.quantityLimit" type="number"></el-input>
</div>
</el-form-item>
<el-form-item class="req" label="团队人数">
<div class="input-center" style="width: 250px;">
<el-input v-model.number="form.completeCompetitionSetup.minTeamSize" type="number"></el-input>
<el-input style="margin-left: 5px;" v-model.number="form.completeCompetitionSetup.maxTeamSize" type="number"></el-input> /
</div>
</el-form-item>
</template>
<el-form-item class="req" label="报名邀请码">
<div class="input-center" style="width: 550px;">
<el-radio v-model="form.completeCompetitionSetup.isNeedCode" :label="0">不需要</el-radio>
<el-radio v-model="form.completeCompetitionSetup.isNeedCode" :label="1">需要</el-radio>
<el-input style="width: 250px" placeholder="请输入4位邀请码或点击随机生成" v-model="form.completeCompetitionSetup.invitationCode" :disabled="form.completeCompetitionSetup.isNeedCode === 0"></el-input>
<el-button v-if="form.completeCompetitionSetup.isNeedCode === 1" @click="randomInv">随机</el-button>
</div>
</el-form-item>
<el-form-item class="req" label="竞赛详情">
<quill :border="true" v-model="form.description" :height="400" />
</el-form-item>
<el-form-item label="附件">
<el-upload
:on-remove="handleAnnexRemove"
:on-error="uploadError"
:before-upload="beforeUpload"
:on-success="uploadAnnexSuccess"
:limit="5"
:on-exceed="handleExceedAnnex"
:action="this.api.fileupload"
:headers="headers"
:file-list="fileList"
name="file"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
<p>支持扩展名.rar .zip .doc .docx .pdf .jpg...</p>
</div>
</el-upload>
</el-form-item>
</el-form>
</div>
</div>
<el-dialog title="请勾选院校" :visible.sync="rangeVisible" width="580px" custom-class="range-dia" :close-on-click-modal="false">
<div class="range-wrap">
<el-cascader
ref="range"
class="range-cas"
key="range"
v-model="range"
:props="props"
:show-all-levels="false"
clearable
filterable
:before-filter="beforeFilter"
:options="rangeList"
@change="rangeChange"
@visible-change="rangeViChange"
@input.native="rangeSearch"></el-cascader>
<el-tag
v-for="(tag, i) in rangeChecked"
:key="tag.value"
class="range-check"
closable
:disable-transitions="false"
@close="val => closeRange(i)">
{{tag.label}}
</el-tag>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="rangeVisible = false"> </el-button>
<el-button size="small" type="primary" @click="rangeSubmit"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import util from "@/libs/util";
import quill from "@/components/quill";
import Setting from "@/setting";
export default {
props: ['editing'],
data() {
const that = this
return {
headers: {
token: util.local.get(Setting.tokenKey)
},
form: {
id: this.$route.query.id || '',
platformSource: 1, // (0:1)
isOpen: 0, // (0 1 0)
name: '',
sponsor: '',
undertaker: '',
competitionScope: 0, // (0: 1: 2:)
competitionRangeList: [], //
competitionAnnexList: [], //
coverUrl: '',
carouselUrl: '',
publishStatus: 0,
signUpStartTime: '',
signUpEndTime: '',
playStartTime: '',
playEndTime: '',
description: '',
releaseType: 0,
completeCompetitionSetup: {
competitionType: 0,
invitationCode: '',
isNeedCode: 0,
maxTeamSize: '',
minTeamSize: '',
quantityLimit: '',
}
},
fileName: '',
signupTime: [],
playTime: [],
sponsorList: [""],
undertakerList: [""],
fileList: [],
pickerOptions: {
disabledDate: time => {
return time.getTime() < new Date().getTime() - 86400000;
}
},
rangeVisible: false,
range: [],
rangeInit: [],
rangeName: '',
rangeChecked: [],
schools: [],
rangeList: [],
rangeTimer: null,
//
props: {
multiple: true,
checkStrictly: true,
lazy: true,
lazyLoad (node, resolve) {
//
const input = document.querySelector('.el-cascader__search-input')
const checked = that.rangeChecked
if (input && input.value.trim()) {
const val = input.value.trim()
return that.schools.filter(e => e.label.includes(val))
}
const { level, value } = node
//
if (!level) {
that.$get(that.api.queryProvince).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.provinceId
e.label = e.provinceName
e.children = []
e.disabled = !!checked.find(n => n.provinceId == e.provinceId && !n.cityId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else if (level === 1) {
//
that.$get(that.api.queryCity, {
provinceId: value
}).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.cityId
e.label = e.cityName
e.children = []
e.disabled = !!checked.find(n => n.cityId == e.cityId && n.provinceId == e.provinceId && !n.schoolId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else if (level === 2) {
//
that.$get(that.api.getSchoolsByProvince, {
provinceId: node.data.provinceId,
cityId: value,
schoolName: ''
}).then(({ list }) => {
const data = []
list.map(e => {
e.value = e.schoolId
e.label = e.schoolName
e.leaf = true
e.disabled = !!checked.find(n => n.schoolId == e.schoolId && n.cityId == e.cityId && n.provinceId == e.provinceId)
data.push(e)
})
resolve(data)
}).catch(res => {})
} else {
resolve([])
}
}
},
submiting: false,
updateTime: 0,
};
},
components: {
quill
},
watch: {
// ,
form: {
handler(){
this.updateTime++
},
deep:true
},
signupTime: function(val) {
const { form } = this
if (val) {
form.signUpStartTime = val[0];
form.signUpEndTime = val[1];
} else {
form.signUpStartTime = ''
form.signUpEndTime = ''
}
},
playTime: function(val) {
const { form } = this
if (val) {
form.playStartTime = val[0]
form.playEndTime = val[1]
} else {
form.playStartTime = ''
form.playEndTime = ''
}
}
},
mounted() {
this.getData()
this.getSchool()
},
methods: {
getData() {
const { id } = this.form
id && this.$post(`${this.api.getCompetition}?competitionId=${id}`).then(({ competition }) => {
this.$parent.publishStatus = competition.publishStatus
this.$parent.releaseType = competition.releaseType
this.$parent.setupId = competition.completeCompetitionSetup.setupId
if (competition.signUpStartTime) this.signupTime = [competition.signUpStartTime, competition.signUpEndTime]
if (competition.playStartTime) this.playTime = [competition.playStartTime, competition.playEndTime]
this.sponsorList = competition.sponsor.split(",")
this.undertakerList = competition.undertaker.split(",")
//
const fileList = competition.competitionAnnexList
if (fileList) {
const files = []
fileList.map(e => {
files.push({
name: e.fileName,
url: e.filePath
})
})
this.fileList = files
} else {
competition.competitionAnnexList = []
}
//
const ranges = competition.competitionRangeList
if (ranges) {
const range = []
ranges.map(e => {
const item = [+e.provinceId]
e.cityId && item.push(+e.cityId)
e.schoolId && item.push(+e.schoolId)
range.push(item)
})
this.range = range
}
// name
const rangeName = competition.competitionRangeRespList
if (rangeName) {
const range = []
rangeName.map(e => {
range.push(e.type ? (e.cityName || e.provinceName) : e.schoolName)
})
this.rangeName = range.join(',')
}
this.form = competition
this.$nextTick(() => {
this.updateTime = 0
})
}).catch(err => {})
},
//
showRange() {
this.rangeVisible = true
},
//
rangeChange(val, e) {
const checked = this.$refs.range.getCheckedNodes()
const name = []
const { rangeChecked } = this
checked.map(e => {
rangeChecked.find(n => n.value === e.value && n.label == e.label) || name.push(e.data) // push
})
this.rangeChecked.push(...name)
},
//
rangeViChange(e) {
//
if (e) {
this.rangeList = []
}
},
//
getSchool() {
this.$get(this.api.querySchoolData).then(({ list }) => {
const result = []
list.map(e => {
e.value = e.schoolId
e.label = e.schoolName
e.leaf = true
result.push(e)
})
this.schools = result
}).catch(res => {})
},
//
beforeFilter() {
return false
},
//
rangeSearch(el) {
const val = el.target.value.trim()
const checked = this.rangeChecked
if (!val) {
this.rangeList = []
return false
}
clearTimeout(this.rangeTimer)
this.rangeTimer = setTimeout(() => {
let result = this.schools.filter(e => {
if (e.label.includes(val)) {
e.disabled = !!checked.find(n => n.schoolId == e.schoolId)
return e
}
}) //
this.rangeList = result
}, 100)
},
closeRange(i) {
this.rangeChecked.splice(i, 1)
},
//
rangeSubmit() {
// id
const data = []
const checked = this.rangeChecked
checked.map(e => {
data.push({
provinceId: e.provinceId || '',
cityId: e.cityId || '',
schoolId: e.schoolId || '',
type: e.schoolId ? 0 : 1
})
})
this.form.competitionRangeList = data
// name
const name = []
checked.map(e => {
name.push(e.label)
})
this.rangeName = name.join('、')
this.rangeVisible = false
},
handleExceed(files, fileList) {
util.warningMsg(`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`);
},
handleExceedAnnex(files, fileList) {
util.warningMsg(`当前限制选择 5 个文件,如需更换,请删除一个文件再重新选择!`);
},
uploadSuccess(res) {
const { coverUrl } = this.form
coverUrl && this.$del(`${this.api.fileDeletion}?keys=${coverUrl}`).then(res => {}).catch(res => {})
this.form.coverUrl = res.data.filesResult.fileUrl
},
uploadLgSuccess(res) {
const { carouselUrl } = this.form
carouselUrl && this.$del(`${this.api.fileDeletion}?keys=${carouselUrl}`).then(res => {}).catch(res => {})
this.form.carouselUrl = res.data.filesResult.fileUrl
},
//
uploadAnnexSuccess(res) {
const file = res.data.filesResult
const { id } = this.form
const data = {
contestId: id || '',
fileName: this.fileName,
filePath: file.fileUrl || file.fileId
}
this.form.competitionAnnexList.push(data)
//
id && this.$post(this.api.saveAnnex, data).then(res => {}).catch(res => {})
},
//
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 10
if (!isLt2M) util.warningMsg('请上传小于10MB的附件!')
if (isLt2M) {
this.fileName = file.name
return true
} else {
return false
}
},
uploadError(err, file, fileList) {
this.$message({
message: "上传出错,请重试!",
type: "error",
center: true
})
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${file.name}`);
},
handleRemove(file, fileList) {
this.$del(`${this.api.fileDeletion}?keys=${this.form.coverUrl}`).then(res => {
this.form.coverUrl = ''
}).catch(res => {})
},
handleLgRemove(file, fileList) {
this.$del(`${this.api.fileDeletion}?keys=${this.form.carouselUrl}`).then(res => {
this.form.carouselUrl = ''
}).catch(res => {})
},
handleAnnexRemove(file, fileList) {
const { url, name } = file
url && this.$del(`${this.api.fileDeletion}?keys=${url}`).then(res => {}).catch(res => {})
const id = this.form.competitionAnnexList.find(e => e.fileName === name).id
id && this.$post(`${this.api.deleteAnnex}?id=${id}`).then(res => {}).catch(res => {})
},
//
randomInv() {
let result = ''
for (let i = 0; i < 4; i++) {
result += Math.floor(Math.random() * 10);
}
this.form.completeCompetitionSetup.invitationCode = result
},
/**
* @description 提交
* status 0草稿1发布
* next 0返回1不做任何操作2下一步
* releaseType 发布类型0发布信息1完整比赛
* */
save(status, next = 0, releaseType = 0) {
const { form } = this
form.sponsor = this.sponsorList.filter(d => d).join();
form.undertaker = this.undertakerList.filter(d => d).join();
if (!form.name) return util.warningMsg("请填写竞赛名称");
//
// if (status) {
if (form.competitionScope == 2 && !form.competitionRangeList.length) return util.warningMsg('请选择区域、院校')
if (!form.sponsor) return util.warningMsg("请填写主办方");
if (!form.signUpStartTime) return util.warningMsg("请选择报名时间");
let now = new Date().getTime();
let signUpStartTime = new Date(form.signUpStartTime).getTime();
let signUpEndTime = new Date(form.signUpEndTime).getTime();
let playStartTime = new Date(form.playStartTime).getTime();
// if (signUpStartTime && now > signUpStartTime) return util.warningMsg("");
if (!form.playStartTime) return util.warningMsg("请选择竞赛时间");
if (playStartTime && signUpEndTime && playStartTime < signUpEndTime) return util.warningMsg("竞赛时间不能早于报名结束时间");
const { competitionType, quantityLimit, minTeamSize, maxTeamSize, isNeedCode, invitationCode } = form.completeCompetitionSetup
//
if (competitionType) {
if (quantityLimit === '') return util.warningMsg('请填写报名团队数上限')
if (quantityLimit < 0) return util.warningMsg('报名团队数上限不得小于0')
if (minTeamSize === '') return util.warningMsg('请填写团队人数下限')
if (minTeamSize < 2) return util.warningMsg('团队人数下限不得小于2')
if (maxTeamSize === '') return util.warningMsg('请填写团队人数上限')
if (minTeamSize > maxTeamSize) return util.warningMsg('团队人数上限不得小于下限')
} else { //
if (quantityLimit === '') return util.warningMsg('请填写报名人数上限')
if (quantityLimit < 0) return util.warningMsg('报名人数上限不得小于0')
}
if (isNeedCode && (!invitationCode || invitationCode.length !== 4)) return util.warningMsg('请填写四位数邀请码')
if (!form.description) return util.warningMsg("请填写竞赛详情");
// }
this.$parent.showLoad()
form.publishStatus = status
form.ztOpen = status ? 0 : 1 //
form.releaseType = releaseType
form.id = this.$route.query.id
if (form.id) {
this.$post(this.api.editCompetition, form).then(res => {
this.$parent.hideLoad()
util.successMsg("修改成功");
this.$emit('next', next)
}).catch(err => {
this.$parent.hideLoad()
});
} else {
this.$post(this.api.addCompetition, form).then(({ competitionId, setupId }) => {
this.$parent.hideLoad()
util.successMsg("创建成功");
this.$emit('next', next, setupId, competitionId)
}).catch(err => {
this.$parent.hideLoad()
});
}
},
//
preview() {
util.local.set('match', this.form)
window.open(this.$router.resolve('/matchPreview').href)
},
addSponsor() {
this.sponsorList.push("");
},
delSponsor(index) {
this.sponsorList.splice(index, 1);
},
addOrganizer() {
this.undertakerList.push("");
},
delOrganizer(index) {
this.undertakerList.splice(index, 1);
}
}
};
</script>
<style scoped lang="scss">
$upload-width: 220px;
$upload-height: 140px;
$upload-lg-height: 150px;
/deep/ .avatar-uploader {
.el-upload {
position: relative;
width: $upload-width;
height: $upload-height;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
overflow: hidden;
&:hover {
border-color: #cb221c;
}
.uploader-default {
display: flex;
height: $upload-height;
flex-direction: column;
justify-content: center;
text-align: center;
background: rgba(0, 0, 0, 0.04);
i {
font-size: 20px;
font-weight: bold;
color: #8c939d;
}
p {
margin-top: 10px;
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
line-height: 1;
}
}
}
&.avatar-uploader-lg {
.el-upload {
width: 100%;
max-width: 960px;
height: $upload-lg-height;
.uploader-default {
height: $upload-lg-height;
}
}
}
.avatar {
display: block;
width: $upload-width;
height: $upload-height;
}
.avatar-lg {
display: block;
width: 100%;
height: $upload-lg-height;
}
.el-upload__tip {
margin-top: 0;
p {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
line-height: 1;
&:first-child {
margin-bottom: 5px;
}
}
}
}
/deep/ .d-inline-block {
width: 216px;
.el-select, .el-input {
width: 100%;
}
}
.inline-input {
.input-wrap {
display: flex;
align-items: center;
margin-bottom: 10px;
.el-input {
display: inline-block;
width: 216px;
margin-right: 8px;
}
.remove {
width: 16px;
height: 16px;
background: url("../../../assets/img/close.png") 0 0/cover no-repeat;
cursor: pointer;
}
}
.add-btn {
margin-left: 32px;
}
}
.add-btn {
display: flex;
justify-content: center;
align-items: center;
width: 216px;
line-height: 32px;
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
background-color: transparent;
border: 1px dashed rgba(0, 0, 0, 0.15);
border-radius: 4px;
cursor: pointer;
i {
margin-right: 8px;
font-size: 14px;
font-weight: bold;
}
}
.range-check {
display: inline-block;
margin: 0 0 10px 10px;
}
/deep/.range-cas {
.el-tag {
display: none;
}
}
.input-center {
display: flex;
align-items: center;
width: 216px;
white-space: nowrap;
.el-input {
margin-right: 5px;
}
}
.el-steps {
justify-content: center;
}
/deep/.req {
.el-form-item__label {
&:before {
content: '*';
margin-right: 5px;
font-size: 18px;
vertical-align: middle;
color: #f00;
}
}
}
</style>

@ -0,0 +1,427 @@
<template>
<div>
<div class="page">
<div class="page-content">
<div class="p-title">赛程与规则设置</div>
<el-form :model="form" :rules="validRules" label-width="170px" label-suffix=":" size="small" :disabled="!editing && id">
<el-form-item label="竞赛类型">
{{ step1.completeCompetitionSetup.competitionType ? '团队赛(' + step1.completeCompetitionSetup.minTeamSize + '-' + step1.completeCompetitionSetup.maxTeamSize + '人/队)' : '个人赛' }} <span class="tips">如需修改请返回上一步</span>
</el-form-item>
<el-form-item prop="rule" label="赛制">
<el-radio v-for="(rule, i) in rules" :key="i" v-model="form.rule" :label="rule.id">{{ rule.name }}</el-radio>
<p class="tips">积分赛包含多个竞赛阶段每个阶段的成绩都包含在最终总成绩里最后一轮结束后总成绩排名靠前的参赛者得到获奖资格</p>
<p class="tips">淘汰赛包含多个竞赛阶段每个阶段结束后之后只有部分参赛者能晋级下一阶段晋级最后一轮且在最后一轮排名靠前的参赛者得到获奖资格</p>
<p class="tips">单项赛仅包含一个竞赛阶段单项的成绩排名即为最终排名排名靠前的参赛者得到获奖资格</p>
<p class="tips">系统默认排名规则优先按分数排名分数高则排名靠前分数相同则按用时排名用时短则排名靠前</p>
</el-form-item>
<template v-if="form.rule !== 2">
<el-form-item prop="stageNum" label="阶段数量">
<div class="input-center">
<el-select v-model="form.stageNum" @change="stageChange">
<el-option v-for="i in 10" :key="i" :label="i" :value="i"></el-option>
</el-select>
</div>
<div v-if="step1.completeCompetitionSetup.competitionType" class="tips">
(团队赛是否限制队内每个成员只能参加一个阶段赛项
<el-radio v-model="form.teamLimit" :label="1"></el-radio>
<el-radio v-model="form.teamLimit" :label="0"></el-radio>
)
</div>
</el-form-item>
<el-form-item prop="resultCalculationMethod" label="总成绩计算方式">
<el-radio v-model="form.resultCalculationMethod" :label="0">各阶段成绩加权求和</el-radio>
<el-radio v-model="form.resultCalculationMethod" :label="1">各阶段成绩直接求和</el-radio>
<el-radio v-model="form.resultCalculationMethod" :label="2">各阶段成绩取平均值</el-radio>
<p class="tips">若选择加权求和则需要设置每个阶段成绩所占权重且权重总和须为100%</p>
</el-form-item>
<el-form-item prop="stageNum" label="阶段设置">
<div v-for="(item, i) in form.competitionStageList" :key="i" class="step-set">
<div class="line">
{{ serials[i] }}阶段 <el-input v-model="item.stageName" clearable placeholder="请输入阶段名称,如“初赛”" style="width: 200px"></el-input>
</div>
<div class="line">
<span class="req">*</span>
比赛方式
<el-radio v-for="(method, i) in methods" :key="i" v-model="item.method" :label="method.id">{{ method.name }}</el-radio>
</div>
<div v-if="step1.completeCompetitionSetup.competitionType" class="line">
<span class="req">*</span>
团队参赛人数限制
<el-radio v-model="item.teamNumLimitOpt" :label="0">不限制</el-radio>
<el-radio v-model="item.teamNumLimitOpt" :label="1">自定义</el-radio>
<el-input v-model.number="item.teamNumLimit" type="number" style="width: 150px;" :disabled="item.teamNumLimitOpt === 0"></el-input>
<span class="tips">可限制本阶段单个团队的出战人数</span>
</div>
<div v-if="step1.completeCompetitionSetup.competitionType" class="line">
<span class="req">*</span>
团队成绩计算方式
<el-radio v-for="(j, i) in teamCalculationMethods" :key="i" v-model="item.teamCalculationMethod" :label="j.id">{{ j.name }}</el-radio>
<span class="tips">可设置本阶段的团队取分规则</span>
</div>
<div v-if="form.rule === 1 && i !== form.competitionStageList.length - 1" class="line" style="display: flex;">
<p>
<span class="req">*</span>晋级规则
</p>
<div>
<div class="line">
本阶段成绩排名前
<el-input v-model.number="item.peopleLimit" type="number" style="width: 100px"></el-input>
可晋级下一阶段比赛
</div>
<div class="line">
本阶段成绩排名前
<el-input v-model.number="item.percentageLimit" type="number" style="width: 100px"></el-input>
%可晋级下一阶段比赛
</div>
<div>
本阶段成绩
<el-select v-model="item.operator" style="width: 80px;margin-right: 10px">
<el-option v-for="i in operators" :key="i" :label="i" :value="i"></el-option>
</el-select>
<el-input v-model="item.score" type="number" style="width: 100px"></el-input>
可晋级下一阶段比赛
</div>
</div>
</div>
<div v-if="!form.rule" class="line">
<span class="req">*</span>
占总成绩权重
<el-input v-model.number="item.pointWeight" type="number" :disabled="form.resultCalculationMethod != 0" style="width: 150px;"></el-input> %
</div>
<div class="line">
<span class="req">*</span>
成绩公布时间
阶段比赛结束后
<el-input v-model="item.resultAnnouncementTime" type="number" style="width: 120px"></el-input>
小时公布阶段比赛成绩
</div>
<div class="line">
<span class="req">*</span>
是否公布成绩详情
<el-radio v-model="item.resultsDetails" :label="0"></el-radio>
<el-radio v-model="item.resultsDetails" :label="1"></el-radio>
<p class="tips">若选择则公布成绩详情竞赛结束后参赛者可查看自己的比赛成绩得分详情</p>
<p class="tips">若选择则不公布成绩详情参赛者只能知晓自己的分数及排名不能查看得分详情</p>
</div>
</div>
</el-form-item>
</template>
<template v-else>
<el-form-item prop="stageNum" label="规则设置">
<div class="step-set">
<div class="line">
<span class="req">*</span>
比赛方式
<el-radio v-for="(method, i) in methods" :key="i" v-model="form.competitionStageList[0].method" :label="method.id">{{ method.name }}</el-radio>
</div>
<div v-if="step1.completeCompetitionSetup.competitionType" class="line">
<span class="req">*</span>
团队成绩计算方式
<el-radio v-for="(j, i) in teamCalculationMethods" :key="i" v-model="form.competitionStageList[0].teamCalculationMethod" :label="j.id">{{ j.name }}</el-radio>
<span class="tips">可设置本阶段的团队取分规则</span>
</div>
</div>
</el-form-item>
<el-form-item prop="resultAnnouncementTime" label="成绩公布时间">
阶段比赛结束后
<el-input v-model="form.competitionStageList[0].resultAnnouncementTime" type="number" style="width: 120px"></el-input>
小时公布阶段比赛成绩
</el-form-item>
<el-form-item prop="resultsDetails" label="是否公布成绩详情">
<el-radio v-model="form.competitionStageList[0].resultsDetails" :label="0"></el-radio>
<el-radio v-model="form.competitionStageList[0].resultsDetails" :label="1"></el-radio>
<p class="tips">若选择则公布成绩详情竞赛结束后参赛者可查看自己的比赛成绩得分详情</p>
<p class="tips">若选择则不公布成绩详情参赛者只能知晓自己的分数及排名不能查看得分详情</p>
</el-form-item>
</template>
</el-form>
</div>
</div>
</div>
</template>
<script>
import util from "@/libs/util";
import quill from "@/components/quill";
import Const from '@/const/match'
export default {
props: ['setupId', 'editing'],
data() {
return {
id: this.$route.query.id,
updateTime: 0,
step1: this.$parent.$refs.step1.form,
serials: ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'],
form: {
resultCalculationMethod: 0,
rule: 0,
stageNum: 3,
teamLimit: 1,
competitionStageList: [
{
method: 0,
number: 1,
pointWeight: '',
stageName: '',
teamCalculationMethod: 0,
peopleLimit: '',
percentageLimit: '',
scoreLimit: '',
operator: '>',
score: '',
teamNumLimit: '',
teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
},
{
method: 0,
number: 2,
pointWeight: '',
stageName: '',
teamCalculationMethod: 0,
peopleLimit: '',
percentageLimit: '',
scoreLimit: '',
operator: '>',
score: '',
teamNumLimit: '',
teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
},
{
method: 0,
number: 3,
pointWeight: '',
stageName: '',
teamCalculationMethod: 0,
peopleLimit: '',
percentageLimit: '',
scoreLimit: '',
operator: '>',
score: '',
teamNumLimit: '',
teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
}
]
},
validRules: {
rule: [
{ required: true, trigger: 'change' }
],
stageNum: [
{ required: true, trigger: 'change' }
],
resultCalculationMethod: [
{ required: true, trigger: 'change' }
],
resultAnnouncementTime: [
{ required: true, message: '请输入成绩公布时间', trigger: 'change' }
],
resultsDetails: [
{ required: true, trigger: 'change' }
],
},
ruleForm: {},
rules: Const.rules,
methods: Const.methods,
teamCalculationMethods: Const.teamCalculationMethods,
operators: ['>', '>=', '=', '<', '<=']
};
},
watch: {
// ,
form: {
handler(){
this.updateTime++
},
deep:true
},
},
mounted() {
this.ruleForm = JSON.parse(JSON.stringify(this.form.competitionStageList[0]))
this.id && this.getData()
this.step1 || this.getStep1() //
},
methods: {
getData() {
this.$get(this.api.getCompetitionRule, {
competitionId: this.id
}).then(res => {
const rule = res.competitionRule
if (rule) {
rule.competitionStageList.map(e => {
const limit = e.scoreLimit
if (limit) {
e.operator = limit.replace(/\d+/, '')
const num = limit.replace(/\D+/, '')
if (num) e.score = num
}
e.teamNumLimitOpt = e.teamNumLimit ? 1 : 0
e.teamCalculationMethod = +e.teamCalculationMethod
})
this.form = rule
}
this.$nextTick(() => {
this.updateTime = 0
})
}).catch(res => {})
},
//
getStep1() {
this.$post(`${this.api.getCompetition}?competitionId=${this.id}`).then(({ competition }) => {
this.step1 = competition
const { minTeamSize, maxTeamSize, competitionType } = this.step1.completeCompetitionSetup
competitionType && minTeamSize !== '' && maxTeamSize !== '' && minTeamSize == maxTeamSize && this.teamCalculationMethods.pop() //
}).catch(err => {})
},
//
stageChange(val) {
const list = []
for (let i = 1; i <= val; i++) {
const form = JSON.parse(JSON.stringify(this.ruleForm))
form.number = i
list.push(form)
}
this.form.competitionStageList = list
},
//
save(status, next = 0) {
const { form } = this
const { rule } = form
let invalid = 0
let pointWeight = 0
const { step1 } = this
for (const e of form.competitionStageList) {
if (rule !== 2 && !e.stageName) {
invalid = 1
util.errorMsg('请输入阶段名称')
break
}
const { competitionType, maxTeamSize } = step1.completeCompetitionSetup // 01
// rule: 012
//
if (!rule) {
//
if (form.resultCalculationMethod == 0 && e.pointWeight === '') {
invalid = 1
util.errorMsg('请输入权重')
break
}
pointWeight += e.pointWeight //
} else if (rule === 1) { //
if (e.score !== '') e.scoreLimit = e.operator + e.score
}
if (rule !== 2 && competitionType && e.teamNumLimitOpt) {
if (e.teamNumLimit === '') {
invalid = 1
util.errorMsg('请输入团队参数人数限制')
break
} else if (e.teamNumLimit < 0) {
invalid = 1
util.errorMsg('团队参数人数不得小于0')
break
} else if (maxTeamSize !== '' && e.teamNumLimit > maxTeamSize) {
invalid = 1
util.errorMsg('团队参数人数不得大于团队人数上限')
break
}
}
if (!e.resultsDetails) {
e.resultAnnouncementTime = +e.resultAnnouncementTime
if (e.resultAnnouncementTime === '') {
invalid = 1
util.errorMsg('请填写成绩公布时间')
break
}
if (e.resultAnnouncementTime < 0) {
invalid = 1
util.errorMsg('成绩公布时间不得小于0')
break
}
}
}
if (invalid) return
if (form.resultCalculationMethod == 0 && !rule && pointWeight > 0 && pointWeight !== 100) return util.errorMsg('权重须等于100,请重新输入')
this.$parent.showLoad()
if (form.ruleId) {
this.$post(this.api.editCompetitionRule, form).then(res => {
this.$parent.hideLoad()
util.successMsg("修改成功");
this.$emit('next', next)
}).catch(err => {
this.$parent.hideLoad()
})
} else {
form.setupId = this.setupId
this.$post(this.api.addCompetitionRule, form).then(res => {
this.$parent.hideLoad()
util.successMsg("创建成功");
this.$emit('next', next)
}).catch(err => {
this.$parent.hideLoad()
})
}
},
}
};
</script>
<style scoped lang="scss">
/deep/ .d-inline-block {
width: 216px;
.el-select, .el-input {
width: 100%;
}
}
.inline-input {
.input-wrap {
display: flex;
align-items: center;
margin-bottom: 10px;
.el-input {
display: inline-block;
width: 216px;
margin-right: 8px;
}
.remove {
width: 16px;
height: 16px;
background: url("../../../assets/img/close.png") 0 0/cover no-repeat;
cursor: pointer;
}
}
.add-btn {
margin-left: 32px;
}
}
.input-center {
display: flex;
align-items: center;
width: 216px;
white-space: nowrap;
.el-input {
margin-right: 5px;
}
}
.step-set {
padding: 15px;
background-color: #fbfbfb;
}
.tips {
font-size: 13px;
color: #959595;
}
.req {
color: #f00;
}
.line {
margin-bottom: 10px;
}
</style>

@ -0,0 +1,290 @@
<template>
<div>
<div v-show="!setVisible" class="page">
<div class="page-content">
<div class="p-title">比赛内容设置</div>
<el-form label-width="170px" label-suffix=":" size="small" :disabled="!editing && id">
<div v-for="(item, i) in form" :key="i" class="step">
<div class="title">
<span>{{ item.stageName }}{{ nums[i] }}阶段 | {{ methods.find(e => e.id === item.method).name }} </span>
<el-button v-if="item.method !== 2" type="primary" @click="toSet(i)">设置</el-button>
</div>
<el-form-item class="req" prop="time" label="比赛时间">
<span v-if="item.method !== 2 && item.startTime">{{ item.startTime + ' ' + item.endTime }}</span>
<div style="display: flex;align-items: center;" v-if="item.method === 2">
<el-date-picker
v-model="item.time"
type="datetimerange"
range-separator="~"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
@change="timeChange">
</el-date-picker>
<el-alert
style="width: auto;padding: 0px 16px;margin-left: 10px;"
:title="'(请设置在 ' + step1.playStartTime + ' ~ ' + step1.playEndTime + '间)'"
type="error"
:closable="false"
effect="dark">
</el-alert>
</div>
</el-form-item>
<template v-if="item.method === 2">
<el-form-item class="req" label="比赛地点">
<el-input v-model="item.offlineAddress" style="width: 80%"></el-input>
</el-form-item>
<el-form-item class="req" label="比赛内容">
<el-input v-model="item.contentDescription" type="textarea" style="width: 80%"></el-input>
</el-form-item>
<el-form-item class="req" label="评分规则">
<el-input v-model="item.scoreRule" type="textarea" style="width: 80%"></el-input>
</el-form-item>
</template>
<template v-else>
<el-form-item class="req" prop="cid" label="课程系统">
{{ item.systemName }}
</el-form-item>
<el-form-item class="req" prop="assessmentId" label="已选择考核">
{{ item.projectName }}
</el-form-item>
<el-form-item class="req" prop="resultAnnouncementTime" label="比赛地点">
<div class="line">
<el-checkbox v-model="item.onlineButton">线上</el-checkbox>
<el-input v-model="item.onlineAddress" clearable placeholder="请输入比赛网址" :disabled="!item.onlineButton" style="width: 400px;margin-left: 10px"></el-input>
</div>
<div class="line">
<el-checkbox v-model="item.offlineButton">线下</el-checkbox>
<el-input v-model="item.offlineAddress" clearable placeholder="请输入地址" :disabled="!item.offlineButton" style="width: 400px;margin-left: 10px"></el-input>
</div>
</el-form-item>
</template>
</div>
</el-form>
</div>
</div>
<set v-if="setVisible" :form.sync="form[curStep]" :step1.sync="step1" @hideSet="hideSet" />
</div>
</template>
<script>
import util from "@/libs/util";
import set from './set'
export default {
props: ['setupId', 'competitionId', 'editing'],
data() {
return {
id: this.$route.query.id,
updateTime: 0,
step1: this.$parent.$refs.step1.form,
nums: ['一', '二', '三'],
methods: [
{
id: 0,
name: '实操'
},
{
id: 1,
name: '理论'
},
{
id: 2,
name: '线下'
}
],
originForm: {
cid: '',
contentDescription: '',
endTime: '',
scoreRule: '',
stageId: '',
startTime: '',
systemId: '',
offlineAddress: '',
offlineButton: 0,
onlineAddress: '',
onlineButton: 0,
time: []
},
form: [],
setVisible: false,
curStep: 0,
timeInvalid: false
};
},
components: {
set
},
watch: {
// ,
form: {
handler(){
this.updateTime++
},
deep:true
},
},
mounted() {
this.handleForm()
},
methods: {
// form
handleForm() {
// id
this.$post(`${this.api.queryCompetitionStageBySetupId}?setupId=${this.setupId}`).then(res => {
res.competitionStages.map(e => {
const form = e.competitionContent || JSON.parse(JSON.stringify(this.originForm))
if (form.startTime) form.time = [form.startTime, form.endTime]
form.offlineButton = !!form.offlineButton
form.onlineButton = !!form.onlineButton
form.method = e.method
form.stageId = e.stageId
form.stageName = e.stageName
this.form.push(form)
})
this.$nextTick(() => {
this.updateTime = 0
})
}).catch(res => {})
},
//
toSet(i) {
this.curStep = i
this.$parent.showBtns = false
this.setVisible = true
},
//
hideSet(form) {
if (form) this.form[this.curStep] = form
this.setVisible = false
this.$parent.showBtns = true
},
//
timeChange(val) {
if (val.length) {
const startTime = new Date(val[0])
const endTime = new Date(val[1])
const { playStartTime, playEndTime } = this.step1
if (startTime < new Date(playStartTime) || endTime > new Date(playEndTime)) {
this.timeInvalid = true
return util.warningMsg('设置的阶段比赛时间必须要在第一步设置的竞赛时间范围内,请重新设置。')
}
this.timeInvalid = false
const { form, curStep } = this
for (const i in form) {
//
if (i !== curStep) {
const time1 = new Date(form[i].startTime)
const time2 = new Date(form[i].endTime)
if ((startTime >= time1 && startTime <= time2) || (endTime >= time1 && endTime <= time2)) {
util.warningMsg('请注意,所设置的时间与已设置的阶段时间重合。')
break
}
}
}
}
},
//
publish() {
this.competitionId && this.$post(`${this.api.publishCompetition}?competitionId=${this.competitionId}&publishStatus=1`).then(res => {}).catch(err => {})
},
//
save(status, next = 0) {
const { form } = this
let invalid = 0
if (this.timeInvalid) return util.errorMsg('设置的阶段比赛时间必须要在第一步设置的竞赛时间范围内,请重新设置。')
for (const e of form) {
if (!e.time.length) {
invalid = 1
util.errorMsg('请选择比赛时间')
break
}
e.startTime = e.time[0]
e.endTime = e.time[1]
if (e.method !== 2 && !e.cid) {
invalid = 1
util.errorMsg('请选择课程')
break
}
if (e.method === 2) { // 线
if (!e.offlineAddress) {
invalid = 1
util.errorMsg('请输入比赛地点')
break
}
if (!e.contentDescription) {
invalid = 1
util.errorMsg('请输入比赛内容')
break
}
if (!e.scoreRule) {
invalid = 1
util.errorMsg('请输入评分规则')
break
}
} else {
if (e.onlineButton && !e.onlineAddress) {
invalid = 1
util.errorMsg('请输入线上地点')
break
}
if (e.offlineButton && !e.offlineAddress) {
invalid = 1
util.errorMsg('请输入线下地点')
break
}
}
}
if (invalid) return
//
for (const e of form) {
e.offlineButton = e.offlineButton ? 1 : 0
e.onlineButton = e.onlineButton ? 1 : 0
}
this.$parent.showLoad()
this.$post(this.api[form[0].contentId ? 'editCompetitionContent' : 'addCompetitionContent'], {
competitionContents: form
}).then(res => {
//
!form[0].contentId && status && this.publish(status)
this.$parent.hideLoad()
util.successMsg((status ? '发布' : '保存') + '成功')
this.$emit('next', next)
}).catch(err => {
this.$parent.hideLoad()
})
},
}
};
</script>
<style scoped lang="scss">
.step {
padding-bottom: 10px;
background-color: #f9f9f9;
.title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
margin-bottom: 10px;
background-color: #ededed;
}
}
.line {
margin-bottom: 10px;
}
/deep/.req {
.el-form-item__label {
&:before {
content: '*';
margin-right: 5px;
font-size: 18px;
vertical-align: middle;
color: #f00;
}
}
}
</style>

@ -0,0 +1,53 @@
<template>
<div class="wrap">
<div class="modal">
<el-steps :space="200" :active="4" finish-status="success">
<el-step title="大赛信息填写"></el-step>
<el-step title="赛程与规则设置"></el-step>
<el-step title="比赛内容设置"></el-step>
<el-step title="发布"></el-step>
</el-steps>
<h1>大赛已发布</h1>
<div class="btns">
<el-button type="primary" @click="back">确定</el-button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
};
},
mounted() {
},
methods: {
back() {
this.$router.push(`/match?page=${this.$store.state.matchPage}`)
}
}
};
</script>
<style scoped lang="scss">
.wrap {
height: calc(100vh - 300px);
background-color: #fefefe;
}
.modal {
width: 500px;
padding-top: 150px;
margin: 0 auto ;
h1 {
margin: 20px;
text-align: center;
}
.btns {
text-align: center;
}
}
</style>

@ -58,7 +58,7 @@
{{ scope.row.publishStatus ? '已发布' : '未发布' }} {{ scope.row.publishStatus ? '已发布' : '未发布' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="time" label="竞赛时间" align="center" width="270"> <el-table-column prop="time" label="竞赛时间" align="center" width="300">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.playStartTime }} ~ {{ scope.row.playEndTime }} {{ scope.row.playStartTime }} ~ {{ scope.row.playEndTime }}
</template> </template>
@ -190,7 +190,7 @@ export default {
]), ]),
getList() { getList() {
const { form } = this const { form } = this
this.$post(this.api.contestPageConditionQueryByOccupationlab, { this.$post(this.api.competitionPageConditionQueryByOccupationlab, {
pageNum: this.page, pageNum: this.page,
pageSize: this.pageSize, pageSize: this.pageSize,
competitionScope: 0, // (0: 1: 2.) competitionScope: 0, // (0: 1: 2.)
@ -240,7 +240,7 @@ export default {
type: "warning" type: "warning"
}) })
.then(() => { .then(() => {
this.$post(`${this.api.deleteContest}?contestId=${row.id}`).then(res => { this.$post(`${this.api.batchDeleteCompetition}?competitionIds=${row.id}`).then(res => {
util.successMsg("删除成功"); util.successMsg("删除成功");
this.getData(); this.getData();
}).catch(res => { }).catch(res => {
@ -249,27 +249,6 @@ export default {
.catch(() => { .catch(() => {
}); });
}, },
delAllData() {
if (this.multipleSelection.length != "") {
this.$confirm("此删除操作不可逆,是否确认删除选中项?", "提示", {
type: "warning"
}).then(() => {
let delList = this.multipleSelection.map(item => {
return item.id;
});
this.$post(this.api.deleteContest, delList).then(res => {
this.multipleSelection = [];
this.$refs.table.clearSelection();
util.successMsg("删除成功");
this.getData();
}).catch(res => {
});
}).catch(() => {
});
} else {
util.errorMsg("请先选择数据 !");
}
},
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val; this.multipleSelection = val;
}, },
@ -279,8 +258,8 @@ export default {
this.$confirm("确定要删除吗?", "提示", { this.$confirm("确定要删除吗?", "提示", {
type: "warning" type: "warning"
}).then(() => { }).then(() => {
let ids = this.multipleSelection.map(i => 'contestIds=' + i.id); let ids = this.multipleSelection.map(i => 'competitionIds=' + i.id);
this.$post(`${this.api.batchDeleteContest}?${ids.join('&')}`).then(res => { this.$post(`${this.api.batchDeleteCompetition}?${ids.join('&')}`).then(res => {
this.getData(); this.getData();
this.$message.success("删除成功"); this.$message.success("删除成功");
this.$refs.table.clearSelection() this.$refs.table.clearSelection()
@ -303,8 +282,8 @@ export default {
return date; return date;
}, },
disable(val, row) { disable(val, row) {
this.$post(this.api.disabledEvents, { this.$post(this.api.disabledEventsCompetition, {
contestId: row.id, competitionId: row.id,
isOpen: val, isOpen: val,
type: 1 // (01) type: 1 // (01)
}).then(res => { }).then(res => {
@ -318,8 +297,8 @@ export default {
this.$confirm('是否发布该大赛?', '提示', { this.$confirm('是否发布该大赛?', '提示', {
type: 'success' type: 'success'
}).then(() => { }).then(() => {
this.$post(this.api.disabledEvents, { this.$post(this.api.disabledEventsCompetition, {
contestId: row.id, competitionId: row.id,
isOpen: val, isOpen: val,
type: 1 // (01) type: 1 // (01)
}).then(res => { }).then(res => {

@ -10,14 +10,11 @@
<div class="tabs"> <div class="tabs">
<a class="item" v-for="(item,index) in tabs" :key="index" :class="{active: index == active}" @click="tabChange(index)">{{ item }}</a> <a class="item" v-for="(item,index) in tabs" :key="index" :class="{active: index == active}" @click="tabChange(index)">{{ item }}</a>
</div> </div>
<!-- 大赛详情 --> <MatchDetail v-if="active == 'tab1'" ref="detail" />
<MatchDetail v-if="active == 'first'" ref="detail" /> <MatchArch v-if="active == 'tab2'" />
<!-- 竞赛进展 --> <MatchProgress v-else-if="active == 'tab3'" />
<MatchProgress v-else-if="active == 'second'" /> <notice v-else-if="active == 'tab4'" />
<!-- 公告通知 --> <MatchSignup v-else-if="active == 'tab5'" />
<notice v-else-if="active == 'third'" />
<!-- 报名人员 -->
<MatchSignup v-else />
</div> </div>
</div> </div>
@ -26,34 +23,28 @@
<script> <script>
import Setting from "@/setting"; import Setting from "@/setting";
import MatchDetail from "../add"; import MatchDetail from "../add";
import MatchArch from "./matchArch";
import MatchProgress from "./matchProgress"; import MatchProgress from "./matchProgress";
import notice from "./notice"; import notice from "./notice";
import MatchSignup from "./matchSignup"; import MatchSignup from "./matchSignup";
import { mapState } from "vuex";
export default { export default {
name: "matchManage", name: "matchManage",
data() { data() {
return { return {
name: this.$route.query.name, name: this.$route.query.name,
active: this.$route.query.tab || "first", active: this.$route.query.tab || "tab1",
tabs: { tabs: {
first: "大赛详情", tab1: "大赛详情",
second: "竞赛进展", tab2: "大赛成绩管理",
third: "公告通知", tab3: "竞赛进展",
fourth: "报名人员" tab4: "公告通知",
tab5: "报名人员"
} }
}; };
}, },
computed: {
...mapState("user", [
'page'
]),
...mapState('auth', [
'btns'
])
},
components: { components: {
MatchDetail, MatchDetail,
MatchArch,
MatchProgress, MatchProgress,
notice, notice,
MatchSignup MatchSignup
@ -63,16 +54,16 @@ export default {
}, },
methods: { methods: {
initTabs() { initTabs() {
const { btns } = this const btns = this.$store.state.btns
const tab1 = btns.includes('/match/list:管理:大赛详情') const tab1 = btns.includes('/match:管理:大赛详情')
const tab2 = btns.includes('/match/list:管理:竞赛进展') const tab2 = btns.includes('/match:管理:竞赛进展')
const tab3 = btns.includes('/match/list:管理:公告通知') const tab3 = btns.includes('/match:管理:公告通知')
const tab4 = btns.includes('/match/list:管理:报名人员') const tab4 = btns.includes('/match:管理:报名人员')
tab1 || delete this.tabs.first tab1 || delete this.tabs.tab1
tab2 || delete this.tabs.second tab2 || delete this.tabs.tab2
tab3 || delete this.tabs.third tab3 || delete this.tabs.tab3
tab4 || delete this.tabs.fourth tab4 || delete this.tabs.tab4
const type = this.$route.query.tab const type = this.$route.query.tab
const keys = Object.keys(this.tabs) const keys = Object.keys(this.tabs)
this.active = keys.includes(type) ? type : keys[0] this.active = keys.includes(type) ? type : keys[0]
@ -83,9 +74,9 @@ export default {
// confirm // confirm
handleSave(i) { handleSave(i) {
// //
if (this.active === 'first') { if (this.active === 'tab1') {
const detail = this.$refs.detail const detail = this.$refs.detail
if (detail.updateTime > 1) { if (detail.step < 4 && detail.$refs['step' + detail.step].updateTime) {
this.$confirm(`编辑的内容未保存,是否保存并且发布?`, '提示', { this.$confirm(`编辑的内容未保存,是否保存并且发布?`, '提示', {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
@ -102,16 +93,20 @@ export default {
return true return true
} }
}, },
//
backPage(){ backPage(){
this.$router.push(`/match?page=${this.page}`) this.$router.push(`/match?page=${this.$store.state.matchPage}&platformSource=${this.$store.state.platformSource}`)
}, },
// tab
tabSwitch(i) { tabSwitch(i) {
this.active = i this.active = i
this.$router.push(`/match/manage?id=${this.$route.query.id}&tab=${i}&name=${this.name}`) this.$router.push(`/match/manage?id=${this.$route.query.id}&tab=${i}&name=${this.name}`)
}, },
// tab
backOrTab(i) { backOrTab(i) {
i ? this.tabSwitch(i) : this.backPage() i ? this.tabSwitch(i) : this.backPage()
}, },
// tab
tabChange(i) { tabChange(i) {
this.handleSave(i) && this.tabSwitch(i) this.handleSave(i) && this.tabSwitch(i)
} }

@ -0,0 +1,109 @@
<template>
<!-- 报名人员 -->
<div class="page-content" style="padding: 24px">
<el-collapse v-model="curStep">
<el-collapse-item v-for="(item, i) in list" :key="i" :title="item.stageName" :name="item.stageId">
<div class="line">
<span>比赛方式{{ item.methodName }}</span>
<span>比赛形式{{ item.competitionType ? '团队赛' : '个人赛' }}</span>
<span>赛制{{ item.ruleName }}</span>
<span>状态{{ item.status }}</span>
<span>竞赛起止时间{{ item.startTime + ' ~ ' + item.endTime }}</span>
<div>
<el-button type="primary" @click="toRank(item, i)">排名</el-button>
<el-button @click="toArch(item)">成绩管理</el-button>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script>
import util from "@/libs/util";
import Const from '@/const/match'
export default {
name: "matchArch",
data() {
return {
id: +this.$route.query.id,
list: [],
form: {},
timer: null,
curStep: []
};
},
mounted() {
this.$once('hook:beforeDestroy', function() {
clearInterval(this.timer)
})
this.getData()
},
methods: {
getData() {
this.$post(`${this.api.getCompetition}?competitionId=${this.id}`).then(({ competition }) => {
this.form = competition
this.timer = setInterval(this.handleStatus, 1000)
this.getArch()
}).catch(err => {})
},
getArch() {
this.$post(this.api.detailsOfCompetitionStage, {
pageNum: 1,
pageSize: 100,
contestId: this.id,
}).then(({ page }) => {
const list = page.records
list.map(e => {
e.methodName = Const.methods.find(n => n.id === e.method).name
e.ruleName = Const.rules.find(n => n.id === e.rule).name
})
this.curStep = list.map(e => e.stageId)
this.list = list
}).catch(res => {});
},
//
handleStatus() {
const now = new Date()
this.form.competitionStage.map(e => {
const startTime = new Date(e.startTime)
const endTime = new Date(e.endTime)
const item = this.list.find(n => n.stageId == e.stageId)
if (item) {
if (now < startTime) {
this.$set(item, 'status', '未开始')
} else if (now >= startTime && now <= endTime) {
this.$set(item, 'status', '比赛中')
} else if (now > endTime) {
this.$set(item, 'status', '已完成')
}
}
})
console.log("🚀 ~ file: matchArch.vue:87 ~ handleStatus ~ this.status", this.list)
},
//
toRank(row, i) {
this.$router.push(`/matchRank?id=${this.id}&stageId=${row.stageId}&index=${i}&method=${row.method}&competitionType=${row.competitionType}&rule=${row.rule}`)
},
//
toArch(row) {
this.$router.push(`/matchArchList?id=${this.id}&stageId=${row.stageId}&method=${row.method}&competitionType=${row.competitionType}`)
}
}
};
</script>
<style lang="scss" scoped>
/deep/.el-collapse-item__header {
font-size: 16px;
color: #333;
}
.line {
display: flex;
justify-content: space-around;
align-items: center;
span {
margin-right: 30px;
}
}
</style>

@ -0,0 +1,381 @@
<template>
<div>
<el-card shadow="hover" class="m-b-20 head-card">
<div class="flex-between m-b-20">
<el-page-header @back="$router.back()" content="成绩管理"></el-page-header>
</div>
</el-card>
<el-card v-if="method != 2" shadow="hover" class="m-b-20">
<div class="stat">
<div class="nums">
<div class="item">
<p class="name">实验总人数</p>
<p class="val">{{ totalNumber }}</p>
</div>
<div class="item">
<p class="name">实验平均分</p>
<p class="val">{{ avgScore }}</p>
</div>
</div>
<div class="chart" id="chart"></div>
</div>
</el-card>
<el-card shadow="hover" class="m-b-20">
<div class="flex-between m-b-20">
<div>
<el-input
size="small"
placeholder="请输入学校/学生姓名"
prefix-icon="el-icon-search"
v-model="keyword" clearable
style="width: 300px"
></el-input>
</div>
<div>
<el-button v-if="method == 2" type="primary" @click="batchImport">上传成绩</el-button>
<el-button type="primary" @click="delAllData">批量删除</el-button>
<el-button type="primary" @click="exportData">导出</el-button>
</div>
</div>
<el-table :data="list" class="table" ref="table" stripe header-align="center" @selection-change="handleSelectionChange" row-key="reportId">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center">
<template slot-scope="scope">
{{ scope.$index + (page - 1) * pageSize + 1 }}
</template>
</el-table-column>
<el-table-column prop="schoolName" label="学校" min-width="150" align="center"></el-table-column>
<el-table-column v-if="competitionType" prop="teamName" label="团队名称" min-width="100" align="center"></el-table-column>
<el-table-column prop="userName" label="学生姓名" min-width="100" align="center"></el-table-column>
<el-table-column prop="workNumber" label="学号" min-width="100" align="center"></el-table-column>
<el-table-column prop="score" label="分数" width="90" align="center"></el-table-column>
<el-table-column prop="timeSum" label="耗时" width="90" align="center">
<template slot-scope="scope">
{{ scope.row.timeSum }}min
</template>
</el-table-column>
<el-table-column prop="submitTime" label="提交时间" min-width="150" align="center">
</el-table-column>
<el-table-column label="操作" align="center" width="160">
<template slot-scope="scope">
<el-button type="text" @click="show(scope.row)">查看成绩报告</el-button>
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background layout="total, prev, pager, next" :total="total" @current-change="handleCurrentChange" :current-page="page">
</el-pagination>
</div>
</el-card>
<el-dialog title="批量导入" :visible.sync="importVisible" width="24%" :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
ref="importStaff"
name="file"
accept=".xls,.xlsx"
:on-remove="handleRemove"
:on-error="uploadError"
:on-success="uploadSuccess"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:action="this.api.importStaff"
:file-list="uploadList"
:headers="headers"
>
<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 size="small" @click="importVisible = false"> </el-button>
<el-button size="small" type="primary" @click="uploadSure"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Setting from "@/setting";
import util from "@/libs/util";
import * as echarts from "echarts";
import axios from 'axios';
export default {
data() {
return {
id: +this.$route.query.id,
stageId: +this.$route.query.stageId,
method: +this.$route.query.method,
competitionType: +this.$route.query.competitionType,
keyword: "",
searchTimer: null,
list: [],
multipleSelection: [],
page: 1,
pageSize: 10,
total: 0,
totalNumber: 0, //
avgScore: 0, //
importVisible: false,
uploadList: [],
uploadFaild: false,
exportCode: '',
headers: {
token: sessionStorage.getItem("token")
},
statData: {}
};
},
watch: {
keyword: function(val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.initData();
}, 500);
}
},
mounted() {
this.getData()
},
methods: {
getData() {
this.$post(this.api.stageGradeManagementList, {
pageNum: this.page,
pageSize: this.pageSize,
competitionId: this.id,
keyWord: this.keyword,
stageId: this.stageId,
isNakadai: 1
}).then(({ data, page }) => {
this.total = page.total
this.list = page.records
this.statData = data
this.avgScore = (+data.avgScore).toFixed(2)
this.totalNumber = data.totalNumber
this.getChart()
}).catch(res => {})
},
initData() {
this.page = 1
this.getData()
},
//
show(row) {
this.$router.push(`/matchReport?reportId=${row.reportId}`)
},
// ()
exportData() {
// id
if (this.multipleSelection.length) {
const ids = this.multipleSelection.map(e => e.reportId)
url += `&ids=${ids.toString()}`
}
axios.post(this.api.exportExperimentalResultsInBatch, this.list, {
responseType: 'blob'
}).then((res) => {
util.downloadFileDirect(`赛事成绩.xls`,new Blob([res.data]))
}).catch(res => {})
},
handleDelete(row) { //
this.$confirm("确定要删除吗?", "提示", {
type: "warning"
}).then(() => {
this.$post(this.api.batchDeleteContestGrade, {
ids: [this.method == 2 ? row.scoreId : row.reportId],
competitionId: this.id,
stageId: row.stageId
}).then(res => {
util.successMsg("删除成功");
this.getData();
}).catch(res => {
});
}).catch(() => {
});
},
delAllData() { //
if (this.multipleSelection.length) {
this.$confirm("该项目下的所有成绩报告将会删除,是否继续?", "提示", {
type: "warning"
}).then(() => {
let ids = this.multipleSelection.map(item => {
return item.reportId;
});
this.$post(this.api.deleteExperimentalReport, ids).then(res => {
this.multipleSelection = [];
this.$refs.table.clearSelection();
util.successMsg("删除成功");
this.getData();
}).catch(res => {
});
}).catch(() => {
});
} else {
util.errorMsg("请先选择数据 !");
}
},
handleSelectionChange(val) { //
this.multipleSelection = val;
},
handleCurrentChange(val) { //
this.page = val;
this.getData();
},
getChart() { // 线
const data = []
const { statData } = this
for (let i = 1; i <= 10; i++) {
data.push(statData['num' + i])
}
let myChart = echarts.init(document.getElementById("chart"));
myChart.setOption({
title: { text: "实验分数分布图" },
tooltip: {},
xAxis: {
name: "分数",
type: "category",
boundaryGap: false,
interval: 10,
data: ["0-10", "11-20", "21-30", "31-40", "41-50", "51-60", "61-70", "71-80", "81-90", "91-100"]
},
yAxis: {
name: "人数",
type: "value",
interval: 1
},
series: [{
data,
type: "line",
areaStyle: {},
color: ["#8191fd"]
}]
});
},
//
batchImport() {
this.importVisible = true
this.uploadList = []
this.uploadFaild = false
},
//
download() {
location.href = this.api.gradeImport
},
//
handleExceed(files, fileList) {
util.warningMsg(
`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`
)
},
//
showFaild() {
location.href = `${this.api.performanceExportFailure}?exportCode=${this.exportCode}`
},
uploadSuccess(res, file, fileList) {
this.uploadFaild = false
if (res.status === 200) {
if (res.data.exportCode) {
this.exportCode = res.data.exportCode
this.uploadFaild = true
util.errorMsg(`本次上传有${res.data.failureNum}个错误信息录入`)
}
} else {
res.message ? util.errorMsg(res.message) : util.errorMsg("上传失败,请检查数据")
}
},
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.studentType = 1
this.keyWord = ''
this.$refs.orgTree.setCurrentKey(null)
this.getOrg()
}
}
};
</script>
<style lang="scss" scoped>
/deep/ .head-card {
.el-card__body {
padding-bottom: 0px;
.el-tabs__header {
margin-bottom: 1px;
.el-tabs__nav-wrap::after {
display: none;
}
.el-tabs__item {
font-size: 18px;
}
}
}
}
.stat {
display: flex;
.nums {
display: flex;
align-items: center;
margin-right: 20px;
.item:nth-child(1) {
background-image: url('../../../assets/img/total.png');
}
.item:nth-child(2) {
background-image: url('../../../assets/img/avg.png');
}
.item {
width: 300px;
padding: 30px 30px;
margin: 0 10px;
box-sizing: border-box;
border-radius: 8px;
background-size: 100% 100%;
background-repeat: no-repeat;
p {
font-size: 18px;
color: #ffffff;
}
.val {
margin-top: 10px;
color: #ffffff;
font-size: 36px;
}
}
}
.chart {
flex: 1;
height: 300px;
}
}
</style>

@ -0,0 +1,562 @@
<template>
<div>
<el-card shadow="hover" class="m-b-20 head-card">
<div class="flex-between">
<el-page-header @back="$router.back()" content="参赛信息与成绩"></el-page-header>
</div>
</el-card>
<el-card shadow="hover" class="m-b-20">
<table v-if="form.completeCompetitionSetup.competitionType" class="table m-b-20">
<tr>
<th width="150">团队名称</th>
<td>
<el-input :disabled="!editing" v-model="info.team.teamName"></el-input>
</td>
<th width="150">团队邀请码</th>
<td>
<el-input :disabled="!editing" v-model="info.team.invitationCode"></el-input>
</td>
</tr>
</table>
<div v-if="form.completeCompetitionSetup.competitionType && status < 4" class="m-b-20 text-center">
<el-button type="primary" @click="edit(1)">{{ editing ? '保存' : '编辑' }}</el-button>
</div>
<table class="table">
<tr>
<th width="150">姓名</th>
<td>{{ info.person.userName }}</td>
</tr>
<tr>
<th>学号</th>
<td>{{ info.person.workNumber }}</td>
</tr>
<tr>
<th>学校</th>
<td>{{ info.person.schoolName }}</td>
</tr>
<template v-if="form.completeCompetitionSetup.competitionType">
<template>
<tr>
<th>队长</th>
<td>{{ info.caption.userName }}{{ info.caption.schoolName && ',' + info.caption.schoolName }}{{ info.caption.workNumber && ',' + info.caption.workNumber }}</td>
</tr>
<tr>
<th>团队成员</th>
<td>
<el-tag v-for="(item, i) in info.teamDetail" :key="i" style="margin-right: 5px">{{ item.userName }}</el-tag>
</td>
</tr>
</template>
</template>
<tr>
<th width="130">指导老师</th>
<td>
<div class="plus">
<i class="el-icon-circle-plus-outline icon" @click="addAdvisor"></i>
</div>
<div v-for="(item, i) in info.teamInstructors" :key="i" class="line">
<el-input placeholder="请输入姓名" v-model="item.name" clearable size="mini" :disabled="!item.edit"></el-input>
<el-input placeholder="请输入职务" maxlength="10" v-model="item.position" clearable size="mini" :disabled="!item.edit"></el-input>
<el-input placeholder="请输入手机号" maxlength="11" v-model="item.phone" clearable size="mini" :disabled="!item.edit"></el-input>
<template v-if="status < 5">
<i v-if="item.edit" class="el-icon-check icon" @click="submitAdvisor(item)"></i>
<i v-else class="el-icon-edit icon" @click="editAdvisor(item)"></i>
<i class="el-icon-delete icon" @click="delAdvisor(item, i)"></i>
</template>
</div>
</td>
</tr>
<tr>
<th>竞赛阶段</th>
<td>
<table class="table tc">
<tr>
<th width="80">序号</th>
<th>赛项阶段名称</th>
<template v-if="form.completeCompetitionSetup.competitionType">
<th width="110">参赛人数限制</th>
<th>允许参赛人员</th>
<th v-if="info.team.captain === 0 && form.rule === 0">总分</th>
</template>
<th>竞赛成绩</th>
</tr>
<template v-if="info.stages.length">
<tr v-for="(item, i) in info.stages" :key="i">
<td>{{ i + 1 }}</td>
<td>{{ item.stageName }}</td>
<template v-if="form.completeCompetitionSetup.competitionType">
<td>{{ item.teamNumLimit || '不限制' }}</td>
<td>{{ item.teamParticipantIds || '无' }}</td>
<td v-if="info.team.captain === 0 && form.rule === 0 && !i" :rowspan="info.stages.length">{{ info.totalScore }}</td>
</template>
<td>
<span v-if="item.score" class="m-r-10">分数{{item.score}}</span>
<el-button type="text" :disabled="item.resultsDetails === 1 || (form.completeCompetitionSetup.competitionType && !item.reportId) || (form.completeCompetitionSetup.competitionType === 0 && !item.reportId)" @click="show(item)">查看成绩详情</el-button>
</td>
</tr>
</template>
<tr v-else>
<td colspan="6">暂无数据</td>
</tr>
</table>
<el-alert
v-if="form.completeCompetitionSetup.competitionType"
style="margin-top: 10px;"
:title="'注:请团长(团队创建人)设置各阶段参赛成员,只有被选择的允许参赛成员可进入对应阶段比赛' + (info.teamLimit ? ',每个团队成员只能参加一个赛项阶段' : '') + '!'"
type="warning"
show-icon>
</el-alert>
</td>
</tr>
</table>
<template v-if="form.completeCompetitionSetup.competitionType">
<div class="l-title m-t-20">团队成员</div>
<div class="flex-center">
<p>队长{{ info.caption.userName }}</p>
<el-button type="primary" @click="transfer">转让队长</el-button>
</div>
<el-table :data="info.teamDetail" stripe header-align="center">
<el-table-column prop="userName" label="成员姓名" min-width="100" align="center"></el-table-column>
<el-table-column prop="schoolName" label="学校" min-width="100" align="center"></el-table-column>
<el-table-column prop="workNumber" label="学号" min-width="100" align="center"></el-table-column>
<el-table-column prop="createTime" label="加入时间" width="180" align="center"></el-table-column>
<el-table-column label="操作" align="center" width="160">
<template slot-scope="scope">
<el-button v-if="scope.row.captain" type="text" @click="removeLine(scope.row)">踢出团队</el-button>
</template>
</el-table-column>
</el-table>
</template>
</el-card>
<el-dialog title="选择参赛成员" :visible.sync="transferVisible" :close-on-click-modal="false" width="400px">
<template v-for="(item, i) in info.teamDetail">
<el-radio v-if="item.captain" :key="i" v-model="checkedPlayer" :label="item.teamId">{{ item.userName }}</el-radio>
</template>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="transferSubmit">确定</el-button>
<el-button size="small" @click="transferVisible = false">取消</el-button>
</span>
</el-dialog>
<el-dialog title="选择参赛成员" :visible.sync="chooseVisible" :close-on-click-modal="false" width="400px">
<el-checkbox-group v-model="checkedMembers">
<el-checkbox v-for="(item, i) in chooses" :key="i" :label="item.accountId">{{ item.userName }}</el-checkbox>
</el-checkbox-group>
<p v-if="info.teamLimit" style="margin-top: 15px;font-size: 12px;">当前阶段限制1人参赛且此竞赛每个成员只能参加一个阶段赛项</p>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="chooseSubmit">确定</el-button>
<el-button size="small" @click="chooseVisible = false">取消</el-button>
</span>
</el-dialog>
<el-dialog title="团队得分详情" :visible.sync="memberVisible" width="900px" :close-on-click-modal="false">
<h6 v-if="members.length" style="margin-bottom: 10px;font-size: 16px;">团队名称{{ members[0].teamName }} 阶段名称{{ curRow.stageName }}</h6>
<table class="table tc">
<tr>
<th width="60">序号</th>
<th width="100">姓名</th>
<th width="100">学校</th>
<th width="100">用时</th>
<th width="100">分数</th>
<th width="100">得分详情</th>
</tr>
<template v-if="members.length">
<tr v-for="(item, i) in members" :key="i">
<td>{{ i + 1 }}</td>
<td>{{ item.userName }}</td>
<td>{{ item.schoolName }}</td>
<td>{{ item.timeSum }}min</td>
<td>{{ item.score }}</td>
<td>
<el-button type="text" @click="toReport(item)">查看</el-button>
</td>
</tr>
</template>
<tr v-else>
<td colspan="6">暂无数据</td>
</tr>
</table>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="memberVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Setting from "@/setting";
import util from "@/libs/util";
export default {
data() {
return {
id: +this.$route.query.id,
accountId: +this.$route.query.accountId,
status: 5,
form: {
competitionStage: [],
completeCompetitionSetup: {},
competitionRegistration: {}
},
info: {
isCaption: 0,
person: {},
caption: {},
team: {
captain: 1,
invitationCode: ''
},
stages: [],
teamDetail: [],
teamInstructors: []
},
memberVisible: false,
members: [],
curRow: {},
teamVisible: false,
teams: [],
teamNameRepeat: false,
teamForm: {
accountId: +this.$route.query.accountId,
identification: 1,
competitionId: this.$route.query.id,
registrationInvitationCode: '',
teamName: '',
invitationCode: '',
whetherSignUp: 1
},
curStage: null,
originInfo: {},
originIns: {
position: '',
name: '',
phone: '',
},
checkedPlayer: '',
transferVisible: false,
editing: false,
memberVisible: false,
members: [],
curRow: {},
chooseVisible: false,
checkedMember: '',
checkedMembers: [],
chooses: [],
timerList: [],
timer: null
};
},
mounted() {
this.$once('hook:beforeDestroy', function() {
clearInterval(this.timer)
this.timerList.forEach(n => {
clearInterval(n)
})
this.timerList = []
})
this.getData()
},
methods: {
getData() {
this.$post(`${this.api.getCompetition}?competitionId=${this.id}`).then(({ competition }) => {
this.form = competition
this.timer = setInterval(this.handleStatus, 1000)
this.getInfo()
}).catch(err => {})
},
//
getInfo() {
this.$post(`${this.api.entryInformation}?competitionId=${this.id}&accountId=${this.accountId}`).then(res => {
const info = res.entryInformation
//
if (!info.teamInstructors.length) info.teamInstructors.push(JSON.parse(JSON.stringify(this.originIns)))
if (info.personalDetail) {
info.team = {}
info.teamDetail = []
} else {
info.isCaption = info.team.caption
}
const caption = info.teamDetail.find(e => !e.caption)
info.caption = caption ? caption : {}
info.person = info.personalDetail || info.teamDetail.find(e => e.accountId == info.team.accountId)
this.originInfo = JSON.parse(JSON.stringify(info))
//
const now = Date.now()
this.form.competitionStage.map(e => {
//
if (!e.resultsDetails) {
const endTime = new Date(e.endTime).getTime() + e.resultAnnouncementTime * 3600000 // +
if (now > endTime) { //
info.stages.find(n => n.stageId == e.stageId).showDetail = 1
} else { //
this.timerList.push(setTimeout(this.getInfo, endTime - now))
}
}
})
this.info = info
}).catch(err => {});
},
//
handleStatus() {
let status
const n = this.form
let now = new Date().getTime();
let signUpStartTime = new Date(this.dateCompatible(n.signUpStartTime)).getTime(); //
let signUpEndTime = new Date(this.dateCompatible(n.signUpEndTime)).getTime(); //
let playStartTime = new Date(this.dateCompatible(n.playStartTime)).getTime(); //
let playEndTime = new Date(this.dateCompatible(n.playEndTime)).getTime(); //
if (now < signUpStartTime) { //
status = 0;
} else if (now > signUpStartTime && now < signUpEndTime) { //
status = n.competitionRegistration ? 1 : 2 // 12
} else if (now > signUpEndTime && now < playStartTime) { // ,
status = 3;
} else if (now > playStartTime && now < playEndTime) { //
status = 4
} else if (now > playEndTime) { //
status = 5;
}
this.status = status
},
//
edit(showMsg) {
if (this.editing || !showMsg) {
const { teamId, teamName, invitationCode } = this.info.team
this.$post(this.api.editCompetitionTeam, {
accountId: this.accountId,
identification: 1,
competitionId: this.id,
teamId,
teamName,
invitationCode,
whetherSignUp: 1
}).then(res => {
this.getInfo()
showMsg && util.successMsg('保存成功')
}).catch(res => {})
} else {
this.editing = !this.editing
}
},
//
delAdvisor(row, i) {
if (row.id) {
this.$confirm('确定要删除吗?', '提示', {
type: 'warning'
}).then(() => {
this.$post(`${this.api.deleteAnAdvisor}?id=${row.id}`).then(res => {
util.successMsg('删除成功')
this.getInfo()
}).catch(res => {})
}).catch(() => {})
} else {
this.info.teamInstructors.splice(i, 1)
}
},
//
addAdvisor() {
if (this.info.teamInstructors.length > 4) return util.errorMsg('指导老师仅限添加5个!')
this.info.teamInstructors.push(JSON.parse(JSON.stringify(this.originIns)))
},
//
editAdvisor(row) {
this.$set(row, 'edit', 1)
},
//
submitAdvisor(row) {
if (!row.name) return util.errorMsg('请输入姓名')
const { phone } = row
if (phone && !/^1[3456789]\d{9}$/.test(phone)) return util.errorMsg('请输入正确手机号格式')
this.$post(this.api.addAnAdvisor, {
name: row.name,
competitionId: this.id,
id: row.id,
teamId: this.info.teamId,
phone: row.phone,
position: row.position,
}).then(res => {
util.successMsg((row.id ? '修改' : '新增') + '成功')
this.getInfo()
}).catch(res => {})
},
//
transfer() {
//
const now = new Date()
let start = 0
for (const e of this.form.competitionStage) {
if (now >= new Date(e.startTime) && now <= new Date(e.endTime)) {
util.errorMsg('比赛已经开始,无法转让队长!')
start = 1
break
}
}
if (!start) this.transferVisible = true
},
//
transferSubmit() {
if (!this.checkedPlayer) return util.errorMsg('请选择成员')
this.$post(this.api.captainOfTransfer, {
captainId: this.info.caption.teamId,
playerId: this.checkedPlayer
}).then(res => {
this.checkedPlayer = ''
util.successMsg('转让成功')
this.transferVisible = false
this.getInfo()
}).catch(res => {})
},
//
removeLine(row) {
//
const now = new Date()
let start = 0
for (const e of this.form.competitionStage) {
if (now >= new Date(e.startTime) && now <= new Date(e.endTime)) {
util.errorMsg('比赛已经开始,无法踢出成员!')
start = 1
break
}
}
if (!start) {
this.$confirm('确定要踢出该成员吗?', '提示', {
type: 'warning'
}).then(() => {
this.$post(`${this.api.removeTheLine}?teamId=${this.info.teamId}&competitionId=${this.id}&accountId=${row.accountId}`).then(res => {
util.successMsg('移除成功')
this.getInfo()
if (row.accountId == this.accountId) this.$router.back()
}).catch(res => {})
}).catch(() => {})
}
},
//
selectPar(row) {
const item = this.form.competitionStage.find(e => e.stageId == row.stageId)
if (item) {
//
const now = new Date()
if (now >= new Date(item.startTime) && now <= new Date(item.endTime)) {
return util.errorMsg('该阶段比赛已经开始,无法修改允许参赛人员!')
} else {
const { teamLimit, stages, teamDetail } = this.info
// teamLimit=truestagesparticipantAccountIdsaccountId
if (teamLimit) {
const chooses = []
let ids = []
// accountId
stages.map(e => {
const id = e.participantAccountIds
id && ids.push(...id.split(',').map(n => +n))
})
ids = [...new Set(ids)]
teamDetail.map(e => {
ids.includes(e.accountId) || chooses.push(e) //
})
this.chooses = chooses
} else {
this.chooses = this.info.teamDetail
}
this.curRow = row
this.chooseVisible = true
}
}
},
//
chooseSubmit() {
const accountIds = this.checkedMembers
if (!accountIds.length) return util.errorMsg('请选择参赛成员!')
const limit = this.curRow.teamNumLimit //
if (accountIds.length > limit) return util.errorMsg(`请选择${limit}个以下参赛成员!`) //
this.$post(this.api.stageSelectParticipants, {
accountIds,
competitionId: this.id,
stageId: this.curRow.stageId,
teamId: this.info.teamId
}).then(res => {
this.checkedMembers = []
util.successMsg('修改成功')
this.getInfo()
this.chooseVisible = false
}).catch(res => {})
},
//
show(row) {
//
if (this.form.completeCompetitionSetup.competitionType && this.info.team.captain === 0) { //
this.curRow = row
this.memberVisible = true
const teamId = this.form.competitionRegistration.teamId
if (teamId) {
this.$post(this.api.stageGradeManagementList, {
pageNum: 1,
pageSize: 1000,
competitionId: this.id,
stageId: row.stageId,
isNakadai: 0
}).then(({ page }) => {
this.members = page.records.filter(e => e.teamId === teamId)
}).catch(res => {})
} else {
this.members = []
}
} else if (row.reportId) { // reportId
this.toReport(row)
}
},
//
toReport(row) {
this.$router.push(`/matchReport?reportId=${row.reportId}`)
},
}
};
</script>
<style lang="scss" scoped>
.l-title {
margin-top: 20px;
font-size: 18px;
}
.table {
width: 100%;
border-collapse: collapse;
th, td {
padding: 12px;
border: 1px solid #ebeef5;
}
&.tc {
text-align: center;
}
th {
text-align: center;
background-color: #f8faff;
}
.icon {
margin-right: 10px;
font-size: 16px;
color: #7a7a7a;
cursor: pointer;
}
.plus {
margin-bottom: 10px;
text-align: right;
}
.line {
display: flex;
align-items: center;
margin-bottom: 10px;
.el-input {
margin-right: 15px;
}
}
}
.flex-center {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.text-center {
text-align: center;
}
</style>

@ -26,13 +26,13 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="170"> <el-table-column label="操作" align="center" width="170">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="!scope.row.operate" type="text" @click="operateIt(scope.row)" v-auth="'/match/list:管理:竞赛进展:编辑'">编辑</el-button> <el-button v-if="!scope.row.operate" type="text" @click="operateIt(scope.row)" v-auth="'/match:管理:竞赛进展:编辑'">编辑</el-button>
<el-button v-else type="text" @click="saveData(scope.row)">保存</el-button> <el-button v-else type="text" @click="saveData(scope.row)">保存</el-button>
<el-button type="text" @click="handleDelete(scope.row, scope.$index)" v-auth="'/match/list:管理:竞赛进展:删除'">删除</el-button> <el-button type="text" @click="handleDelete(scope.row, scope.$index)" v-auth="'/match:管理:竞赛进展:删除'">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="plus" @click="addData" v-auth="'/match/list:管理:竞赛进展:新增'"> <div class="plus" @click="addData" v-auth="'/match:管理:竞赛进展:新增'">
<i class="el-icon-circle-plus-outline"></i> <i class="el-icon-circle-plus-outline"></i>
</div> </div>
</div> </div>
@ -40,8 +40,6 @@
<script> <script>
import util from "@/libs/util"; import util from "@/libs/util";
import EventBus from "@/libs/bus.js";
export default { export default {
name: "matchProgress", name: "matchProgress",
data() { data() {
@ -73,18 +71,16 @@ export default {
}, },
mounted() { mounted() {
this.getData(); this.getData();
EventBus.$on("tabClickWait1", () => this.waitSave());
}, },
methods: { methods: {
operateIt(row) { operateIt(row) {
this.$store.commit("match/setWait", 1);
row.operate = true row.operate = true
}, },
getData() { getData() {
this.$get(this.api.getContestProgress, { this.$get(this.api.getCompetitionProgress, {
contestId: this.id competitionId: this.id
}).then(res => { }).then(res => {
this.listData = res.contestProgressList; this.listData = res.competitionProgressList;
for(let index=0; index<this.listData.length; index++) { for(let index=0; index<this.listData.length; index++) {
// //
this.$set(this.listData, index, { operate: false, ...this.listData[index]}) this.$set(this.listData, index, { operate: false, ...this.listData[index]})
@ -94,18 +90,17 @@ export default {
}, },
saveData(row) { saveData(row) {
// //
this.$store.commit("match/setWait", 999);
let data = row; let data = row;
if (data.title.length) { if (data.title.length) {
if (row.id) { if (row.id) {
this.$put(this.api.editContestProgress, data).then(res => { this.$put(this.api.editCompetitionProgress, data).then(res => {
this.touchTime = this.touchTime-1 this.touchTime = this.touchTime-1
util.successMsg("修改成功"); util.successMsg("修改成功");
this.getData(); this.getData();
}).catch(res => { }).catch(res => {
}); });
} else { } else {
this.$post(this.api.addContestProgress, data).then(res => { this.$post(this.api.addCompetitionProgress, data).then(res => {
this.touchTime = this.touchTime-1 this.touchTime = this.touchTime-1
util.successMsg("创建成功"); util.successMsg("创建成功");
this.getData(); this.getData();
@ -136,7 +131,7 @@ export default {
util.successMsg("删除成功"); util.successMsg("删除成功");
}else { }else {
this.touchTime = this.touchTime+1 this.touchTime = this.touchTime+1
this.$del(`${this.api.deleteContestProgress}/${row.id}`).then(res => { this.$del(`${this.api.deleteCompetitionProgress}?competitionProgressId=${row.id}`).then(res => {
util.successMsg("删除成功"); util.successMsg("删除成功");
this.getData(); this.getData();
}).catch(res => { }).catch(res => {
@ -152,7 +147,7 @@ export default {
if (this.listData.length) { if (this.listData.length) {
if (this.listData[this.listData.length - 1].id) { if (this.listData[this.listData.length - 1].id) {
this.listData.push({ this.listData.push({
contestId: this.id, competitionId: this.id,
id: "", id: "",
title: "", title: "",
description: "", description: "",
@ -165,7 +160,7 @@ export default {
} }
} else { } else {
this.listData.push({ this.listData.push({
contestId: this.id, competitionId: this.id,
id: "", id: "",
title: "", title: "",
description: "", description: "",
@ -208,13 +203,11 @@ export default {
message: '保存成功!' message: '保存成功!'
}); });
} }
EventBus.$emit('tabChangeWait', this.$store.state.match.waitIndex)
}).catch(() => { }).catch(() => {
this.$message({ this.$message({
type: 'info', type: 'info',
message: '已取消保存' message: '已取消保存'
}); });
EventBus.$emit('tabChangeWait', this.$store.state.match.waitIndex)
}); });
} }

@ -0,0 +1,476 @@
<template>
<div>
<el-card shadow="hover" class="m-b-20 head-card">
<div class="flex-between m-b-20">
<el-page-header v-if="grades.length" @back="$router.back()" :content="grades[index].stageName + '/排名'"></el-page-header>
</div>
</el-card>
<el-card shadow="hover" class="m-b-20">
<div class="tabs">
<template v-for="(item, i) in grades">
<a v-if="i === index || !item.stageId" :key="i" class="item" :class="{active: item.stageId == active}" @click="tabChange(item.stageId)">{{ item.stageName }}排名</a>
</template>
</div>
<div class="flex-between" style="margin: 20px 0">
<div style="display: inline-flex;align-items: center">
<el-radio v-model="type" :label="0" @change="typeChange">默认系统排序</el-radio>
<el-radio v-model="type" :label="1" @change="typeChange">手动上传</el-radio>
<el-button type="primary" :disabled="type === 0" class="ml20" @click="batchImport">上传文件</el-button>
</div>
<div>
<el-button v-if="(type && uploadData.length) || (!type && !published)" type="primary" @click="cancelPublish(1)">发布排名</el-button>
<el-button v-else type="primary" @click="cancelPublish(0)">取消发布</el-button>
</div>
</div>
<el-table :data="list" class="table" ref="table" stripe header-align="center">
<el-table-column type="index" width="60" label="排名" align="center">
<template slot-scope="scope">
{{ scope.$index + (page - 1) * pageSize + 1 }}
</template>
</el-table-column>
<template v-if="competitionType == 1">
<el-table-column prop="teamName" label="团队名称" min-width="150" align="center"></el-table-column>
<el-table-column prop="leaderName" label="队长" min-width="150" align="center"></el-table-column>
</template>
<el-table-column v-else prop="userName" label="学生姓名" min-width="100" align="center"></el-table-column>
<el-table-column prop="schoolName" label="学校" min-width="100" align="center"></el-table-column>
<el-table-column prop="timeSum" label="用时" width="90" align="center">
<template slot-scope="scope">
{{ scope.row.timeSum }}min
</template>
</el-table-column>
<el-table-column prop="score" label="分数" width="90" align="center"></el-table-column>
<el-table-column label="得分详情" align="center" width="160">
<template slot-scope="scope">
<el-button type="text" @click="show(scope.row, scope.$index)">查看</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background layout="total, prev, pager, next" :total="total" @current-change="handleCurrentChange" :current-page="page">
</el-pagination>
</div>
</el-card>
<el-dialog title="团队得分详情" :visible.sync="teamVisible" width="900px" :close-on-click-modal="false">
<h6 v-if="active" style="margin-bottom: 10px;font-size: 16px;">团队名称{{ curRow.teamName }} 阶段名称{{ stageName }}</h6>
<table class="table tc">
<tr>
<template v-if="!active">
<th width="100">阶段</th>
<th width="100">团队成绩</th>
</template>
<th width="60">序号</th>
<th width="100">姓名</th>
<th width="100">学校</th>
<th width="100">用时</th>
<th width="100">分数</th>
<th width="100">得分详情</th>
</tr>
<tr v-for="(item, i) in teams" :key="i">
<template v-if="!active && item.rowspan">
<td :rowspan="item.rowspan">{{ item.stageName }}</td>
<td :rowspan="item.rowspan">{{ item.teamScore }}</td>
</template>
<td>{{ i + 1 }}</td>
<td>{{ item.userName }}</td>
<td>{{ item.schoolName }}</td>
<td>{{ item.timeSum }}min</td>
<td>{{ item.score }}</td>
<td>
<el-button type="text" @click="toReport(item)">查看</el-button>
</td>
</tr>
<tr v-if="!active">
<td>综合得分</td>
<td>{{ totalScore }}</td>
<td colspan="6">总排名{{ curRow.index }}</td>
</tr>
</table>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="teamVisible = false">确定</el-button>
</span>
</el-dialog>
<el-dialog title="批量导入" :visible.sync="importVisible" width="24%" :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
ref="importStaff"
name="file"
accept=".xls,.xlsx"
:on-remove="handleRemove"
:on-error="uploadError"
:on-success="uploadSuccess"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:action="this.api.batchImportRanking"
:file-list="uploadList"
:headers="headers"
:data="{
competitionId: this.id,
stageId: this.stageId,
isOverallRanking: active ? 0 : 1
}"
>
<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 size="small" @click="importVisible = false"> </el-button>
<el-button size="small" type="primary" @click="uploadSure"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Setting from "@/setting";
import util from "@/libs/util";
import echarts from "echarts";
import axios from 'axios';
export default {
data() {
return {
id: +this.$route.query.id,
stageId: +this.$route.query.stageId,
index: +this.$route.query.index,
method: this.$route.query.method,
competitionType: +this.$route.query.competitionType,
rule: +this.$route.query.rule,
headers: {
token: util.local.get(Setting.tokenKey)
},
active: '',
grades: [],
list: [],
page: 1,
pageSize: 10,
total: 0,
type: 0,
teamVisible: false,
teams: [],
memberVisible: false,
members: [],
importVisible: false,
uploadList: [],
uploadFaild: false,
exportCode: '',
curRow: {},
stageName: '',
totalScore: 0,
published: false,
uploadData: [],
sourceType: ''
};
},
mounted() {
this.getStage()
},
methods: {
//
getData() {
//
this.$post(`${this.api.whetherToPublish}?competitionId=${this.id}&stageId=${this.active || ''}&isOverallRanking=${this.active ? 0 : 1}`).then(({ whetherToPublish }) => {
// >(:(1,0))
whetherToPublish && this.$post(`${this.api.queryPublicationSource}?competitionId=${this.id}&stageId=${this.active || ''}&isOverallRanking=${this.active ? 0 : 1}`).then(({ source }) => {
this.sourceType = source
this.type = source == 1 ? 1 : 0
}).catch(res => {})
this.published = whetherToPublish
this.getRank(whetherToPublish)
}).catch(res => {})
},
//
getRank(whetherToPublish) {
//
if (whetherToPublish) {
console.log(44, this.active)
this.$post(this.api.manuallyRankTheUploadList, {
pageNum: this.page,
pageSize: this.pageSize,
competitionId: this.id,
isOverallRanking: this.active ? 0 : 1,
stageId: this.active || ''
}).then(({ message }) => {
this.list = message.records
this.total = message.total
}).catch(res => {})
} else { //
if (!this.active) {
//
const ids = this.grades.map(e => e.stageId)
this.$post(this.api.overallStandingsInThePointsRace, {
pageNum: this.page,
pageSize: this.pageSize,
competitionId: this.id,
stageIds: ids.splice(0, ids.length - 1)
}).then(({ page }) => {
this.list = page
this.total = total
}).catch(res => {})
} else { //
this.$post(`${this.api.stageRaceRanking}?competitionId=${this.id}&stageId=${this.active}&pageNum=${this.page}&pageSize=${this.pageSize}`).then(({ page, total }) => {
this.list = page
this.total = total
}).catch(res => {})
}
}
},
//
getStage() {
this.$post(this.api.detailsOfCompetitionStage, {
pageNum: 1,
pageSize: 100,
contestId: this.id,
}).then(({ page }) => {
this.grades = page.records.slice(0, this.index + 1)
this.active = this.grades[this.index].stageId
//
this.rule == 0 && this.grades.push({
stageId: 0,
stageName: '总分'
})
this.getData()
}).catch(res => {});
},
//
typeChange(val) {
this.getRank(val)
},
initData() {
this.page = 1
this.getData()
},
// tab
tabChange(i) {
this.active = i
this.initData()
},
//
show(row, i) {
//
if (this.competitionType == 1) {
row.index = i + 1
this.curRow = row
this.teamVisible = true
this.stageName = this.grades.find(e => e.stageId == this.active).stageName
const data = {
pageNum: 1,
pageSize: 100,
competitionId: this.id,
teamId: row.teamId
}
// idid
if (this.active) {
data.stageId = this.active
this.$post(this.api.stageTeamScoreDetails, data).then(({ page }) => {
this.teams = page.records
}).catch(res => {})
} else {
const ids = this.grades.map(e => e.stageId)
data.stageIds = ids.splice(0, ids.length - 1)
this.$post(this.api.detailsOfTotalTeamScores, data).then(({ data, resultCalculationMethod }) => {
const result = []
let totalScore = 0
//
data.map(e => {
const team = e.stageTeamInformation
if (team.length) {
//
const method = e.teamCalculationMethod
const scores = team.map(n => +n.score)
let score
//
if (method == 0) { //
score = Math.max(scores)
} else if (method == 1) { //
score = scores.reduce((prev, next) => prev + next) / scores.length
} else { //
score = scores.reduce((prev, next) => prev + next)
}
team[0].teamScore = score
team[0].rowspan = team.length
team.map(n => {
n = Object.assign(n, e)
})
// 0 */100
if (e.pointWeight && !resultCalculationMethod) totalScore += score * e.pointWeight / 100
result.push(...team)
}
})
//
const teamScores = data.map(e => e.teamScore)
if (resultCalculationMethod === 1) { //
totalScore = teamScores.reduce((prev, next) => prev + next)
} else if (resultCalculationMethod === 2) { //
totalScore = teamScores.reduce((prev, next) => prev + next) / teamScores.length
}
this.totalScore = totalScore
this.teams = result
}).catch(res => {})
}
} else {
this.toReport(row)
}
},
//
toReport(row) {
this.$router.push(`/matchReport?reportId=${row.reportId}`)
},
handleCurrentChange(val) { //
this.page = val;
this.getData();
},
//
publishSubmit() {
let data = this.uploadData // data
if (!this.type) { //
//
const { list, id } = this
const isOverall = this.active ? 0 : 1
if (list.length) {
const isPerson = this.competitionType == 0
list.map(e => {
e.competitionId = id
e.isOverallRanking = isOverall
if (isOverall) delete e.stageId
if (isPerson) e.teamId = null // teamIdnull
})
data = list
}
}
//
this.$post(`${this.api.releaseVerification}?competitionId=${this.id}&stageId=${this.active || ''}&isOverallRanking=${this.active ? 0 : 1}&whetherToManuallyPublish=${this.type}`).then(res => {
this.$post(this.api.publishRanking, data).then(res => {
this.uploadData = []
this.getData()
util.successMsg('发布成功!')
}).catch(res => {})
}).catch(res => {})
},
//
publish() {
//
if (this.published && this.sourceType != !this.type) {
this.$confirm(this.sourceType && !this.type ?
'手动排名已发布,是否要取消手动排名的内容,改为为系统排名的内容?' :
'默认系统排名已发布,是否要取消系统排名的内容,改为为手动发布的内容?', '提示', {
type: 'success'
}).then(() => {
this.publishSubmit()
}).catch(() => {})
} else {
this.publishSubmit()
}
},
//
cancelPublish(publish) {
const ids = this.grades.map(e => e.stageId)
const stageIds = ids.splice(0, ids.length - 1)
const query = []
stageIds.map(e => {
query.push('stageIds=' + e)
})
//
//
if (publish) {
this.$post(this.api.deleteLastPublication, {
competitionId: this.id,
isOverallRanking: this.active ? 0 : 1,
stageIds: this.active ? [this.active] : stageIds
}).then(res => {
this.publish()
}).catch(res => {})
} else {
this.$post(`${this.api.cancelRanking}?competitionId=${this.id}&isOverallRanking=${this.active ? 0 : 1}&${this.active ? 'stageIds=' + this.active : query.join('&')}`).then(res => {
this.getData()
util.successMsg('取消发布成功!')
}).catch(res => {})
}
},
//
batchImport() {
this.importVisible = true
this.uploadList = []
this.uploadFaild = false
},
//
download() {
location.href = this.api[this.competitionType == 1 ? 'rankImportTeam' : 'rankImportPerson']
},
//
handleExceed(files, fileList) {
util.warningMsg(
`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`
)
},
//
showFaild() {
axios.get(`${this.api.rankExportFailure}?exportCode=${this.exportCode}&competitionId=${this.id}`, {
headers: this.headers,
responseType: 'blob'
}).then((res) => {
util.downloadFileDirect(`批量导入排名管理失败数据导出.xls`, new Blob([res.data]))
}).catch(res => {})
},
uploadSuccess(res, file, fileList) {
this.uploadFaild = false
if (res.status === 200) {
const { exportCode } = res.data
if (exportCode) {
this.exportCode = exportCode
this.uploadFaild = true
util.errorMsg(`本次上传有${res.data.failureNum}个错误信息录入`)
}
this.uploadData = res.data.data
} else {
res.message ? util.errorMsg(res.message) : util.errorMsg("上传失败,请检查数据")
}
},
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.list = this.uploadData
this.uploadData.length && util.successMsg('请检查数据后,点击发布排名以发布数据!')
}
}
};
</script>
<style lang="scss" scoped>
.table {
width: 100%;
border-collapse: collapse;
th, td {
padding: 12px;
border: 1px solid #ebeef5;
}
&.tc {
text-align: center;
}
th {
text-align: center;
background-color: #f8faff;
}
}
</style>

@ -0,0 +1,370 @@
<template>
<div class="wrap">
<el-card shadow="hover" class="m-b-20">
<el-page-header @back="$router.back()" content="查看报告"></el-page-header>
</el-card>
<div class="content">
<div class="text-right">
<el-button type="primary" @click="exportPage">导出报告</el-button>
</div>
<h6 class="r-title">标准实验报告</h6>
<div class="info">
<h6 class="l-title">
<img src="@/assets/img/info1.png" alt="">
基本信息
</h6>
<ul :class="['info-list', {edit: editing}]">
<li>
<label>学生姓名</label>
<el-input v-if="editing" v-model="infoData.userName" disabled></el-input>
<span v-else>{{ infoData.userName }}</span>
</li>
<li>
<label>学生学号</label>
<el-input v-if="editing" v-model="infoData.workNumber" disabled></el-input>
<span v-else>{{ infoData.workNumber }}</span>
</li>
<li>
<label>实验时间</label>
<el-input v-if="editing" v-model="infoData.submitTime" disabled></el-input>
<span v-else>{{ infoData.submitTime }}</span>
</li>
<li>
<label>实验成绩</label>
<el-input v-if="editing" v-model="infoData.score" disabled></el-input>
<div v-else class="score-wrap">
<em>{{ infoData.score }}</em>
<img src="@/assets/img/point.png" alt="">
</div>
</li>
<li>
<label>学生班级</label>
<el-input v-if="editing" v-model="infoData.workNumber"></el-input>
<span v-else>{{ infoData.workNumber }}</span>
</li>
<li>
<label>指导老师</label>
<el-input v-if="editing" v-model="infoData.instructor"></el-input>
<span v-else>{{ infoData.instructor }}</span>
</li>
<li>
<label>实验学时</label>
<el-input v-if="editing" v-model="infoData.period"></el-input>
<span v-else>{{ infoData.period }}</span>
</li>
</ul>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report2.png" alt="">
实验项目名称
</h6>
<el-input v-if="editing" v-model="form.projectName" type="textarea"></el-input>
<div v-else class="pre-wrap" v-html="form.projectName"></div>
</div>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report3.png" alt="">
实验目的
</h6>
<div :class="['pre-wrap', {edit: editing}]" v-html="form.purpose"></div>
</div>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report4.png" alt="">
实验数据
</h6>
<el-table :data="expData" class="table" border stripe header-align="center">
<el-table-column type="index" label="序号" align="center" width="60">
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="judgmentName" label="判分点" width="270" align="center"></el-table-column>
<el-table-column v-if='project' prop="judgmentName" label="考核点" align="center" width="150">
<template slot-scope="scope">
<div v-for="(item, index) in scope.row.lcRuleRecords" :key="index">
<span>
<span>{{index+1}}. </span>{{item.name}}
</span>
</div>
</template>
</el-table-column>
<el-table-column prop="ruleAnswer" label="参考答案" style='word-wrap: break-word'>
<template slot-scope="scope">
<div v-if='scope.row.lcRuleRecords'>
<div v-for="(item, index) in scope.row.lcRuleRecords" :key="index">
<span>
<span>{{index+1}}. </span>{{item.ruleAnswer}}
</span>
</div>
</div>
<div v-else v-html="scope.row.referenceAnswer"></div>
</template>
</el-table-column>
<el-table-column prop="userAnswer" label="学生答案">
<template slot-scope="scope">
<div v-if='scope.row.lcRuleRecords'>
<div v-for="(item, index) in scope.row.lcRuleRecords" :key="index">
<span v-if='item.userAnswer'>
<span>{{index+1}}. </span>{{item.userAnswer}}
</span>
<span v-else>
<span>{{index+1}}. </span>未填写
</span>
</div>
</div>
<div v-else v-html='scope.row.answer' style='white-space: pre-wrap'></div>
<template v-if="scope.row.runThePictureList">
<img v-for="(img, i) in scope.row.runThePictureList" :key="i" width="200" class="result-pic" :src="img" alt="">
</template>
</template>
</el-table-column>
<el-table-column prop="score" label="得分" width="80" align="center"></el-table-column>
</el-table>
</div>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report5.png" alt="">
实验总结与体会
</h6>
<quill v-if="editing" :border="true" v-model="form.summarize" :minHeight="150" :height="150" />
<div v-else class="pre-wrap" v-html="form.summarize"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import util from "@/libs/util";
export default {
data() {
return {
reportId: this.$route.query.reportId,
title: "实验报告",
form: {
className: "",
instructor: "",
period: "",
projectName: "",
summarize: "",
},
infoData: {},
expData: [],
editing: false,
loadIns: null,
loading: false,
project:false,
userScores: []
};
},
mounted() {
this.getData()
},
methods: {
getData() { //
this.$get(`${this.api.reportDetail}?reportId=${this.reportId}`).then(({ report, userScores }) => {
this.form = report
this.expData = userScores
this.project = this.expData.find(e => e.lcRuleRecords) // lcRuleRecords
let form = this.form;
this.infoData = {
workNumber: form.workNumber,
experimentalClassName: form.experimentalClassName,
instructor: form.instructor,
period: form.period,
laboratory: form.laboratory,
submitTime: form.submitTime,
score: form.score,
userName: form.userName
}
const data = report.data
this.userScores = userScores
// data使
if (!data) {
this.handleList(userScores)
this.$post(this.api.editExperimentalData, {
reportId,
data: JSON.stringify(userScores)
}).then(res => {}).catch(err => {})
} else {
this.handleList(userScores.find(e => e.lcRuleRecords) ? userScores : JSON.parse(data))
}
}).catch(res => {})
},
//
handleList(list) {
this.project = list.find(e => e.lcRuleRecords) // lcRuleRecords
if (this.project) {
list.map(e => {
e.assessmentPoint = ''
e.referenceAnswer = ''
e.answer = ''
e.lcRuleRecords.map((n, i) => {
e.assessmentPoint += `${i + 1}.${n.name}`
e.referenceAnswer += `${i + 1}.${n.ruleAnswer}`
e.answer += `${i + 1}.${n.userAnswer}`
})
})
}
this.expData = list
},
exportPage() {
const form = Object.assign(this.form, this.infoData)
const list = JSON.parse(JSON.stringify(this.expData))
list.map((e, i) => {
const item = this.userScores.find(n => n.judgmentId == e.judgmentId)
if (item && item.runThePicture) e.runThePicture = item.runThePicture
if (item && item.runThePictureList) e.runThePictureList = item.runThePictureList
e.id = i + 1
// if (e.referenceAnswer && typeof e.referenceAnswer === 'string') e.referenceAnswer = e.referenceAnswer.replace(/<[^>]+>/g, '').replace(/(&nbsp;|&amp;|%s)/g, '').replace(/>/g, '&gt;').replace(/</g, '&lt;')
if (e.answer && typeof e.answer === 'string') e.answer = e.answer.replace(/<[^>]+>/g, '').replace(/(&nbsp;|&amp;|%s)/g, '').replace(/>/g, '&gt;').replace(/</g, '&lt;')
})
for (const i in form) {
if (form[i] && typeof form[i] === 'string') form[i] = form[i].replace(/<[^>]+>/g, '')
}
form.purpose = form.purpose.replace(/<[^>]+>/g, '')
this.$post(this.project ? this.api.exportBankExperimentReport : this.api.exportLabReport, {
...form,
experimentalData: list
}).then(res => {
console.log(res)
util.downloadFileDirect(`实验报告.docx`,new Blob([res]))
}).catch(res => {})
},
}
};
</script>
<style lang="scss" scoped>
.wrap {
padding: 12px 300px 20px;
}
.text-right {
text-align: right;
}
code, kbd, samp{
font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
word-wrap: break-word;
white-space: pre-wrap;
}
/deep/ pre{
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
word-break:break-all;
overflow:hidden;
font-size: 12px;
font-weight:400;
font-family:'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif
}
.content {
padding: 16px 40px;
background: #fff;
&.loading {
padding-top: 30px;
}
.r-title {
margin-bottom: 40px;
font-size: 24px;
text-align: center;
color: #333;
}
.info {
padding: 20px 16px;
border: 1px solid #E1E6F2;
}
.l-title {
display: flex;
align-items: center;
padding: 5px 8px;
margin-bottom: 12px;
font-size: 14px;
color: #333;
background-color: #f7f5ff;
}
.info-list {
display: flex;
flex-wrap: wrap;
padding: 10px 0 0 20px;
li {
display: inline-flex;
width: 23%;
padding: 0 10px;
margin-bottom: 34px;
}
&.edit {
li {
align-items: center;
}
}
label {
font-size: 14px;
color: #333;
white-space: nowrap;
}
span {
min-width: 150px;
padding: 0 10px 3px;
border-bottom: 1px solid #E1E6F2;
}
/deep/.el-input {
width: 174px;
}
}
.score-wrap {
position: relative;
min-width: 150px;
border-bottom: 1px solid #E1E6F2;
em {
position: absolute;
top: -12px;
left: 30px;
font-family: din;
font-size: 30px;
font-weight: 600;
color: #0B1D30;
}
img {
position: absolute;
bottom: -15px;
left: 0;
}
}
/deep/.el-textarea .el-textarea__inner, .pre-wrap {
min-height: 72px;
padding: 10px 16px;
font-size: 14px;
color: #333;
&.edit {
color: #ABB3C6;
border: 1px solid #CACFDB;
border-radius: 4px;
background-color: #F6F7F9;
}
}
/deep/ .table th {
background-color: #e5dfff !important;
.cell {
line-height: 35px;
color: #fff;
}
}
}
.result-pic {
margin: 10px 0;
}
@media (max-width: 1650px) {
.wrap {
padding: 12px 200px 20px;
}
}
@media (max-width: 1430px) {
.wrap {
padding: 12px 100px 20px;
}
}
</style>

@ -5,22 +5,31 @@
<ul class="filter"> <ul class="filter">
<li> <li>
<label>搜索</label> <label>搜索</label>
<el-input placeholder="请输入姓名/手机号/学校" prefix-icon="el-icon-search" v-model="keyword" clearable size="mini"></el-input> <el-input placeholder="请输入姓名/手机号/团队名称/学号" prefix-icon="el-icon-search" v-model="keyword" clearable size="mini" style="width: 250px"></el-input>
</li>
<li>
<label>参赛人员状态</label>
<el-select v-model="isDisable" @change="initData">
<el-option v-for="(item, i) in statusList" :key="i" :label="item.name" :value="item.id"></el-option>
</el-select>
</li> </li>
</ul> </ul>
<div> <div>
<el-button type="primary" round @click="exportAll" v-auth="'/match/list:管理:报名人员:全部导出'">导出</el-button> <el-button type="primary" round @click="add" v-auth="'/match:管理:报名人员:新增'">新增</el-button>
<el-button type="primary" round @click="exportAll" v-auth="'/match:管理:报名人员:导出'">批量导出</el-button>
</div> </div>
</div> </div>
<el-table ref="table" :data="listData" class="table" stripe header-align="center" @selection-change="handleSelectionChange" row-key="id"> <el-table ref="table" :data="listData" class="table" stripe header-align="center" @selection-change="handleSelectionChange" row-key="id" @sort-change="sortChange">
<el-table-column type="selection" width="80" align="center" :reserve-selection="true"></el-table-column> <el-table-column type="selection" width="80" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center"> <el-table-column type="index" width="60" label="序号" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.$index + (page - 1) * pageSize + 1 }} {{ scope.$index + (page - 1) * pageSize + 1 }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="school" label="学校"> <el-table-column prop="school" label="学校" sortable="custom">
</el-table-column>
<el-table-column prop="teamName" label="团队名称" sortable="custom">
</el-table-column> </el-table-column>
<el-table-column prop="username" label="学生姓名"> <el-table-column prop="username" label="学生姓名">
</el-table-column> </el-table-column>
@ -28,113 +37,378 @@
</el-table-column> </el-table-column>
<el-table-column prop="phone" label="手机号"> <el-table-column prop="phone" label="手机号">
</el-table-column> </el-table-column>
<el-table-column prop="gmtCreate" label="报名时间"> <el-table-column label="操作" align="center" width="320">
</el-table-column>
<!-- <el-table-column label="操作" align="center" width="170">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="edit(scope.row)">编辑</el-button>
<el-button type="text" @click="info(scope.row)">参赛信息与成绩</el-button>
<el-switch <el-switch
v-model="scope.row.isDisable" v-model="scope.row.isDisable"
:active-text="scope.row.isDisable ? '开' : '关'" :active-text="scope.row.isDisable ? '关' : '开'"
:active-value="1" :active-value="0"
:inactive-value="0" :inactive-value="1"
style="margin: 0 10px 0 5px" style="margin: 0 10px 0 5px"
@change="switchOff($event,scope.row,scope.$index)" @change="switchOff($event,scope.row,scope.$index)"
></el-switch> ></el-switch>
</template> </template>
</el-table-column> --> </el-table-column>
</el-table> </el-table>
<div class="pagination"> <div class="pagination">
<el-pagination background layout="total, prev, pager, next" :total="totals" @current-change="handleCurrentChange" :current-page="page"> <el-pagination background layout="total, prev, pager, next" :total="total" @current-change="handleCurrentChange" :current-page="page">
</el-pagination> </el-pagination>
</div> </div>
<el-dialog :title="(form.accountId ? '编辑' : '新增') + '参赛人员'" :visible.sync="addVisible" width="400px" class="dialog" :close-on-click-modal="false" @close="closeAdd">
<el-form ref="form" :model="form" :rules="rules" label-width="80px" style='margin-right: 10px;'>
<el-form-item prop="schoolId" label="院校">
<el-select v-model="form.schoolId" filterable style="width: 100%">
<el-option v-for="(item, i) in clients" :key="i" :label="item.customerName" :value="item.schoolId"></el-option>
</el-select>
</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="userName" label="学生姓名">
<el-input v-model="form.userName" placeholder="请输入学生姓名" :disabled="isAdd"></el-input>
</el-form-item>
<el-form-item label="账号角色">
学生
</el-form-item>
<el-form-item prop="teamId" label="所属团队">
<div style="display: flex;align-items: center">
<el-select v-model="form.teamId" :disabled="formEnable && isAdd" filterable style="width: 240px;margin-right: 10px">
<el-option v-for="(item, i) in teams" :key="i" :label="item.teamName" :value="item.teamId"></el-option>
</el-select>
<el-button v-if="isAdd && !formEnable" type="text" @click="createTeam">创建团队</el-button>
</div>
</el-form-item>
<el-form-item prop="phone" label="手机号">
<el-input v-model="form.phone" maxlength="11" placeholder="请输入手机号" :disabled="isAdd"></el-input>
</el-form-item>
<el-form-item prop="email" label="邮箱">
<el-input v-model="form.email" placeholder="请输入邮箱" :disabled="isAdd"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addVisible = false">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</span>
</el-dialog>
<el-dialog title="创建团队" :visible.sync="teamVisible" :close-on-click-modal="false" width="300px">
<el-form class="dia-form">
<el-form-item>
<el-input placeholder="请输入团队名称" maxlength="10" v-model="teamForm.teamName"></el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="请设置团队邀请码" maxlength="6" v-model="teamForm.invitationCode"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="teamSubmit">确定并报名</el-button>
<el-button size="small" @click="teamVisible = false">取消</el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import util from "@/libs/util"; import util from "@/libs/util";
import Setting from "@/setting";
import axios from 'axios' import axios from 'axios'
import Setting from "@/setting";
export default { export default {
name: "matchSignup", name: "matchSignup",
data() { data() {
return { return {
token: util.local.get(Setting.tokenKey), token: util.local.get(Setting.tokenKey),
id: this.$route.query.id, id: +this.$route.query.id,
isDisable: '',
statusList: [
{
id: '',
name: '不限'
},
{
id: 1,
name: '已禁用'
},
{
id: 0,
name: '未禁用'
}
],
keyword: "", keyword: "",
listData: [], listData: [],
multipleSelection: [], multipleSelection: [],
page: 1, page: 1,
pageSize: 10, pageSize: 10,
totals: 0 total: 0,
schoolOrder: '',
teamOrder: '',
clients: [],
addVisible: false,
formEnable: true,
isAdd: false,
form: {
captain: 0,
competitionId: this.$route.query.id,
userName: '',
workNumber: '',
schoolId: '',
teamId: '',
whetherSignUp: 0,
phone: '',
email: '',
identification: 1,
uniqueIdentification: Date.now(),
password: Setting.initialPassword
},
rules: {
schoolId: [
{ required: true, message: "请选择所属院校", trigger: "change" }
],
workNumber: [
{ required: true, message: "请输入学号", trigger: "blur" }
],
teamId: [
{ required: true, message: "请选择所属团队", trigger: "change" }
]
},
submiting: false,
teamVisible: false,
teams: [],
teamNameRepeat: false,
teamForm: {
competitionId: this.$route.query.id,
registrationInvitationCode: '',
teamName: '',
invitationCode: '',
whetherSignUp: 1,
identification: 1
},
originForm: {}
}; };
}, },
watch: { watch: {
keyword: function(val) { keyword: function(val) {
clearTimeout(this.searchTimer); clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => { this.searchTimer = setTimeout(() => {
this.getData(); this.initData();
}, 500); }, 500);
} }
}, },
mounted() { mounted() {
this.getData(); this.initData()
this.getClient()
this.getTeam()
}, },
methods: { methods: {
getData() { getData() {
this.$post(this.api.queryApplicantByCondition, { this.$post(this.api.queryRegistrationByCondition, {
pageNum: this.page, pageNum: this.page,
pageSize: this.pageSize, pageSize: this.pageSize,
contestId: this.id, competitionId: this.id,
keyWord: this.keyword, keyWord: this.keyword,
isDisable: this.isDisable,
schoolOrder: this.schoolOrder,
teamOrder: this.teamOrder,
}).then(({ data }) => { }).then(({ data }) => {
this.listData = data.records; this.listData = data.records;
this.totals = data.total; this.total = data.total;
this.$refs.table.clearSelection(); this.$refs.table.clearSelection();
}).catch(res => { }).catch(res => {
}); });
}, },
initData() {
this.page = 1
this.getData()
},
handleSelectionChange(val) { handleSelectionChange(val) {
this.multipleSelection = val; this.multipleSelection = val;
}, },
onSearch() {
this.page = 1;
this.getData();
},
handleCurrentChange(val) { handleCurrentChange(val) {
this.page = val; this.page = val;
this.getData(); this.getData();
}, },
switchOff(val, row, index) { switchOff(val, row, index) {
this.$put(`${this.api.disableApplicant}/${row.id}/${val}`) this.$put(`${this.api.disableRegistration}?competitionRegistrationId=${row.id}&isDisable=${val}`).then(res => {}).catch(err => {});
.then(res => {
})
.catch(err => {
});
}, },
disalbeAllData() { //
if (this.multipleSelection.length != "") { sortChange(column) {
let newArr = this.multipleSelection; // 12
let delList = newArr.map(item => { if (column.prop === 'school') this.schoolOrder = column.order ? column.order === 'ascending' ? 2 : 1 : ''
return item.id; if (column.prop === 'teamName') this.teamOrder = column.order ? column.order === 'ascending' ? 2 : 1 : ''
}); this.getData()
},
this.$confirm("确定要禁用吗?", "提示", { //
type: "warning" add() {
this.isAdd = true
this.addVisible = true
},
//
edit(row) {
this.isAdd = false
this.addVisible = true
row.userName = row.username
row.id = row.accountId
this.originForm = JSON.parse(JSON.stringify(row))
this.form = JSON.parse(JSON.stringify(row))
},
//
workNumberChange() {
const { form } = this
form.schoolId && form.schoolId && this.$get(`${this.api.enquireAboutSchoolStudents}?schoolId=${form.schoolId}&workNumber=${form.workNumber}`).then(({ account }) => {
if (account) {
account
this.form = account
}
this.formEnable = !account
}).catch(res => {})
},
//
submitForm() {
const { form } = this
if (!this.isAdd) { //
this.$post(this.api.updateUser, {
hrUserAccount: {
...form,
id: form.id,
},
hrUserInfo: {
userId: form.userId,
schoolId: form.schoolId,
email: form.email
}
}).then(res => {
//
if (this.originForm.teamId !== form.teamId) {
this.$post(this.api.joinCompetitionTeam, {
accountId: form.id,
competitionId: this.id,
teamId: form.teamId,
identification: 1,
whetherSignUp: 1
}).then(res => {
this.addVisible = false
this.getData()
this.submiting = false
util.successMsg('编辑成功!')
}).catch(res => {
this.submiting = false
}) })
.then(() => { } else {
this.$put(`${this.api.disableContests}?ids=${delList.join(",")}`).then(res => { this.addVisible = false
this.multipleSelection = []; this.getData()
util.successMsg("禁用成功"); this.submiting = false
this.getData(); util.successMsg('编辑成功!')
}
}).catch(res => { }).catch(res => {
}); this.submiting = false
}) })
.catch(() => {
});
} else { } else {
util.errorMsg("请先选择数据 !"); //
this.$post(this.api.joinCompetitionTeam, {
accountId: form.id,
competitionId: this.id,
teamId: this.form.teamId,
identification: 1,
whetherSignUp: 1
}).then(res => {
this.addVisible = false
this.getData()
this.submiting = false
util.successMsg('报名成功!')
}).catch(res => {
this.submiting = false
})
}
},
//
submit() {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.submiting) return false
this.submiting = true
const { form } = this
const team = this.teams.find(e => e.teamId == form.teamId)
if (team && team.invitationCode) form.invitationCode = team.invitationCode
// id-id-schoolId-workNumber
form.account = `${Setting.platformId}-0-${form.schoolId}-${form.workNumber}`
const { phone } = form
if (phone && !/^1[3456789]\d{9}$/.test(phone)) {
return util.errorMsg("请输入正确的手机号/邮箱")
} else {
this.submitForm()
}
}
})
},
//
closeAdd() {
this.form = {
captain: 0,
competitionId: this.id,
userName: '',
workNumber: '',
schoolId: '',
teamId: '',
whetherSignUp: 0,
phone: '',
email: '',
identification: 1,
uniqueIdentification: Date.now(),
password: Setting.initialPassword
}
},
//
createTeam() {
this.teamForm = {
competitionId: this.id,
registrationInvitationCode: '',
teamName: '',
invitationCode: '',
whetherSignUp: 1,
identification: 1
} }
this.teamVisible = true
},
//
getClient() {
this.$post(this.api.queryCustomer,{
page: 1,
size: 1000,
supplierId: ''
}).then(res => {
this.clients = res.message.list
}).catch(res => {})
},
//
getTeam() {
this.$get(this.api.searchTeam, {
teamName: '',
competitionId: this.id
}).then(({ teamList }) => {
this.teams = teamList
}).catch(res => {})
},
//
teamSubmit() {
const form = this.teamForm
if (!form.teamName) return util.errorMsg('请输入团队名称')
if (form.invitationCode.length !== 6) return util.errorMsg('请输入6位数团队邀请码')
form.accountId = this.form.id
this.$post(this.api.addCompetitionTeam, form).then(res => {
this.teamVisible = false
this.addVisible = false
this.getData()
util.successMsg('报名成功!')
}).catch(res => {})
},
//
info(row) {
this.$router.push(`/matchInfo?id=${this.id}&accountId=${row.accountId}`)
}, },
exportAll() { exportAll() {
const data = this.multipleSelection const data = this.multipleSelection
@ -149,13 +423,29 @@ export default {
util.downloadFileDirect(`报名人员.xls`, new Blob([res.data])) util.downloadFileDirect(`报名人员.xls`, new Blob([res.data]))
}).catch(res => {}) }).catch(res => {})
} else { } else {
location.href = `${this.api.excelExport}?contestId=${this.id}` axios.get(`${this.api.excelExport}?competitionId=${this.id}`, {
headers: {
token: this.token
},
responseType: 'blob'
}).then((res) => {
util.downloadFileDirect(`报名人员.xls`, new Blob([res.data]))
}).catch(res => {})
} }
} }
} }
}; };
</script> </script>
<style scoped> <style lang="scss" scoped>
/deep/.dia-form {
.w-100 {
width: 100%;
}
.tips {
display: flex;
justify-content: center;
align-items: center;
}
}
</style> </style>

@ -2,7 +2,7 @@
<!-- 报名人员 --> <!-- 报名人员 -->
<div class="page-content" style="padding: 24px"> <div class="page-content" style="padding: 24px">
<div class="tool" style="justify-content: flex-end"> <div class="tool" style="justify-content: flex-end">
<el-button type="primary" round @click="add" v-auth="'/match/list:管理:公告通知:新增'">新增</el-button> <el-button type="primary" round @click="add" v-auth="'/match:管理:公告通知:新增'">新增</el-button>
</div> </div>
<el-table ref="table" :data="listData" class="table" stripe header-align="center" @selection-change="handleSelectionChange" row-key="id"> <el-table ref="table" :data="listData" class="table" stripe header-align="center" @selection-change="handleSelectionChange" row-key="id">
@ -24,10 +24,10 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="250"> <el-table-column label="操作" align="center" width="250">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="edit(scope.row)" v-auth="'/match/list:管理:公告通知:编辑'">编辑</el-button> <el-button type="text" @click="edit(scope.row)" v-auth="'/match:管理:公告通知:编辑'">编辑</el-button>
<el-button type="text" @click="del(scope.row)" v-auth="'/match/list:管理:公告通知:删除'">删除</el-button> <el-button type="text" @click="del(scope.row)" v-auth="'/match:管理:公告通知:删除'">删除</el-button>
<el-switch <el-switch
v-auth="'/match/list:管理:公告通知:启用'" v-auth="'/match:管理:公告通知:启用'"
v-model="scope.row.isOpen" v-model="scope.row.isOpen"
:active-text="scope.row.isOpen ? '关' : '开'" :active-text="scope.row.isOpen ? '关' : '开'"
:active-value="0" :active-value="0"
@ -53,6 +53,7 @@ export default {
name: "matchSignup", name: "matchSignup",
data() { data() {
return { return {
token: util.local.get(Setting.tokenKey),
id: this.$route.query.id, id: this.$route.query.id,
keyword: "", keyword: "",
listData: [], listData: [],
@ -75,7 +76,7 @@ export default {
}, },
methods: { methods: {
getData() { getData() {
this.$post(`${this.api.queryAnnouncementByContestId}?pageNum=${this.pageNo}&pageSize=${this.pageSize}&contestId=${this.id}`).then(({ data }) => { this.$post(`${this.api.queryAnnouncementByCompetitionId}?pageNum=${this.pageNo}&pageSize=${this.pageSize}&competitionId=${this.id}`).then(({ data }) => {
this.listData = data.records this.listData = data.records
this.totals = data.total this.totals = data.total
this.$refs.table.clearSelection() this.$refs.table.clearSelection()
@ -125,10 +126,10 @@ export default {
} }
}, },
add() { add() {
this.$router.push(`/match/noticeDetail?contestId=${this.id}`) this.$router.push(`noticeDetail?competitionId=${this.id}`)
}, },
edit(row) { edit(row) {
this.$router.push(`/match/noticeDetail?id=${row.id}&contestId=${this.id}`) this.$router.push(`noticeDetail?id=${row.id}&competitionId=${this.id}`)
} }
} }
}; };

@ -38,8 +38,8 @@
</el-upload> </el-upload>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button v-if="!form.id" @click="save(0)" v-auth="'/match/list:管理:公告通知:草稿'">草稿</el-button> <el-button v-if="!form.id" @click="save(0)" v-auth="'/match:管理:公告通知:草稿'">草稿</el-button>
<el-button type="primary" @click="save(1)" v-auth="'/match/list:管理:公告通知:发布'">发布</el-button> <el-button type="primary" @click="save(1)" v-auth="'/match:管理:公告通知:发布'">发布</el-button>
<el-button @click="back">取消</el-button> <el-button @click="back">取消</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -61,7 +61,7 @@ export default {
}, },
form: { form: {
id: this.$route.query.id, id: this.$route.query.id,
contestId: this.$route.query.contestId, competitionId: this.$route.query.competitionId,
announcementText: '', announcementText: '',
announcementTitle: '', announcementTitle: '',
announcementAnnexList: [], announcementAnnexList: [],
@ -75,6 +75,16 @@ export default {
components: { components: {
quill quill
}, },
watch: {
// ,
form: {
handler(){
this.updateTime++
if(this.updateTime > 1) this.$store.commit('match/setWait', 0)
},
deep:true
},
},
mounted() { mounted() {
this.form.id && this.getData() this.form.id && this.getData()
}, },
@ -134,7 +144,6 @@ export default {
this.form.announcementAnnexList.push(data) this.form.announcementAnnexList.push(data)
// //
id && this.$post(this.api.saveAnnouncementAnnex, data).then(res => {}).catch(res => {}) id && this.$post(this.api.saveAnnouncementAnnex, data).then(res => {}).catch(res => {})
console.log(44, this.form)
}, },
// //
beforeUpload(file) { beforeUpload(file) {

@ -127,15 +127,15 @@ export default {
} }
}, },
getProgress() { // getProgress() { //
this.$get(this.api.getContestProgress, { this.$get(this.api.getCompetitionProgress, {
contestId: this.form.id competitionId: this.form.id
}).then(res => { }).then(res => {
this.progress = res.contestProgressList.reverse() this.progress = res.competitionProgressList.reverse()
}).catch(err => {}); }).catch(err => {});
}, },
// //
getNotice() { getNotice() {
this.$post(`${this.api.queryAnnouncementByContestId}?pageNum=1&pageSize=1000&contestId=${this.form.id}`).then(({ data }) => { this.$post(`${this.api.queryAnnouncementByCompetitionId}?pageNum=1&pageSize=1000&competitionId=${this.form.id}`).then(({ data }) => {
const records = data.records.filter(e => e.status) // status 0稿 1 const records = data.records.filter(e => e.status) // status 0稿 1
records.map(e => { records.map(e => {
e.announcementText = e.announcementText.replace(/<img.*?(?:>|\/>)/gi, '') e.announcementText = e.announcementText.replace(/<img.*?(?:>|\/>)/gi, '')

@ -341,13 +341,13 @@ export default {
}, },
handleCurrentChange(val) { // handleCurrentChange(val) { //
this.page = val; this.page = val;
this.$router.push({ // this.$router.push({
path: 'list', // path: '/project/list',
query: { // query: {
...this.$route.query, // ...this.$route.query,
page: val // page: val
} // }
}) // })
this.getData(); this.getData();
}, },
add() { // add() { //

@ -372,13 +372,14 @@ export default {
}).catch(res => {}) }).catch(res => {})
// //
this.$post(this.api.academicLeadersRanking).then(({ data }) => { this.$post(`${this.api.academicLeadersRanking}?pageNum=1&pageSize=20`).then(({ data }) => {
const list = data.records
// //
data.sort((a, b) => { list.sort((a, b) => {
if (a.avgScore == b.avgScore) return b.practiceNumber - a.practiceNumber if (a.avgScore == b.avgScore) return b.practiceNumber - a.practiceNumber
return b.avgScore - a.avgScore return b.avgScore - a.avgScore
}) })
this.achs = data this.achs = list
}).catch(res => {}) }).catch(res => {})
this.scrollTable() this.scrollTable()
}, },

@ -112,7 +112,7 @@
<template slot-scope="scope">学生</template> <template slot-scope="scope">学生</template>
</el-table-column> </el-table-column>
<el-table-column prop="loginNumber" label="登录次数" align="center" width="100"></el-table-column> <el-table-column prop="loginNumber" label="登录次数" align="center" width="100"></el-table-column>
<el-table-column prop="lastLoginTime" label="上次登录时间" align="center" width="120"></el-table-column> <el-table-column prop="lastLoginTime" label="上次登录时间" align="center" width="180"></el-table-column>
<el-table-column label="操作" align="center" width="300"> <el-table-column label="操作" align="center" width="300">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-auth v-if="isRemove" type="text" @click="removeStudent(scope.row)">移除</el-button> <el-button v-auth v-if="isRemove" type="text" @click="removeStudent(scope.row)">移除</el-button>

@ -27,7 +27,7 @@ if (isPro) {
host = "http://121.37.12.51/"; // 中台测试服 host = "http://121.37.12.51/"; // 中台测试服
// host = 'https://www.occupationlab.com/' // 正式服 // host = 'https://www.occupationlab.com/' // 正式服
host = "http://192.168.31.51:9000/"; // 榕 host = "http://192.168.31.51:9000/"; // 榕
// host = 'http://192.168.31.116:9000/'; // 赓 host = 'http://192.168.31.116:9000/'; // 赓
} }
const Setting = { const Setting = {
@ -87,7 +87,7 @@ const Setting = {
// 相同路由,不同参数间进行切换,是否强力更新 // 相同路由,不同参数间进行切换,是否强力更新
sameRouteForceUpdate: false, sameRouteForceUpdate: false,
// 是否使用动态路由(即角色权限,开启了的话就会取后端返回的权限树来显示头部导肮和页面按钮) // 是否使用动态路由(即角色权限,开启了的话就会取后端返回的权限树来显示头部导肮和页面按钮)
dynamicRoute: true, dynamicRoute: false,
// 文件上传 // 文件上传
upload: { upload: {
apiURL: uploadURL, apiURL: uploadURL,

Loading…
Cancel
Save