Переглянути джерело

feat(gameScore): 添加奖金表格汇总功能和项目总分显示

- 添加表格汇总行计算逻辑,显示各项目分数合计
- 在项目表头显示项目总分信息
- 实现汇总行根据完成状态进行颜色标识(已完成绿色,未完成红色)
- 修复leaderPoint和extraPoint输入验证,限制最小值为0
- 优化表格列固定显示和高度设置
- 移除右侧工具栏组件临时注释
zhou 1 місяць тому
батько
коміт
d7991b1610
1 змінених файлів з 145 додано та 17 видалено
  1. 145 17
      src/views/system/gameScore/gameScoreBonus.vue

+ 145 - 17
src/views/system/gameScore/gameScoreBonus.vue

@@ -1,4 +1,4 @@
-<template>
+<template>
   <div class="p-2">
     <el-card shadow="never">
       <template #header>
@@ -28,7 +28,7 @@
               <!-- <span class="countdown-description">数据将自动刷新</span> -->
             </div>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="loadBonusData" :columns="columns"></right-toolbar>
+          <!-- <right-toolbar v-model:showSearch="showSearch" @queryTable="loadBonusData" ></right-toolbar> -->
         </el-row>
       </template>
       
@@ -36,31 +36,39 @@
         v-loading="bonusLoading" 
         border 
         :data="bonusDataList" 
+        :key="bonusDataList.length + '_' + bonusProjectList.length"
+        max-height="calc(100vh - 200px)"
+        show-summary
+        :summary-method="getSummaries"
         @selection-change="handleBonusSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="序号" align="center" width="80">
+        <el-table-column type="selection" width="55" align="center" fixed="left" />
+        <el-table-column label="序号" align="center" width="80" fixed="left">
           <template #default="scope">
             {{ scope.$index + 1 }}
           </template>
         </el-table-column>
-        <el-table-column label="代表队名称" align="center" prop="teamName" width="150" />
-        <el-table-column label="排名" align="center" prop="rank" width="80" />
-        <el-table-column label="总分" align="center" prop="totalScore" width="100" />
+        <el-table-column label="代表队名称" align="center" prop="teamName" width="150" fixed="left" />
+        <el-table-column label="排名" align="center" prop="rank" width="80" fixed="left" />
+        <el-table-column label="总分" align="center" prop="totalScore" width="100" fixed="left" />
         
         <!-- 动态项目列 -->
         <el-table-column 
           v-for="project in bonusProjectList" 
           :key="project.projectId" 
+          :prop="project.projectId + ''"
           align="center" 
           width="120">
           <template #header>
-            <el-button 
-              link 
-              type="primary" 
-              @click="goToProjectDetail(project)"
-              class="project-header-link">
-              {{ project.projectName }}
-            </el-button>
+            <div class="project-header-container">
+              <el-button 
+                link 
+                type="primary" 
+                @click="goToProjectDetail(project)"
+                class="project-header-link">
+                {{ project.projectName }}
+              </el-button>
+              <div class="project-total-score">总分: {{ getProjectTotal(project.scoreValue) }}</div>
+            </div>
           </template>
           <template #default="scope">
             {{ scope.row.projectScores[project.projectName] || 0 }}
@@ -73,7 +81,8 @@
             <el-input 
               v-model="scope.row.leaderPoint" 
               type="number" 
-              step="0.01"
+              :min="0"
+              step="1"
               size="small"
               @input="calculateTotalScore(scope.row)"
               @blur="calculateTotalScore(scope.row)" />
@@ -86,7 +95,8 @@
             <el-input 
               v-model="scope.row.extraPoint" 
               type="number" 
-              step="0.01"
+              :min="0"
+              step="1"
               size="small"
               @input="calculateTotalScore(scope.row)"
               @blur="calculateTotalScore(scope.row)" />
@@ -211,6 +221,10 @@ const loadBonusData = async () => {
 
 // 计算总分
 const calculateTotalScore = (row: any) => {
+  // 强制修正负数为0
+  if (row.leaderPoint < 0) row.leaderPoint = 0;
+  if (row.extraPoint < 0) row.extraPoint = 0;
+  
   let totalScore = 0;
   
   // 累加各项目积分
@@ -322,6 +336,62 @@ const refreshBonusData = async () => {
   startCountdown();
 };
 
+// 获取项目总分(积分分值之和)
+const getProjectTotal = (scoreValue: string | null | undefined) => {
+  if (!scoreValue) return 0;
+  // 兼容逗号、中文逗号和空格分隔
+  const values = scoreValue.split(/[,,\s]+/);
+  return values.reduce((sum, val) => sum + (Number(val) || 0), 0);
+};
+
+// 表格汇总行计算逻辑
+const getSummaries = (param: any) => {
+  const { columns, data } = param;
+  const sums: string[] = [];
+  
+  columns.forEach((column: any, index: number) => {
+    // 根据图片,合计应该显示在第一个单元格(index 1,因为 index 0 是勾选框)
+    if (index === 1) {
+       sums[index] = '合计';
+       return;
+    }
+    
+    // 查找当前列对应的项目
+    const identifier = column.property;
+    if (!identifier) {
+      sums[index] = '';
+      return;
+    }
+    
+    const project = bonusProjectList.value.find(p => String(p.projectId) === identifier || p.projectName === identifier);
+    if (project) {
+      const values = data.map((item: any) => Number(item.projectScores[project.projectName] || 0));
+      const total = values.reduce((prev: number, curr: number) => prev + curr, 0);
+      const target = getProjectTotal(project.scoreValue);
+      const isFinished = total >= target;
+      
+      sums[index] = total.toString();
+
+      // 暴力美学:异步修改 DOM 样式,绕过 Element Plus 汇总行渲染限制
+      nextTick(() => {
+        const columnId = column.id;
+        const footerCells = document.querySelectorAll(`.el-table__footer-wrapper .${columnId}, .el-table__fixed-footer-wrapper .${columnId}`);
+        footerCells.forEach((cell: any) => {
+          if (cell) {
+            cell.style.color = isFinished ? '#67C23A' : '#F56C6C';
+            cell.style.fontWeight = 'bold';
+            cell.style.fontSize = '14px';
+          }
+        });
+      });
+    } else {
+      sums[index] = '';
+    }
+  });
+  
+  return sums;
+};
+
 // 跳转到项目详情页
 const goToProjectDetail = (project: any) => {
   const event = gameEventStore.defaultEventInfo;
@@ -369,7 +439,7 @@ onMounted(async () => {
 });
 </script>
 
-<style scoped>
+<style scoped lang="scss">
 
 /* 倒计时信息区域样式 */
 .countdown-info {
@@ -455,6 +525,38 @@ onMounted(async () => {
   box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2) !important;
 }
 
+.project-header-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 2px;
+}
+
+.project-total-score {
+  font-size: 11px;
+  color: #909399;
+  font-weight: normal;
+}
+
+// 汇总行样式穿透 (包含固定列部分的汇总行)
+:deep(.el-table__footer-wrapper),
+:deep(.el-table__fixed-footer-wrapper) {
+  .summary-finished {
+    color: #67c23a !important;
+    font-weight: bold !important;
+    .cell {
+      color: #67c23a !important;
+    }
+  }
+  .summary-unfinished {
+    color: #f56c6c !important;
+    font-weight: bold !important;
+    .cell {
+      color: #f56c6c !important;
+    }
+  }
+}
+
 /* 表头样式增强 */
 .el-table .el-table__header .project-header-link {
   font-weight: 600;
@@ -463,6 +565,32 @@ onMounted(async () => {
   min-width: 80px;
 }
 
+</style>
+
+<style lang="scss">
+/* 全局样式覆盖,确保汇总行显色 */
+.el-table {
+  .el-table__footer-wrapper,
+  .el-table__fixed-footer-wrapper {
+    .summary-finished {
+      color: #67c23a !important;
+      font-weight: bold !important;
+      .cell {
+        color: #67c23a !important;
+      }
+    }
+    .summary-unfinished {
+      color: #f56c6c !important;
+      font-weight: bold !important;
+      .cell {
+        color: #f56c6c !important;
+      }
+    }
+  }
+}
+</style>
+
+<style scoped lang="scss">
 /* 响应式设计 */
 @media (max-width: 1200px) {
   .countdown-info {