فهرست منبع

feat(system): 新增排名分组树形查询接口并优化排行榜页面功能

- 新增 `treeListRankGroup` 接口用于查询排名分组树列表
- 在 GameTeamQuery 类型定义中新增 `rgId` 字段用于排名分组筛选
- 优化 RankingBoardPage 页面的分组筛选逻辑,使用树形结构替代扁平化数据
- 更新排行榜页面使用新的树形分组数据结构进行筛选
- 修复 GameTeam 页面分组下拉框样式和查询参数
- 移除不必要的分页和排序参数,优化性能
zhou 3 هفته پیش
والد
کامیت
812bdf604d

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

@@ -175,6 +175,8 @@ export interface GameTeamQuery extends PageQuery {
 
   projectId?: string | number;
 
+  rgId?: string | number;
+
   /**
    * 人数
    */

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

@@ -16,6 +16,17 @@ export const listRankGroup = (query?: RankGroupQuery): AxiosPromise<RankGroupVO[
   });
 };
 
+/**
+ * 查询排名分组树列表
+ */
+export const treeListRankGroup = (query?: RankGroupQuery): AxiosPromise<RankGroupVO[]> => {
+  return request({
+    url: '/system/rankGroup/treeList',
+    method: 'get',
+    params: query
+  });
+};
+
 /**
  * 查询排名分组详细
  * @param rgId

+ 22 - 42
src/views/system/gameEvent/RankingBoardPage.vue

@@ -152,9 +152,9 @@
                   >
                     <el-option label="全部队伍" :value="ALL_GROUPS_VALUE"></el-option>
                     <el-option
-                      v-for="group in flatRankGroupOptions"
+                      v-for="group in rankGroupOptions"
                       :key="group.rgId"
-                      :label="group.displayName"
+                      :label="group.rgName"
                       :value="group.rgId"
                     />
                   </el-select>
@@ -390,52 +390,35 @@ const loadTeamProjectScores = async () => {
   handleRankGroupChange(selectedRankGroupId.value);
 };
 
-// 扁平化的分组选项
-const flatRankGroupOptions = ref<any[]>([]);
-
-// 将树形结构打平
-const flattenTree = (tree: any[]) => {
-  const arr: any[] = [];
-  tree.forEach(node => {
-    arr.push({
-      ...node,
-      displayName: node.rgName
-    });
-    if (node.children && node.children.length > 0) {
-      arr.push(...flattenTree(node.children));
-    }
-  });
-  return arr;
-};
-
-// 获取所有子节点ID(包含自身)
-const getAllChildIds = (rgId: string | number, options: any[]): (string | number)[] => {
-  const ids: (string | number)[] = [rgId];
-  const findChildren = (parentId: string | number) => {
-    options.forEach(opt => {
-      if (opt.parentId === parentId) {
-        ids.push(opt.rgId);
-        findChildren(opt.rgId);
-      }
-    });
-  };
-  findChildren(rgId);
-  return ids;
-};
-
 // 处理分组筛选变化
 const handleRankGroupChange = (rgId: string | number | null | undefined) => {
   if (rgId === null || rgId === undefined || rgId === '' || rgId === ALL_GROUPS_VALUE) {
     // 显示所有队伍
     filteredTeamScores.value = teamScores.value;
   } else {
-    // 获取当前选中分组及其所有子分组的ID集合
-    const targetIds = getAllChildIds(rgId, flatRankGroupOptions.value);
-    // 筛选所属分组在集合内的队伍
+    // 筛选所属分组的队伍(现在后端已处理父子关系,但此处是前端过滤已加载的数据)
+    // 注意:如果看板是全量数据在前端筛选,前端依然需要知道哪些子分组属于该父分组
+    // 或者更简单的做法:看板数据在获取时就根据 rgId 传参给后端。
+    
+    // 鉴于看板通常是全量计算好后前端切分,我们这里保留一个简单的打平用于辅助过滤
+    const targetIds = getFlatIds(rgId);
     filteredTeamScores.value = teamScores.value.filter(team => targetIds.includes(team.rgId));
   }
 };
 
+// 从扁平列表中获取某个节点下的所有子孙ID(用于前端筛选)
+const getFlatIds = (rgId: string | number) => {
+  const ids: (string | number)[] = [rgId];
+  const targetIdStr = String(rgId);
+  // 遍历扁平列表,通过 ancestors 祖级路径识别所有子孙节点
+  rankGroupOptions.value.forEach(item => {
+    if (item.ancestors && item.ancestors.split(',').includes(targetIdStr)) {
+      ids.push(item.rgId);
+    }
+  });
+  return ids;
+};
+
 // 获取分组选项
 const loadRankGroupOptions = async () => {
   try {
@@ -443,10 +426,7 @@ const loadRankGroupOptions = async () => {
       eventId: defaultEventInfo.value.eventId,
       status: '0'
     });
-    // 保存原始树形数据
-    rankGroupOptions.value = res.data;
-    // 打平用于下拉显示
-    flatRankGroupOptions.value = flattenTree(res.data);
+    rankGroupOptions.value = res.rows;
   } catch (error) {
     console.error('获取分组列表失败:', error);
   }

+ 13 - 6
src/views/system/gameTeam/index.vue

@@ -20,6 +20,16 @@
                 />
               </el-select>
             </el-form-item>
+            <el-form-item label="排名分组" prop="rgId">
+              <el-select v-model="queryParams.rgId" placeholder="请选择分组" clearable filterable style="width: 200px" >
+                <el-option
+                  v-for="item in rankGroupOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -48,7 +58,7 @@
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:gameTeam:export']">导出</el-button>
           </el-col>
           <el-col :span="6" >
-            <el-select v-model="selectedRankGroupId" placeholder="选择分组" clearable style="width: 100px;">
+            <el-select v-model="selectedRankGroupId" placeholder="选择分组" clearable filterable style="width: 150px;">
               <el-option
                 v-for="item in rankGroupOptions"
                 :key="item.value"
@@ -323,13 +333,10 @@ const handleSelectionChange = (selection: GameTeamVO[]) => {
 const getRankGroupOptions = async () => {
   try {
     const res = await listRankGroup({
-      pageNum: 1,
-      pageSize: 1000,
       eventId: queryParams.value.eventId,
-      orderByColumn: undefined,
-      isAsc: undefined,
+      status: '0'
     });
-    rankGroupOptions.value = res.rows.map(item => ({
+    rankGroupOptions.value = res.rows.map((item: any) => ({
       label: item.rgName,
       value: item.rgId
     }));

+ 4 - 5
src/views/system/rankGroup/index.vue

@@ -107,7 +107,7 @@
 </template>
 
 <script setup name="RankGroup" lang="ts">
-import { listRankGroup, getRankGroup, delRankGroup, addRankGroup, updateRankGroup } from '@/api/system/rankGroup';
+import { treeListRankGroup, getRankGroup, delRankGroup, addRankGroup, updateRankGroup } from '@/api/system/rankGroup';
 import { RankGroupVO, RankGroupQuery, RankGroupForm } from '@/api/system/rankGroup/types';
 import { useGameEventStore } from '@/store/modules/gameEvent';
 
@@ -160,7 +160,7 @@ const { queryParams, form, rules } = toRefs(data);
 /** 查询排名分组列表 */
 const getList = async () => {
   loading.value = true;
-  const res = await listRankGroup(queryParams.value);
+  const res = await treeListRankGroup(queryParams.value);
   rankGroupList.value = res.data;
   loading.value = false;
 };
@@ -168,9 +168,8 @@ const getList = async () => {
 /** 查询菜单下拉树结构 */
 const getTreeSelect = async () => {
   rankGroupOptions.value = [];
-  const res = await listRankGroup({ eventId: queryParams.value.eventId });
-  const group: any = { rgId: 0, rgName: '顶级分组', children: [] };
-  group.children = res.data;
+  const res = await treeListRankGroup({ eventId: queryParams.value.eventId });
+  const group: any = { rgId: 0, rgName: '顶级分组', children: res.data };
   rankGroupOptions.value.push(group);
 };