Bläddra i källkod

Merge branch 'wk-dev' into dev

# Conflicts:
#	src/layout/components/Navbar.vue
#	src/views/system/gameEventGroup/index.vue
#	src/views/system/gameEventProject/index.vue
#	src/views/system/gameScore/index.vue
wenkai 1 dag sedan
förälder
incheckning
d0abff6b5b

+ 6 - 6
src/layout/components/Navbar.vue

@@ -274,21 +274,21 @@ watch(
     gap: 8px;
     padding: 0 16px;
     height: 100%;
-    background: rgba(246, 246, 247, 0.1);
+    background: rgba(64, 158, 255, 0.1);
     border-radius: 4px;
-    border: 1px solid rgba(249, 251, 252, 0.2);
-    
+    border: 1px solid rgba(64, 158, 255, 0.2);
+
     .event-icon {
       font-size: 16px;
       color: #409eff;
     }
-    
+
     .event-label {
       font-size: 14px;
       color: #606266;
       font-weight: 500;
     }
-    
+
     .event-name {
       font-size: 14px;
       color: #409eff;
@@ -298,7 +298,7 @@ watch(
       text-overflow: ellipsis;
       white-space: nowrap;
     }
-    
+
     .event-code {
       font-size: 12px;
       height: 20px;

+ 60 - 29
src/views/system/gameEvent/components/bibViewerDialog.vue

@@ -6,7 +6,7 @@
         <!-- 左侧配置面板 -->
         <el-col :span="12">
           <el-form :model="bibForm" label-width="100px">
-            <el-form-item label="背景图片">
+            <el-form-item label="背景图片" required>
               <el-upload ref="bgUploadRef" :limit="1" :auto-upload="false" :on-change="handleBgImageChange" accept="image/*" drag>
                 <el-icon class="el-icon--upload">
                   <i-ep-upload-filled />
@@ -16,7 +16,7 @@
               </el-upload>
             </el-form-item>
 
-            <el-form-item label="Logo图片">
+            <el-form-item label="Logo图片" required>
               <el-upload ref="logoUploadRef" :limit="1" :auto-upload="false" :on-change="handleLogoImageChange" accept="image/*" drag>
                 <el-icon class="el-icon--upload">
                   <i-ep-upload-filled />
@@ -49,9 +49,9 @@
                 v-if="logoImageUrl"
                 class="draggable-element logo-element"
                 :style="{
-                    left: bibForm.logoX + 'px',
-                    top: bibForm.logoY + 'px'
-                  }"
+                  left: bibForm.logoX + 'px',
+                  top: bibForm.logoY + 'px'
+                }"
                 @mousedown="startDrag($event, 'logo')"
               >
                 <img :src="logoImageUrl" alt="Logo" style="max-width: 80px; max-height: 80px" />
@@ -61,9 +61,9 @@
               <div
                 class="draggable-element barcode-element"
                 :style="{
-                    left: bibForm.qRCodeX + 'px',
-                    top: bibForm.qRCodeY + 'px'
-                  }"
+                  left: bibForm.qRCodeX + 'px',
+                  top: bibForm.qRCodeY + 'px'
+                }"
                 @mousedown="startDrag($event, 'barcode')"
               >
                 <svg
@@ -99,10 +99,10 @@
               <div
                 class="event-name-preview"
                 :style="{
-                    fontSize: Math.min(28, Math.max(18, bibForm.fontSize * 0.7)) + 'px',
-                    color: 'black',
-                    fontFamily: '黑体'
-                  }"
+                  fontSize: Math.min(28, Math.max(18, bibForm.fontSize * 0.7)) + 'px',
+                  color: 'black',
+                  fontFamily: '黑体'
+                }"
               >
                 赛事名称
               </div>
@@ -111,13 +111,13 @@
               <div
                 class="draggable-element number-element"
                 :style="{
-                    left: '50%',
-                    top: '50%',
-                    transform: 'translate(-50%, -50%)',
-                    fontSize: Math.min(bibForm.fontSize, 56) + 'px',
-                    color: bibForm.fontColorHex,
-                    fontFamily: bibForm.fontName
-                  }"
+                  left: '50%',
+                  top: '50%',
+                  transform: 'translate(-50%, -50%)',
+                  fontSize: Math.min(bibForm.fontSize, 56) + 'px',
+                  color: bibForm.fontColorHex,
+                  fontFamily: bibForm.fontName
+                }"
               >
                 1234
               </div>
@@ -250,6 +250,9 @@ const handleBgImageChange = async (file: any) => {
     } catch (error) {
       console.error('获取图片尺寸失败:', error);
     }
+
+    // 添加成功提示
+    proxy?.$modal.msgSuccess('背景图片上传成功');
   }
 };
 
@@ -262,6 +265,9 @@ const handleLogoImageChange = (file: any) => {
       logoImageUrl.value = e.target?.result as string;
     };
     reader.readAsDataURL(file.raw);
+
+    // 添加成功提示
+    proxy?.$modal.msgSuccess('Logo图片上传成功');
   }
 };
 
@@ -396,14 +402,18 @@ const processImageToRatio = (file: File, targetRatio: number, targetRatio2: numb
       ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, newWidth, newHeight);
 
       // 转换为Blob
-      canvas.toBlob((blob) => {
-        if (blob) {
-          const processedFile = new File([blob], file.name, { type: file.type });
-          resolve(processedFile);
-        } else {
-          resolve(file);
-        }
-      }, file.type || 'image/jpeg', 0.9);
+      canvas.toBlob(
+        (blob) => {
+          if (blob) {
+            const processedFile = new File([blob], file.name, { type: file.type });
+            resolve(processedFile);
+          } else {
+            resolve(file);
+          }
+        },
+        file.type || 'image/jpeg',
+        0.9
+      );
     };
     img.src = URL.createObjectURL(file);
   });
@@ -411,11 +421,17 @@ const processImageToRatio = (file: File, targetRatio: number, targetRatio2: numb
 
 // 生成参赛证文件
 const handleGenerateBibFile = async () => {
+  // 校验必须上传背景图和logo
   if (!bgImageFile.value) {
     proxy?.$modal.msgError('请上传背景图片');
     return;
   }
 
+  if (!logoImageFile.value) {
+    proxy?.$modal.msgError('请上传Logo图片');
+    return;
+  }
+
   bibDialog.loading = true;
   try {
     let qRCodeX = bibForm.qRCodeX;
@@ -545,7 +561,6 @@ defineExpose({
 </script>
 
 <style scoped lang="scss">
-
 /* 生成参赛证样式 */
 .bib-generator {
   padding: 20px;
@@ -558,6 +573,22 @@ defineExpose({
   text-align: center;
 }
 
+/* 必填项样式 */
+.bib-generator .el-form-item.is-required .el-form-item__label::before {
+  content: '*';
+  color: #f56c6c;
+  margin-right: 4px;
+}
+
+/* 上传成功状态样式 */
+.bib-generator .el-upload--success {
+  border-color: #67c23a;
+}
+
+.bib-generator .el-upload--success .el-upload__text {
+  color: #67c23a;
+}
+
 .preview-container {
   border: 2px dashed #ddd;
   border-radius: 8px;
@@ -629,7 +660,7 @@ defineExpose({
 
 .event-name-preview {
   position: absolute;
-  top: 20%;
+  top: 5%;
   left: 50%;
   transform: translateX(-50%);
   font-weight: bold;

+ 1 - 13
src/views/system/gameEvent/index.vue

@@ -15,9 +15,6 @@
                 <el-option v-for="dict in game_event_type" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
-            <el-form-item label="开始时间" prop="startTime">
-              <el-date-picker clearable v-model="queryParams.startTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始时间" />
-            </el-form-item>
             <el-form-item label="是否默认赛事" prop="isDefault">
               <el-select v-model="queryParams.isDefault" placeholder="请选择是否默认赛事" clearable>
                 <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
@@ -348,14 +345,7 @@
 </template>
 
 <script setup name="GameEvent" lang="ts">
-import {
-  listGameEvent,
-  changeEventDefault,
-  delGameEvent,
-  addGameEvent,
-  updateGameEvent,
-  generateNumberTable
-} from '@/api/system/gameEvent';
+import { listGameEvent, changeEventDefault, delGameEvent, addGameEvent, updateGameEvent, generateNumberTable } from '@/api/system/gameEvent';
 import { GameEventVO, GameEventQuery, GameEventForm } from '@/api/system/gameEvent/types';
 import { getEventMdByEventAndType, editEventMd } from '@/api/system/eventMd';
 import { EventMdVO, EventMdForm } from '@/api/system/eventMd/types';
@@ -970,7 +960,6 @@ const handleGenerateBib = () => {
   }
 };
 
-
 onMounted(() => {
   // 获取默认赛事信息
   gameEventStore.fetchDefaultEvent();
@@ -1018,7 +1007,6 @@ onActivated(() => {
   justify-content: center;
 }
 
-
 .operation-buttons .el-button:hover {
   transform: translateY(-1px);
   box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);

+ 22 - 27
src/views/system/gameEventGroup/index.vue

@@ -112,9 +112,9 @@
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="项目类型" prop="projectTypeFilter">
-              <el-select 
-                v-model="formProjectTypeFilter" 
-                placeholder="请选择项目类型" 
+              <el-select
+                v-model="formProjectTypeFilter"
+                placeholder="请选择项目类型"
                 style="width: 100%"
                 @change="handleFormProjectTypeFilterChange"
                 :disabled="!!form.groupId"
@@ -130,9 +130,9 @@
           </el-col>
           <el-col :span="12">
             <el-form-item label="项目" prop="projectId">
-              <el-select 
-                v-model="form.projectId" 
-                placeholder="请选择项目" 
+              <el-select
+                v-model="form.projectId"
+                placeholder="请选择项目"
                 style="width: 100%"
                 @change="handleFormProjectChange"
                 :disabled="!formProjectTypeFilter || !!form.groupId"
@@ -147,7 +147,7 @@
             </el-form-item>
           </el-col>
         </el-row>
-        
+
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="组别名称" prop="groupName">
@@ -202,15 +202,13 @@
           </el-col>
           <el-col :span="12">
             <el-form-item label="组别开始时间" prop="beginTime">
-              <el-date-picker
+              <el-time-picker
                 v-model="form.beginTime"
-                type="datetime"
                 placeholder="选择开始时间"
-                format="YYYY-MM-DD HH:mm"
-                value-format="YYYY-MM-DD HH:mm:ss"
+                format="HH:mm"
+                value-format="HH:mm"
                 style="width: 100%"
                 :disabled="!form.projectId"
-                :disabled-date="disabledDate"
               />
             </el-form-item>
           </el-col>
@@ -366,11 +364,11 @@ const calculatedEndTime = computed(() => {
   if (!form.value.beginTime || !form.value.duration || !form.value.includeGroupNum) {
     return '';
   }
-  
+
   const beginTime = new Date(form.value.beginTime);
   const totalMinutes = form.value.duration * form.value.includeGroupNum;
   const endTime = new Date(beginTime.getTime() + totalMinutes * 60 * 1000);
-  
+
   return endTime.toLocaleString('zh-CN', {
     month: '2-digit',
     day: '2-digit',
@@ -385,10 +383,10 @@ const disabledDate = (time: Date) => {
   if (!form.value.projectId) return false;
   const project = projectList.value.find(p => p.projectId === form.value.projectId);
   if (!project || !project.startTime || !project.endTime) return false;
-  
+
   const projectStart = new Date(project.startTime);
   const projectEnd = new Date(project.endTime);
-  
+
   // 禁用项目时间范围外的日期
   return time < projectStart || time > projectEnd;
 };
@@ -426,7 +424,7 @@ const handleFormProjectTypeFilterChange = () => {
   form.value.trackNum = undefined;
   form.value.fieldNum = undefined;
   form.value.duration = undefined;
-  
+
   // 重新获取项目列表以更新过滤
   getProjectList();
 };
@@ -436,10 +434,7 @@ const handleFormProjectChange = () => {
   if (form.value.projectId) {
     const selectedProject = projectList.value.find(p => p.projectId === form.value.projectId);
     if (selectedProject) {
-      // 自动设置默认开始时间为项目开始时间
-      if (selectedProject.startTime && !form.value.beginTime) {
-        form.value.beginTime = selectedProject.startTime;
-      }
+      // 可以在这里设置一些默认值或者进行其他处理
       console.log('选中的项目:', selectedProject);
     }
   }
@@ -528,29 +523,29 @@ const submitForm = () => {
 
       // 自动计算结束时间
       if (form.value.beginTime && form.value.duration && form.value.includeGroupNum) {
-        const beginTime = new Date(form.value.beginTime);
+        const beginTime = new Date(`2000-01-01 ${form.value.beginTime}`);
         const totalMinutes = form.value.duration * form.value.includeGroupNum;
         const endTime = new Date(beginTime.getTime() + totalMinutes * 60 * 1000);
-        form.value.endTime = endTime.toISOString().slice(0, 19).replace('T', ' ');
+        form.value.endTime = endTime.toTimeString().slice(0, 5);
       }
 
       // 验证时间范围
       if (form.value.beginTime && form.value.endTime) {
         const beginTime = new Date(form.value.beginTime);
         const endTime = new Date(form.value.endTime);
-        
+
         if (beginTime >= endTime) {
           proxy?.$modal.msgError('组别结束时间必须晚于开始时间');
           return;
         }
-        
+
         // 验证组别时间是否在项目时间范围内
         if (form.value.projectId) {
           const selectedProject = projectList.value.find(p => p.projectId === form.value.projectId);
           if (selectedProject && selectedProject.startTime && selectedProject.endTime) {
             const projectStart = new Date(selectedProject.startTime);
             const projectEnd = new Date(selectedProject.endTime);
-            
+
             if (beginTime < projectStart || endTime > projectEnd) {
               proxy?.$modal.msgError('组别比赛时间必须在项目比赛时间范围内');
               return;
@@ -561,7 +556,7 @@ const submitForm = () => {
 
       buttonLoading.value = true;
       const submitForm = { ...form.value };
-      
+
       if (form.value.groupId) {
         await updateGameEventGroup(submitForm).finally(() => (buttonLoading.value = false));
       } else {

+ 11 - 11
src/views/system/gameEventProject/index.vue

@@ -76,10 +76,10 @@
         </el-table-column>
         <el-table-column label="裁判组" align="center" prop="refereeGroup" v-if="columns[8].visible">
           <template #default="scope">
-            <el-button 
-              v-if="scope.row.refereeGroups" 
-              type="primary" 
-              size="small" 
+            <el-button
+              v-if="scope.row.refereeGroups"
+              type="primary"
+              size="small"
               @click="handleViewRefereeGroup(scope.row.refereeGroups, scope.row.projectName)"
             >
               查看裁判组 ({{ scope.row.refereeGroups.length }}人)
@@ -89,13 +89,13 @@
         </el-table-column>
         <!-- <el-table-column label="参赛组数" align="center" prop="groupNum" />
         <el-table-column label="参赛人数" align="center" prop="participateNum" /> -->
-        <el-table-column label="录取名次" align="center" prop="roundType" v-if="columns[9].visible" />
-        <el-table-column label="积分分值" align="center" prop="scoreValue" v-if="columns[10].visible" />
-        <el-table-column label="排序方式" align="center" prop="orderType" v-if="columns[11].visible">
+        <el-table-column label="轮次" align="center" prop="roundType" v-if="columns[9].visible" />
+        <el-table-column label="排序方式" align="center" prop="orderType" v-if="columns[10].visible">
           <template #default="scope">
             {{ scope.row.orderType === '0' ? '升序' : '降序' }}
           </template>
         </el-table-column>
+        <el-table-column label="积分分值" align="center" prop="scoreValue" v-if="columns[11].visible" />
         <!-- <el-table-column label="奖项" align="center" prop="award" />
         <el-table-column label="计时点名称" align="center" prop="timePoint" />
         <el-table-column label="控制盒编号" align="center" prop="boxCode" />
@@ -157,12 +157,12 @@
             <el-radio value="1">降序</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="录取名次" prop="roundType">
-          <el-input v-model="form.roundType" placeholder="请输入录取名次" />
-        </el-form-item>
         <el-form-item label="积分分值" prop="scoreValue">
           <el-input v-model="form.scoreValue" placeholder="请输入积分分值" />
         </el-form-item>
+        <el-form-item label="轮次" prop="roundType">
+          <el-input v-model="form.roundType" placeholder="请输入轮次" />
+        </el-form-item>
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker clearable v-model="form.startTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开始时间">
           </el-date-picker>
@@ -191,7 +191,7 @@
         </div>
       </template>
     </el-dialog>
-    
+
     <!-- 裁判组查看对话框 -->
     <RefereeGroupDialog ref="refereeGroupDialogRef" />
   </div>

+ 25 - 25
src/views/system/gameScore/index.vue

@@ -70,7 +70,7 @@
         </el-table-column>
         <el-table-column label="项目" align="center" prop="projectName" v-if="columns[1].visible" />
         <!-- <el-table-column label="分组" align="center" prop="groupType" v-if="columns[2].visible" /> -->
-        
+
         <el-table-column label="状态" align="center" prop="status" v-if="columns[4].visible">
           <template #default="scope">
             <el-select v-model="scope.row.status" placeholder="请选择状态">
@@ -203,12 +203,12 @@ const printScores = async () => {
       text: '正在准备打印数据...',
       background: 'rgba(0, 0, 0, 0.7)'
     });
-    
+
     let projectsToPrint = [];
-    
+
     // 如果有选择项目,则打印选中的项目
     if (ids.value.length > 0) {
-      projectsToPrint = projectList.value.filter(project => 
+      projectsToPrint = projectList.value.filter(project =>
         ids.value.includes(project.projectId)
       );
     } else {
@@ -238,30 +238,30 @@ const printScores = async () => {
             projectId: project.projectId,
             classification: project.classification,
             pageNum: 1,
-            pageSize: 1000 
+            pageSize: 1000
           });
-          
+
           // 获取成绩数据并补充队伍和运动员信息
           const scores = scoreRes.rows || [];
-          
+
           // 按积分排序,取前3名
           const sortedScores = scores
             .filter(score => score.scorePoint && score.scorePoint > 0) // 只显示有积分的成绩
             .sort((a: any, b: any) => (b.scorePoint || 0) - (a.scorePoint || 0)) // 按积分降序排列
             .slice(0, 3); // 只取前3名
-          
+
           const scoresWithDetails = await Promise.all(
             sortedScores.map(async (score: any) => {
               let teamName = '-';
               let athleteName = '-';
-              
+
               try {
                 // 获取队伍信息
                 if (score.teamId) {
                   const teamRes = await getGameTeam(score.teamId);
                   teamName = teamRes.data.teamName || `队伍${score.teamId}`;
                 }
-                
+
                 // 获取运动员信息
                 if (score.athleteId) {
                   const athleteRes = await getGameAthlete(score.athleteId);
@@ -270,7 +270,7 @@ const printScores = async () => {
               } catch (error) {
                 console.warn('获取队伍或运动员信息失败:', error);
               }
-              
+
               return {
                 ...score,
                 teamName,
@@ -278,7 +278,7 @@ const printScores = async () => {
               };
             })
           );
-          
+
           return {
             ...project,
             scores: scoresWithDetails
@@ -298,18 +298,18 @@ const printScores = async () => {
 
     // 构建打印HTML内容
     const printHtml = buildPrintHtml(projectsWithScores);
-    
+
     // 使用 Blob 和 URL.createObjectURL 来避免弹窗拦截问题
     const blob = new Blob([printHtml], { type: 'text/html' });
     const url = URL.createObjectURL(blob);
-    
+
     // 创建隐藏的 iframe 来处理打印
     const iframe = document.createElement('iframe');
     iframe.style.position = 'absolute';
     iframe.style.top = '-9999px';
     iframe.style.left = '-9999px';
     document.body.appendChild(iframe);
-    
+
     iframe.onload = () => {
       try {
         // 打印完成后清理
@@ -321,14 +321,14 @@ const printScores = async () => {
         console.error('清理打印资源失败:', error);
       }
     };
-    
+
     // 在 iframe 中加载并打印
     const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
     if (iframeDoc) {
       iframeDoc.open();
       iframeDoc.write(printHtml);
       iframeDoc.close();
-      
+
       // 等待内容加载后打印
       setTimeout(() => {
         try {
@@ -359,7 +359,7 @@ const printScores = async () => {
  */
 const buildPrintHtml = (projects: any[]) => {
   const printTime = new Date().toLocaleString('zh-CN');
-  
+
   let html = `
     <!DOCTYPE html>
     <html>
@@ -394,14 +394,14 @@ const buildPrintHtml = (projects: any[]) => {
   // 为每个项目添加成绩表格
   projects.forEach(project => {
     const scores = project.scores || [];
-    
+
     html += `
       <div class="project-section">
         <div class="project-title">
-          <span class="title-label">${project.projectId}</span>  
-          <span class="title-label">${getProjectTypeName(project.projectType)} ${project.projectName}</span> 
+          <span class="title-label">${project.projectId}</span>
+          <span class="title-label">${getProjectTypeName(project.projectType)} ${project.projectName}</span>
         </div>
-        
+
         <table class="score-table">
           <thead>
             <tr>
@@ -468,7 +468,7 @@ const formatScore = (score: number | string) => {
  */
 const getProjectTypeName = (type: string) => {
   if (!game_project_type.value || !type) return '未知';
-  
+
   const typeItem = game_project_type.value.find((item: any) => item.value === type);
   return typeItem ? typeItem.label : '未知';
 };
@@ -477,7 +477,7 @@ const exportScoresNames = () => {
   // 获取默认赛事ID
   const event = gameEventStore.defaultEventInfo;
   const eventId = event?.eventId;
-  
+
   if (!eventId) {
     proxy?.$modal.msgWarning('未指定赛事,无法导出');
     return;
@@ -542,4 +542,4 @@ onMounted(() => {
     refreshData();
   // });
 });
-</script>
+</script>

+ 9 - 4
src/views/system/gameTeam/index.vue

@@ -47,17 +47,22 @@
 
       <el-table v-loading="loading" border :data="gameTeamList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="teamId" v-if="columns[0].visible" />
-        <el-table-column label="队伍编号" align="center" prop="teamCode" v-if="columns[1].visible" />
+        <!--        <el-table-column label="主键" align="center" prop="teamId" v-if="columns[0].visible" />-->
+        <!--        <el-table-column label="队伍编号" align="center" prop="teamCode" v-if="columns[1].visible" />-->
         <el-table-column label="赛事名称" align="center" prop="eventName" v-if="columns[2].visible" />
         <el-table-column label="队伍名称" align="center" prop="teamName" v-if="columns[3].visible" />
-        <el-table-column label="团队描述" align="center" prop="teamDescribe" v-if="columns[4].visible" />
+        <el-table-column label="团队描述" align="center" prop="teamDescribe" v-if="columns[4].visible">
+          <template #default="scope">
+            <span v-if="scope.row.teamDescribe">{{ scope.row.teamDescribe }}</span>
+            <span v-else>该团队暂无描述</span>
+          </template>
+        </el-table-column>
         <el-table-column label="领队" align="center" prop="leader" v-if="columns[5].visible" />
         <el-table-column label="人数" align="center" prop="athleteNum" v-if="columns[6].visible" />
         <el-table-column label="号码段" align="center" prop="numberRange" v-if="columns[7].visible" />
         <el-table-column label="状态" align="center" prop="status" v-if="columns[8].visible">
           <template #default="scope">
-            <dict-tag :options="sys_normal_disable" :value="scope.row.status"/>
+            <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
           </template>
         </el-table-column>
         <el-table-column label="备注" align="center" prop="remark" v-if="columns[9].visible" />