Browse Source

Merge branch 'dev_zlt' into dev

zhou 2 days ago
parent
commit
67bf31bdcd

+ 13 - 0
src/api/system/gameScore/index.ts

@@ -133,3 +133,16 @@ export const recalculateRankingsAndPoints = (eventId: string | number, projectId
     params: { eventId, projectId }
   });
 };
+
+/**
+ * 导出成绩汇总表
+ * @param eventId 赛事ID
+ */
+export const exportScoresSummary = (eventId: string | number) => {
+  return request({
+    url: '/system/gameScore/exportScoresSummary',
+    method: 'post',
+    params: { eventId },
+    responseType: 'blob' // 确保以二进制流形式接收响应
+  });
+};

+ 6 - 0
src/router/index.ts

@@ -143,6 +143,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         name: 'GameScoreEdit',
         meta: { title: '修改成绩', icon: 'form' }
       },
+      {
+        path: 'print/:projectId',
+        component: () => import('@/views/system/gameScore/print.vue'),
+        name: 'GameScorePrint',
+        meta: { title: '打印成绩', icon: 'form' }
+      }
     ]
   },
   {

+ 2 - 2
src/utils/request.ts

@@ -124,8 +124,8 @@ service.interceptors.response.use(
     // 获取错误信息
     const msg = errorCode[code] || res.data.msg || errorCode['default'];
     // 二进制数据则直接返回
-    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
-      return res.data;
+    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer' || res.config.responseType === 'blob' || res.config.responseType === 'arraybuffer') {
+      return res;
     }
     if (code === 401) {
       // prettier-ignore

+ 0 - 1
src/views/system/gameEventProject/index.vue

@@ -208,7 +208,6 @@ import {
 import { listGameEventGroup } from '@/api/system/gameEventGroup';
 import { GameEventProjectVO, GameEventProjectQuery, GameEventProjectForm } from '@/api/system/gameEventProject/types';
 import RefereeGroupDialog from './RefereeGroupDialog.vue';
-import { orderBy } from 'element-plus/es/components/table/src/util';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { game_score_type, game_project_type, game_project_classification } = toRefs<any>(proxy?.useDict('game_score_type', 'game_project_type','game_project_classification'));

+ 2 - 2
src/views/system/gameScore/gameScoreEdit.vue

@@ -191,7 +191,7 @@ const form = reactive({
   projectId: '',
   athleteCode: '',
   userId: 0,
-  teamName: '', // 个人项目和团体项目都使用
+  teamName: '',
   updateTime: '',
 });
 
@@ -298,7 +298,7 @@ const editScore = (row: any) => {
     userId: row.userId,
     eventId: row.eventId,
     projectId: row.projectId,
-    teamName: row.teamName || '', // 统一使用teamName字段
+    teamName: row.teamName || '',
     updateTime: row.updateTime,
   });
 };

+ 395 - 107
src/views/system/gameScore/index.vue

@@ -4,16 +4,6 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <!-- <el-form-item label="赛事" prop="eventId">
-              <el-select v-model="queryParams.eventId" placeholder="请选择赛事" clearable filterable @change="handleEventChange">
-                <el-option
-                  v-for="event in eventList"
-                  :key="event.eventId"
-                  :label="event.eventName"
-                  :value="event.eventId">
-                </el-option>
-              </el-select>
-            </el-form-item> -->
             <el-form-item label="项目" prop="projectId">
               <el-select v-model="queryParams.projectId" placeholder="请选择项目" clearable filterable>
                 <el-option
@@ -43,14 +33,11 @@
             <el-button type="primary" @click="refreshData">刷新</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="primary" @click="printScores">打印成绩(仅有名次)</el-button>
+            <el-button type="primary" @click="printScores">打印成绩(前3名)</el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="primary" @click="exportScoresNames">导出成绩(全部)</el-button>
           </el-col>
-          <el-col :span="1.5">
-            <el-button type="primary" @click="exportNumberMapping">导出号码对照表</el-button>
-          </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
         </el-row>
       </template>
@@ -109,19 +96,26 @@
 </template>
 
 <script setup name="GameScore" lang="ts">
-import { listGameScore, getGameScore, delGameScore, addGameScore, updateGameScore } from '@/api/system/gameScore';
+import { listGameScore, getGameScore, delGameScore, addGameScore, updateGameScore, getProjectScoreData, exportScoresSummary } from '@/api/system/gameScore';
 import { getDefaultEvent } from '@/api/system/gameEvent'
 import { listGameEventProject } from '@/api/system/gameEventProject';
+import { getGameTeam } from '@/api/system/gameTeam';
+import { getGameAthlete } from '@/api/system/gameAthlete';
 import { GameScoreVO, GameScoreQuery, GameScoreForm } from '@/api/system/gameScore/types';
 import { GameEventVO, GameEventQuery } from '@/api/system/gameEvent/types';
 import { GameEventProjectVO, GameEventProjectQuery } from '@/api/system/gameEventProject/types';
+import { GameTeamVO } from '@/api/system/gameTeam/types';
+import { GameAthleteVO } from '@/api/system/gameAthlete/types';
+import { ElLoading, ElMessage } from 'element-plus';
+import { useGameEventStore } from '@/store/modules/gameEvent';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { game_project_type } = toRefs<any>(proxy?.useDict('game_project_type'));
 const router = useRouter();
 
 // 默认赛事信息
-const defaultEvent = ref<GameEventVO>({} as GameEventVO)
+// const defaultEvent = ref<GameEventVO>({} as GameEventVO)
+const gameEventStore = useGameEventStore();
 const gameScoreList = ref<GameScoreVO[]>([]);
 
 const buttonLoading = ref(false);
@@ -179,113 +173,407 @@ const data = reactive<PageData<GameScoreForm, GameScoreQuery>>({
 });
 
 const { queryParams, form, rules } = toRefs(data);
-// 添加额外的ref用于处理默认事件ID
-const defaultEventId = computed(() => defaultEvent.value?.eventId);
-
-// 监听默认事件变化
-watchEffect(() => {
-  if (defaultEventId.value) {
-    form.value.eventId = defaultEventId.value;
-    queryParams.value.eventId = defaultEventId.value;
-  }
-});
-
-// 获取默认赛事
-const getDefaultEventInfo = async () => {
-  try {
-    const res = await getDefaultEvent()
-    defaultEvent.value = res.data
-  } catch (error) {
-    ElMessage.error('获取默认赛事失败')
-  }
-}
 
 /** 查询成绩列表 */
 const getList = async () => {
-  if (!queryParams.value.eventId) {
-    proxy?.$modal.msgWarning('未指定默认赛事');
-    loading.value = false;
-    return;
-  }
-  // loading.value = true;
+  // if (!queryParams.value.eventId) {
+  //   proxy?.$modal.msgWarning('未指定默认赛事');
+  //   loading.value = false;
+  //   return;
+  // }
+  loading.value = true;
   const res = await listGameScore(queryParams.value);
   gameScoreList.value = res.rows;
   total.value = res.total;
   loading.value = false;
 }
 
-/** 查询所有赛事列表 */
-// const getEventList = async () => {
-//   const res = await listGameEvent({
-//     // status: '0', // 只查询正常状态的赛事
-//     pageNum: 1,
-//     pageSize: 1000
-//   } as GameEventQuery);
-//   eventList.value = res.rows;
-  
-//   // 如果有赛事数据,默认选择第一个
-//   if (res.rows && res.rows.length > 0) {
-//     queryParams.value.eventId = res.rows[0].eventId;
-//     // 触发赛事变更事件,加载相关项目、队伍、运动员数据
-//     handleEventChange(queryParams.value.eventId);
-//     await getScoreStatus(queryParams.value.projectId);
-//   }
-// }
-
-/** 根据赛事ID查询项目列表 */
-// const getProjectList = async (eventId?: string | number) => {
-//   if (!eventId) return;
-//   const res = await listGameEventProject({
-//     eventId: eventId,
-//     // status: '0', // 只查询正常状态的项目
-//     pageNum: 1,
-//     pageSize: 1000
-//   } as GameEventProjectQuery);
-//   projectList.value = res.rows;
-// }
-
-
-/** 赛事变更事件 */
-// const handleEventChange = async (eventId: string | number | undefined) => {
-//   if (!eventId) {
-//     // 清空相关下拉框数据
-//     projectList.value = [];
-//     return;
-//   }
-  
-//   // 加载相关数据
-//   await Promise.all([
-//     getProjectList(eventId),
-//   ]);
-// }
-
-// const getScoreStatus = async (projectId: string | number) => {
-//   const score = await listGameScore({
-//     projectId: projectId,
-//     pageNum: 1,
-//     pageSize: 100
-//   });
-//   if (!score || !score.rows.length) {
-//     return '0';
-//   }
-//   return '1';
-// };
 /**
  * 刷新数据
  */
 const refreshData = async () => {
   await loadProjects();
 };
+// 打印成绩
 const printScores = async () => {
-  console.log('打印成绩逻辑待实现');
-  // await loadProjects();
+  try {
+    // 显示加载状态
+    const loadingInstance = ElLoading.service({
+      lock: true,
+      text: '正在准备打印数据...',
+      background: 'rgba(0, 0, 0, 0.7)'
+    });
+    
+    let projectsToPrint = [];
+    
+    // 如果有选择项目,则打印选中的项目
+    if (ids.value.length > 0) {
+      projectsToPrint = projectList.value.filter(project => 
+        ids.value.includes(project.projectId)
+      );
+    } else {
+      // 如果没有选择项目,提示用户是否打印所有项目
+      try {
+        await proxy?.$modal.confirm('未选择项目,是否打印当前页面所有项目?');
+        projectsToPrint = [...projectList.value];
+      } catch {
+        // 用户取消操作
+        loadingInstance.close();
+        return;
+      }
+    }
+
+    if (projectsToPrint.length === 0) {
+      proxy?.$modal.msgWarning('没有可打印的项目');
+      loadingInstance.close();
+      return;
+    }
+
+    // 为每个项目获取成绩数据
+    const projectsWithScores = await Promise.all(
+      projectsToPrint.map(async (project) => {
+        try {
+          const scoreRes = await getProjectScoreData({
+            eventId: project.eventId,
+            projectId: project.projectId,
+            classification: project.classification,
+            pageNum: 1,
+            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);
+                  athleteName = athleteRes.data.name || `运动员${score.athleteId}`;
+                }
+              } catch (error) {
+                console.warn('获取队伍或运动员信息失败:', error);
+              }
+              
+              return {
+                ...score,
+                teamName,
+                athleteName
+              };
+            })
+          );
+          
+          return {
+            ...project,
+            scores: scoresWithDetails
+          };
+        } catch (error) {
+          console.error(`获取项目 ${project.projectName} 成绩失败:`, error);
+          return {
+            ...project,
+            scores: []
+          };
+        }
+      })
+    );
+
+    // 关闭加载状态
+    loadingInstance.close();
+
+    // 构建打印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 {
+        // 打印完成后清理
+        setTimeout(() => {
+          document.body.removeChild(iframe);
+          URL.revokeObjectURL(url);
+        }, 1000);
+      } catch (error) {
+        console.error('清理打印资源失败:', error);
+      }
+    };
+    
+    // 在 iframe 中加载并打印
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
+    if (iframeDoc) {
+      iframeDoc.open();
+      iframeDoc.write(printHtml);
+      iframeDoc.close();
+      
+      // 等待内容加载后打印
+      setTimeout(() => {
+        try {
+          iframe.contentWindow?.focus();
+          iframe.contentWindow?.print();
+        } catch (error) {
+          proxy?.$modal.msgError('打印失败,请检查浏览器设置');
+          console.error('打印失败:', error);
+        }
+      }, 500);
+    } else {
+      proxy?.$modal.msgError('无法创建打印窗口,请检查浏览器设置');
+      document.body.removeChild(iframe);
+      URL.revokeObjectURL(url);
+    }
+  } catch (error) {
+    if (error === 'cancel') {
+      // 用户取消操作
+      return;
+    }
+    console.error('打印失败:', error);
+    proxy?.$modal.msgError('打印失败');
+  }
+};
+
+/**
+ * 构建打印HTML内容
+ */
+const buildPrintHtml = (projects: any[]) => {
+  const printTime = new Date().toLocaleString('zh-CN');
+  
+  let html = `
+    <!DOCTYPE html>
+    <html>
+    <head>
+      <meta charset="UTF-8">
+      <title>赛事成绩打印 - 前3名</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; }
+        .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; } }
+      </style>
+    </head>
+    <body>
+      <div class="print-header">
+        <h1>赛事管理系统 - 成绩打印(前3名)</h1>
+        <p>打印时间: ${printTime}</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> 
+        </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) => {
+        const rankClass = index === 0 ? 'rank-1' : index === 1 ? 'rank-2' : 'rank-3';
+        html += `
+          <tr class="${rankClass}">
+            <td>${score.classification === '0' ? '个人项目' : '团体项目'}</td>
+            <td>第${index + 1}名</td>
+            <td>${score.teamName || '-'}</td>
+            <td>${score.athleteId || '-'}</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 += `
+    </body>
+    </html>
+  `;
+
+  return html;
 };
-const exportNumberMapping = async () => {
-  console.log('导出号码对照表逻辑待实现');
-  // await loadProjects();
+
+/**
+ * 格式化成绩显示
+ */
+const formatScore = (score: number | string) => {
+  if (score === null || score === undefined || score === '') return '-';
+  return score.toString();
 };
+
+/**
+ * 获取项目类型名称
+ */
+const getProjectTypeName = (type: string) => {
+  const typeMap: Record<string, string> = {
+    '1': '田径',
+    '2': '游泳', 
+    '3': '球类',
+    '4': '其他'
+  };
+  return typeMap[type] || '未知';
+};
+
 const exportScoresNames = async () => { 
-  console.log('导出成绩逻辑待实现');
+  try {
+    // 显示加载状态
+    const loadingInstance = ElLoading.service({
+      lock: true,
+      text: '正在导出成绩汇总表...',
+      background: 'rgba(0, 0, 0, 0.7)'
+    });
+
+    // 获取默认赛事ID
+    const event = gameEventStore.defaultEventInfo;
+    const eventId = event?.eventId;
+    
+    if (!eventId) {
+      proxy?.$modal.msgWarning('未指定赛事,无法导出');
+      loadingInstance.close();
+      return;
+    }
+
+    // 调用导出接口
+    const response = await exportScoresSummary(eventId);
+    
+    // 校验响应是否为有效的二进制数据
+    if (!response || !response.data || !(response.data instanceof Blob)) {
+      proxy?.$modal.msgError('导出失败:服务器返回数据异常');
+      loadingInstance.close();
+      return;
+    }
+
+    // 创建Blob时,明确指定类型
+    const blob = new Blob([response.data], { 
+      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 
+    });
+    
+    // 验证Blob大小是否合理(防止空文件)
+    if (blob.size === 0) {
+      proxy?.$modal.msgError('导出失败:生成的文件为空');
+      loadingInstance.close();
+      return;
+    }
+
+    const url = window.URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.href = url;
+    link.download = `成绩汇总表_${new Date().toLocaleDateString()}.xlsx`;
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(url);
+    
+    loadingInstance.close();
+    proxy?.$modal.msgSuccess('导出成功');
+    
+  } catch (error) {
+    console.error('导出失败:', error);
+    let errorMessage = '未知错误';
+    
+    if (error instanceof Error) {
+      errorMessage = error.message;
+    } else if (typeof error === 'string') {
+      errorMessage = error;
+    } else if (error && typeof error === 'object' && 'message' in error) {
+      errorMessage = String(error.message);
+    }
+    
+    // 尝试获取更详细的错误信息
+    if (error && typeof error === 'object' && 'response' in error) {
+      const response = (error as any).response;
+      if (response && response.data) {
+        try {
+          if (response.data instanceof Blob) {
+            // 如果是blob,尝试读取错误信息
+            const reader = new FileReader();
+            reader.onload = function(e) {
+              try {
+                const text = e.target?.result as string;
+                const errorObj = JSON.parse(text);
+                if (errorObj.msg) {
+                  errorMessage = errorObj.msg;
+                }
+              } catch (parseError) {
+                console.warn('无法解析错误响应:', parseError);
+              }
+            };
+            reader.readAsText(response.data);
+          } else if (typeof response.data === 'string') {
+            errorMessage = response.data;
+          } else if (response.data.msg) {
+            errorMessage = response.data.msg;
+          }
+        } catch (parseError) {
+          console.warn('解析错误响应失败:', parseError);
+        }
+      }
+    }
+    
+    proxy?.$modal.msgError('导出失败:' + errorMessage);
+  }
 };
 
 /**
@@ -308,7 +596,7 @@ const handleQuery = () => {
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   // 保留默认赛事ID
-  queryParams.value.eventId = defaultEvent.value?.eventId;
+  // queryParams.value.eventId = defaultEvent.value?.eventId;
   handleQuery();
 };
 
@@ -332,9 +620,9 @@ const navigateToEditPage = (row: GameEventProjectVO) => {
 };
 
 onMounted(() => {
-  getDefaultEventInfo().then(() => {
+  // getDefaultEventInfo().then(() => {
     getList();
     refreshData();
-  });
+  // });
 });
 </script>

+ 285 - 0
src/views/system/gameScore/print.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="print-container">
+    <!-- 打印按钮 -->
+    <div class="print-actions" v-if="!isPrinting">
+      <el-button type="primary" @click="printPage">打印</el-button>
+      <el-button @click="goBack">返回</el-button>
+    </div>
+
+    <!-- 打印内容 -->
+    <div class="print-content" ref="printContent">
+      <div class="print-header">
+        <h1>赛事管理系统 - 成绩打印(前3名)</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 }}
+        </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>
+            <tr v-for="(score, index) in project.scores.slice(0, 3)" :key="score.scoreId" 
+                :class="index === 0 ? 'rank-1' : index === 1 ? 'rank-2' : 'rank-3'">
+              <td>{{ score.classification === '0' ? '个人项目' : '团体项目' }}</td>
+              <td>第{{ index + 1 }}名</td>
+              <td>{{ score.teamName || '-' }}</td>
+              <td>{{ score.athleteId || '-' }}</td>
+              <td>{{ score.athleteName || '-' }}</td>
+              <td>{{ formatScore(score.individualPerformance || score.teamPerformance) }}</td>
+              <td>{{ score.scorePoint || 0 }}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="GameScorePrint">
+import { ref, onMounted, computed } 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'
+
+const route = useRoute()
+const router = useRouter()
+
+// 响应式数据
+const isPrinting = ref(false)
+const printContent = ref<HTMLElement>()
+const projectScores = ref<any[]>([])
+const loading = ref(false)
+
+// 计算属性
+const printTime = computed(() => {
+  return new Date().toLocaleString('zh-CN')
+})
+
+// 格式化成绩显示
+const formatScore = (score: number | string) => {
+  if (score === null || score === undefined || score === '') return '-'
+  return score.toString()
+}
+
+// 获取项目成绩数据
+const loadProjectScores = async () => {
+  try {
+    loading.value = true
+    
+    // 从路由参数获取项目ID
+    const projectId = route.params.projectId as string
+    if (!projectId) {
+      ElMessage.error('项目ID不能为空')
+      return
+    }
+
+    // 获取项目信息
+    const projectRes = await getGameEventProject(projectId)
+    const project = projectRes.data
+
+    // 获取项目成绩数据
+    const scoreRes = await getProjectScoreData({
+      eventId: project.eventId,
+      projectId: project.projectId,
+      classification: project.classification,
+      pageNum: 1,
+      pageSize: 1000 // 获取所有成绩
+    })
+
+    // 处理数据格式
+    const scores = scoreRes.rows || []
+    const sortedScores = scores.sort((a: GameScoreVO, b: GameScoreVO) => {
+      return (a.scoreRank || 0) - (b.scoreRank || 0)
+    })
+
+    // 构建项目成绩数据结构
+    projectScores.value = [{
+      projectId: project.projectId,
+      projectName: project.projectName,
+      projectTypeName: getProjectTypeName(project.projectType),
+      // groupTypeName: getGroupTypeName(project.groupType),
+      classification: project.classification,
+      scores: sortedScores.map(score => ({
+        ...score,
+        teamName: getTeamName(score.teamId),
+        athleteName: getAthleteName(score.athleteId)
+      }))
+    }]
+
+  } catch (error) {
+    console.error('加载项目成绩失败:', error)
+    ElMessage.error('加载项目成绩失败')
+  } finally {
+    loading.value = false
+  }
+}
+
+// 获取项目类型名称
+const getProjectTypeName = (type: string) => {
+  const typeMap: Record<string, string> = {
+    '1': '田径',
+    '2': '游泳',
+    '3': '球类',
+    '4': '其他'
+  }
+  return typeMap[type] || '未知'
+}
+
+// 获取分组类型名称
+// 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
+  
+  // 等待DOM更新后执行打印
+  nextTick(() => {
+    window.print()
+    isPrinting.value = false
+  })
+}
+
+// 返回上一页
+const goBack = () => {
+  router.go(-1)
+}
+
+// 页面加载时获取数据
+onMounted(() => {
+  loadProjectScores()
+})
+</script>
+
+<style scoped>
+.print-container {
+  padding: 20px;
+  background: white;
+}
+
+.print-actions {
+  margin-bottom: 20px;
+  text-align: center;
+}
+
+.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;
+}
+
+.rank-1 { background-color: #fff7e6; font-weight: bold; }
+.rank-2 { background-color: #f6ffed; }
+.rank-3 { background-color: #f0f9ff; }
+
+/* 打印样式 */
+@media print {
+  .print-actions {
+    display: none;
+  }
+  
+  .print-container {
+    padding: 0;
+  }
+  
+  .project-section {
+    page-break-inside: avoid;
+  }
+  
+  .score-table th {
+    background: #f8f9fa !important;
+    -webkit-print-color-adjust: exact;
+  }
+}
+</style> 

+ 3 - 3
src/views/system/gameTeam/index.vue

@@ -169,9 +169,9 @@ const columns = ref<FieldOption[]>([
   { key: 2, label: '赛事名称', visible: false },
   { key: 3, label: '队伍名称', visible: true },
   { key: 4, label: '团队描述', visible: true },
-  { key: 5, label: '领队', visible: false },
-  { key: 6, label: '人数', visible: false },
-  { key: 7, label: '号码段', visible: false },
+  { key: 5, label: '领队', visible: true },
+  { key: 6, label: '人数', visible: true },
+  { key: 7, label: '号码段', visible: true },
   { key: 8, label: '状态', visible: false },
   { key: 9, label: '备注', visible: false }
 ]);