Explorar o código

feat(gameEvent): 添加排行榜导出和打印功能

- 为个人排行榜添加导出CSV和打印按钮
- 为团队排行榜添加导出CSV和打印按钮
- 实现导出个人和团队排行榜到CSV文件功能
- 实现打印个人和团队排行榜功能
- 添加弹窗让用户选择导出/打印前几名数据
- 引入file-saver库处理文件下载
- 添加Element Plus的Download和Printer图标
zhou hai 1 mes
pai
achega
b416136d44
Modificáronse 1 ficheiros con 237 adicións e 1 borrados
  1. 237 1
      src/views/system/gameEvent/RankingBoardPage.vue

+ 237 - 1
src/views/system/gameEvent/RankingBoardPage.vue

@@ -58,6 +58,24 @@
                     />
                     <span class="control-label">名</span>
                   </div>
+                  <el-button
+                    type="success"
+                    size="small"
+                    :icon="Download"
+                    @click="exportPersonalRanking"
+                    class="export-btn"
+                  >
+                    导出
+                  </el-button>
+                  <el-button
+                    type="warning"
+                    size="small"
+                    :icon="Printer"
+                    @click="printPersonalRanking"
+                    class="print-btn"
+                  >
+                    打印
+                  </el-button>
                   <el-button
                     type="primary"
                     size="small"
@@ -152,6 +170,24 @@
                     />
                     <span class="control-label">名</span>
                   </div>
+                  <el-button
+                    type="success"
+                    size="small"
+                    :icon="Download"
+                    @click="exportTeamRanking"
+                    class="export-btn"
+                  >
+                    导出
+                  </el-button>
+                  <el-button
+                    type="warning"
+                    size="small"
+                    :icon="Printer"
+                    @click="printTeamRanking"
+                    class="print-btn"
+                  >
+                    打印
+                  </el-button>
                   <el-button
                     type="primary"
                     size="small"
@@ -205,7 +241,8 @@ import { useGameEventStore } from '@/store/modules/gameEvent';
 import { storeToRefs } from 'pinia';
 import { listRankGroup } from '@/api/system/rankGroup'; 
 import { RankGroupVO } from '@/api/system/rankGroup/types'; 
-import { Refresh } from '@element-plus/icons-vue';
+import { Refresh, Download, Printer } from '@element-plus/icons-vue';
+import { saveAs } from 'file-saver';
 
 // 定义队伍积分排行榜的数据结构
 interface TeamScore {
@@ -605,6 +642,205 @@ const refreshPersonalRanking = async () => {
   }
 };
 
+// 导出个人排行榜
+const exportPersonalRanking = async () => {
+  if (athleteScoreList.value.length === 0) {
+    ElMessage.warning('没有可导出的数据');
+    return;
+  }
+
+  try {
+    const { value } = await ElMessageBox.prompt('请输入导出前几名(不输入则导出全部)', '导出确认', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      inputPattern: /^\d*$/,
+      inputErrorMessage: '请输入数字'
+    });
+
+    const count = value ? parseInt(value) : athleteScoreList.value.length;
+    const exportData = athleteScoreList.value.slice(0, count);
+
+    const columns = ['排名', '姓名', '队伍名称', '项目', '成绩'];
+    const csvContent = exportData.map((item, index) => {
+      const rank = getRankDisplay(item, index, athleteScoreList.value);
+      const name = item.name || item.athleteName || '';
+      const team = item.teamName || '';
+      const project = selectedProjectName.value;
+      // 在成绩前加 \t 防止 Excel 自动转换格式
+      const scoreValue = item.individualPerformance || item.score || item.totalScore || '';
+      const score = `\t${scoreValue}`;
+      return [rank, name, team, project, score].join(',');
+    });
+
+    const blob = new Blob(['\ufeff' + columns.join(',') + '\n' + csvContent.join('\n')], { type: 'text/csv;charset=utf-8;' });
+    saveAs(blob, `${selectedProjectName.value}_个人排行榜.csv`);
+  } catch (e) {
+    // 点击取消不执行
+  }
+};
+
+// 打印个人排行榜
+const printPersonalRanking = async () => {
+  if (athleteScoreList.value.length === 0) {
+    ElMessage.warning('没有可打印的数据');
+    return;
+  }
+
+  try {
+    const { value } = await ElMessageBox.prompt('请输入打印前几名(不输入则打印全部)', '打印确认', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      inputPattern: /^\d*$/,
+      inputErrorMessage: '请输入数字'
+    });
+
+    const count = value ? parseInt(value) : athleteScoreList.value.length;
+    const printData = athleteScoreList.value.slice(0, count);
+
+    const title = `${selectedProjectName.value} - 个人排行榜`;
+    const columns = ['排名', '姓名', '队伍名称', '项目', '成绩'];
+    const rows = printData.map((item, index) => {
+      const rank = getRankDisplay(item, index, athleteScoreList.value);
+      const name = item.name || item.athleteName || '';
+      const team = item.teamName || '';
+      const project = selectedProjectName.value;
+      const score = item.individualPerformance || item.score || item.totalScore || '';
+      return [rank, name, team, project, score];
+    });
+
+    generatePrintPage(title, columns, rows);
+  } catch (e) {
+    // 取消
+  }
+};
+
+// 导出团队排行榜
+const exportTeamRanking = async () => {
+  if (filteredTeamScores.value.length === 0) {
+    ElMessage.warning('没有可导出的数据');
+    return;
+  }
+
+  try {
+    const { value } = await ElMessageBox.prompt('请输入导出前几名(不输入则导出全部)', '导出确认', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      inputPattern: /^\d*$/,
+      inputErrorMessage: '请输入数字'
+    });
+
+    const count = value ? parseInt(value) : filteredTeamScores.value.length;
+    const exportData = filteredTeamScores.value.slice(0, count);
+
+    const columns = ['排名', '项目', '队伍名称', '组别', '成绩'];
+    const csvContent = exportData.map((item, index) => {
+      const rank = getRankDisplay(item, index, filteredTeamScores.value);
+      const project = selectedTeamProjectName.value;
+      const teamName = item.name || item.teamName || '';
+      const groupName = item.rgName || '-';
+      const scoreValue = item.teamPerformance || item.score || item.totalScore || '';
+      const score = `\t${scoreValue}`;
+      return [rank, project, teamName, groupName, score].join(',');
+    });
+
+    const blob = new Blob(['\ufeff' + columns.join(',') + '\n' + csvContent.join('\n')], { type: 'text/csv;charset=utf-8;' });
+    saveAs(blob, `${selectedTeamProjectName.value}_团队排行榜.csv`);
+  } catch (e) {
+    // 取消
+  }
+};
+
+// 打印团队排行榜
+const printTeamRanking = async () => {
+  if (filteredTeamScores.value.length === 0) {
+    ElMessage.warning('没有可打印的数据');
+    return;
+  }
+
+  try {
+    const { value } = await ElMessageBox.prompt('请输入打印前几名(不输入则打印全部)', '打印确认', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      inputPattern: /^\d*$/,
+      inputErrorMessage: '请输入数字'
+    });
+
+    const count = value ? parseInt(value) : filteredTeamScores.value.length;
+    const printData = filteredTeamScores.value.slice(0, count);
+
+    const title = `${selectedTeamProjectName.value} - 团队排行榜`;
+    const columns = ['排名', '项目', '队伍名称', '组别', '成绩'];
+    const rows = printData.map((item, index) => {
+      const rank = getRankDisplay(item, index, filteredTeamScores.value);
+      const project = selectedTeamProjectName.value;
+      const teamName = item.name || item.teamName || '';
+      const groupName = item.rgName || '-';
+      const score = item.teamPerformance || item.score || item.totalScore || '';
+      return [rank, project, teamName, groupName, score];
+    });
+
+    generatePrintPage(title, columns, rows);
+  } catch (e) {
+    // 取消
+  }
+};
+
+// 通用打印页面生成函数
+const generatePrintPage = (title, columns, rows) => {
+  const printWindow = window.open('', '_blank');
+  if (!printWindow) return;
+
+  const tableHtml = `
+    <table border="1" style="width:100%; border-collapse: collapse; margin-top: 20px;">
+      <thead>
+        <tr style="background-color: #f5f7fa;">
+          ${columns.map(col => `<th style="padding: 10px; border: 1px solid #ddd;">${col}</th>`).join('')}
+        </tr>
+      </thead>
+      <tbody>
+        ${rows.map(row => `
+          <tr>
+            ${row.map(cell => `<td style="padding: 10px; border: 1px solid #ddd; text-align: center;">${cell}</td>`).join('')}
+          </tr>
+        `).join('')}
+      </tbody>
+    </table>
+  `;
+
+  printWindow.document.write(`
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <meta charset="utf-8">
+        <title>${title}</title>
+        <style>
+          body { font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; color: #333; padding: 20px; }
+          h1 { text-align: center; color: #303133; margin-bottom: 30px; }
+          .print-time { text-align: right; color: #909399; font-size: 14px; margin-bottom: 10px; }
+          @media print {
+            @page { margin: 1cm; }
+            button { display: none; }
+          }
+        </style>
+      </head>
+      <body>
+        <h1>${title}</h1>
+        <div class="print-time">打印时间:${new Date().toLocaleString()}</div>
+        ${tableHtml}
+        <script>
+          window.onload = function() {
+            setTimeout(function() {
+              window.print();
+              // window.close(); // 打印完自动关闭可选
+            }, 500);
+          };
+        <\/script>
+      </body>
+    </html>
+  `);
+  printWindow.document.close();
+};
+
 // 刷新团队排行榜数据
 const refreshTeamRanking = async () => {
   teamRefreshing.value = true;