Эх сурвалжийг харах

feat(gameTeam): 增加队伍分组管理、优化排行榜功能

- 新增移动分组功能,支持批量更新队伍的排名分组
- 在队伍列表中添加分组列,显示队伍所属的排名分组
- 实现排行榜页面,展示不同分组的队伍积分排名
- 优化导入页面布局,增加分组筛选和移动分组按钮
zhou 4 долоо хоног өмнө
parent
commit
7e355b624b

+ 12 - 0
src/api/system/gameTeam/index.ts

@@ -88,3 +88,15 @@ export const updateTeamAthletes = (teamId: string | number, athleteIds: Array<st
     }
   });
 };
+
+/**
+ * 批量更新队伍的排名分组
+ * @param teamIds 队伍ID列表
+ */
+export const moveGroup = (data: { teamIds: (string | number)[], rgId: number }) => {
+  return request({
+    url: '/system/gameTeam/moveGroup',
+    method: 'put',
+    data: data
+  });
+};

+ 15 - 0
src/api/system/gameTeam/types.ts

@@ -8,6 +8,11 @@ export interface GameTeamVO {
    */
   eventId: string | number;
 
+  /**
+   * 排名分组id
+   */
+  rgId: string | number;
+
   /**
    * 赛事名称
    */
@@ -75,6 +80,11 @@ export interface GameTeamForm extends BaseEntity {
    */
   eventId?: string | number;
 
+  /**
+ * 排名分组id
+ */
+  rgId?: string | number;
+
   /**
    * 队伍名称
    */
@@ -188,3 +198,8 @@ export interface GameTeamQuery extends PageQuery {
    */
   params?: any;
 }
+
+export interface MoveGroupRequest {
+  teamIds: number[];
+  rgId: number;
+}

+ 63 - 0
src/api/system/rankGroup/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { RankGroupVO, RankGroupForm, RankGroupQuery } from '@/api/system/rankGroup/types';
+
+/**
+ * 查询排名分组列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listRankGroup = (query?: RankGroupQuery): AxiosPromise<RankGroupVO[]> => {
+  return request({
+    url: '/system/rankGroup/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询排名分组详细
+ * @param rgId
+ */
+export const getRankGroup = (rgId: string | number): AxiosPromise<RankGroupVO> => {
+  return request({
+    url: '/system/rankGroup/' + rgId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增排名分组
+ * @param data
+ */
+export const addRankGroup = (data: RankGroupForm) => {
+  return request({
+    url: '/system/rankGroup',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改排名分组
+ * @param data
+ */
+export const updateRankGroup = (data: RankGroupForm) => {
+  return request({
+    url: '/system/rankGroup',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除排名分组
+ * @param rgId
+ */
+export const delRankGroup = (rgId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/rankGroup/' + rgId,
+    method: 'delete'
+  });
+};

+ 81 - 0
src/api/system/rankGroup/types.ts

@@ -0,0 +1,81 @@
+export interface RankGroupVO {
+  /**
+   * 主键
+   */
+  rgId: string | number;
+
+  /**
+   * 赛事ID
+   */
+  eventId: string | number;
+
+  /**
+   * 分组名
+   */
+  rgName: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface RankGroupForm extends BaseEntity {
+  /**
+   * 主键
+   */
+  rgId?: string | number;
+
+  /**
+   * 赛事ID
+   */
+  eventId?: string | number;
+
+  /**
+   * 分组名
+   */
+  rgName?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface RankGroupQuery extends PageQuery {
+
+  /**
+   * 赛事ID
+   */
+  eventId?: string | number;
+
+  /**
+   * 分组名
+   */
+  rgName?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 0 - 58
src/views/system/gameEvent/RankingBoard.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="ranking-board">
-    <el-tabs v-model="activeTab" type="border-card">
-      <el-tab-pane label="积分排行榜" name="score">
-        <el-table :data="scoreRankings" border style="width: 100%">
-          <el-table-column prop="name" label="姓名" />
-          <el-table-column prop="score" label="积分" />
-        </el-table>
-      </el-tab-pane>
-      <el-tab-pane label="个人成绩排行榜" name="personal">
-        <el-table :data="personalRankings" border style="width: 100%">
-          <el-table-column prop="name" label="姓名" />
-          <el-table-column prop="project" label="项目" />
-          <el-table-column prop="score" label="成绩" />
-        </el-table>
-      </el-tab-pane>
-      <el-tab-pane label="团队成绩排行榜" name="team">
-        <el-table :data="teamRankings" border style="width: 100%">
-          <el-table-column prop="teamName" label="团队名称" />
-          <el-table-column prop="score" label="总分" />
-        </el-table>
-      </el-tab-pane>
-    </el-tabs>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref, onMounted } from 'vue';
-import { listScoreRanking, listPersonalRanking, listTeamRanking } from '@/api/system/gameEvent/eventRank';
-
-const props = defineProps({
-  eventId: {
-    type: String,
-    required: true
-  }
-});
-
-const activeTab = ref('score');
-const scoreRankings = ref([]);
-const personalRankings = ref([]);
-const teamRankings = ref([]);
-
-onMounted(async () => {
-  await loadRankings();
-});
-
-const loadRankings = async () => {
-  const [scoreRes, personalRes, teamRes] = await Promise.all([
-    listScoreRanking(props.eventId),
-    listPersonalRanking(props.eventId),
-    listTeamRanking(props.eventId)
-  ]);
-
-  scoreRankings.value = scoreRes.rows;
-  personalRankings.value = personalRes.rows;
-  teamRankings.value = teamRes.rows;
-};
-</script>

+ 112 - 21
src/views/system/gameEvent/RankingBoardPage.vue

@@ -1,8 +1,8 @@
 <template>
   <div class="ranking-board-page">
-    <!-- 添加赛事名称标题 -->
+    <!-- 赛事名称标题 -->
     <div class="event-title">
-      <h2>管网通天下 逐梦新征程详情</h2>
+      <h2>{{defaultEventInfo ? defaultEventInfo.eventName : '赛事排行榜'}}</h2>
       <p>随时掌握运动会情况</p>
     </div>
     <el-row :gutter="20" class="ranking-row">
@@ -29,13 +29,32 @@
         <el-card shadow="hover" class="ranking-card">
           <template #header>
             <div class="card-header header-three">
-              <span>团队积分排行榜</span>
+              <div class="team-ranking-header">
+                <span>团队积分排行榜</span>
+                <!-- 添加分组筛选下拉框 -->
+                <el-select 
+                  v-model="selectedRankGroupId" 
+                  placeholder="选择分组" 
+                  clearable 
+                  size="small" 
+                  @change="handleRankGroupChange"
+                  style="margin-left: 10px; width: 120px;"
+                >
+                  <el-option label="全部队伍" :value="null"></el-option>
+                  <el-option
+                    v-for="group in rankGroupOptions"
+                    :key="group.rgId"
+                    :label="group.rgName"
+                    :value="group.rgId"
+                  />
+                </el-select>
+              </div>
             </div>
           </template>
           <div class="ranking-list ranking-list-full">
-            <div v-for="(item, index) in teamScores" :key="index" class="ranking-item">
+            <div v-for="(item, index) in filteredTeamScores" :key="index" class="ranking-item">
               <div class="item-content">
-                <span class="item-rank">{{ getRankDisplay(item, index, teamScores) }}</span>
+                <span class="item-rank">{{ getRankDisplay(item, index, filteredTeamScores) }}</span>
                 <span class="item-team-name">{{ item.teamName }}</span>
                 <span class="item-score">{{ item.score }}分</span>
               </div>
@@ -86,9 +105,14 @@ import { getProjectProgress } from '@/api/system/gameEvent/projectProgress';
 import { useRouter,useRoute } from 'vue-router';
 import { GameTeamVO } from '@/api/system/gameTeam/types';
 import { ProjectProgressVo, GroupProgressVo } from '@/api/system/gameEvent/types';
+import { useGameEventStore } from '@/store/modules/gameEvent';
+import { storeToRefs } from 'pinia';
+import { listRankGroup } from '@/api/system/rankGroup'; // 新增导入
+import { RankGroupVO } from '@/api/system/rankGroup/types'; // 新增导入
 
 // 定义队伍积分排行榜的数据结构
 interface TeamScore {
+  rgId: string | number;
   teamId: string | number;
   teamName: string;
   score: number;
@@ -98,8 +122,12 @@ interface TeamScore {
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { game_project_type } = toRefs<any>(proxy?.useDict('game_project_type'));
 
-const route = useRoute();
-const eventId = route.params.eventId as string;
+// const route = useRoute();
+// const eventId = route.params.eventId as string;
+
+//从pinia中获取默认赛事信息
+const gameEventStore = useGameEventStore();
+const { defaultEventInfo } = storeToRefs(gameEventStore);
 
 const athleteScoreList = ref([]);
 
@@ -110,12 +138,17 @@ const progressPercentage = computed(() => totalTasks.value > 0 ? (completedTasks
 const projectProgress = ref<ProjectProgressVo[]>([]);
 
 const teamScores = ref<TeamScore[]>([]);
+const filteredTeamScores = ref<TeamScore[]>([]); // 用于显示筛选后的队伍积分
+
+// 分组相关数据
+const rankGroupOptions = ref<RankGroupVO[]>([]);
+const selectedRankGroupId = ref<number | null>(null);
 
 // 获取队伍积分排行榜
 const loadTeamScores = async () => {
   // 获取所有成绩数据
   const scoreRes = await listGameScore({
-    eventId: eventId,
+    eventId: defaultEventInfo.value.eventId,
     pageNum: 1,
     pageSize: 1000,
     orderByColumn: '',
@@ -125,7 +158,7 @@ const loadTeamScores = async () => {
   
   // 获取所有队伍信息
   const teamRes = await listGameTeam({
-    eventId: eventId,
+    eventId: defaultEventInfo.value.eventId,
     pageNum: 1,
     pageSize: 1000,
     orderByColumn: '',
@@ -152,7 +185,8 @@ const loadTeamScores = async () => {
       teamId: team.teamId,
       teamName: team.teamName,
       score: teamScoreMap.get(team.teamId) || 0,
-      rank: 0 // 占位符,稍后设置
+      rank: 0, // 占位符,稍后设置
+      rgId: team.rgId // 添加分组ID
     };
   });
   
@@ -177,12 +211,58 @@ const loadTeamScores = async () => {
   }
   
   teamScores.value = teamScoreList;
+  // 默认显示所有队伍
+  filteredTeamScores.value = teamScoreList;
+};
+
+// 处理分组筛选变化
+const handleRankGroupChange = (rgId: number | null) => {
+  if (rgId === null) {
+    // 显示所有队伍
+    filteredTeamScores.value = teamScores.value;
+  } else {
+    // 筛选指定分组的队伍
+    filteredTeamScores.value = teamScores.value.filter(team => team.rgId === rgId);
+  }
+  
+  // 重新计算排名
+  let currentRank = 1;
+  for (let i = 0; i < filteredTeamScores.value.length; i++) {
+    const team = filteredTeamScores.value[i];
+    const currentScore = team.score || 0;
+    
+    if (i > 0) {
+      const previousScore = filteredTeamScores.value[i - 1].score || 0;
+      if (currentScore !== previousScore) {
+        currentRank = i + 1;
+      }
+    }
+    
+    team.rank = currentRank;
+  }
+};
+
+// 获取分组选项
+const loadRankGroupOptions = async () => {
+  try {
+    const res = await listRankGroup({
+      eventId: defaultEventInfo.value.eventId,
+      pageNum: 1,
+      pageSize: 1000,
+      orderByColumn: undefined,
+      isAsc: undefined,
+      status: '0'
+    });
+    rankGroupOptions.value = res.rows;
+  } catch (error) {
+    console.error('获取分组列表失败:', error);
+  }
 };
 
 // 加载项目进度信息
 const loadProjectProgress = async () => {
   try {
-    const res = await getProjectProgress(eventId);
+    const res = await getProjectProgress(defaultEventInfo.value.eventId);
     projectProgress.value = res.data || [];
     
     // 计算已完成和总任务数
@@ -309,15 +389,19 @@ const getRankDisplay = (item: any, index: number, list: any[]) => {
 };
 
 onMounted(async () => {
-  const res = await listScoreRanking(eventId);
-  // 按照totalScore字段降序排序(分数高的在前面)
-  athleteScoreList.value = res.data.sort((a, b) => b.totalScore - a.totalScore);
-  
-  // 加载队伍积分排行榜
-  await loadTeamScores();
-  
-  // 加载项目进度信息
-  await loadProjectProgress();
+  try{
+    const res = await listScoreRanking(defaultEventInfo.value.eventId.toString());
+    // 按照totalScore字段降序排序(分数高的在前面)
+    athleteScoreList.value = res.data.sort((a, b) => b.totalScore - a.totalScore);
+    // 加载队伍积分排行榜
+    await loadTeamScores();
+    // 加载分组选项
+    await loadRankGroupOptions();
+    // 加载项目进度信息
+    await loadProjectProgress();
+  } catch (error) {
+    console.error('加载数据失败:', error);
+  }
 });
 </script>
 
@@ -326,7 +410,7 @@ onMounted(async () => {
   padding: 20px;
   height: calc(100vh - 120px);
 }
-/* 新增赛事名称标题样式 */
+/* 赛事名称标题样式 */
 .event-title {
   margin-bottom: 20px;
   text-align: center;
@@ -361,6 +445,13 @@ onMounted(async () => {
   text-align: center;
 }
 
+/* 团队排行榜头部样式 */
+.team-ranking-header {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
 .progress-info {
   margin-bottom: 15px;
 }

+ 2 - 2
src/views/system/gameEvent/index.vue

@@ -73,11 +73,11 @@
           <el-col :span="1.5">
             <el-button type="info" plain icon="View" @click="handlePreviewDefault" v-hasPermi="['system:gameEvent:view']">预览 </el-button>
           </el-col>
-          <el-col :span="1.5">
+          <!-- <el-col :span="1.5">
             <el-button type="warning" plain icon="DataAnalysis" @click="handleGameDataDefault" v-hasPermi="['system:gameEvent:gameData']"
               >排行榜
             </el-button>
-          </el-col>
+          </el-col> -->
           <el-col :span="1.5">
             <el-button type="primary" plain icon="EditPen" @click="handleWriteArticleDefault" v-hasPermi="['system:gameEvent:writeArticle']"
               >编写文章

+ 24 - 0
src/views/system/gameScore/index.vue

@@ -35,6 +35,11 @@
           <el-col :span="1.5">
             <el-button type="primary" @click="printScores">打印成绩(前3名)</el-button>
           </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="DataAnalysis" @click="handleGameDataDefault" v-hasPermi="['system:gameEvent:gameData']"
+              >排行榜
+            </el-button>
+          </el-col>
           <!-- <el-col :span="1.5">
             <el-button type="primary" @click="exportScoresNames">导出成绩(全部)</el-button>
           </el-col> -->
@@ -107,6 +112,7 @@ import { getGameTeam } from '@/api/system/gameTeam';
 import { getGameAthlete } from '@/api/system/gameAthlete';
 import { GameScoreVO, GameScoreQuery, GameScoreForm } from '@/api/system/gameScore/types';
 import { GameEventProjectVO, GameEventProjectQuery } from '@/api/system/gameEventProject/types';
+import { GameEventVO } from '@/api/system/gameEvent/types';
 import { useGameEventStore } from '@/store/modules/gameEvent';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -214,6 +220,24 @@ const { queryParams, form, rules } = toRefs(data);
 //   }
 // };
 
+/** 比赛数据(默认赛事) */
+const handleGameDataDefault = async () => {
+  const defaultEvent = gameEventStore.defaultEventInfo;
+  if (!defaultEvent) {
+    proxy?.$modal.msgError('请先设置默认赛事');
+    return;
+  }
+  handleGameData(defaultEvent);
+};
+
+// 比赛成绩按钮点击事件
+const handleGameData = (row: GameEventVO) => {
+  router.push({
+    name: 'RankingBoardPage',
+    params: { eventId: row.eventId }
+  });
+};
+
 // 加载加分数据
 const loadBonusData = async (eventId: string | number) => {
   try {

+ 125 - 35
src/views/system/gameTeam/index.vue

@@ -23,24 +23,37 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameTeam:add']">新增 </el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:gameTeam:add']">新增</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameTeam:edit']"
-              >修改
-            </el-button>
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:gameTeam:edit']">修改</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameTeam:remove']"
-              >删除
-            </el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:gameTeam:remove']">删除</el-button>
           </el-col>
-          <!-- <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameTeam:export']">导出 </el-button>
-          </el-col> -->
           <el-col :span="1.5">
-            <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:gameTeam:import']"> 导入 </el-button>
+            <el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:gameTeam:import']">导入</el-button>
+          </el-col>
+          <el-col :span="6" >
+            <el-select v-model="selectedRankGroupId" placeholder="选择分组" clearable style="width: 100px;">
+              <el-option
+                v-for="item in rankGroupOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+            <el-button 
+              type="warning" 
+              icon="Switch" 
+              :disabled="!selectedRankGroupId"
+              @click="handleMoveGroup"
+              v-hasPermi="['system:gameTeam:edit']"
+            >
+              移动分组
+            </el-button>
           </el-col>
+          
           <right-toolbar v-model:showSearch="showSearch" :columns="columns" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>
@@ -51,21 +64,28 @@
         <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="rgId" v-if="columns[4].visible">
+          <template #default="scope">
+            <span v-if="scope.row.rgId">{{ rankGroupMap.get(scope.row.rgId) }}</span>
+            <span v-else style="color: #999;">未分组</span>
+          </template>
+        </el-table-column>
+        
+        <el-table-column label="团队描述" align="center" prop="teamDescribe" v-if="columns[5].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">
+        <el-table-column label="领队" align="center" prop="leader" v-if="columns[6].visible" />
+        <el-table-column label="人数" align="center" prop="athleteNum" v-if="columns[7].visible" />
+        <el-table-column label="号码段" align="center" prop="numberRange" v-if="columns[8].visible" />
+        <el-table-column label="状态" align="center" prop="status" v-if="columns[9].visible">
           <template #default="scope">
             <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" />
+        <el-table-column label="备注" align="center" prop="remark" v-if="columns[10].visible" />
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
             <el-tooltip content="修改" placement="top">
@@ -146,11 +166,13 @@
 </template>
 
 <script setup name="GameTeam" lang="ts">
-import { listGameTeam, getGameTeam, delGameTeam, addGameTeam, updateGameTeam } from '@/api/system/gameTeam';
-// import { getDefaultEvent } from '@/api/system/gameEvent';
+import { listGameTeam, getGameTeam, delGameTeam, addGameTeam, updateGameTeam, moveGroup } from '@/api/system/gameTeam';
+import { listRankGroup } from '@/api/system/rankGroup';
 import { GameTeamVO, GameTeamQuery, GameTeamForm } from '@/api/system/gameTeam/types';
+import { RankGroupVO } from '@/api/system/rankGroup/types';
 import { globalHeaders } from '@/utils/request';
 import { GameEventVO } from '@/api/system/gameEvent/types';
+import { orderBy } from 'element-plus/es/components/table/src/util';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
@@ -166,18 +188,23 @@ const multiple = ref(true);
 const total = ref(0);
 const eventOptions = ref<Array<{ label: string; value: string | number }>>([]);
 
+// 新增:排名分组相关
+const rankGroupOptions = ref<Array<{ label: string; value: string | number }>>([]);
+const selectedRankGroupId = ref<number | null>(null);
+
 // 列显隐数据
 const columns = ref<FieldOption[]>([
   { key: 0, label: '队伍id', visible: false },
   { key: 1, label: '队伍编号', visible: false },
   { key: 2, label: '赛事名称', visible: false },
   { key: 3, label: '队伍名称', visible: true },
-  { key: 4, label: '团队描述', visible: true },
-  { 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 }
+  { key: 4, label: '排名分组名', visible: true }, // 新增
+  { key: 5, label: '团队描述', visible: true },
+  { key: 6, label: '领队', visible: true },
+  { key: 7, label: '人数', visible: true },
+  { key: 8, label: '号码段', visible: true },
+  { key: 9, label: '状态', visible: false },
+  { key: 10, label: '备注', visible: false }
 ]);
 
 const queryFormRef = ref<ElFormInstance>();
@@ -206,6 +233,7 @@ const upload = reactive<ImportOption>({
 });
 const initFormData: GameTeamForm = {
   eventId: undefined,
+  rgId:undefined,
   teamName: undefined,
   teamCode: undefined,
   leader: undefined,
@@ -247,15 +275,15 @@ const data = reactive<PageData<GameTeamForm, GameTeamQuery>>({
 
 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;
-  }
-});
+// // 添加额外的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 () => {
@@ -267,6 +295,13 @@ watchEffect(() => {
 //   }
 // };
 
+const rankGroupMap = computed(() => {
+  const map = new Map();
+  rankGroupOptions.value.forEach(option => {
+    map.set(option.value, option.label);
+  });
+  return map;
+});
 /** 查询参赛队伍列表 */
 const getList = async () => {
   // if (!queryParams.value.eventId) {
@@ -326,6 +361,61 @@ const handleSelectionChange = (selection: GameTeamVO[]) => {
   ids.value = selection.map((item) => item.teamId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
+  
+  // 如果没有选中队伍,清空选择的分组
+  if (selection.length === 0) {
+    selectedRankGroupId.value = null;
+  }
+};
+
+/** 新增:获取排名分组选项列表 */
+const getRankGroupOptions = async () => {
+  try {
+    const res = await listRankGroup({
+      pageNum: 1,
+      pageSize: 1000, // 获取所有排名分组
+      eventId: queryParams.value.eventId,
+      orderByColumn: undefined,
+      isAsc: undefined,
+    });
+    rankGroupOptions.value = res.rows.map(item => ({
+      label: item.rgName,
+      value: item.rgId
+    }));
+  } catch (error) {
+    console.error('获取排名分组列表失败:', error);
+  }
+};
+
+/** 新增:移动分组操作 */
+const handleMoveGroup = async () => {
+  if (!selectedRankGroupId.value) {
+    proxy?.$modal.msgWarning('请选择目标排名分组');
+    return;
+  }
+  
+  if (ids.value.length === 0) {
+    proxy?.$modal.msgWarning('请选择要移动的队伍');
+    return;
+  }
+  
+  try {
+    await proxy?.$modal.confirm(`确认将选中的 ${ids.value.length} 个队伍移动到选定的排名分组吗?`);
+    
+    loading.value = true;
+    await moveGroup({
+      teamIds: ids.value,
+      rgId: selectedRankGroupId.value
+    });
+    
+    proxy?.$modal.msgSuccess('移动分组成功');
+    selectedRankGroupId.value = null;
+    await getList();
+  } catch (error) {
+    console.error('移动分组失败:', error);
+  } finally {
+    loading.value = false;
+  }
 };
 
 /** 新增按钮操作 */
@@ -416,6 +506,6 @@ const importTemplate = () => {
 
 onMounted(() => {
   getList();
-  // getEventOptions();
+  getRankGroupOptions(); // 新增
 });
 </script>

+ 237 - 0
src/views/system/rankGroup/index.vue

@@ -0,0 +1,237 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="分组名" prop="rgName">
+              <el-input v-model="queryParams.rgName" placeholder="请输入分组名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:rankGroup:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:rankGroup:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:rankGroup:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:rankGroup:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="rankGroupList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="分组id" align="center" prop="rgId" v-if="false" />
+        <el-table-column label="赛事ID" align="center" prop="eventId" v-if="false" />
+        <el-table-column label="分组名" align="center" prop="rgName" />
+        <el-table-column label="更新时间" align="center" prop="updateTime" />
+        <el-table-column label="状态" align="center" prop="status" v-if="false" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:rankGroup:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:rankGroup:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改排名分组对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="rankGroupFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="分组名" prop="rgName">
+          <el-input v-model="form.rgName" placeholder="请输入分组名" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="RankGroup" lang="ts">
+import { listRankGroup, getRankGroup, delRankGroup, addRankGroup, updateRankGroup } from '@/api/system/rankGroup';
+import { RankGroupVO, RankGroupQuery, RankGroupForm } from '@/api/system/rankGroup/types';
+import { useGameEventStore } from '@/store/modules/gameEvent';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const rankGroupList = ref<RankGroupVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const rankGroupFormRef = ref<ElFormInstance>();
+
+let gameEventStore = null;
+let defaultEventInfo = null;
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const getInitFormData = () => ({
+  rgId: undefined,
+  eventId: defaultEventInfo?.eventId,  // 使用可选链操作符防止报错
+  rgName: undefined,
+  updateTime: undefined,
+  status: '0',
+  remark: undefined
+});
+const data = reactive<PageData<RankGroupForm, RankGroupQuery>>({
+  form: getInitFormData(),
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    eventId: defaultEventInfo?.eventId,  // 同样使用可选链操作符
+    rgName: undefined,
+    status: '0',
+    orderByColumn: undefined,
+    isAsc: undefined,
+    params: {}
+  },
+  rules: {
+    rgName: [
+      { required: true, message: "分组名不能为空", trigger: "blur" }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询排名分组列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listRankGroup(queryParams.value);
+  rankGroupList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = getInitFormData();
+  rankGroupFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: RankGroupVO[]) => {
+  ids.value = selection.map(item => item.rgId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加排名分组";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: RankGroupVO) => {
+  reset();
+  const _rgId = row?.rgId || ids.value[0]
+  const res = await getRankGroup(_rgId);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改排名分组";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  rankGroupFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.rgId) {
+        await updateRankGroup(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addRankGroup(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: RankGroupVO) => {
+  const _rgIds = row?.rgId || ids.value;
+  await proxy?.$modal.confirm('是否确认删除排名分组编号为"' + _rgIds + '"的数据项?').finally(() => loading.value = false);
+  await delRankGroup(_rgIds);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('system/rankGroup/export', {
+    ...queryParams.value
+  }, `rankGroup_${new Date().getTime()}.xlsx`)
+}
+
+// 在 onMounted 中初始化 store 后更新表单数据
+onMounted(() => {
+  gameEventStore = useGameEventStore();
+  defaultEventInfo = gameEventStore.defaultEventInfo;
+  
+  form.value.eventId = defaultEventInfo?.eventId;
+  queryParams.value.eventId = defaultEventInfo?.eventId;
+  
+  getList();
+});
+
+</script>