Browse Source

退出参赛 && 打分状态 && 样式调整

XW 1 year ago
parent
commit
0711aa8a7f

+ 1 - 1
Contest/contest.client/src/api/http.js

@@ -216,7 +216,7 @@ function checkToken() {
 function refreshToken() {
     refreshing = true
     let areaRoute = window.location.pathname.split('/')
-    axios.post('/api/activity/login-portal', {
+    axios.post('/activity/login-portal', {
         "route": areaRoute[1],
         "token": localStorage.getItem('auth_token')
     }).then(res => {

+ 4 - 1
Contest/contest.client/src/main.js

@@ -15,7 +15,10 @@ import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
 
 let app = createApp(App)
 
-axios.defaults.baseURL = 'https://test.teammodel.cn'
+// 根据域名判断调用测试站接口
+let hostName = window.location.hostname
+
+axios.defaults.baseURL = hostName ? 'https://test.teammodel.cn' : ''
 
 app.config.globalProperties.$axios = axios
 app.config.globalProperties.$api = api

+ 15 - 4
Contest/contest.client/src/pinia/common.js

@@ -7,19 +7,26 @@ import { defineStore } from 'pinia'
 export const useStore = defineStore('main', {
     state: () => {
         return {
+            srvAdr: '', // 伺服器位置 China, Global
+            srvAdrType: '', // 正式站 product 測試站 test
             userInfo: {},
             schoolList: [],
             website: {},
         }
     },
     getters: {
-        numAdd: (state) => {
-
-        }
+        getSrvAdr: (state) => {
+            return state.srvAdr
+        },
     },
     actions: {
+        setSrvAdr(value) {
+            this.srvAdr = value
+        },
+        setSrvAdrType(value) {
+            this.srvAdrType = value
+        },
         setSchoolList(value) {
-            console.log('746545111');
             this.schoolList = value
         },
         setUserInfo(value) {
@@ -28,5 +35,9 @@ export const useStore = defineStore('main', {
         setWebsite(value) {
             this.website = value
         },
+        checkSrvAdr(context) {
+            let hostname = window.location.hostname
+            let domainUrl = hostname
+        }
     }
 })

+ 45 - 8
Contest/contest.client/src/view/Home.vue

@@ -1,16 +1,13 @@
 <template>
     <div class="common-layout">
         <el-container style="height: 100%;">
-            <el-header style="position: relative;">
-                <el-menu
-                    :default-active="activeIndex"
-                    class="el-menu-demo"
-                    mode="horizontal"
-                >
+            <el-header>
+                <el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal">
                     <el-menu-item v-for="(item, index) in menuList.list" :key="index" :index="item.menuName" v-show="item.isShow">
                         <router-link :to="{name: item.router}">{{ item.name }}</router-link>
                     </el-menu-item>
                 </el-menu>
+                <span class="website-name">{{ store.website.name }}</span>
                 <div class="head-photo">
                     <el-dropdown>
                         <el-avatar :size="35" :src="userInfo?.picture" />
@@ -157,7 +154,7 @@ function getWebsite() {
         }
         proxy.$api.getWebsite(params).then(res => {
             if(res.code === 200) {
-                res.website.blobUrl = res.blobUrl
+                // res.website.blobUrl = res.blobUrl
                 store.setWebsite(res.website)
                 otherWebsite.value = res.websites
             } else if(res.code === 2) {
@@ -262,6 +259,7 @@ watch(ticket, (newValue, oldValue) => {
         position: absolute;
         top: 11px;
         right: 40px;
+        margin-right: 40px;
         cursor: pointer;
         .user_avatar{
             width: 36px;
@@ -270,8 +268,47 @@ watch(ticket, (newValue, oldValue) => {
             background-color: #72727f;
         }
     }
+    .website-name {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+    }
     .el-header {
-        padding: 0;
+        position: relative;
+        // padding: 0;
+        // border-bottom: solid 1px var(--el-menu-border-color);
+        background-color: #15559a;
+        color: #fff;
+        
+        a {
+            text-decoration: none; /* 移除链接默认的下划线 */
+        }
+        
+        .el-menu {
+            background-color: #15559a;
+            min-width: 350px;
+        }
+        .el-menu--horizontal.el-menu {
+            border-bottom: none;
+        }
+        .el-menu--horizontal>.el-menu-item {
+            color: #F1F1E6;
+        }
+
+        .el-menu--horizontal>.el-menu-item.is-active {
+            border-bottom-color: #fff;
+            // border-bottom: none;
+            color: #fff !important;
+            // font-size: 16px;
+            font-weight: bold;
+        }
+        .el-menu--horizontal .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal .el-menu-item:not(.is-disabled):hover {
+            background-color: #15559a;
+            // font-size: 16px;
+            // font-weight: bold;
+            border-bottom: 2px solid #fff;
+        }
     }
     .el-avatar {
         outline: none;

+ 24 - 9
Contest/contest.client/src/view/myactivity/MyActivity.less

@@ -7,6 +7,8 @@
         width: 20%;
         height: 100%;
         border-right: 1px solid #ccc;
+        // background-color: #15559a;
+        // color: #fff;
 
         .list-icon {
             width: auto;
@@ -34,6 +36,7 @@
 
         .list-info {
             padding: 15px 20px;
+            // margin: 0 5px;
 
             &>p {
                 margin-bottom: 10px;
@@ -89,18 +92,24 @@
 
         .list-info:hover,
         .list-info-active {
-            background-color: #E1EFF6;
+            background-color: #EBECED;
             cursor: pointer;
+            border-radius: 5px 0 0 5px;
         }
     }
 
     .act-info {
         width: 80%;
-        height: 100%;
+        height: auto;
+        padding: 20px;
+        background-color: #e8f3fd;
+        // background-color: #f4f4f4;
 
         .info-box {
-            padding: 20px;
-            height: 90%;
+            padding: 20px 30px;
+            height: 95%;
+            background-color: #fff;
+            border-radius: 5px;
 
             .demo-tabs {
                 height: 100%;
@@ -211,18 +220,24 @@
         .not-sign {
             // height: 100%;
             text-align: center;
-            margin-top: 10%;
+            // margin-top: 10%;
 
-            &>p {
+            &>p,
+            &>span {
                 font-size: 20px;
-                margin-top: 20px;
+                margin: 20px 0;
                 color: #3d3d3d;
                 font-weight: bold;
 
-                &:nth-of-type(2):hover {
+                /* &:nth-of-type(2):hover {
                     cursor: pointer;
                     color: #19be6b;
-                }
+                } */
+            }
+
+            &>span:hover {
+                cursor: pointer;
+                color: #19be6b;
             }
         }
     }

+ 44 - 13
Contest/contest.client/src/view/myactivity/MyActivity.vue

@@ -86,6 +86,9 @@
                                                 <el-button size="small" type="danger" @click="deleteMem(scope.$index, scope.row)" v-if="isLeader && !scope.row.myself && !scope.row.identity">
                                                     删除
                                                 </el-button>
+                                                <el-button size="small" @click="exitAct(scope.$index, scope.row)" v-if="scope.row.myself">
+                                                    退出
+                                                </el-button>
                                             </template>
                                         </el-table-column>
                                     </el-table>
@@ -107,7 +110,7 @@
                                             </div>
                                         </div>
                                     </template>
-                                    <!-- <template v-if="contest?.upload && !contest?.upload.captainUpload || contest?.upload.captainUpload && isLeader"> -->
+                                    <template v-if="contest?.upload && !contest?.upload.captainUpload || contest?.upload.captainUpload && isLeader">
                                         <el-upload class="upload-demo" ref="refUpload" :file-list="fileList" :accept="accept" drag multiple :on-change="handleChange" :auto-upload="false" action="">
                                             <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
                                             <div class="el-upload__text">
@@ -129,12 +132,11 @@
                                         <el-button type="success" @click="uploadFile()">
                                             上传
                                         </el-button>
-                                    <!-- </template> -->
-                                    <!-- <template v-else-if="contest?.upload && contest?.upload.captainUpload && !isLeader">
-                                        <div>
-                                            本次活动由组长上传作品
-                                        </div>
-                                    </template> -->
+                                    </template>
+                                    <div v-else-if="contest?.upload && contest?.upload.captainUpload && !isLeader" style="text-align: center; margin-top: 10px;">
+                                        <el-icon color="#ba6b26"><Warning /></el-icon>
+                                        本次活动由组长上传作品
+                                    </div>
                                 </div>
                             </el-scrollbar>
                         </template>
@@ -170,13 +172,13 @@
                     </el-tab-pane>
                 </el-tabs>
             </div>
-            <div class="not-sign" v-else>
-                <el-icon size="120" color="#f4a83e"><WarnTriangleFilled /></el-icon>
+            <div class="info-box not-sign" v-else>
+                <el-icon size="120" color="#f4a83e" style="margin-top: 10%;"><WarnTriangleFilled /></el-icon>
                 <p>还未报名该活动</p>
-                <p @click="toSign()">
+                <span @click="toSign()">
                     马上去报名
                     <el-icon><DArrowRight /></el-icon>
-                </p>
+                </span>
             </div>
         </div>
         <el-empty v-else description="暂无活动" :image-size="300" />
@@ -198,7 +200,7 @@
 </template>
 
 <script setup>
-import { Search, Delete, UserFilled, Clock, UploadFilled, CaretBottom, CaretRight, WarnTriangleFilled, DArrowRight, CloseBold } from '@element-plus/icons-vue'
+import { Search, Delete, UserFilled, Clock, UploadFilled, CaretBottom, CaretRight, WarnTriangleFilled, DArrowRight, CloseBold, Warning } from '@element-plus/icons-vue'
 import { ElMessageBox, ElMessage, FormRules, ElLoading } from 'element-plus'
 import { getCurrentInstance, nextTick, onMounted, reactive, ref, toRaw, watch } from 'vue'
 import { useStore } from "@/pinia/common"
@@ -331,6 +333,7 @@ function getEnroll() {
                     res.enroll.contest.enrollInfos.forEach(filed => {
                         res.enroll[filed.code] = filed.val
                     })
+                    res.enroll.myself = true
                     memberData.value  = [res.enroll]
                     // loading.close()
                 }
@@ -394,7 +397,35 @@ function changeLeader(index, data) {
     })
 }
 
-
+function exitAct(index, info) {
+    if(info.identity) {
+        ElMessage({
+            type: 'warning',
+            message: '请移交队长后再退出参赛'
+        })
+    } else {
+        ElMessageBox.confirm(`确认退出活动?`).then(() => {
+            let params = {
+                grant_type: 'cancel-enroll',
+                activityId: actInfo.value.id
+            }
+            proxy.$api.teaContest(params).then(res => {
+                if(res.code === 204) {
+                    ElMessage({
+                        type: 'success',
+                        message: '已退出活动'
+                    })
+                } else {
+                    ElMessage({
+                        type: 'warning',
+                        message: '退出失败'
+                    })
+                }
+            })
+            
+        })
+    }
+}
 
 function deleteMem(index, info) {
     console.log(index, info);

+ 31 - 3
Contest/contest.client/src/view/myactivity/MyReview.less

@@ -91,16 +91,29 @@
 
         .list-info:hover,
         .list-info-active {
-            background-color: #E1EFF6;
+            background-color: #EBECED;
             cursor: pointer;
+            border-radius: 5px 0 0 5px;
         }
     }
 
     .act-info {
         width: 80%;
+        height: auto;
+        padding: 20px;
+        background-color: #e8f3fd;
+        // background-color: #f4f4f4;
 
         .info-box {
             padding: 20px;
+            padding: 20px 30px;
+            height: 95%;
+            background-color: #fff;
+            border-radius: 5px;
+
+            .demo-tabs {
+                height: 100%;
+            }
 
             .upload-demo {
                 margin: 20px 0;
@@ -162,19 +175,32 @@
                     .review-header-works {
                         display: flex;
                         height: 300px;
+                        background-color: #f4f4f4;
+                        border-radius: 5px;
+                        // padding: 10px 15px;
 
                         .works-list {
                             width: 20%;
+                            height: 90%;
+                            // border-right: 1px solid #ccc;
+                            margin-left: 10px;
+                            margin-top: 10px;
 
                             .files-list {
-                                padding: 10px 10px;
+                                padding: 13px 15px;
                                 border-radius: 5px;
                                 color: #535353;
                                 cursor: pointer;
+
+                                &:hover {
+                                    background-color: #638ED4;
+                                    color: #fff;
+                                }
                             }
 
                             .active-file {
-                                background-color: #E1EFF6;
+                                background-color: #638ED4;
+                                color: #fff;
                             }
                         }
 
@@ -182,6 +208,8 @@
                             width: 80%;
                             text-align: center;
                             line-height: 300px;
+                            margin: 10px 0;
+
                             img {
                                 height: 100%;
                             }

+ 87 - 24
Contest/contest.client/src/view/myactivity/MyReview.vue

@@ -23,7 +23,7 @@
                                 </p> -->
                                 <span v-if="item.isFinish" class="info-end">已结束</span>
                                 <span v-else :class="['info-end', item.completeCount === item.taskCount ? 'info-review' : 'info-progress']">{{ item.completeCount === item.taskCount ? '已评审' : '评审中' }}</span>
-                                <span v-show="!item.isFinish" class="info-end">需评审 {{ item.taskCount - item.completeCount }} 个</span>
+                                <span v-show="!item.isFinish && item.completeCount != item.taskCount" class="info-end">需评审 {{ item.taskCount - item.completeCount }} 个</span>
                             </div>
                             <p class="info-time">
                                 <el-icon><Clock /></el-icon>
@@ -39,7 +39,7 @@
             <div class="info-box">
                 <el-tabs v-model="actTab" class="demo-tabs">
                     <el-tab-pane label="作品评审" name="user">
-                        <el-scrollbar height="750px">
+                        <el-scrollbar>
                             <div>
                                 <p @click="skModule = !skModule" class="module-box">
                                     <el-icon v-show="skModule"><CaretBottom /></el-icon>
@@ -65,12 +65,14 @@
                                             </div>
                                             <div class="review-header-works">
                                                 <div class="works-list">
-                                                    <div v-for="(item, index) in workInfo.files" :key="index" @click="changeFile(item, index)" :class="['files-list', {'active-file': fileIndex === index}]">
-                                                        <p>{{ item.name }}</p>
-                                                    </div>
+                                                    <el-scrollbar>
+                                                        <div v-for="(item, index) in workInfo.files" :key="index" @click="changeFile(item, index)" :class="['files-list', {'active-file': fileIndex === index}]">
+                                                            <p>{{ item.name }}</p>
+                                                        </div>
+                                                    </el-scrollbar>
                                                 </div>
                                                 <div class="works-info">
-                                                    <video v-if="fileInfo.type === 'video'" :id="'video'" :src="fileInfo.urlShow" controls="controls" style="border-radius: 5px;max-height: 700px;max-width: 100%;" />
+                                                    <video v-if="fileInfo.type === 'video'" :id="'video'" :src="fileInfo.urlShow" controls="controls" style="border-radius: 5px;max-height: 280px;max-width: 100%;" />
                                                     <img v-else-if="fileInfo.type === 'image'" :src="fileInfo.urlShow" style="border-radius: 5px;max-height: 700px !important;max-width: 100% !important;" />
                                                     <audio v-else-if="fileInfo.type === 'audio'" controls>
                                                         <source :src="fileInfo.urlShow" />
@@ -87,7 +89,7 @@
                                                 </div>
                                             </div>
                                         </div>
-                                        <div style="text-align: right; margin: 10px 0;" v-show="workList.length && workList.length != 1">
+                                        <div style="text-align: right; margin: 10px 20px;" v-show="workList.length && workList.length != 1">
                                             <el-button type="info" @click="changeWork()" v-show="workIndex">上一位</el-button>
                                             <el-button type="info" @click="changeWork(true)" v-show="workIndex != (workList.length - 1)">下一位</el-button>
                                             <el-button type="info" @click="scoring = true" :icon="Notebook" circle />
@@ -150,14 +152,19 @@
         </div>
         <el-empty v-else description="暂无活动" :image-size="300" />
         <el-dialog v-model="scoring" title="请选择打分作品" width="40%">
-            <div v-for="item in workList" :key="item.id">
+            <!-- <div v-for="item in workList" :key="item.id">
                 姓名:{{ item.name }} 学段:{{ item.period }} 学科:{{ item.subject }}
-            </div>
+            </div> -->
             <div>
                 <el-table :data="workList" row-key="id" highlight-current-row default-expand-all @current-change="handleCurrentChange">
                     <el-table-column prop="name" label="姓名" align="center" width="170" />
                     <el-table-column prop="period" label="学段" align="center" width="100" />
                     <el-table-column prop="subject" label="学科" align="center" width="100" />
+                    <el-table-column label="成绩" align="center" width="100">
+                        <template #default="scope">
+                            <p>{{ scope.row.score === -1 ? '-' : scope.row.score }}</p>
+                        </template>
+                    </el-table-column>
                     <!-- <el-table-column label="作品" align="center">
                         <template #default="scope">
                             <p>
@@ -197,7 +204,7 @@
 </template>
 
 <script setup>
-import { Search, Delete, Clock, CaretBottom, CaretRight, Avatar, Management, Notebook, Picture, Film, Paperclip, FolderRemove } from '@element-plus/icons-vue'
+import { Search, Delete, Clock, CaretBottom, CaretRight, Avatar, Management, Notebook, Picture, Film, Paperclip, FolderRemove, CloseBold } from '@element-plus/icons-vue'
 import { ElMessageBox, ElMessage, FormRules } from 'element-plus'
 import { getCurrentInstance, onMounted, reactive, ref, watch, watchEffect } from 'vue'
 
@@ -217,6 +224,8 @@ let workList = ref([])
 let workIndex = ref(0)
 // 展示作品信息
 let workInfo = ref({})
+// 作品信息
+let workInfoShow = ref({})
 let fileIndex = ref(0)
 let fileInfo = ref({})
 let previewFile = ref({})
@@ -231,8 +240,6 @@ let skModule = ref(true)
 // 作品列表弹出框
 let scoring = ref(false)
 
-// 作品信息
-let workInfoShow = reactive({})
 // workInfo = workList[0]
 
 getTaskList()
@@ -284,6 +291,17 @@ function getRuleScore(arr, workInfo, isRestore) {
     return arr
 }
 
+function checkNotScore(arr) {
+    let notScore = arr.find(item => {
+        let needBreak = undefined
+        if(item.children.length) {
+            needBreak = checkNotScore(item.children)
+        }
+        return needBreak || item.score === null
+    })
+    return notScore
+}
+
 function getUploadInfo(info) {
     if(!info?.files) {
         let params = {
@@ -293,9 +311,14 @@ function getUploadInfo(info) {
         }
         proxy.$api.getTaskList(params).then(res => {
             if(res?.upload) {
-                info.files = res.upload.files.map(item => {
+                info.files = []
+                res.upload.files.map(item => {
                     item.urlShow = actInfo.value.url + item.url + '?' + actInfo.value.sas
-                    return item
+                    if(item.type === 'video') {
+                        info.files.unshift(item)
+                    } else {
+                        info.files.push(item)
+                    }
                 })
                 workInfo.value = info
                 // changeFile(workInfo.value.files[0], 0)
@@ -312,19 +335,29 @@ function getUploadInfo(info) {
 
 function changeWork(type) {
     ElMessageBox.confirm(`当前分数不会保存,是否切换?`).then(() => {
+        workIndex.value = type ? workIndex.value + 1 : workIndex.value - 1
         if(ruleInfo.value.scoreDetail) {
             ruleInfo.value.trees = getRuleScore(ruleInfo.value.trees, workList.value[workIndex.value].detailScore, true)
         } else {
             reviewScore.value = null
         }
-        workIndex.value = type ? workIndex.value + 1 : workIndex.value - 1
         getUploadInfo(workList.value[workIndex.value])
     })
 }
 
 function saveScore() {
-    let totalScore = 0
-
+    let notScore = undefined
+    if(ruleInfo.value.scoreDetail) {
+        // 排查细项打分时,是否有遗漏(可以打0分)
+        notScore = checkNotScore(ruleInfo.value.trees)
+    }
+    if(notScore || reviewScore.value === null) {
+        ElMessage({
+            type: 'warning',
+            message: ruleInfo.value.scoreDetail ? '有细项未打分' : '未打分'
+        })
+        return
+    }
     let params = {
         grant_type: 'decide-score',
         activityId: taskList.value[taskIndex.value].activityId,
@@ -342,10 +375,30 @@ function saveScore() {
     } else {
         params.scoreData.score += reviewScore.value
     }
+    // 可以打0分
+    /* if(!params.scoreData.score) {
+        ElMessage({
+            type: 'warning',
+            message: '分数不能为0'
+        })
+        return
+    } */
     console.log(params);
     proxy.$api.getTaskList(params).then(res => {
         if(res.code === 200) {
-
+            ElMessage({
+                type: 'success',
+                message: '保存成功'
+            })
+            if(!workList.value[workIndex.value].detailScore.length) {
+                taskList.value[taskIndex.value].completeCount = res.completeCount
+            }
+            workList.value = res.contestTasks
+        } else {
+            ElMessage({
+                type: 'warning',
+                message: '保存失败'
+            })
         }
     })
 }
@@ -385,15 +438,21 @@ function changeFile(info, index) {
     }
 }
 
-
 // 选择将要打分作品
-let handleCurrentChange = (key, keyIndex) => {
-    workInfoShow = key
+function handleCurrentChange(key, keyIndex) {
+    workInfoShow.value = key
 }
+
 // 确认开始打分
-let startWork = () => {
-    // workInfo = workInfoShow
-    Object.assign(workInfo.value, workInfoShow)
+function startWork() {
+    workIndex.value = workList.value.findIndex(item => item.uploadId === workInfoShow.value.uploadId)
+    if(ruleInfo.value.scoreDetail) {
+        ruleInfo.value.trees = getRuleScore(ruleInfo.value.trees, workList.value[workIndex.value].detailScore, true)
+    } else {
+        reviewScore.value = null
+    }
+    workInfo.value = workInfoShow.value
+    getUploadInfo(workInfo.value)
     scoring.value = false
 }
 </script>
@@ -406,5 +465,9 @@ let startWork = () => {
     .el-upload-list__item-info {
         height: 20px;
     }
+    .el-tabs__content,
+    .el-tab-pane {
+        height: 97%;
+    }
 }
 </style>