赛事联调

dev_202303
yujialong 2 years ago
parent edacc49466
commit f42ce357be
  1. 31
      package-lock.json
  2. 1
      package.json
  3. 3
      src/assets/css/main.css
  4. BIN
      src/assets/img/avg.png
  5. BIN
      src/assets/img/point.png
  6. BIN
      src/assets/img/report2.png
  7. BIN
      src/assets/img/report3.png
  8. BIN
      src/assets/img/report4.png
  9. BIN
      src/assets/img/report5.png
  10. BIN
      src/assets/img/total.png
  11. 48
      src/const/match.js
  12. 20
      src/router/index.js
  13. 2
      src/setting.js
  14. 29
      src/utils/api.js
  15. 3
      src/views/course/AddCurriculum.vue
  16. 22
      src/views/match/add/index.vue
  17. 33
      src/views/match/add/set.vue
  18. 18
      src/views/match/add/step1.vue
  19. 79
      src/views/match/add/step2.vue
  20. 65
      src/views/match/add/step3.vue
  21. 106
      src/views/match/list/index.vue
  22. 135
      src/views/match/manage/matchArch.vue
  23. 381
      src/views/match/manage/matchArchList.vue
  24. 95
      src/views/match/manage/matchInfo.vue
  25. 443
      src/views/match/manage/matchRank.vue
  26. 370
      src/views/match/manage/matchReport.vue
  27. 231
      src/views/match/manage/matchSignup.vue
  28. 2
      src/views/match/manage/notice.vue
  29. 3
      src/views/order/AddOrder.vue
  30. 3
      src/views/system/staff.vue

31
package-lock.json generated

@ -4080,6 +4080,22 @@
"safer-buffer": "^2.1.0" "safer-buffer": "^2.1.0"
} }
}, },
"echarts": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.1.tgz",
"integrity": "sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==",
"requires": {
"tslib": "2.3.0",
"zrender": "5.4.1"
},
"dependencies": {
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
}
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -13498,6 +13514,21 @@
"camelcase": "^5.0.0", "camelcase": "^5.0.0",
"decamelize": "^1.2.0" "decamelize": "^1.2.0"
} }
},
"zrender": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.1.tgz",
"integrity": "sha512-M4Z05BHWtajY2241EmMPHglDQAJ1UyHQcYsxDNzD9XLSkPDqMq4bB28v9Pb4mvHnVQ0GxyTklZ/69xCFP6RXBA==",
"requires": {
"tslib": "2.3.0"
},
"dependencies": {
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
}
} }
} }
} }

@ -12,6 +12,7 @@
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"blueimp-md5": "^2.19.0", "blueimp-md5": "^2.19.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"echarts": "^5.4.1",
"element-theme": "^2.0.1", "element-theme": "^2.0.1",
"element-ui": "^2.13.0", "element-ui": "^2.13.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",

@ -469,3 +469,6 @@ li {
.el-tooltip__popper { .el-tooltip__popper {
max-width: 500px !important; max-width: 500px !important;
} }
.no-atTheMoment > .el-picker-panel__footer > .el-button--text:first-child{
display: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

@ -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: '总分'
}
],
}

@ -137,6 +137,26 @@ let router = new Router({
component: () => import( '../views/match/manage/noticeDetail'), component: () => import( '../views/match/manage/noticeDetail'),
// meta: { title: '数据管理' } // meta: { title: '数据管理' }
}, },
{
path: '/matchArchList',
component: () => import( '../views/match/manage/matchArchList'),
// meta: { title: '数据管理' }
},
{
path: '/matchRank',
component: () => import( '../views/match/manage/matchRank'),
// meta: { title: '数据管理' }
},
{
path: '/matchReport',
component: () => import( '../views/match/manage/matchReport'),
// meta: { title: '数据管理' }
},
{
path: '/matchInfo',
component: () => import( '../views/match/manage/matchInfo'),
// meta: { title: '数据管理' }
},
{ {
path: `/theoreticalCourse`, path: `/theoreticalCourse`,
component: () => import("../views//theoreticalCourse/list"), component: () => import("../views//theoreticalCourse/list"),

@ -11,7 +11,7 @@ if (isDev) {
jumpPath = 'http://192.168.31.125:8087/' // 本地调试-需要启动本地判分点系统 jumpPath = 'http://192.168.31.125:8087/' // 本地调试-需要启动本地判分点系统
host = 'http://121.37.12.51/' host = 'http://121.37.12.51/'
// host = 'https://huorantech.cn/' // host = 'https://huorantech.cn/'
// 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/'// 赓
} else if (isPro) { } else if (isPro) {
jumpPath = 'https://www.huorantech.cn/judgmentPoint/' jumpPath = 'https://www.huorantech.cn/judgmentPoint/'

@ -1,7 +1,8 @@
import Setting from "@/setting"; import Setting from "@/setting";
const host = Setting.apiBaseURL const host = Setting.apiBaseURL
const host1 = 'http://192.168.31.137:9000/' const host1 = 'http://192.168.31.51:9000/'
const host2 = 'http://121.37.12.51/'
const uploadURL = Setting.uploadURL const uploadURL = Setting.uploadURL
export default { export default {
@ -302,8 +303,9 @@ export default {
disabledEventsCompetition: `competition/competition/management/disabledEvents`, disabledEventsCompetition: `competition/competition/management/disabledEvents`,
editCompetition: `competition/competition/management/editCompetition`, editCompetition: `competition/competition/management/editCompetition`,
getCompetition: `competition/competition/management/getCompetition`, getCompetition: `competition/competition/management/getCompetition`,
disabledEventsCompetition: `competition/competition/management/disabledEvents`,
getProjectAssessmentByCompetition: `occupationlab/occupationlab/projectManage/getProjectAssessmentByCompetition`, getProjectAssessmentByCompetition: `occupationlab/occupationlab/projectManage/getProjectAssessmentByCompetition`,
publishCompetition: `competition/competition/management/publishCompetition`,
detailsOfCompetitionStage: `${host1}competition/competition/management/detailsOfCompetitionStage`,
// 赛事内容 // 赛事内容
addCompetitionContent: `competition/competition/content/addCompetitionContent`, addCompetitionContent: `competition/competition/content/addCompetitionContent`,
@ -332,6 +334,9 @@ export default {
excelExport: `competition/competition/registration/excelExport`, excelExport: `competition/competition/registration/excelExport`,
exportDataInBatches: `competition/competition/registration/exportDataInBatches`, exportDataInBatches: `competition/competition/registration/exportDataInBatches`,
queryRegistrationByCondition: `competition/competition/registration/queryRegistrationByCondition`, queryRegistrationByCondition: `competition/competition/registration/queryRegistrationByCondition`,
searchTeam: `competition/competition/team/searchTeam`,
joinCompetitionTeam: `competition/competition/team/joinCompetitionTeam`,
addCompetitionTeam: `competition/competition/team/addCompetitionTeam`,
// 赛事公告 // 赛事公告
addAnnouncement: `competition/competition/announcement/addAnnouncement`, addAnnouncement: `competition/competition/announcement/addAnnouncement`,
amendmentAnnouncement: `competition/competition/announcement/amendmentAnnouncement`, amendmentAnnouncement: `competition/competition/announcement/amendmentAnnouncement`,
@ -339,4 +344,24 @@ export default {
disableAnnouncement: `competition/competition/announcement/disableAnnouncement`, disableAnnouncement: `competition/competition/announcement/disableAnnouncement`,
queryAnnouncementByCompetitionId: `competition/competition/announcement/queryAnnouncementByCompetitionId`, queryAnnouncementByCompetitionId: `competition/competition/announcement/queryAnnouncementByCompetitionId`,
queryAnnouncementDetails: `competition/competition/announcement/queryAnnouncementDetails`, queryAnnouncementDetails: `competition/competition/announcement/queryAnnouncementDetails`,
// 赛事成绩
batchDeleteContestGrade: `${host1}competition/competition/performance/batchDeleteContestGrade`,
batchImportGrades: `${host1}competition/competition/performance/batchImportGrades`,
exportExperimentalResultsInBatch: `${host1}competition/competition/performance/exportExperimentalResultsInBatch`,
performanceExportFailure: `${host1}competition/competition/performance/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`,
rankExportFailure: `${host1}competition/competition/rank/exportFailure`,
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`,
gradeImport: `${host2}template/赛事成绩导入模板.xlsx`,
rankImportTeam: `${host2}template/赛事排名导入模板(团队赛).xlsx`,
rankImportPerson: `${host2}template/赛事排名导入模板(个人赛).xlsx`,
}; };

@ -544,7 +544,6 @@ export default {
}, },
mounted() { mounted() {
console.log(33, this.$store.state.btns.includes('/curriculum:支持多系统组课'))
this.getSubject() this.getSubject()
this.getConfig() this.getConfig()
this.getSystem() this.getSystem()
@ -1025,7 +1024,7 @@ export default {
const id = this.form.cid const id = this.form.cid
const updateTime = this.updateTime const updateTime = this.updateTime
// //
if ((id && updateTime > 1) || (!id && updateTime)) { if ((id && updateTime > 2) || (!id && updateTime)) {
this.$confirm(`编辑的内容未保存,是否保存?`, '提示', { this.$confirm(`编辑的内容未保存,是否保存?`, '提示', {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {

@ -29,14 +29,15 @@
<div class="page-content"> <div class="page-content">
<step1 v-show="step === 1" ref="step1" :editing.sync="editing" @next="next" /> <step1 v-show="step === 1" ref="step1" :editing.sync="editing" @next="next" />
<step2 v-if="step === 2" ref="step2" :setupId.sync="setupId" @next="next" /> <step2 v-if="step === 2" ref="step2" :setupId.sync="setupId" @next="next" />
<step3 v-if="step === 3" ref="step3" :setupId.sync="setupId" @next="next" /> <step3 v-if="step === 3" ref="step3" :setupId.sync="setupId" :competitionId.sync="competitionId" @next="next" />
<step4 v-if="step === 4" /> <step4 v-if="step === 4" />
<div v-if="step !== 4" class="btns"> <div v-if="step !== 4 && showBtns" class="btns">
<!-- 处于编辑状态或者新增才显示这几个按钮 --> <!-- 处于编辑状态(列表点编辑按钮进来默认是查看状态不可编辑点了编辑按钮才可编辑)或者新增才显示这几个按钮 -->
<template v-if="editing || !id"> <template v-if="editing || !id">
<el-button v-if="!id" @click="save(0)">保存{{ releaseType ? '草稿' : '' }}</el-button> <el-button v-if="!id" @click="save(0)">保存{{ releaseType ? '草稿' : '' }}</el-button>
<el-button v-if="!releaseType" type="primary" @click="save(1)" v-auth="'/match:管理:大赛详情:发布'">发布</el-button> <el-button v-if="step === 2 || step === 3" type="primary" @click="prev" v-auth="'/match:管理:大赛详情:发布'">上一步</el-button>
<el-button v-if="!releaseType || (releaseType && step === 3)" type="primary" @click="save(1)" v-auth="'/match:管理:大赛详情:发布'">发布</el-button>
<el-button v-else type="primary" @click="save(id ? 1 : 0, 2)" v-auth="'/match:管理:大赛详情:保存并下一步'">保存并下一步</el-button> <el-button v-else type="primary" @click="save(id ? 1 : 0, 2)" v-auth="'/match:管理:大赛详情:保存并下一步'">保存并下一步</el-button>
</template> </template>
<el-button type="danger" @click="preview" v-auth="'/match:管理:大赛详情:预览'">预览</el-button> <el-button type="danger" @click="preview" v-auth="'/match:管理:大赛详情:预览'">预览</el-button>
@ -59,12 +60,14 @@ export default {
data() { data() {
return { return {
id: this.$route.query.id, id: this.$route.query.id,
competitionId: '',
step: 1, step: 1,
submiting: false, submiting: false,
updateTime: 0, updateTime: 0,
setupId: '', setupId: '',
releaseType: 0, releaseType: 0,
editing: 0 editing: 0,
showBtns: true
}; };
}, },
components: { components: {
@ -82,12 +85,17 @@ export default {
save(status, next = 0) { save(status, next = 0) {
this.$refs['step' + this.step].save(status, next, this.releaseType) this.$refs['step' + this.step].save(status, next, this.releaseType)
}, },
//
prev() {
this.step--
},
// //
next(next, id) { next(next, setupId, competitionId) {
if (!next) { if (!next) {
this.$router.push(`/match?page=${this.$store.state.matchPage}`) this.$router.push(`/match?page=${this.$store.state.matchPage}`)
} else if (next === 2) { } else if (next === 2) {
if (id) this.setupId = id if (setupId) this.setupId = setupId
if (competitionId) this.competitionId = competitionId
this.step++ this.step++
} }
}, },

@ -26,7 +26,8 @@
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"> value-format="yyyy-MM-dd HH:mm:ss"
@change="timeChange">
</el-date-picker> </el-date-picker>
</div> </div>
</div> </div>
@ -98,7 +99,7 @@
<script> <script>
import util from "@/libs/util"; import util from "@/libs/util";
export default { export default {
props: ['form'], props: ['form', 'step1'],
data() { data() {
return { return {
curriculumList: [], curriculumList: [],
@ -109,6 +110,7 @@ export default {
total: 0, total: 0,
sysId: '', sysId: '',
permissionsKeys: ['练习', '考核', '竞赛'], permissionsKeys: ['练习', '考核', '竞赛'],
timeInvalid: false
}; };
}, },
watch: { watch: {
@ -174,10 +176,37 @@ export default {
this.page = val; this.page = val;
this.getProject(); this.getProject();
}, },
//
timeChange(val) {
console.log("🚀 ~ file: set.vue:180 ~ timeChange ~ val", 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() { save() {
const { form } = this const { form } = this
if (!form.time.length) return util.warningMsg('请选择比赛时间') if (!form.time.length) return util.warningMsg('请选择比赛时间')
// if (this.timeInvalid) return util.warningMsg('')
if (!form.cid) return util.warningMsg('请选择课程') if (!form.cid) return util.warningMsg('请选择课程')
if (!form.projectId) return util.warningMsg('请选择项目') if (!form.projectId) return util.warningMsg('请选择项目')
const { systemId, projectName } = this.projects.find(e => e.projectId == form.projectId) const { systemId, projectName } = this.projects.find(e => e.projectId == form.projectId)

@ -116,7 +116,7 @@
<el-input placeholder="请输入人数" v-model="form.completeCompetitionSetup.quantityLimit" type="number"></el-input> <el-input placeholder="请输入人数" v-model="form.completeCompetitionSetup.quantityLimit" type="number"></el-input>
</div> </div>
</el-form-item> </el-form-item>
<template v-else> <template v-if="form.completeCompetitionSetup.competitionType">
<el-form-item label="报名团队数上限"> <el-form-item label="报名团队数上限">
<div class="input-center"> <div class="input-center">
<el-input placeholder="请输入团队数" v-model="form.completeCompetitionSetup.quantityLimit" type="number"></el-input> <el-input placeholder="请输入团队数" v-model="form.completeCompetitionSetup.quantityLimit" type="number"></el-input>
@ -238,8 +238,8 @@ export default {
} }
}, },
fileName: '', fileName: '',
signupTime: '', signupTime: [],
playTime: '', playTime: [],
sponsorList: [""], sponsorList: [""],
undertakerList: [""], undertakerList: [""],
fileList: [], fileList: [],
@ -368,8 +368,8 @@ export default {
id && this.$post(`${this.api.getCompetition}?competitionId=${id}`).then(({ competition }) => { id && this.$post(`${this.api.getCompetition}?competitionId=${id}`).then(({ competition }) => {
this.$parent.releaseType = competition.releaseType this.$parent.releaseType = competition.releaseType
this.$parent.setupId = competition.completeCompetitionSetup.setupId this.$parent.setupId = competition.completeCompetitionSetup.setupId
this.signupTime = [competition.signUpStartTime, competition.signUpEndTime] if (competition.signUpStartTime) this.signupTime = [competition.signUpStartTime, competition.signUpEndTime]
this.playTime = [competition.playStartTime, competition.playEndTime] if (competition.playStartTime) this.playTime = [competition.playStartTime, competition.playEndTime]
this.sponsorList = competition.sponsor.split(",") this.sponsorList = competition.sponsor.split(",")
this.undertakerList = competition.undertaker.split(",") this.undertakerList = competition.undertaker.split(",")
// //
@ -591,7 +591,7 @@ export default {
let signUpEndTime = new Date(form.signUpEndTime).getTime(); let signUpEndTime = new Date(form.signUpEndTime).getTime();
let playStartTime = new Date(form.playStartTime).getTime(); let playStartTime = new Date(form.playStartTime).getTime();
// if (signUpStartTime && now > signUpStartTime) return util.warningMsg(""); // if (signUpStartTime && now > signUpStartTime) return util.warningMsg("");
if (!form.playStartTime && status == 1) return util.warningMsg("请选择竞赛时间"); if (!form.playStartTime) return util.warningMsg("请选择竞赛时间");
if (playStartTime && playStartTime < signUpEndTime) return util.warningMsg("竞赛时间不能早于报名结束时间"); if (playStartTime && playStartTime < signUpEndTime) return util.warningMsg("竞赛时间不能早于报名结束时间");
const { competitionType, minTeamSize, maxTeamSize, isNeedCode, invitationCode } = form.completeCompetitionSetup const { competitionType, minTeamSize, maxTeamSize, isNeedCode, invitationCode } = form.completeCompetitionSetup
// //
@ -601,7 +601,7 @@ export default {
if (minTeamSize >= maxTeamSize) return util.warningMsg('团队人数上限不得小于下限') if (minTeamSize >= maxTeamSize) return util.warningMsg('团队人数上限不得小于下限')
} }
if (isNeedCode && (!invitationCode || invitationCode.length !== 4)) return util.warningMsg('请填写四位数邀请码') if (isNeedCode && (!invitationCode || invitationCode.length !== 4)) return util.warningMsg('请填写四位数邀请码')
if (!form.description && status == 1) return util.warningMsg("请填写竞赛详情"); if (!form.description) return util.warningMsg("请填写竞赛详情");
} }
form.publishStatus = status form.publishStatus = status
@ -613,9 +613,9 @@ export default {
this.$emit('next', next) this.$emit('next', next)
}).catch(err => {}); }).catch(err => {});
} else { } else {
this.$post(this.api.addCompetition, form).then(({ setupId }) => { this.$post(this.api.addCompetition, form).then(({ competitionId, setupId }) => {
util.successMsg("创建成功"); util.successMsg("创建成功");
this.$emit('next', next, setupId) this.$emit('next', next, setupId, competitionId)
}).catch(err => {}); }).catch(err => {});
} }
}, },

@ -28,7 +28,7 @@
) )
</div> </div>
</el-form-item> </el-form-item>
<el-form-item v-if="form.rule === 1" prop="resultCalculationMethod" label="总成绩计算方式"> <el-form-item prop="resultCalculationMethod" label="总成绩计算方式">
<el-radio v-model="form.resultCalculationMethod" :label="0">各阶段成绩加权求和</el-radio> <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="1">各阶段成绩直接求和</el-radio>
<el-radio v-model="form.resultCalculationMethod" :label="2">各阶段成绩取平均值</el-radio> <el-radio v-model="form.resultCalculationMethod" :label="2">各阶段成绩取平均值</el-radio>
@ -88,10 +88,26 @@
占总成绩权重 占总成绩权重
<el-input v-model.number="item.pointWeight" type="number" style="width: 150px;"></el-input> % <el-input v-model.number="item.pointWeight" type="number" style="width: 150px;"></el-input> %
</div> </div>
<div class="line">
<span class="req">*</span>
成绩公布时间
阶段比赛结束后
<el-input v-model.number="item.resultAnnouncementTime" 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> </div>
</el-form-item> </el-form-item>
</template> </template>
<el-form-item v-else prop="stageNum" label="规则设置"> <template v-else>
<el-form-item prop="stageNum" label="规则设置">
<div class="step-set"> <div class="step-set">
<div class="line"> <div class="line">
<span class="req">*</span> <span class="req">*</span>
@ -108,15 +124,16 @@
</el-form-item> </el-form-item>
<el-form-item prop="resultAnnouncementTime" label="成绩公布时间"> <el-form-item prop="resultAnnouncementTime" label="成绩公布时间">
阶段比赛结束后 阶段比赛结束后
<el-input v-model.number="form.resultAnnouncementTime" style="width: 120px"></el-input> <el-input v-model.number="form.competitionStageList[0].resultAnnouncementTime" style="width: 120px"></el-input>
小时公布阶段比赛成绩 小时公布阶段比赛成绩
</el-form-item> </el-form-item>
<el-form-item prop="resultsDetails" label="是否公布成绩详情"> <el-form-item prop="resultsDetails" label="是否公布成绩详情">
<el-radio v-model="form.resultsDetails" :label="0"></el-radio> <el-radio v-model="form.competitionStageList[0].resultsDetails" :label="0"></el-radio>
<el-radio v-model="form.resultsDetails" :label="1"></el-radio> <el-radio v-model="form.competitionStageList[0].resultsDetails" :label="1"></el-radio>
<p class="tips">若选择则公布成绩详情竞赛结束后参赛者可查看自己的比赛成绩得分详情</p> <p class="tips">若选择则公布成绩详情竞赛结束后参赛者可查看自己的比赛成绩得分详情</p>
<p class="tips">若选择则不公布成绩详情参赛者只能知晓自己的分数及排名不能查看得分详情</p> <p class="tips">若选择则不公布成绩详情参赛者只能知晓自己的分数及排名不能查看得分详情</p>
</el-form-item> </el-form-item>
</template>
</el-form> </el-form>
</div> </div>
</div> </div>
@ -133,9 +150,7 @@ export default {
id: this.$route.query.id, id: this.$route.query.id,
step1: this.$parent.$refs.step1.form, step1: this.$parent.$refs.step1.form,
form: { form: {
resultAnnouncementTime: '',
resultCalculationMethod: 0, resultCalculationMethod: 0,
resultsDetails: 0,
rule: 0, rule: 0,
stageNum: 3, stageNum: 3,
teamLimit: 1, teamLimit: 1,
@ -149,10 +164,12 @@ export default {
peopleLimit: '', peopleLimit: '',
percentageLimit: '', percentageLimit: '',
scoreLimit: '', scoreLimit: '',
operator: '>+', operator: '>',
score: '', score: '',
teamNumLimit: '', teamNumLimit: '',
teamNumLimitOpt: 0, teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
}, },
{ {
method: 0, method: 0,
@ -163,10 +180,12 @@ export default {
peopleLimit: '', peopleLimit: '',
percentageLimit: '', percentageLimit: '',
scoreLimit: '', scoreLimit: '',
operator: '>+', operator: '>',
score: '', score: '',
teamNumLimit: '', teamNumLimit: '',
teamNumLimitOpt: 0, teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
}, },
{ {
method: 0, method: 0,
@ -177,10 +196,12 @@ export default {
peopleLimit: '', peopleLimit: '',
percentageLimit: '', percentageLimit: '',
scoreLimit: '', scoreLimit: '',
operator: '>+', operator: '>',
score: '', score: '',
teamNumLimit: '', teamNumLimit: '',
teamNumLimitOpt: 0, teamNumLimitOpt: 0,
resultAnnouncementTime: '',
resultsDetails: 0,
} }
] ]
}, },
@ -265,7 +286,20 @@ export default {
this.$get(this.api.getCompetitionRule, { this.$get(this.api.getCompetitionRule, {
competitionId: this.id competitionId: this.id
}).then(res => { }).then(res => {
if (res.competitionRule) this.form = res.competitionRule 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
}
if (e.teamNumLimit) e.teamNumLimitOpt = e.teamNumLimit ? 1 : 0
e.teamCalculationMethod = +e.teamCalculationMethod
})
this.form = rule
}
}).catch(res => {}) }).catch(res => {})
}, },
// //
@ -284,13 +318,14 @@ export default {
const { rule } = form const { rule } = form
let invalid = 0 let invalid = 0
let pointWeight = 0 let pointWeight = 0
const { step1 } = this
for (const e of form.competitionStageList) { for (const e of form.competitionStageList) {
if (!e.stageName) { if (rule !== 2 && !e.stageName) {
invalid = 1 invalid = 1
util.errorMsg('请输入阶段名称') util.errorMsg('请输入阶段名称')
break break
} }
const { competitionType } = this.step1.completeCompetitionSetup // 01 const { competitionType, maxTeamSize } = step1.completeCompetitionSetup // 01
// rule: 012 // rule: 012
// //
if (!rule) { if (!rule) {
@ -301,18 +336,28 @@ export default {
break break
} }
pointWeight += e.pointWeight // pointWeight += e.pointWeight //
if (competitionType && e.teamNumLimitOpt && e.teamNumLimit === '') { } else if (rule === 1) { //
if (e.score !== '') e.scoreLimit = e.operator + e.score
}
if (rule !== 2 && competitionType && e.teamNumLimitOpt) {
if (e.teamNumLimit === '') {
invalid = 1 invalid = 1
util.errorMsg('请输入团队参数人数限制') util.errorMsg('请输入团队参数人数限制')
break break
} else if (e.teamNumLimit > maxTeamSize) {
invalid = 1
util.errorMsg('团队参数人数不得大于团队人数上限')
break
} }
} else if (rule === 1) { // }
e.scoreLimit = e.operator + e.score if (!e.resultsDetails && e.resultAnnouncementTime === '') {
invalid = 1
util.errorMsg('请填写成绩公布时间')
break
} }
} }
if (invalid) return if (invalid) return
if (!rule && pointWeight > 0 && pointWeight !== 100) return util.errorMsg('权重须等于100,请重新输入') if (!rule && pointWeight > 0 && pointWeight !== 100) return util.errorMsg('权重须等于100,请重新输入')
if (form.resultAnnouncementTime === '') return util.errorMsg('请填写成绩公布时间')
if (form.ruleId) { if (form.ruleId) {
this.$post(this.api.editCompetitionRule, form).then(res => { this.$post(this.api.editCompetitionRule, form).then(res => {
util.successMsg("修改成功"); util.successMsg("修改成功");

@ -19,7 +19,8 @@
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"> value-format="yyyy-MM-dd HH:mm:ss"
@change="timeChange">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<template v-if="item.method === 2"> <template v-if="item.method === 2">
@ -56,7 +57,7 @@
</div> </div>
</div> </div>
<set v-if="setVisible" :form.sync="form[curStep]" @hideSet="hideSet" /> <set v-if="setVisible" :form.sync="form[curStep]" :step1.sync="step1" @hideSet="hideSet" />
</div> </div>
</template> </template>
@ -64,9 +65,10 @@
import util from "@/libs/util"; import util from "@/libs/util";
import set from './set' import set from './set'
export default { export default {
props: ['setupId'], props: ['setupId', 'competitionId'],
data() { data() {
return { return {
id: this.$route.query.id,
step1: this.$parent.$refs.step1.form, step1: this.$parent.$refs.step1.form,
step2: this.$parent.$refs.step2.form, step2: this.$parent.$refs.step2.form,
nums: ['一', '二', '三'], nums: ['一', '二', '三'],
@ -100,7 +102,8 @@ export default {
}, },
form: [], form: [],
setVisible: false, setVisible: false,
curStep: 0 curStep: 0,
timeInvalid: false
}; };
}, },
components: { components: {
@ -116,6 +119,7 @@ export default {
}, },
}, },
mounted() { mounted() {
console.log(33, this.step1)
this.handleForm() this.handleForm()
}, },
methods: { methods: {
@ -124,9 +128,12 @@ export default {
// id // id
this.$post(`${this.api.queryCompetitionStageBySetupId}?setupId=${this.setupId}`).then(res => { this.$post(`${this.api.queryCompetitionStageBySetupId}?setupId=${this.setupId}`).then(res => {
res.competitionStages.map(e => { res.competitionStages.map(e => {
const form = JSON.parse(JSON.stringify(this.originForm)) const form = e.competitionContent || JSON.parse(JSON.stringify(this.originForm))
form.stageId = e.stageId if (form.startTime) form.time = [form.startTime, form.endTime]
form.offlineButton = !!form.offlineButton
form.onlineButton = !!form.onlineButton
form.method = e.method form.method = e.method
form.stageId = e.stageId
form.stageName = e.stageName form.stageName = e.stageName
this.form.push(form) this.form.push(form)
}) })
@ -135,12 +142,44 @@ export default {
// //
toSet(i) { toSet(i) {
this.curStep = i this.curStep = i
this.$parent.showBtns = false
this.setVisible = true this.setVisible = true
}, },
// //
hideSet(form) { hideSet(form) {
if (form) this.form[this.curStep] = form if (form) this.form[this.curStep] = form
this.setVisible = false this.setVisible = false
this.$parent.showBtns = true
},
//
timeChange(val) {
console.log("🚀 ~ file: set.vue:180 ~ timeChange ~ val", 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.$post(`${this.api.publishCompetition}?competitionId=${this.competitionId}&publishStatus=1`).then(res => {}).catch(err => {})
}, },
// //
save(status, next = 0) { save(status, next = 0) {
@ -162,20 +201,16 @@ export default {
e.offlineButton = e.offlineButton ? 1 : 0 e.offlineButton = e.offlineButton ? 1 : 0
e.onlineButton = e.onlineButton ? 1 : 0 e.onlineButton = e.onlineButton ? 1 : 0
} }
if (this.timeInvalid) return util.errorMsg('设置的阶段比赛时间必须要在第一步设置的竞赛时间范围内,请重新设置。')
if (invalid) return if (invalid) return
if (form[0].contentId) { this.$post(this.api[form[0].contentId ? 'editCompetitionContent' : 'addCompetitionContent'], {
this.$post(this.api.editCompetitionContent, form).then(res => {
util.successMsg("修改成功");
this.$emit('next', next)
}).catch(err => {})
} else {
this.$post(this.api.addCompetitionContent, {
competitionContents: form competitionContents: form
}).then(res => { }).then(res => {
util.successMsg("创建成功"); //
!form[0].contentId && status && this.publish(status)
util.successMsg((status ? '发布' : '保存') + '成功')
this.$emit('next', next) this.$emit('next', next)
}).catch(err => {}) }).catch(err => {})
}
}, },
} }
}; };

@ -70,14 +70,15 @@
{{ 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="290">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.playStartTime }} ~ {{ scope.row.playEndTime }} {{ scope.row.playStartTime }} ~ {{ scope.row.playEndTime }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" width="160"></el-table-column> <el-table-column prop="createTime" label="创建时间" align="center" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="180"> <el-table-column label="操作" align="center" width="260">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="scope.row.playingStages.length" type="text" @click="editEndTime(scope.row)" v-auth>修改结束时间</el-button>
<el-button type="text" @click="manage(scope.row)" v-auth>管理</el-button> <el-button type="text" @click="manage(scope.row)" v-auth>管理</el-button>
<el-button type="text" @click="delData(scope.row)" v-auth>删除</el-button> <el-button type="text" @click="delData(scope.row)" v-auth>删除</el-button>
<el-switch <el-switch
@ -96,13 +97,38 @@
<el-pagination background layout="total, prev, pager, next" :total="total" @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="修改当前阶段结束时间" :visible.sync="modifyVisible" width="900px" :close-on-click-modal="false">
<el-table :data="curRow.playingStages" class="table" ref="table" stripe header-align="center">
<el-table-column prop="stageName" label="阶段名称" min-width="100" align="center"></el-table-column>
<el-table-column label="竞赛起止时间" width="300" align="center">
<template slot-scope="scope">
{{ scope.row.startTime + ' ~ ' + scope.row.endTime }}
</template>
</el-table-column>
<el-table-column label="结束时间调整为" align="center" width="280">
<template slot-scope="scope">
<el-date-picker
popper-class="no-atTheMoment"
v-model="scope.row.newEndTime"
placeholder="请选择结束时间"
type="datetime"
:picker-options="pickerOptions">
</el-date-picker>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="modifyVisible = false"> </el-button>
<el-button size="small" type="primary" @click="modifySubmit"> </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 Setting from "@/setting";
export default { export default {
name: "match", name: "match",
data() { data() {
@ -173,7 +199,19 @@ export default {
page: +this.$route.query.page || 1, page: +this.$route.query.page || 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
transferPublishStatus: [ "未发布", "已发布"] transferPublishStatus: [ "未发布", "已发布"],
modifyVisible: false,
curRow: {
playingStages: []
},
pickerOptions: {
shortcuts: [{
text: '此刻',
onClick(picker) {
picker.$emit('pick', new Date(Date.now() + 1000 * 60 * 5))
}
}]
}
}; };
}, },
watch: { watch: {
@ -220,7 +258,25 @@ export default {
platformSource: form.platformSource === '' ? null : form.platformSource, platformSource: form.platformSource === '' ? null : form.platformSource,
startTime: form.startTime || null, startTime: form.startTime || null,
}).then(({ data }) => { }).then(({ data }) => {
this.matchData = data.records const list = data.records
const now = new Date()
list.map(e => {
e.playingStages = []
//
if (now >= new Date(e.playStartTime) && now <= new Date(e.playEndTime)) {
//
if (e.competitionStageList) {
for (const n of e.competitionStageList) {
//
if (now >= new Date(n.startTime) && now <= new Date(n.endTime)) {
e.playingStages.push(n)
}
}
}
}
})
this.matchData = list
console.log("🚀 ~ file: index.vue:286 ~ getList ~ list", list)
this.total = data.total this.total = data.total
this.$refs.table.clearSelection() this.$refs.table.clearSelection()
if (!this.matchData.length && this.total) { if (!this.matchData.length && this.total) {
@ -231,10 +287,6 @@ export default {
}, },
getData() { getData() {
this.getList() this.getList()
// if (!Setting.isDev) {
// clearInterval(this.timer)
// this.timer = setInterval(this.getList, 1000)
// }
}, },
initData() { initData() {
this.page = 1; this.page = 1;
@ -243,6 +295,30 @@ export default {
add() { add() {
this.$router.push("/addMatch"); this.$router.push("/addMatch");
}, },
//
editEndTime(row) {
this.modifyVisible = true
row.newEndTime = ''
this.curRow = row
},
//
modifySubmit() {
const row = this.curRow
const data = []
row.competitionStageList.map(e => {
const stage = row.playingStages.find(n => n.contentId === e.contentId && n.newEndTime)
if (stage && stage.newEndTime) stage.endTime = this.formatDate('yyyy-MM-dd hh:mm:ss', stage.newEndTime)
data.push(stage || e)
})
this.$post(this.api.editCompetitionContent, {
competitionContents: data
}).then(res => {
util.successMsg('修改成功')
this.modifyVisible = false
this.getData()
}).catch(err => {})
},
//
manage(row) { manage(row) {
this.$router.push(`/matchManage?id=${row.id}&name=${row.competitionName}`) this.$router.push(`/matchManage?id=${row.id}&name=${row.competitionName}`)
}, },
@ -310,8 +386,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: 0 // (01) type: 0 // (01)
}).then(res => { }).then(res => {
@ -325,13 +401,13 @@ 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: 0 // (01) type: 0 // (01)
}).then(res => { }).then(res => {
row.publishStatus = 1 row.publishStatus = 1
this.$post(this.api.editContest, row).then(res => { this.$post(`${this.api.publishCompetition}?competitionId=${row.id}&publishStatus=1`).then(res => {
this.getData() this.getData()
val == 1 ? util.warningMsg('禁用成功') : util.successMsg('启用成功') val == 1 ? util.warningMsg('禁用成功') : util.successMsg('启用成功')
}).catch(err => {}) }).catch(err => {})
@ -342,7 +418,7 @@ export default {
} else { } else {
this.disable(val, row) this.disable(val, row)
} }
} },
} }
}; };
</script> </script>

@ -1,19 +1,19 @@
<template> <template>
<!-- 报名人员 --> <!-- 报名人员 -->
<div class="page-content" style="padding: 24px"> <div class="page-content" style="padding: 24px">
<el-collapse v-model="curStep" accordion> <el-collapse v-model="curStep">
<el-collapse-item title="一致性 Consistency" :name="1"> <el-collapse-item v-for="(item, i) in list" :key="i" :title="item.stageName" :name="i">
<div>与现实生活一致与现实生活的流程逻辑保持一致遵循用户习惯的语言和概念</div> <div class="line">
<div>在界面中一致所有的元素和结构需保持一致比如设计样式图标和文本元素的位置等</div> <span>比赛方式{{ item.methodName }}</span>
</el-collapse-item> <span>比赛形式{{ item.competitionType ? '团队赛' : '个人赛' }}</span>
<el-collapse-item title="反馈 Feedback" :name="2"> <span>赛制{{ item.ruleName }}</span>
<div>控制反馈通过界面样式和交互动效让用户可以清晰的感知自己的操作</div> <span>状态已完成</span>
<div>页面反馈操作后通过页面元素的变化清晰地展现当前状态</div> <span>竞赛起止时间{{ item.startTime + ' ~ ' + item.endTime }}</span>
</el-collapse-item> <div>
<el-collapse-item title="效率 Efficiency" :name="3"> <el-button type="primary" @click="toRank(item, i)">排名</el-button>
<div>简化流程设计简洁直观的操作流程</div> <el-button @click="toArch(item)">成绩管理</el-button>
<div>清晰明确语言表达清晰且表意明确让用户快速理解进而作出决策</div> </div>
<div>帮助用户识别界面简单直白让用户快速识别而非回忆减少用户记忆负担</div> </div>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</div> </div>
@ -21,98 +21,57 @@
<script> <script>
import util from "@/libs/util"; import util from "@/libs/util";
import axios from 'axios' import Const from '@/const/match'
export default { export default {
name: "matchArch", name: "matchArch",
data() { data() {
return { return {
curStep: 1 id: +this.$route.query.id,
list: [],
curStep: 0
}; };
}, },
watch: {
keyword: function(val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.getData();
}, 500);
}
},
mounted() { mounted() {
// this.getData(); this.getData()
}, },
methods: { methods: {
getData() { getData() {
this.$post(this.api.queryApplicantByCondition, { this.$post(this.api.detailsOfCompetitionStage, {
pageNum: this.page, pageNum: 1,
pageSize: this.pageSize, pageSize: 100,
contestId: this.id, contestId: this.id,
keyWord: this.keyword, }).then(({ page }) => {
}).then(({ data }) => { const list = page.records
this.listData = data.records; list.map(e => {
this.totals = data.total; e.methodName = Const.methods.find(n => n.id === e.method).name
this.$refs.table.clearSelection(); e.ruleName = Const.rules.find(n => n.id === e.rule).name
}).catch(res => {
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleCurrentChange(val) {
this.page = val;
this.getData();
},
switchOff(val, row, index) {
this.$put(`${this.api.disableApplicant}/${row.id}/${val}`)
.then(res => {
}) })
.catch(err => { this.list = list
}); }).catch(res => {});
}, },
disalbeAllData() { //
if (this.multipleSelection.length != "") { toRank(row, i) {
let newArr = this.multipleSelection; this.$router.push(`/matchRank?id=${this.id}&stageId=${row.stageId}&index=${i}&method=${row.method}&competitionType=${row.competitionType}&rule=${row.rule}`)
let delList = newArr.map(item => {
return item.id;
});
this.$confirm("确定要禁用吗?", "提示", {
type: "warning"
})
.then(() => {
this.$put(`${this.api.disableContests}?ids=${delList.join(",")}`).then(res => {
this.multipleSelection = [];
util.successMsg("禁用成功");
this.getData();
}).catch(res => {
});
})
.catch(() => {
});
} else {
util.errorMsg("请先选择数据 !");
}
},
exportAll() {
const data = this.multipleSelection
if (data.length) {
data.map((e, i) => e.id = i + 1)
axios.post(this.api.exportDataInBatches, data, {
headers: {
token: this.token
}, },
responseType: 'blob' //
}).then((res) => { toArch(row) {
util.downloadFileDirect(`报名人员.xls`, new Blob([res.data])) this.$router.push(`/matchArchList?id=${this.id}&stageId=${row.stageId}&method=${row.method}&competitionType=${row.competitionType}`)
}).catch(res => {})
} else {
location.href = `${this.api.excelExport}?contestId=${this.id}`
}
} }
} }
}; };
</script> </script>
<style scoped> <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> </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.staffTemplate
},
//
handleExceed(files, fileList) {
util.warningMsg(
`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`
)
},
//
showFaild() {
location.href = `${this.api.exportFailure}?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,95 @@
<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 class="table">
<tr>
<th width="150">姓名</th>
<td></td>
</tr>
<tr>
<th>学号</th>
<td></td>
</tr>
<tr>
<th>学校</th>
<td></td>
</tr>
<tr>
<th>指导老师</th>
<td></td>
</tr>
<tr>
<th>竞赛阶段</th>
<td>
<table class="table">
<tr>
<th width="80">序号</th>
<th>赛项阶段名称</th>
<th>竞赛成绩</th>
</tr>
</table>
</td>
</tr>
</table>
</el-card>
</div>
</template>
<script>
import Setting from "@/setting";
import util from "@/libs/util";
import echarts from "echarts";
export default {
data() {
return {
id: this.$route.query.id,
stageId: this.$route.query.stageId,
method: this.$route.query.method,
competitionType: this.$route.query.competitionType,
rule: this.$route.query.rule,
info: {},
};
},
mounted() {
// this.getData()
},
methods: {
getData() {
this.$post(`${this.api.stageRaceRanking}?competitionId=${this.id}&stageId=${this.stageId}`).then(({ data }) => {
this.list = data
}).catch(res => {})
},
//
show(row) {
//
if (this.competitionType == 1) {
this.teamVisible = true
} else {
}
// this.$router.push(`show?reportId=${row.reportId}`)
},
}
};
</script>
<style lang="scss" scoped>
.table {
width: 100%;
border-collapse: collapse;
th, td {
padding: 12px;
border: 1px solid #ebeef5;
}
th {
background-color: #f6f4ff;
}
}
</style>

@ -0,0 +1,443 @@
<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 shadow="hover" class="m-b-20">
<div class="tabs">
<a v-for="(item, i) in grades" :key="i" class="item" :class="{active: item.stageId == active}" @click="tabChange(item.stageId)">{{ item.stageName }}排名</a>
</div>
<div class="flex-between" style="margin: 20px 0">
<div style="display: inline-flex;align-items: center">
<el-radio v-model="type" :label="0">默认系统排序</el-radio>
<el-radio v-model="type" :label="1">手动上传</el-radio>
<el-button type="primary" :disabled="type === 0" class="ml20" @click="batchImport">上传文件</el-button>
</div>
<div>
<el-button v-if="!type" type="primary" @click="cancelPublish(1)">发布排名</el-button>
<el-button v-if="!type" 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: sessionStorage.getItem("token")
},
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
};
},
mounted() {
this.getStage()
},
methods: {
//
getData() {
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
if (this.rule == 0) this.grades.push({
stageId: 0,
stageName: '总分'
})
this.getData()
}).catch(res => {});
},
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();
},
//
publish() {
const promises = []
const { list, id } = this
const result = []
const isOverall = this.active ? 0 : 1
const data = {
pageNum: 1,
pageSize: 1000,
competitionId: this.id,
}
// idid
// if (this.active) {
// data.stageId = this.active
// } else {
// const ids = this.grades.map(e => e.stageId)
// data.stageIds = ids.splice(0, ids.length - 1).join()
// }
// list.map(e => {
// data.teamId = e.teamId
// const temp = JSON.parse(JSON.stringify(data))
// temp.teamId = e.teamId
// promises.push(new Promise((resolve,reject) => {
// //
// this.$post(this.api[this.active ? 'stageTeamScoreDetails' : 'multipleStageTeamScoreDetails'], temp).then(({ page }) => {
// const { records } = page
// records.map(n => {
// n.teamId = e.teamId
// n.competitionId = id
// n.isOverallRanking = isOverall
// })
// result.push(...records)
// resolve()
// }).catch(res => {})
// }))
// })
// Promise.all(promises).then(_ => {
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
})
this.$post(this.api.publishRanking, this.list).then(res => {
util.successMsg('发布成功!')
}).catch(res => {})
}
// })
},
//
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 => {
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}`, {
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) {
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>
.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,7 +5,7 @@
<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>
<li> <li>
<label>参赛人员状态</label> <label>参赛人员状态</label>
@ -16,7 +16,7 @@
</ul> </ul>
<div> <div>
<el-button type="primary" round @click="add" v-auth="'/match:管理:报名人员:新增'">新增</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> <el-button type="primary" round @click="exportAll" v-auth="'/match:管理:报名人员:导出'">批量导出</el-button>
</div> </div>
</div> </div>
@ -37,25 +37,69 @@
</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="0" :active-value="0"
inactive-value="1" :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="新增参赛人员" :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.customerId"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="userName" label="姓名">
<el-input v-model="form.userName" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item prop="workNumber" label="学号">
<el-input v-model="form.workNumber" placeholder="请输入学号"></el-input>
</el-form-item>
<el-form-item prop="teamId" label="所属团队">
<div style="display: flex;align-items: center">
<el-select v-model="form.teamId" 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 type="text" @click="createTeam">创建团队</el-button>
</div>
</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>
@ -68,7 +112,8 @@ export default {
data() { data() {
return { return {
token: sessionStorage.getItem('token'), token: sessionStorage.getItem('token'),
id: this.$route.query.id, id: +this.$route.query.id,
status: '',
statusList: [ statusList: [
{ {
id: '', id: '',
@ -88,7 +133,44 @@ export default {
multipleSelection: [], multipleSelection: [],
page: 1, page: 1,
pageSize: 10, pageSize: 10,
totals: 0 total: 0,
clients: [],
addVisible: false,
form: {
competitionId: this.$route.query.id,
userName: '',
workNumber: '',
schoolId: '',
teamId: '',
whetherSignUp: 0
},
rules: {
schoolId: [
{ required: true, message: "请选择所属院校", trigger: "change" }
],
userName: [
{ required: true, message: "请输入姓名", trigger: "blur" }
],
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: 0
},
}; };
}, },
watch: { watch: {
@ -100,7 +182,9 @@ export default {
} }
}, },
mounted() { mounted() {
this.initData(); this.initData()
this.getClient()
this.getTeam()
}, },
methods: { methods: {
getData() { getData() {
@ -111,7 +195,7 @@ export default {
keyWord: this.keyword, keyWord: this.keyword,
}).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 => {
}); });
@ -128,36 +212,91 @@ export default {
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 => { .then(res => {
}) })
.catch(err => { .catch(err => {
}); });
}, },
disalbeAllData() { //
if (this.multipleSelection.length != "") { add() {
let newArr = this.multipleSelection; this.addVisible = true
let delList = newArr.map(item => { },
return item.id; //
}); submit() {
this.$refs.form.validate((valid) => {
this.$confirm("确定要禁用吗?", "提示", { if (valid) {
type: "warning" if (this.submiting) return false
}) this.submiting = true
.then(() => { this.$post(this.api.joinCompetitionTeam, this.form).then(res => {
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 => {})
}); }
}) })
.catch(() => { },
}); //
} else { closeAdd() {
util.errorMsg("请先选择数据 !"); this.form = {
competitionId: this.id,
userName: '',
workNumber: '',
schoolId: '',
teamId: '',
whetherSignUp: 0
} }
}, },
//
createTeam() {
this.teamForm = {
competitionId: this.id,
registrationInvitationCode: '',
teamName: '',
invitationCode: '',
whetherSignUp: 0
}
this.teamVisible = true
},
//
getClient() {
const sid = this.$store.state.dataPer.find(e => e.permissionName === '客户管理')
this.$post(this.api.queryCustomer,{
page: 1,
size: 1000,
supplierId: (sid && !sid.all) ? sid.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位数团队邀请码')
this.$post(this.api.addCompetitionTeam, form).then(res => {
this.teamVisible = false
this.form.teamId
}).catch(res => {})
},
//
edit(row) {
},
//
info(row) {
this.$router.push(`/matchInfo?id=${this.id}`)
},
exportAll() { exportAll() {
const data = this.multipleSelection const data = this.multipleSelection
if (data.length) { if (data.length) {
@ -171,13 +310,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}?competitionId=${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>

@ -76,7 +76,7 @@ export default {
}, },
methods: { methods: {
getData() { getData() {
this.$post(`${this.api.queryAnnouncementByCompetitionId}?pageNum=${this.pageNo}&pageSize=${this.pageSize}&CompetitionId=${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()

@ -1540,7 +1540,8 @@ export default {
customerId: this.form.customerId, customerId: this.form.customerId,
id: row.dataOrCourseId, id: row.dataOrCourseId,
startTime: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(), startTime: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(),
endTime: row.endTime endTime: row.endTime,
authority: row.authority
}).then(res => { }).then(res => {
orderRepeat.includes(cId) && orderRepeat.splice(orderRepeat.findIndex(e => e === cId), 1) orderRepeat.includes(cId) && orderRepeat.splice(orderRepeat.findIndex(e => e === cId), 1)
if (res.endTime) { if (res.endTime) {

@ -235,6 +235,7 @@ export default {
} }
} }
const phonePass = (rule, value, callback) => { const phonePass = (rule, value, callback) => {
console.log("🚀 ~ file: staff.vue:238 ~ phonePass ~ value", value)
if (value) { if (value) {
const pattern = /^1[3456789]\d{9}$/ const pattern = /^1[3456789]\d{9}$/
if(pattern.test(value)){ if(pattern.test(value)){
@ -311,7 +312,7 @@ export default {
{ required: true, message: "请选择授权角色", trigger: "change" } { required: true, message: "请选择授权角色", trigger: "change" }
], ],
phone: [ phone: [
{ validator: phonePass, trigger: 'blur' } { required: true, validator: phonePass, trigger: 'blur' }
], ],
email: [ email: [
{ validator: emailPass, trigger: 'blur' } { validator: emailPass, trigger: 'blur' }

Loading…
Cancel
Save