<template> <div class="wrap index"> <div class="banner" :style="{backgroundImage: 'url(' + (form.carouselUrl || 'https://huoran.oss-cn-shenzhen.aliyuncs.com/20220613/png/1536269450851409920.png') + ')'}"></div> <div class="center-con"> <div class="center-wrap"> <breadcrumb ref="breadcrumb" :data="'全部赛事/' + form.name"></breadcrumb> <div class="content"> <div :class="['tool flex-between', {logView: !logView}]"> <el-tabs v-model="curType" @tab-click="typeChange"> <el-tab-pane v-for="(item, index) in typeList" :key="index" :label="item.name" :name="item.id"></el-tab-pane> </el-tabs> <div class="action"> <p class="end-text" v-if="end"> 距离{{ endList[status] }}还有 <em>{{ end }}</em> </p> <div class="m-l-20"> <p v-if="status" :class="['sign-status', {signing: status == 2,signed: status == 1,playing: status == 4}]">{{ form.competitionRegistration ? '已报名' : '未报名' }}</p> <el-dropdown v-if="playingStages.length > 1" @command="chooseStage"> <el-button type="primary" style="background-color: #f96d6d;border: 0;"> 选择竞赛<i class="el-icon-arrow-down el-icon--right"></i> </el-button> <el-dropdown-menu slot="dropdown"> <el-dropdown-item v-for="(item, i) in playingStages" :key="i" :command="item">进入{{ item.stageName }}</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <div v-else-if="status != 4 || (status == 4 && curStage)" class="status" :class="{wait: status == 0,signing: status == 2,signed: status == 1,playing: status == 4,finish: status == 3 || status == 5}" :title="statusList[status]" @click.stop="signup">{{ statusList[status] }}</div> </div> </div> </div> <div class="info"> <h6 class="title">{{ form.name }}</h6> <div class="meta">最近编辑时间:{{ form.updateTime }}</div> </div> <div v-show="curType < 4"> <div class="l-title" id="part1"><img src="@/assets/img/label.png" alt=""> 竞赛信息</div> <div v-if="form.description" class="texts ql-editor" v-html="form.description"></div> <template v-if="form.competitionAnnexList"> <h6 class="p-title">附件下载</h6> <ul class="files"> <li v-for="(item, i) in form.competitionAnnexList" :key="i"> <el-link v-if="item.canPreview" class="m-r-10" type="primary" @click="preview(item)">{{ item.fileName }}</el-link> <span v-else class="fileName">{{ item.fileName }}</span> <el-link type="primary" :underline="false" @click="download(item)">下载</el-link> </li> </ul> </template> <template v-if="form.releaseType"> <div class="l-title"><img src="@/assets/img/label.png" alt=""> 赛程、规则与内容</div> <h6 class="rule-title">共{{ form.competitionStage.length }}个竞赛阶段<template v-if="info.teamLimit">,同一个团队每个成员只能参加一个阶段赛项</template></h6> <div v-for="(rule, i) in form.competitionStage" :key="i" class="rule"> <p style="font-size: 16px;color: #333;">{{ rule.stageName }}</p> <p>比赛时间:{{ rule.startTime && rule.startTime + ' ~ ' + rule.endTime }}</p> <p>比赛方式:{{ methods.find(e => e.id == rule.method) && methods.find(e => e.id == rule.method).name }}</p> <p v-if="rule.method != 2">课程系统:{{ rule.systemName }}</p> <p v-if="rule.onlineButton && rule.method != 2">线上地点:{{ rule.onlineAddress }}</p> <p v-if="rule.offlineButton">线下地点:{{ rule.offlineAddress }}</p> <template v-if="rule.method === 2"> <p>比赛地点:{{ rule.offlineAddress }}</p> <p>比赛内容:{{ rule.contentDescription }}</p> <p>评分规则:{{ rule.scoreRule }}</p> </template> <template v-if="form.completeCompetitionSetup.competitionType"> <p>每个团队参赛人数限制:{{ rule.teamNumLimit || '不限制' }}</p> <p>团队成绩计算方式:{{ teamCalculationMethods.find(e => e.id == rule.teamCalculationMethod) && teamCalculationMethods.find(e => e.id == rule.teamCalculationMethod).name }}</p> </template> <!-- <p v-if="rule.resultAnnouncementTime != 0">阶段比赛结束后{{ rule.resultAnnouncementTime }}小时,公布阶段比赛成绩。</p> --> <div v-if="form.rule === 1 && i !== form.competitionStage.length - 1" class="flex"> <p>晋级规则:</p> <div> <p v-if="rule.peopleLimit">本阶段成绩排名前{{ rule.peopleLimit }}队,可晋级下一阶段比赛</p> <p v-if="rule.percentageLimit">本阶段成绩排名前{{ rule.percentageLimit }}%,可晋级下一阶段比赛</p> <p v-if="rule.scoreLimit">本阶段成绩{{ rule.scoreLimit }}分,可晋级下一阶段比赛</p> </div> </div> </div> </template> <!-- 进展 --> <div class="l-title" id="part2"><img src="@/assets/img/label.png" alt=""> 竞赛进展</div> <ul class="progress" v-if="progress.length"> <li v-for="(item,index) in progress" :key="index" :class="item.status == 0 ? 'not' : (item.status == 1 ? 'ing' : 'done')"> <i class="dot"></i> <p class="name">{{item.title}}</p> <p class="desc">{{item.description}}</p> </li> <img class="rocket" src="@/assets/img/rocket.png" alt=""> </ul> <template v-else> <div class="empty"> <div> <img src="@/assets/img/none.png" alt=""> <p>暂无数据</p> </div> </div> </template> <!-- 公告 --> <div class="l-title" id="part3"><img src="@/assets/img/label.png" alt=""> 通知公告</div> <ul class="notice-list" v-if="notices.length"> <li v-for="(item, i) in notices" :key="i" @click="toNotice(item)"> <h6>{{ item.announcementTitle }}</h6> <p class="meta">{{ item.updateTime }}</p> <div class="des" v-html="item.announcementText"></div> </li> </ul> <template v-else> <div class="empty"> <div> <img src="@/assets/img/none.png" alt=""> <p>暂无通知公告</p> </div> </div> </template> </div> <!-- 竞赛排名 --> <template v-if="curType == 4"> <div class="l-title"><img src="@/assets/img/label.png" alt=""> 竞赛排名</div> <el-tabs v-model="curArch" @tab-click="getRank"> <el-tab-pane v-for="(item, index) in arches" :key="index" :label="item.stageName + '排名'" :name="item.stageId"></el-tab-pane> </el-tabs> <el-table ref="table" :data="ranks" stripe max-height="440px" header-align="center"> <el-table-column type="index" width="60" label="排名" align="center"></el-table-column> <template v-if="form.completeCompetitionSetup.competitionType"> <el-table-column prop="teamName" label="团队名称"></el-table-column> <el-table-column prop="leaderName" label="队长"></el-table-column> </template> <el-table-column v-else prop="userName" label="姓名"></el-table-column> <el-table-column prop="realSchool" label="学校"></el-table-column> <el-table-column prop="school" label="用时"> <template slot-scope="scope"> {{ scope.row.timeSum }}min </template> </el-table-column> <el-table-column prop="score" label="分数"></el-table-column> </el-table> </template> <template v-if="curType == 5"> <div class="l-title m-t-20"><img src="@/assets/img/label.png" alt=""> 参赛信息</div> <table v-if="form.completeCompetitionSetup.competitionType && info.team.captain === 0" 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" maxlength="6" v-model="info.team.invitationCode"></el-input> </td> </tr> </table> <div v-if="form.completeCompetitionSetup.competitionType && info.team.captain === 0 && status < 4" class="m-b-20 text-center"> <el-button type="primary" @click="edit(1)">{{ editing ? '保存' : '编辑' }}</el-button> </div> <table class="table"> <template v-if="!form.completeCompetitionSetup.competitionType || info.team.captain"> <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> <template v-if="form.completeCompetitionSetup.competitionType"> <tr v-if="info.team.captain"> <th width="150">团队名称:</th> <td> <span>{{ info.team.teamName }}</span> </td> </tr> <template v-if="info.team.captain"> <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> <tr v-if="info.team.captain"> <th>团队邀请码:</th> <td> <span>{{ info.team.invitationCode }}</span> </td> </tr> </template> </template> <tr> <th width="130">指导老师:</th> <td v-if="info.team.captain === 0 || !form.completeCompetitionSetup.competitionType"> <div v-if="status != 5" 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> <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> <td v-else> <table class="table tc"> <tr> <th width="60">姓名</th> <th width="100">职务</th> <th width="100">手机号</th> </tr> <template v-if="info.teamInstructors.length"> <tr v-for="(item, i) in info.teamInstructors" :key="i"> <td width="60">{{ item.name }}</td> <td width="100">{{ item.position }}</td> <td width="100">{{ item.phone }}</td> </tr> </template> <tr v-else> <td colspan="3">暂无数据</td> </tr> </table> </td> </tr> <tr> <th>竞赛阶段:</th> <td> <table class="table tc"> <tr> <th width="70">序号</th> <th width="120">赛项阶段名称</th> <template v-if="form.completeCompetitionSetup.competitionType"> <th width="110">参赛人数限制</th> <th>允许参赛人员</th> </template> <th v-if="form.rule === 0" width="70">总分</th> <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 || form.name }}</td> <template v-if="form.completeCompetitionSetup.competitionType"> <td>{{ item.teamNumLimit || '不限制' }}</td> <td> <template v-if="item.participants"> <el-tag v-for="tag in item.participants" :key="tag.name" class="m-r-5 m-b-5" :closable="info.team.captain === 0 && status < 4" @close="removePar(tag, item)"> {{tag.name}} </el-tag> </template> <span v-else class="m-r-5">无</span> <i v-if="info.team.captain === 0 && status < 4" class="el-icon-edit icon" @click="selectPar(item)"></i> </td> </template> <!-- 积分赛才需要显示总分这个字段,直接第一行合并表格 --> <td v-if="form.rule === 0 && !i" :rowspan="info.stages.length">{{ info.totalScore }}</td> <td> <!-- 成绩公布时间到了后,才显示下面的分数和查看详情按钮 --> <template v-if="item.showDetail"> <span v-if="item.score >= 0" class="m-r-10">分数{{item.score}}</span> <!-- 团队赛 or (个人赛 并且 是否公布成绩详情勾选的是 并且 有reportId) --> <el-button v-if="form.completeCompetitionSetup.competitionType || (!form.completeCompetitionSetup.competitionType && item.resultsDetails === 0 && item.reportId)" type="text" @click="show(item)">查看成绩详情</el-button> </template> </td> </tr> </template> <tr v-else> <td colspan="6">暂无数据</td> </tr> </table> <!-- 团队&&队长才显示 --> <el-alert v-if="form.completeCompetitionSetup.competitionType && info.team.captain === 0" style="margin-top: 10px;" :title="'注:请团长(团队创建人)设置各阶段参赛成员,只有被选择的允许参赛成员可进入对应阶段比赛' + (info.teamLimit ? ',每个团队成员只能参加一个赛项阶段' : '') + '!'" type="warning" show-icon> </el-alert> </td> </tr> </table> <!-- 团队&&队长才显示 --> <template v-if="form.completeCompetitionSetup.competitionType && info.team.captain === 0"> <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-alert style="margin: 10px 0;" title="请确保团队成员人数满足大赛要求,否则无法参加比赛" type="success" :closable="false"> </el-alert> <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> </template> </div> </div> </div> <el-dialog title="报名" :visible.sync="peopleSignupVisible" :close-on-click-modal="false" width="300px"> <el-form class="dia-form"> <el-form-item> <el-input placeholder="请输入4位数大赛邀请码" maxlength="4" v-model="peopleSignupForm.registrationInvitationCode"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button size="small" type="primary" @click="peopleSignupSubmit">报名</el-button> <el-button size="small" @click="peopleSignupVisible = false">取消</el-button> </span> </el-dialog> <el-dialog title="报名" :visible.sync="enterVisible" :close-on-click-modal="false" width="300px" @close="enterClose"> <el-form class="dia-form"> <p style="margin-bottom: 5px">请选择要加入的团队</p> <el-form-item> <el-select class="w-100" v-model="enterForm.teamId" filterable> <el-option v-for="(item, i) in teams" :key="i" :label="item.teamName" :value="item.teamId"></el-option> </el-select> </el-form-item> <el-form-item> <el-input placeholder="请输入团队邀请码" maxlength="6" v-model="enterForm.invitationCode"></el-input> </el-form-item> <el-form-item v-if="form.completeCompetitionSetup.isNeedCode"> <el-input placeholder="请输入大赛邀请码" maxlength="6" v-model="enterForm.registrationInvitationCode"></el-input> </el-form-item> <p class="tips"> 查找不到团队?点击 <el-link :underline="false" type="primary" @click="toTeam">创建团队</el-link> </p> </el-form> <span slot="footer" class="dialog-footer"> <el-button size="small" type="primary" @click="enterSubmit">报名</el-button> <el-button size="small" @click="enterVisible = false">取消</el-button> </span> </el-dialog> <el-dialog title="创建团队" :visible.sync="teamVisible" :close-on-click-modal="false" width="300px" @close="teamClose"> <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-item v-if="form.completeCompetitionSetup.isNeedCode"> <el-input placeholder="请输入大赛邀请码" maxlength="6" v-model="teamForm.registrationInvitationCode"></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> <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 && curRow.teamNumLimit" style="margin-top: 15px;font-size: 12px;">注:当前阶段限制{{ curRow.teamNumLimit }}人参赛,且此竞赛每个成员只能参加一个阶段赛项。</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 v-if="curRow.resultsDetails === 0 && item.reportId" 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> <el-dialog :title="curStage ? curStage.stageName : ''" :visible.sync="stageVisible" :close-on-click-modal="false" width="600px" @close="stageClose"> <template v-if="curStage && curStage.competitionStageContentSetting"> <div v-if="curStage.competitionStageContentSetting.systemLink" class="m-b-20"> <span class="fs-14">进入比赛:</span> <el-button type="danger" style="font-size: 13px" @click="toOffline">进入{{ curStage.stageName }}</el-button> </div> <div v-if="curStage.competitionStageContentSetting.fileUrl" class="flex m-b-20 fs-14"> <span style="padding-top: 7px">文件下载:</span> <div> <div v-for="(file, i) in curStage.competitionStageContentSetting.fileList" :key="i"> <span style="margin-right: 10px;color: #606266;">{{ file.name }}</span> <el-button type="text" style="font-size: 14px" @click="download({fileName: file.name, filePath: file.url})">点击下载</el-button> </div> </div> </div> <div class="flex m-b-20"> <span class="fs-14">文件上传:</span> <el-upload class="file-upload flex-1" :disabled="uploading" :before-upload="beforeUpload" :on-remove="handleRemove" :on-error="uploadError" :before-remove="beforeRemove" :on-preview="handlePreview" :limit="1" action="" :on-exceed="handleExceed" :file-list="fileList" :http-request="handleRequest" name="file"> <el-button size="small" type="primary" :loading="uploading" :disabled="!!(fileList && fileList.length) || uploading">{{ uploading ? '正在上传' : fileList && fileList.length ? '已上传' : '上传文件' }}</el-button> <div slot="tip" class="el-upload__tip relative"> <el-progress v-if="uploading" class="m-b-5" :stroke-width="3" :percentage="uploadProgress"></el-progress> <p>请上传大小1G以内的文件,支持常见文件格式。</p> <p>只允许上传一个文件,如有多个文件则需打包再上传。</p> <div v-if="fileList.length" class="download" @click="downloadFile">下载</div> </div> </el-upload> </div> <div class="fs-14">说明:{{ curStage.competitionStageContentSetting.stageExplain }}</div> </template> <span slot="footer" class="dialog-footer"> <el-button size="small" @click="stageVisible = false">关闭</el-button> </span> </el-dialog> </div> </template> <script> import { mapState, mapMutations } from "vuex"; import breadcrumb from '@/components/breadcrumb' import Util from '@/libs/util' import Setting from "@/setting" import Const from '@/const/match' import OSS from 'ali-oss' import OssConfig from '@/components/upload/config.js' import Oss from '@/components/upload/upload.js' export default { name: 'matchdetail', data () { return { headers: { token: Util.local.get(Setting.tokenKey) }, token: Util.local.get(Setting.tokenKey), id: +this.$route.query.id, end: '', status: '', statusList: ["等待报名", "取消报名", "立即报名", "报名截止", "进入竞赛", "已结束"], endList: ["报名开始", "报名截止", "报名截止", "竞赛开始", "竞赛结束", ""], rules: Const.rules, methods: Const.methods, teamCalculationMethods: Const.teamCalculationMethods, timerList: [], form: { name: '', coverUrl: '', description: '', signUpStartTime: '', signUpEndTime: '', playStartTime: '', playEndTime: '', competitionStage: [], completeCompetitionSetup: {}, competitionRegistration: {} }, curType: '1', typeList: [ { id: '1', name: '竞赛信息' }, { id: '2', name: '竞赛进展' }, { id: '3', name: '通知公告' }, { id: '4', name: '竞赛排名' }, ], progress: [], timer: null, rankTimer: null, notices: [], noticeDetail: {}, curArch: '0', arches: [], ranks: [], enterVisible: false, enterForm: { competitionId: this.$route.query.id, teamId: '', invitationCode: '', registrationInvitationCode: '', whetherSignUp: 1 }, teamVisible: false, teams: [], teamNameRepeat: false, teamForm: { competitionId: this.$route.query.id, registrationInvitationCode: '', teamName: '', invitationCode: '', whetherSignUp: 1 }, curStage: null, choosing: false, originInfo: {}, info: { isCaption: 0, person: {}, caption: {}, team: { captain: 1, invitationCode: '' }, stages: [], teamDetail: [], teamInstructors: [] }, originIns: { id: '', position: '', name: '', phone: '', }, checkedPlayer: '', transferVisible: false, editing: false, memberVisible: false, members: [], curRow: {}, chooseVisible: false, checkedMember: '', checkedMembers: [], chooses: [], lastType: 1, playingStages: [], peopleSignupVisible: false, peopleSignupForm: { registrationInvitationCode: '' }, submiting: false, stageVisible: false, filesResult: {}, fileList: [], curFileId: '', client: null, uploading: false, uploadProgress: 0, now: '' }; }, computed: { ...mapState("user", [ "userId", 'account', 'logView' ]), }, components: { breadcrumb }, mounted () { this.$once('hook:beforeDestroy', function () { clearInterval(this.timer) clearInterval(this.rankTimer) this.timerList.forEach(n => { clearTimeout(n) }) this.timerList = [] }) this.getData() this.getProgress() this.getNotice() this.getTeam() this.initOss() }, methods: { ...mapMutations('match', [ 'SET_SOURCE' ]), getData () { // 获取竞赛信息 clearInterval(this.timer) this.$post(`${this.api.getCompetition}?competitionId=${this.id}`).then(async ({ competition }) => { const list = competition.competitionAnnexList // 附件 if (list) { list.map(e => { const { filePath } = e e.canPreview = Util.canPreview(filePath.substr(filePath.lastIndexOf('.') + 1)) }) } this.form = competition const reg = competition.competitionRegistration // 选择的赛事类型为设置完整比赛的才展示竞赛排名和参赛信息 if (competition.releaseType) { const arches = [] // 积分赛才有总分排名 competition.rule === 0 ? arches.push({ stageId: '0', stageName: '总分' }) : (this.curArch = competition.competitionStage[0].stageId + '') arches.push(...competition.competitionStage) arches.map(e => e.stageId = e.stageId + '') this.arches = arches // 报名后才显示参赛信息 if (reg && !this.typeList.find(e => e.id == 5)) { this.typeList.push({ id: '5', name: '参赛信息' }) this.getInfo() } this.token && this.intervalRank() } else { this.typeList = this.typeList.slice(0, 3) } this.$refs.breadcrumb.update('全部赛事/' + competition.name) this.now = await Util.getNow() this.handleStatus() this.timer = setInterval(() => { this.now = new Date(this.now.setSeconds(this.now.getSeconds() + 1)) this.handleStatus() }, 1000) }).catch(err => { }) }, // 定时处理时间及状态 handleStatus () { const { form } = this let total = '' let time = '' let status = '' let signUpStartTime = new Date(this.core.dateCompatible(form.signUpStartTime)) // 报名开始时间 let signUpEndTime = new Date(this.core.dateCompatible(form.signUpEndTime)) // 报名结束时间 let playStartTime = new Date(this.core.dateCompatible(form.playStartTime)) // 比赛开始时间 let playEndTime = new Date(this.core.dateCompatible(form.playEndTime)) // 比赛结束时间 const { now } = this if (now < signUpStartTime) { // 报名没开始 status = 0 total = signUpStartTime - now } else if (now > signUpStartTime && now < signUpEndTime) { // 报名进行中 // 1已报名,2立即报名(没登录的情况下,直接显示立即报名,登录了则取报名信息,有则已报名,无则立即报名) status = this.token ? (form.competitionRegistration ? 1 : 2) : 2 total = signUpEndTime - now } else if (now > signUpEndTime && now < playStartTime) { // 报名结束了,但比赛没开始 status = 3 total = playStartTime - now } else if (now > playStartTime && now < playEndTime) { // 比赛进行中 // 如果是完整比赛 if (form.releaseType) { // 进行中的赛事,则遍历每个阶段的开始结束时间,看阶段比赛是否开始 let curStage = null const stages = form.competitionStage if (stages) { this.playingStages = [] form.competitionRegistration && stages.forEach(e => { if (now >= new Date(e.startTime) && now <= new Date(e.endTime) && (e.method !== 2 || this.offlineCanEntry(e))) this.playingStages.push(e) }) let endText = '' for (const i in stages) { const e = stages[i] const startTime = new Date(e.startTime) const endTime = new Date(e.endTime) if (now < startTime) { // 阶段比赛未开始,不显示进入比赛按钮 endText = '阶段开始' total = startTime - now break } else if (now >= startTime && now <= endTime) { // 阶段比赛进行中,显示进入比赛按钮 // 非线下赛事 if (e.method !== 2) { if (form.competitionRegistration) { // 报名了才能进入比赛 this.statusList[4] = e.count ? '已提交' : '进入' + e.stageName curStage = e } else if (!this.token) { this.statusList[4] = '进入' + e.stageName curStage = e } } else if (this.offlineCanEntry(e) && form.competitionRegistration) { // 线下(输入了系统链接或者上传文件选择了是,才需要显示进入按钮) // 当系统链接为空,且上传文件为否时,无需展示入口 // 当系统链接不为空,且上传文件为否时,点击入口,直接跳转到链接页面,无需弹窗 // 当上传文件为是时,点击入口需弹窗,共两种样式 this.statusList[4] = '进入' + e.stageName curStage = e } endText = '阶段结束' total = endTime - now break } else if (stages[i + 1] && now > endTime && now < new Date(stages[i + 1].startTime)) { // 过了该阶段的结束时间,但是没到下个阶段的开始时间,不显示进入比赛按钮 endText = '阶段开始' total = new Date(stages[i + 1].startTime) - now break } else if (i === stages.length - 1) { // 当前时间在比赛开始结束时间之间,并且是最后一个阶段结束时间之后 this.$set(form, 'stageName', '') endText = '竞赛结束' total = playEndTime - now break } } this.endList[4] = endText } if (!this.choosing) this.curStage = curStage } else { // 仅发布信息 total = playEndTime - now } status = 4 } else if (now > playEndTime) { // 比赛结束 status = 5 } this.status = status total = total / 1000 --total if (total > 86400) { // 超过一天则显示天数 // clearInterval(this.timer) this.end = Math.floor(total / 86400) + '天' } else if (total > 0) { // 一天之内,显示时分秒 let hours = Math.floor(total / (60 * 60)) let minutes = Math.floor(total % (60 * 60) / 60) let seconds = Math.floor(total % (60 * 60) % 60) time = `${this.core.formateTime(hours)}:${this.core.formateTime(minutes)}:${this.core.formateTime(seconds)}` if (total > 0) this.end = time } else if (this.status === 5) { // 竞赛结束,清除定时器 clearInterval(this.timer) } }, // 该阶段是否符合线下能进入比赛的条件 offlineCanEntry (stage) { return stage.method === 2 && stage.competitionStageContentSetting && !!(stage.competitionStageContentSetting.systemLink || stage.competitionStageContentSetting.whetherToUploadFiles) }, // 获取竞赛信息 getInfo () { this.$post(`${this.api.entryInformation}?competitionId=${this.id}`).then(async res => { const info = res.entryInformation // 如果是队长,并且没有指导老师,默认添加一个空的 if (info.team && !info.team.captain && !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)) // 把参赛人员的名字和accountId处理成一个数组 info.stages && info.stages.map(e => { if (e.participantAccountIds && e.teamParticipantIds) { const accountIds = e.participantAccountIds.split(',').map(n => +n) const names = e.teamParticipantIds.split(',') const participants = [] accountIds.map((n, i) => { participants.push({ id: n, name: names[i] }) }) e.participants = participants } }) // 设置定时器,阶段比赛结束后公布成绩,到时间后调本接口 const now = await Util.getNow() this.form.competitionStage && this.form.competitionStage.map(e => { // 如果成绩公布时间输入的值大于0(输入的是0则直接公布;没输入值则不公布) const time = e.resultAnnouncementTime if (time >= 0) { const endTime = new Date(e.endTime).getTime() + time * 3600000 // 阶段结束时间+成绩公布时间(成绩公布时间单位是小时,所以要转化为毫秒) console.log("🚀 ~ this.$post ~ endTime:", time, endTime - now) if (now > endTime) { // 如果到了公布时间 info.stages.find(n => n.stageId == e.stageId).showDetail = 1 } else if (endTime - now < 86400000) { // 没有到公布时间,则加定时器,到点后调用 this.timerList.push(setTimeout(this.getInfo, endTime - now)) } } }) this.info = info }).catch(err => { }); }, // 编辑保存 edit (showMsg) { if (this.editing || !showMsg) { const { teamId, teamName, invitationCode } = this.info.team this.$post(this.api.editCompetitionTeam, { competitionId: this.id, teamId, teamName, invitationCode, whetherSignUp: 0 }).then(res => { this.editing = false this.getInfo() showMsg && Util.successMsg('保存成功') }).catch(res => { }) } else { this.editing = !this.editing } }, getProgress () { // 获取竞赛进展 this.$get(this.api.getCompetitionProgress, { competitionId: this.id }).then(res => { this.progress = res.competitionProgressList.reverse() }).catch(err => { }); }, // 公告列表 getNotice () { this.$post(`${this.api.queryAnnouncementByCompetitionId}?pageNum=1&pageSize=1000&competitionId=${this.id}`).then(({ data }) => { const records = data.records.filter(e => e.status) // 只显示已发布的(status 0草稿 1为已发布) records.map(e => { e.announcementText = e.announcementText.replace(/<img.*?(?:>|\/>)/gi, '') }) this.notices = records }).catch(res => { }) }, // 预览附件 preview (item) { const { filePath } = item const suffix = filePath.substr(filePath.lastIndexOf('.') + 1) window.open((Util.isDoc(suffix) ? 'https://view.officeapps.live.com/op/view.aspx?src=' : '') + item.filePath) }, // 下载附件 download (item) { Util.downloadFile(item.fileName, item.filePath) }, // tab切换 前三个滚动,后两个切换 typeChange () { const type = +this.curType // 如果上个选中的是参赛信息,则检查修改数据后有没保存(团队名称、邀请码、指导老师) if (this.lastType == 5) { const { team, teamInstructors } = this.info const { originInfo } = this let notSave = 0 // 如果团队名称或者邀请码有修改 if (team.teamName !== originInfo.team.teamName || team.invitationCode !== originInfo.team.invitationCode) { notSave = 1 } else if (JSON.stringify(teamInstructors) !== JSON.stringify(originInfo.teamInstructors)) { notSave = 2 } if (notSave) { this.$confirm('所填写内容暂未保存,是否保存?', "提示", { type: "warning", closeOnClickModal: false }).then(() => { // 保存团队名称和邀请码 if (notSave === 1) { this.edit() } else { // 保存指导老师 teamInstructors.map(e => { e.name && this.$post(this.api.addAnAdvisor, { name: e.name, competitionId: this.id, id: e.id, teamId: this.form.competitionRegistration ? this.form.competitionRegistration.teamId : '', phone: e.phone, position: e.position, }).then(res => { }).catch(res => { }) }) } }).catch(() => { }) } else { type < 4 && document.querySelector(`#part${type}`).scrollIntoView() } } else { type == 5 && this.getInfo() type < 4 && document.querySelector(`#part${type}`).scrollIntoView() } this.editing = false this.lastType = type }, // 跳转公告详情 toNotice (item) { this.$router.push(`noticeDetail?id=${item.id}&matchId=${this.id}&name=${this.form.name}&end=${this.end}&status=${this.status}`) }, // 获取排名 getRank () { const cur = +this.curArch const data = { pageNum: 1, pageSize: 20, competitionId: this.id, isOverallRanking: cur ? 0 : 1 } data.stageIds = cur ? cur : '' this.token && this.$post(this.api.frontOfficeCompetitionRanking, data).then(({ list }) => { this.ranks = list }).catch(res => { }) }, // 定时调获取排名接口 intervalRank () { this.getRank() // this.rankTimer = setInterval(this.getRank, 1000) }, // 删除指导老师 delAdvisor (row, i) { if (row.id) { this.$confirm('确定要删除吗?', '提示', { type: 'warning' }).then(() => { this.$post(`${this.api.deleteAnAdvisor}?id=${row.id}`).then(res => { Util.successMsg('删除成功') this.info.teamInstructors.splice(i, 1) }).catch(res => { }) }).catch(() => { }) } else { this.info.teamInstructors.splice(i, 1) } }, // 添加指导老师 addAdvisor () { if (this.info.teamInstructors.length > 4) return Util.errorMsg('指导老师仅限添加5个!') const line = JSON.parse(JSON.stringify(this.originIns)) line.edit = 1 this.info.teamInstructors.push(line) }, // 编辑导老师 editAdvisor (row) { this.$set(row, 'edit', 1) }, // 提交指导老师 submitAdvisor (row, i) { 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.form.competitionRegistration ? this.form.competitionRegistration.teamId : '', phone: row.phone, position: row.position, }).then(({ id }) => { Util.successMsg((row.id ? '修改' : '新增') + '成功') if (!row.id) { this.info.teamInstructors[i].id = id } this.info.teamInstructors[i].edit = 0 }).catch(res => { }) }, // 显示转让队长 async transfer () { // 取每个阶段的开始结束时间,有任何阶段开始了都不能转让队长和踢出队员 const now = await Util.getNow() 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 => { }) }, // 踢出团队 async removeLine (row) { // 取每个阶段的开始结束时间,有任何阶段开始了都不能转让队长和踢出队员 const now = await Util.getNow() 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) { let include for (const e of this.info.stages) { if (e.participantAccountIds) { const ids = e.participantAccountIds.split(',').map(n => +n) if (ids.includes(row.accountId)) { include = e.stageName break } } } this.$confirm(include ? `该成员已被指定参加${include},踢出后需重新指定成员参加,是否确认踢出团队?` : '确定要踢出该成员吗?', '提示', { type: 'warning', closeOnClickModal: false }).then(() => { this.$post(`${this.api.removeTheLine}?teamId=${this.info.teamId}&competitionId=${this.id}&accountId=${row.accountId}`).then(res => { Util.successMsg('移除成功') this.getInfo() }).catch(res => { }) }).catch(() => { }) } }, // 移除参赛人员 async removePar (e, stage) { const item = this.form.competitionStage.find(e => e.stageId == stage.stageId) if (item) { // 该阶段已经开始比赛则不能修改 const now = await Util.getNow() if (now >= new Date(item.startTime) && now <= new Date(item.endTime)) { return Util.errorMsg('该阶段比赛已经开始,无法修改允许参赛人员!') } else { this.$confirm('确定要移除该成员吗?', '提示', { type: 'warning' }).then(() => { this.$post(this.api.cancelParticipant, { accountId: e.id, competitionId: this.id, stageId: stage.stageId, teamId: this.info.teamId }).then(res => { Util.successMsg('移除成功') this.getInfo() }).catch(res => { }) }).catch(() => { }) } } }, // 选择参赛人员 async selectPar (row) { const item = this.form.competitionStage.find(e => e.stageId == row.stageId) if (item) { // 该阶段已经开始比赛则不能修改 const now = await Util.getNow() if (now >= new Date(item.startTime) && now <= new Date(item.endTime)) { return Util.errorMsg('该阶段比赛已经开始,无法修改允许参赛人员!') } else { const { teamLimit, stages, teamDetail } = this.info // teamLimit=true,则每个成员只能参加一个阶段的比赛,要获取stages里返回的所有participantAccountIds(参赛人员的accountId),然后不显示这些参赛人员 if (teamLimit) { const chooses = [] let ids = [] // 获取已经允许参赛的人员accountId stages.map(e => { const id = e.participantAccountIds if (e.stageId != row.stageId && 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.checkedMembers = row.participantAccountIds ? row.participantAccountIds.split(',').map(e => +e) : [] // 选中了的人员要回显 this.chooseVisible = true } } }, // 选择参赛人员提交 chooseSubmit () { const accountIds = this.checkedMembers if (!accountIds.length) return Util.errorMsg('请选择参赛成员!') const limit = this.curRow.teamNumLimit // 参赛人数限制 if (limit && accountIds.length > limit) return Util.errorMsg(`请选择${limit}个以下参赛成员!`) // 选择的参赛人员个数不能大于参赛人数限制 this.$post(this.api.stageSelectParticipants, { accountIds, competitionId: this.id, stageId: this.curRow.stageId, teamId: this.info.teamId, platformId: 1 }).then(res => { this.checkedMembers = [] Util.successMsg('修改成功') this.getInfo() this.chooseVisible = false }).catch(res => { }) }, // 查看成绩详情 show (row) { // 团队展示弹框,个人跳转实验报告 if (this.form.completeCompetitionSetup.competitionType) { // 团队比赛则展示团队成员成绩详情 this.memberVisible = true this.curRow = row const teamId = this.form.competitionRegistration.teamId if (teamId) { this.$post(this.api.stageTeamScoreDetails, { pageNum: 1, pageSize: 1000, competitionId: this.id, stageId: row.stageId, teamId }).then(({ page }) => { this.members = page.records }).catch(res => { }) } else { this.members = [] } } else if (row.reportId) { // 个人比赛,并且有reportId,则进入实验报告 this.toReport(row) } }, // 跳转实验报告 toReport (row) { this.$router.push(`/record/show?reportId=${row.reportId}&matchId=${this.id}&matchName=${this.form.name}`) }, // 个人报名提交 peopleSignupSubmit () { this.$post(this.api.addCompetitionRegistration, { competitionId: this.id, registrationInvitationCode: this.peopleSignupForm.registrationInvitationCode }).then(res => { this.peopleSignupVisible = false this.getData() this.$message.success('报名成功') }).catch(res => { }) }, // 团队报名提交 enterSubmit () { const form = this.enterForm if (!form.teamId) return Util.errorMsg('请选择团队') if (!form.invitationCode) return Util.errorMsg('请输入团队邀请码') if (this.form.completeCompetitionSetup.isNeedCode && !form.registrationInvitationCode) return Util.errorMsg('请输入大赛邀请码') this.$post(this.api.joinCompetitionTeam, form).then(res => { this.status = 1 this.enterVisible = false this.getData() Util.successMsg('报名成功!') }).catch(res => { }) }, // 团队关闭 enterClose () { this.enterForm = { competitionId: this.id, teamId: '', registrationInvitationCode: '', invitationCode: '', whetherSignUp: 1 } }, // 创建团队 toTeam () { this.teamVisible = true }, // 获取团队列表 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 (this.teamNameRepeat) return Util.errorMsg('团队名称重复,请重新输入') if (form.invitationCode.length !== 6) return Util.errorMsg('请输入6位数团队邀请码') if (this.form.completeCompetitionSetup.isNeedCode && !form.registrationInvitationCode) return Util.errorMsg('请输入大赛邀请码') this.$post(this.api.addCompetitionTeam, form).then(({ status, data, message }) => { this.teamVisible = false this.enterVisible = false this.getData() Util.successMsg('报名成功!') }).catch(res => { }) }, // 团队关闭 teamClose () { this.teamForm = { competitionId: this.id, teamName: '', invitationCode: '', registrationInvitationCode: '', whetherSignUp: 1 } }, initOss () { this.client = new OSS(OssConfig.config) }, // 附件上传前 beforeUpload (file) { const oversize = file.size / 1024 / 1024 < 1000 if (!oversize) Util.warningMsg('请上传小于1GB的附件!') if (oversize) { return true } else { return false } }, // 自定义进度条 handleProgress (progress) { this.uploadProgress = Number((progress * 100).toFixed(2)) }, // 自定义上传 async handleRequest ({ file }) { try { this.fileList = [] this.uploadProgress = 0 this.uploading = true // 先传到阿里云oss,再把url传给后端 const { name } = await this.client.multipartUpload(Date.now() + '.' + Util.getFileExt(file.name), file, { progress: this.handleProgress }); this.uploading = false const url = 'https://huoran.oss-cn-shenzhen.aliyuncs.com/' + name this.fileList = [{ name: file.name, url }] // 把上传成功后的url存进文件表 const res = await this.$post(this.api.cCompetitionStageFileSave, { competitionId: this.id, fileFormat: Util.getFileExt(file.name), fileName: file.name, filePath: url, fileSize: file.size, stageId: this.curStage.stageId, teamId: this.form.competitionRegistration.teamId }) this.curFileId = res.message Util.successMsg(`上传成功!`); } catch (error) { } }, downloadFile () { const { name, url } = this.fileList[0] Util.downloadFile(name, url) }, handlePreview (file) { window.open((Util.isDoc(Util.getFileExt(file.name)) ? 'https://view.officeapps.live.com/op/view.aspx?src=' : '') + file.url) }, uploadError (err, file, fileList) { this.$message({ message: "上传出错,请重试!", type: "error", center: true }) }, beforeRemove (file, fileList) { return this.$confirm(`确定移除 ${file.name}?`); }, handleExceed (files, fileList) { Util.warningMsg(`当前限制选择 1 个文件,如需更换,请删除上一个文件再重新选择!`); }, // 删除文件表里的文件 handleRemove (file) { Oss.del(file.url) this.fileList = [] this.curFileId && this.$post(this.api.cCompetitionStageFileDel, [this.curFileId]).then(res => { this.curFileId = '' }).catch(res => { }) }, // 提交阶段内容 stageSubmit () { if (this.submiting) return false this.submiting = true }, // 阶段弹框关闭回调 stageClose () { this.choosing = false this.fileList = [] this.getData() }, // 选择要进入的阶段 chooseStage (e) { this.choosing = true this.curStage = e this.signup() }, // 判断是否能进赛事 getAllow () { // 是否允许参加赛事(淘汰赛制) if (this.form.rule === 1) { this.$post(this.api.allowedParticipateCompetition, { competitionId: this.id, number: this.curStage.number, stageId: this.curStage.stageId, teamId: this.form.competitionRegistration.teamId, }).then(res => { this.toSub() }).catch(res => { }) } else { this.toSub() } }, // 立即报名 signup () { const { status, form } = this // 如果登录了 if (Util.local.get(Setting.tokenKey)) { const { competitionType } = form.completeCompetitionSetup if (status == 4) { // 进入比赛 // 线下(规则见handleStatus方法) if (this.curStage.method == 2) { // 直接打开系统链接 if (!this.curStage.competitionStageContentSetting.whetherToUploadFiles) { window.open(this.curStage.competitionStageContentSetting.systemLink) } else { // 显示上传文件弹框 this.stageVisible = true // 文件路径名称处理 if (this.curStage.competitionStageContentSetting && this.curStage.competitionStageContentSetting.fileUrl) { const urls = this.curStage.competitionStageContentSetting.fileUrl.split('|') const names = this.curStage.competitionStageContentSetting.fileName.split('|') this.curStage.competitionStageContentSetting.fileList = [] urls.map((n, i) => { this.curStage.competitionStageContentSetting.fileList.push({ name: names[i], url: n }) }) } const file = this.curStage.competitionStageFile if (file) { this.curFileId = file.id this.fileList = [{ name: file.fileName, url: file.filePath, }] } } } else { // 参加过比赛不让参加 if (this.curStage && this.curStage.count) return Util.errorMsg('您已经参加过该阶段竞赛!') if (form.competitionRegistration.isDisable === 1) return Util.errorMsg('当前用户已被禁赛,如有疑问,请联系平台管理员。') // 被禁用的用户不能进入大赛 // 团队赛,则判断是否为参赛人员 if (competitionType) { this.$post(this.api.isParticipant, { competitionId: this.id, stageId: this.curStage.stageId, teamId: form.competitionRegistration.teamId, }).then(res => { this.getAllow() }).catch(res => { }) } else { this.getAllow() } } } else if (status == 2) { // 报名 // 团队赛报名 if (competitionType) { this.enterVisible = true } else { // 个人赛报名 if (form.completeCompetitionSetup.isNeedCode) { this.peopleSignupForm.registrationInvitationCode = '' this.peopleSignupVisible = true } else { this.$post(this.api.addCompetitionRegistration, { competitionId: this.id }).then(res => { this.getData() this.$message.success('报名成功') }).catch(res => { }) } } } else if (status == 1) { // 已报名,点击取消报名 this.$confirm('是否要取消报名?', '提示', { type: 'success', closeOnClickModal: false }).then(() => { this.$post(`${this.api.cancelRegistration}?competitionId=${this.id}`).then(res => { this.status = 2 this.$message.success('取消报名成功') this.getData() }).catch(res => { }) }).catch(() => { }) } } else { // 如果没登录,提示去登录 this.$confirm('请先登录,是否直接前往登录?', "提示", { type: 'success', closeOnClickModal: false }).then(() => { this.SET_SOURCE(this.id) this.$router.push('/login') }).catch(() => { }) } }, // 线下比赛方式点击进入跳转<比赛地点> toOffline () { window.open(this.curStage.competitionStageContentSetting.systemLink) }, // 进入python系统 toPython () { const form = this.curStage let token = Util.local.get(Setting.tokenKey); Util.cookies.set('assessmentId', '', -1) Util.cookies.set('startTime', '', -1) Util.cookies.set('stopTime', '', -1) Util.cookies.set('projectId', form.projectId) Util.cookies.set('token', token) Util.cookies.set('courseId', form.cid) Util.cookies.set('curriculumName', encodeURIComponent(form.systemName)) Util.cookies.set('systemId', form.systemId) Util.cookies.set('isSubmit', '', -1) Util.cookies.set('className', '', -1) Util.cookies.set('competitionId', this.form.id) Util.cookies.set('stageId', form.stageId) Util.cookies.set('teamId', this.form.competitionRegistration.teamId) Util.cookies.set('stopTime', form.endTime) Util.cookies.set('resultsDetails', form.resultsDetails) Util.cookies.set('resultAnnouncementTime', isNaN(form.resultAnnouncementTime) ? '' : form.resultAnnouncementTime) Util.cookies.set('mallId', form.mallId) Util.cookies.set('fromManager', '', -1) Util.cookies.set('language', '', -1) // 8个python子系统都跳这个地址,子系统会通过cookie里的systemId识别展示哪套系统 location.href = process.env.NODE_ENV === 'development' ? `http://${location.hostname}:8085/#/` : Setting.isPro ? `https://${location.hostname}/pyTrials` : `${location.origin}/pyTrials` }, // 进入子系统 toSub () { const { form } = this const { systemId, systemName, projectId, cid, stageId, startTime, endTime, mallId, resultAnnouncementTime } = this.curStage const competitionId = form.id const teamId = form.competitionRegistration.teamId || '' let token = Util.local.get(Setting.tokenKey); if (systemId == 11) { // 银行系统 location.href = `${Setting.systemPath}/#/index/list?curriculumName=${this.curriculumName}&token=${token}&cid=${cid}&systemId=${systemId}&projectId=${projectId}&competitionId=${competitionId}&stageId=${stageId}&teamId=${teamId}&assessmentId=&classId=&stopTime=&test=true` } else if (systemId == 12) { // 众筹系统 window.open(`http://${Setting.zcPath}?systemId=${systemId}&courseId=${cid}&projectId=${projectId}&token=${token}&userId=${this.userId}&classId=1&competitionId=${competitionId}&stageId=${stageId}&teamId=${teamId}&startTime=${startTime}&endTime=${endTime}&mallId=${mallId}${Setting.isTest ? '&beta=1' : ''}`); } else if (systemId == 19) { // 沙盘 location.href = `${Setting.sandPath}/#/?curriculumName=${systemName}&token=${token}&cid=${cid}&mallId=${mallId}&systemId=${systemId}&projectId=${projectId}&assessmentId=&classId=&startTime=&stopTime=${endTime}&competitionId=${competitionId}&stageId=${stageId}&teamId=${teamId}&resultAnnouncementTime=${isNaN(resultAnnouncementTime) ? '' : resultAnnouncementTime}&userId=${this.userId}&account=${this.account}&referrer=${encodeURIComponent(location.href)}` } else { // python系统 this.toPython(this.curProject) } } } }; </script> <style lang="scss" scoped> .banner { width: 100%; height: 350px; padding: 120px 0 0 20%; color: #fff; background-size: 100% 350px; background-repeat: no-repeat; } .l-title { font-size: 18px; } .main .center-con { background: url(../../../assets/img/match-bg1.png) (0px 95px) / auto auto no-repeat, url(../../../assets/img/match-bg2.png) (98% 300px) / auto auto no-repeat; } .main .center-wrap { margin-top: 30px; } .rule-title { margin-bottom: 10px; font-size: 16px; } .rule { padding: 15px; margin-bottom: 15px; border: 1px solid #dfdfdf; p { font-size: 14px; line-height: 30px; color: #6e6e6e; } } /deep/.el-tabs__item { box-shadow: none !important; } .content { position: relative; padding: 20px 40px; margin-top: 30px; background-color: #fff; .title { width: 67%; margin: 0 auto; font-size: 28px; text-align: center; color: #0b1d30; } .tool { z-index: 100; position: sticky; top: 64px; margin-bottom: 20px; background-color: #fff; &.logView { z-index: 0; } } .info .meta { padding: 16px 0; font-size: 12px; color: #999; text-align: center; } .action { display: inline-flex; align-items: center; } .sign-status { margin-bottom: 10px; text-align: center; font-size: 14px; color: $main-color; &.signing { color: $main-color; } &.signed { color: #52c41a; } &.playing { color: #f96d6d; } &:last-child { margin-bottom: 0; } } .status { max-width: 120px; padding: 0 16px; line-height: 34px; font-size: 14px; color: #fff; background-color: #52c41a; border-radius: 4px; cursor: pointer; @include ellipsis(); &.wait { background-color: #faad14; } &.signing { background-color: $main-color; } &.signed { background-color: #52c41a; } &.playing { background-color: #f96d6d; } &.finish { background-color: #ccc; } } .end-text { font-size: 12px; color: #666; em { font-style: normal; color: #f00; } } .texts { margin: 20px 0 50px; font-size: 14px; line-height: 1.6; text-indent: 2em; overflow: hidden; /deep/img { max-width: 100%; } } .progress { position: relative; width: 95%; padding: 50px 0; margin: 40px auto 80px; text-align: left; &:before { content: ''; position: absolute; top: 0; left: 50%; width: 2px; height: 100%; background-color: #e1e6f2; } &:after { content: ''; position: absolute; top: -10px; left: 430px; border: 8px solid transparent; border-bottom-color: #e1e6f2; } .rocket { position: absolute; bottom: -50px; left: 425px; } li { position: relative; width: 400px; margin-bottom: 42px; .dot { position: absolute; top: 12px; left: 431px; width: 15px; height: 15px; background-color: #dcdcdc; border-radius: 50%; } .name { display: inline-block; padding: 0 19px; margin-bottom: 16px; line-height: 40px; text-align: center; font-size: 16px; color: #fff; border-radius: 20px; background-color: #c4c4c4; } .desc { position: relative; color: #333; font-size: 14px; } &.ing, &.done { .dot { top: 8px; background-color: #007eff; } .name { background-color: #007eff; } } &.ing { .dot { width: 27px; height: 27px; border: 6px solid #e2f1fb; } } &:nth-child(odd) { text-align: right; &.ing { .dot { left: auto; right: -51px; } } .name { &:before { content: ''; z-index: 2; position: absolute; top: 14px; right: -35px; border: 18px solid transparent; border-top-width: 6px; border-bottom-width: 6px; border-left-color: #c4c4c4; } } .desc { text-align: right; } &.ing, &.done { .name { &:before { border-left-color: #007eff; } } } } &:nth-child(even) { margin-left: 482px; .dot { left: -51px; } &.ing { .dot { left: -57px; } } .name { text-align: left; &:after { content: ''; z-index: 2; position: absolute; top: 14px; left: -35px; border: 18px solid transparent; border-top-width: 6px; border-bottom-width: 6px; border-right-color: #c4c4c4; } } .desc { &:before { left: auto; right: -16px; border: 8px solid transparent; border-left-color: #fff; } &:after { left: auto; right: -18px; border: 9px solid transparent; border-left-color: #e6e6e6; } } &.ing, &.done { .name { &:after { border-right-color: #007eff; } } } } &:last-child { margin-bottom: 0; } } } } .files { margin-bottom: 30px; li { display: flex; align-items: center; margin: 10px 0; } .fileName { margin-right: 10px; font-size: 12px; } } .notice-list { text-align: left; li { padding: 16px; margin-bottom: 12px; transition: all 0.3s; cursor: pointer; border-radius: 6px; background-color: #fff; border-bottom: 1px dashed #ebebeb; &:last-child { border-bottom: 0; } } h6 { font-size: 20px; font-weight: 500; color: #0b1d30; &:hover { color: #007eff; } } .meta { margin: 10px 0; font-size: 14px; color: #666; } .des { font-size: 14px; color: #333; line-height: 24px; display: -webkit-box; display: -moz-box; -webkit-box-orient: vertical; -moz-box-orient: vertical; -webkit-line-clamp: 2; -moz-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; } } .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; &:hover { color: #007eff; } } .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; } /deep/.dia-form { .w-100 { width: 100%; } .tips { display: flex; justify-content: center; align-items: center; } } /deep/.file-upload { .el-upload__tip { color: #727272; } .el-progress { white-space: nowrap; } .download { position: absolute; bottom: -25px; right: 0; font-size: 12px; color: #007eff; cursor: pointer; } .el-upload-list__item { width: 70%; &:first-child { margin-top: 5px; } } } </style>