|
@@ -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,12 +96,16 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup name="GameScore" lang="ts">
|
|
|
-import { listGameScore, getGameScore, delGameScore, addGameScore, updateGameScore } from '@/api/system/gameScore';
|
|
|
+import { listGameScore, getGameScore, delGameScore, addGameScore, updateGameScore, getProjectScoreData } 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';
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const { game_project_type } = toRefs<any>(proxy?.useDict('game_project_type'));
|
|
@@ -202,11 +193,11 @@ const getDefaultEventInfo = async () => {
|
|
|
|
|
|
/** 查询成绩列表 */
|
|
|
const getList = async () => {
|
|
|
- if (!queryParams.value.eventId) {
|
|
|
- proxy?.$modal.msgWarning('未指定默认赛事');
|
|
|
- loading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
+ // if (!queryParams.value.eventId) {
|
|
|
+ // proxy?.$modal.msgWarning('未指定默认赛事');
|
|
|
+ // loading.value = false;
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
// loading.value = true;
|
|
|
const res = await listGameScore(queryParams.value);
|
|
|
gameScoreList.value = res.rows;
|
|
@@ -214,62 +205,6 @@ const getList = async () => {
|
|
|
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';
|
|
|
-// };
|
|
|
/**
|
|
|
* 刷新数据
|
|
|
*/
|
|
@@ -277,12 +212,299 @@ 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('打印失败');
|
|
|
+ }
|
|
|
};
|
|
|
-const exportNumberMapping = async () => {
|
|
|
- console.log('导出号码对照表逻辑待实现');
|
|
|
- // await loadProjects();
|
|
|
+
|
|
|
+/**
|
|
|
+ * 构建打印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 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 getGroupTypeName = (groupNum: number) => {
|
|
|
+ if (!groupNum) return '未知组';
|
|
|
+
|
|
|
+ const groupMap: Record<number, string> = {
|
|
|
+ 1: '甲组',
|
|
|
+ 2: '乙组',
|
|
|
+ 3: '丙组',
|
|
|
+ 4: '丁组'
|
|
|
+ };
|
|
|
+ return groupMap[groupNum] || `${groupNum}组`;
|
|
|
};
|
|
|
const exportScoresNames = async () => {
|
|
|
console.log('导出成绩逻辑待实现');
|
|
@@ -308,7 +530,7 @@ const handleQuery = () => {
|
|
|
const resetQuery = () => {
|
|
|
queryFormRef.value?.resetFields();
|
|
|
// 保留默认赛事ID
|
|
|
- queryParams.value.eventId = defaultEvent.value?.eventId;
|
|
|
+ // queryParams.value.eventId = defaultEvent.value?.eventId;
|
|
|
handleQuery();
|
|
|
};
|
|
|
|
|
@@ -332,9 +554,9 @@ const navigateToEditPage = (row: GameEventProjectVO) => {
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
- getDefaultEventInfo().then(() => {
|
|
|
+ // getDefaultEventInfo().then(() => {
|
|
|
getList();
|
|
|
refreshData();
|
|
|
- });
|
|
|
+ // });
|
|
|
});
|
|
|
</script>
|