Explorar o código

```
feat(gameScore): 调整成绩数据类型并优化打印功能

- 将 GameScoreVO 中的 individualPerformance 和 teamPerformance
字段从 number 类型改为 string 类型,以支持更多成绩格式

- 移除成绩过滤逻辑,使所有成绩都能参与排名和打印

- 重构 buildPrintHtml 函数,改进打印样式和结构,
添加对个人成绩和团队成绩的区分显示

- 更新完成人数计算逻辑,支持对字符串和数字类型的成绩进行验证

- 在打印页面中添加 topCount 参数支持,实现动态名次显示
```

zhou hai 3 meses
pai
achega
c1887662f2

+ 2 - 2
src/api/system/gameScore/types.ts

@@ -26,12 +26,12 @@ export interface GameScoreVO {
   /**
    * 个人成绩
    */
-  individualPerformance: number;
+  individualPerformance: string;
 
   /**
    * 团队成绩
    */
-  teamPerformance: number;
+  teamPerformance: string;
 
   /**
    * 成绩类型

+ 112 - 90
src/views/system/gameScore/index.vue

@@ -492,7 +492,7 @@ const printScores = async () => {
 
           // 按积分排序,取前n名(使用用户输入的数量)
           const sortedScores = scores
-            .filter(score => score.scorePoint && score.scorePoint > 0) // 只显示有积分的成绩
+            // .filter(score => score.scorePoint && score.scorePoint > 0) // 只显示有积分的成绩
             .sort((a: any, b: any) => (b.scorePoint || 0) - (a.scorePoint || 0)) // 按积分降序排列
             .slice(0, topCount); // 使用用户输入的数量
 
@@ -606,107 +606,123 @@ const printScores = async () => {
 /**
  * 构建打印HTML内容
  */
-const buildPrintHtml = (projects: any[], topCount: number = 3) => {
-  const printTime = new Date().toLocaleString('zh-CN');
-
-  let html = `
-    <!DOCTYPE html>
+const buildPrintHtml = (projects: any[], topCount: number) => {
+  return `
     <html>
     <head>
       <meta charset="UTF-8">
-      <title>赛事成绩打印 - 前${topCount}名</title>
+      <title>成绩打印(前${topCount}名)</title>
       <style>
-        body { font-family: Arial, sans-serif; margin: 20px; }
-        .print-header { text-align: center; margin-bottom: 30px; border-bottom: 2px solid #333; padding-bottom: 20px; }
-        .print-header h1 { margin: 0 0 10px 0; font-size: 24px; color: #333; }
-        .print-header p { margin: 0; color: #666; font-size: 14px; }
-        .project-section { margin-bottom: 40px; page-break-inside: avoid; }
-        .project-title { font-size: 18px; font-weight: bold; margin-bottom: 15px; padding: 10px; background: #f5f5f5; border-left: 4px solid #409eff; }
-        .title-label { color: #f56c6c; font-weight: bold; }
-        .score-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
-        .score-table th, .score-table td { border: 1px solid #ddd; padding: 8px 12px; text-align: center; }
-        .score-table th { background: #f8f9fa; font-weight: bold; color: #333; }
-        .score-table td { color: #333; }
+        body {
+          font-family: SimSun, 'Times New Roman', serif;
+          padding: 20px;
+          margin: 0;
+          color: #333;
+        }
+        .print-header {
+          text-align: center;
+          margin-bottom: 30px;
+          border-bottom: 2px solid #333;
+          padding-bottom: 20px;
+        }
+        .print-header h1 {
+          margin: 0 0 10px 0;
+          font-size: 24px;
+        }
+        .print-header p {
+          margin: 0;
+          color: #666;
+          font-size: 14px;
+        }
+        .project-section {
+          margin-bottom: 40px;
+          page-break-inside: avoid;
+        }
+        .project-title {
+          font-size: 18px;
+          font-weight: bold;
+          margin-bottom: 15px;
+          padding: 10px;
+          background: #f5f5f5;
+          border-left: 4px solid #409eff;
+        }
+        .title-label {
+          font-weight: bold;
+        }
+        .score-table {
+          width: 100%;
+          border-collapse: collapse;
+          margin-bottom: 20px;
+        }
+        .score-table th,
+        .score-table td {
+          border: 1px solid #ddd;
+          padding: 8px 12px;
+          text-align: center;
+        }
+        .score-table th {
+          background: #f8f9fa;
+          font-weight: bold;
+        }
         .rank-1 { background-color: #fff7e6; font-weight: bold; }
         .rank-2 { background-color: #f6ffed; }
         .rank-3 { background-color: #f0f9ff; }
-        @media print { .project-section { page-break-inside: avoid; } }
+        @media print {
+          @page {
+            margin: 20px;
+          }
+          body {
+            padding: 0;
+          }
+          .project-section {
+            page-break-inside: avoid;
+          }
+        }
       </style>
     </head>
     <body>
       <div class="print-header">
         <h1>赛事管理系统 - 成绩打印(前${topCount}名)</h1>
-        <p>打印时间: ${printTime}</p>
+        <p>打印时间: ${new Date().toLocaleString('zh-CN')}</p>
       </div>
-  `;
-
-  // 为每个项目添加成绩表格
-  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>
+      
+      ${projects.map(project => `
+        <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> 
+          </div>
+          <table class="score-table">
+            <thead>
+              <tr>
+                <th class="title-label">类别</th>
+                <th class="title-label">名次</th>
+                <th class="title-label">队伍</th>
+                <th class="title-label">运动员编号</th>
+                <th class="title-label">姓名</th>
+                <th class="title-label">成绩</th>
+                <th class="title-label">积分</th>
+              </tr>
+            </thead>
+            <tbody>
+              ${project.scores.slice(0, topCount).map((score: any, index: number) => `
+                <tr class="${index === 0 ? 'rank-1' : index === 1 ? 'rank-2' : 'rank-3'}">
+                  <td>${score.classification === '0' ? '个人项目' : '团体项目'}</td>
+                  <td>第${Number(index) + 1}名</td>
+                  <td>${score.teamName || '-'}</td>
+                  <td>${score.athleteCode || '-'}</td>
+                  <td>${score.athleteName || '-'}</td>
+                  <td >${score.classification === '0' ? formatScore(score.individualPerformance) : formatScore(score.teamPerformance)}</td>
+                  <td>${score.scorePoint || 0}</td>
+                </tr>
+              `).join('')}
+            </tbody>
+          </table>
         </div>
-
-        <table class="score-table">
-          <thead>
-            <tr>
-              <th class="title-label">类别</th>
-              <th>名次</th>
-              <th class="title-label">队伍</th>
-              <th class="title-label">运动员编号</th>
-              <th class="title-label">姓名</th>
-              <th class="title-label">成绩</th>
-              <th class="title-label">积分</th>
-            </tr>
-          </thead>
-          <tbody>
-    `;
-
-    if (scores.length > 0) {
-      scores.forEach((score: any, index: number) => {
-        // 动态设置排名样式,只对前3名应用特殊样式
-        let rankClass = '';
-        if (index === 0) rankClass = 'rank-1';
-        else if (index === 1) rankClass = 'rank-2';
-        else if (index === 2) rankClass = 'rank-3';
-        
-        html += `
-          <tr class="${rankClass}">
-            <td>${score.classification === '0' ? '个人项目' : '团体项目'}</td>
-            <td>第${index + 1}名</td>
-            <td>${score.teamName || '-'}</td>
-            <td>${score.athleteCode || '-'}</td>
-            <td>${score.athleteName || '-'}</td>
-            <td>${formatScore(score.individualPerformance || score.teamPerformance)}</td>
-            <td>${score.scorePoint || 0}</td>
-          </tr>
-        `;
-      });
-    } else {
-      html += `
-        <tr>
-          <td colspan="7" style="text-align: center; color: #999;">暂无成绩数据</td>
-        </tr>
-      `;
-    }
-
-    html += `
-          </tbody>
-        </table>
-      </div>
-    `;
-  });
-
-  html += `
+      `).join('')}
     </body>
     </html>
   `;
-
-  return html;
 };
 
 /**
@@ -847,12 +863,16 @@ const calculateParticipantCounts = async () => {
       
       // 计算完赛人数(有成绩记录的)
       const completedScores = scores.filter(score => {
-        const individualScore = parseFloat(score.individualPerformance);
-        const teamScore = parseFloat(score.teamPerformance);
-        
         // 成绩必须存在且大于0
-        return (!isNaN(individualScore) && individualScore > 0) || 
-              (!isNaN(teamScore) && teamScore > 0);
+        const sc = score.classification === '0' ? score.individualPerformance : score.teamPerformance;
+        if (sc === null || sc === undefined || sc === '') {
+          return false;
+        }
+        if (typeof  sc === 'number') {
+          return !isNaN(sc) && sc > 0;
+        }else if(typeof  sc === 'string'){
+          return sc !== '0';
+        }
       });
       console.log('completedScores: ', completedScores);
       (project as any).completedParticipants = completedScores.length;
@@ -907,3 +927,5 @@ onMounted(() => {
   loadProjects();
 });
 </script>
+
+

+ 23 - 35
src/views/system/gameScore/print.vue

@@ -9,22 +9,22 @@
     <!-- 打印内容 -->
     <div class="print-content" ref="printContent">
       <div class="print-header">
-        <h1>赛事管理系统 - 成绩打印(前3名)</h1>
+        <h1>赛事管理系统 - 成绩打印(前{{ topCount }}名)</h1>
         <p>打印时间: {{ printTime }}</p>
       </div>
 
       <!-- 项目成绩表格 -->
       <div v-for="project in projectScores" :key="project.projectId" class="project-section">
         <div class="project-title">
-          <span class="title-label">竞赛序员</span> {{ project.projectId }} 
-          <span class="title-label">项目类型-项目名称</span> {{ project.projectTypeName }} {{ project.projectName }}
+          <span class="title-label">{{ project.projectId }} </span> 
+          <span class="title-label">{{ project.projectTypeName }}-{{ project.projectName }}</span> 
         </div>
         
         <table class="score-table">
           <thead>
             <tr>
               <th class="title-label">类别</th>
-              <th>名次</th>
+              <th class="title-label">名次</th>
               <th class="title-label">队伍</th>
               <th class="title-label">运动员编号</th>
               <th class="title-label">姓名</th>
@@ -33,14 +33,14 @@
             </tr>
           </thead>
           <tbody>
-            <tr v-for="(score, index) in project.scores.slice(0, 3)" :key="score.scoreId" 
+            <tr v-for="(score, index) in project.scores.slice(0, topCount)" :key="score.scoreId" 
                 :class="index === 0 ? 'rank-1' : index === 1 ? 'rank-2' : 'rank-3'">
               <td>{{ score.classification === '0' ? '个人项目' : '团体项目' }}</td>
-              <td>第{{ index + 1 }}名</td>
+              <td>第{{ Number(index) + 1 }}名</td>
               <td>{{ score.teamName || '-' }}</td>
               <td>{{ score.athleteCode || '-' }}</td>
               <td>{{ score.athleteName || '-' }}</td>
-              <td>{{ formatScore(score.individualPerformance || score.teamPerformance) }}</td>
+              <td>{{ score.classification === '0' ? formatScore(score.individualPerformance) : formatScore(score.teamPerformance)}}</td>
               <td>{{ score.scorePoint || 0 }}</td>
             </tr>
           </tbody>
@@ -51,12 +51,13 @@
 </template>
 
 <script setup lang="ts" name="GameScorePrint">
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, computed, nextTick } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { getProjectScoreData } from '@/api/system/gameScore'
 import { getGameEventProject } from '@/api/system/gameEventProject'
 import { GameScoreVO } from '@/api/system/gameScore/types'
 import { GameEventProjectVO } from '@/api/system/gameEventProject/types'
+import { ElMessage } from 'element-plus'
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { game_project_type } = toRefs<any>(proxy?.useDict('game_project_type'));
@@ -69,6 +70,7 @@ const isPrinting = ref(false)
 const printContent = ref<HTMLElement>()
 const projectScores = ref<any[]>([])
 const loading = ref(false)
+const topCount = ref(3) // 默认值为3
 
 // 计算属性
 const printTime = computed(() => {
@@ -86,8 +88,18 @@ const loadProjectScores = async () => {
   try {
     loading.value = true
     
-    // 从路由参数获取项目ID
+    // 从路由参数获取项目ID和topCount
     const projectId = route.params.projectId as string
+    const topCountParam = route.params.topCount as string
+    
+    // 如果有topCount参数,则使用它,否则保持默认值
+    if (topCountParam) {
+      const count = parseInt(topCountParam)
+      if (count > 0) {
+        topCount.value = count
+      }
+    }
+    
     if (!projectId) {
       ElMessage.error('项目ID不能为空')
       return
@@ -105,7 +117,6 @@ const loadProjectScores = async () => {
       pageNum: 1,
       pageSize: 1000 // 获取所有成绩
     })
-
     // 处理数据格式
     const scores = scoreRes.rows || []
     const sortedScores = scores.sort((a: GameScoreVO, b: GameScoreVO) => {
@@ -143,29 +154,6 @@ const getProjectTypeName = (type: string) => {
   return typeItem ? typeItem.label : '未知';
 }
 
-// 获取分组类型名称
-// const getGroupTypeName = (type: string) => {
-//   const groupMap: Record<string, string> = {
-//     '1': '甲组',
-//     '2': '乙组',
-//     '3': '丙组',
-//     '4': '丁组'
-//   }
-//   return groupMap[type] || '未知组'
-// }
-
-// 获取队伍名称(这里需要根据实际API调整)
-// const getTeamName = (teamId: string | number) => {
-//   // 实际项目中应该调用队伍API获取名称
-//   return `队伍${teamId}`
-// }
-
-// // 获取运动员姓名(这里需要根据实际API调整)
-// const getAthleteName = (athleteId: string | number) => {
-//   // 实际项目中应该调用运动员API获取姓名
-//   return `运动员${athleteId}`
-// }
-
 // 打印页面
 const printPage = () => {
   isPrinting.value = true
@@ -233,7 +221,7 @@ onMounted(() => {
 }
 
 .title-label {
-  color: #f56c6c;
+  /* color: #f56c6c; */
   font-weight: bold;
 }
 
@@ -283,4 +271,4 @@ onMounted(() => {
     -webkit-print-color-adjust: exact;
   }
 }
-</style> 
+</style>