Files
hzdyjs/docs/设计文档/database/code-adjust-record-2025-01-27-scholarship-quota-display-implementation.md
T
2025-08-10 00:28:48 +08:00

11 KiB
Raw Blame History

班主任批量申请奖学金页面名额信息显示功能实现

变更日期: 2025-01-27 15:30:00
操作人员: AI Assistant
变更类型: 功能增强
影响模块: 奖学金申请管理

📋 变更概述

在班主任批量申请奖学金页面中新增名额信息显示功能,让班主任能够清楚地看到每个奖学金类型的名额分配情况,包括总名额、已用名额、可用名额等信息。

🎯 需求背景

班主任在进行批量申请奖学金时,需要了解每个奖学金类型的名额情况,以便合理分配和选择,避免申请时才发现名额不足的问题。

🛠️ 技术实现

1. API调用优化

文件: web/src/views/jcbt/scholarship/application/index.vue

变更内容:

  • 修改 handleBatchChange 方法,使用 getScholarshipTypesWithQuota 接口替代 getScholarshipTypesByBatch
  • 新接口能够返回带名额信息的奖学金类型数据
// 变更前
const res: any = await getScholarshipTypesByBatch(batch.id);

// 变更后  
if (classInfo.value.classId) {
  const res: any = await getScholarshipTypesWithQuota(batch.id, classInfo.value.classId);
} else {
  const res: any = await getScholarshipTypesByBatch(batch.id);
}

2. 名额显示逻辑

新增方法:

/** 获取名额状态对应的标签类型 */
const getQuotaStatusType = (type: any) => {
  if (!type.hasQuota && type.quotaStatus === 'unlimited') {
    return 'info'; // 无限制名额
  }
  
  switch (type.quotaStatus) {
    case 'available': return 'success'; // 有可用名额
    case 'full': return 'warning'; // 名额已满
    default: return 'danger'; // 其他情况
  }
};

/** 格式化名额显示文本 */
const formatQuotaText = (type: any) => {
  if (!type.hasQuota && type.quotaStatus === 'unlimited') {
    return '无名额限制';
  }
  
  if (type.hasQuota) {
    return `班级名额: ${type.availableQuota}/${type.totalQuota}`;
  } else {
    return `全校名额: ${type.availableQuota}/${type.totalQuota}`;
  }
};

/** 检查奖学金类型是否可选择 */
const isScholarshipTypeSelectable = (type: any) => {
  if (!type.hasQuota && type.quotaStatus === 'unlimited') {
    return true;
  }
  return type.availableQuota > 0;
};

3. 界面显示优化

批次信息区域:

  • 奖学金类型标签增加名额信息显示
  • 根据名额状态显示不同颜色的标签
<el-tag
  v-for="type in availableScholarshipTypes"
  :key="type.id"
  class="mr-2 mb-2"
  :type="getQuotaStatusType(type)"
>
  <div class="flex flex-col">
    <span>{{ type.name }} - {{ type.levelType }} (¥{{ type.amount }})</span>
    <span class="text-xs mt-1">{{ formatQuotaText(type) }}</span>
  </div>
</el-tag>

学生选择区域:

  • 奖学金类型选择器显示名额信息
  • 名额不足的选项自动禁用
<el-option
  v-for="type in availableScholarshipTypes"
  :key="type.id"
  :label="`${type.name} (¥${type.amount}) - ${formatQuotaText(type)}`"
  :value="type"
  :disabled="!isScholarshipTypeSelectable(type)"
>
  <div class="flex justify-between items-center">
    <span>{{ type.name }} (¥{{ type.amount }})</span>
    <el-tag 
      size="small" 
      :type="getQuotaStatusType(type)"
      class="ml-2"
    >
      {{ formatQuotaText(type) }}
    </el-tag>
  </div>
</el-option>

4. 名额验证逻辑

提交前验证:

  • 检查选择的奖学金类型是否还有名额
  • 检查同一类型申请数量是否超过可用名额
  • 提供详细的错误提示
// 检查选择的奖学金类型是否还有名额
const quotaExceededTypes = new Map();
selectedStudents.value.forEach((student: any) => {
  if (student.selectedScholarshipType && !isScholarshipTypeSelectable(student.selectedScholarshipType)) {
    // 收集名额不足的类型和学生
  }
});

// 检查同一奖学金类型的申请数量是否超过可用名额
const typeApplicationCount = new Map();
// ... 统计逻辑
for (const [typeId, count] of typeApplicationCount.entries()) {
  const scholarshipType = availableScholarshipTypes.value.find(t => t.id === typeId);
  if (scholarshipType && count > scholarshipType.availableQuota) {
    ElMessage.error(`${scholarshipType.name} 申请人数(${count})超过可用名额(${scholarshipType.availableQuota})`);
    return;
  }
}

5. 样式优化

新增CSS样式:

/* 名额信息相关样式 */
.flex-col {
  flex-direction: column;
}

.text-xs {
  font-size: 0.75rem;
}

/* 名额状态颜色 */
.quota-status-available {
  color: #67c23a;
}

.quota-status-warning {
  color: #e6a23c;
}

.quota-status-full {
  color: #f56c6c;
}

.quota-status-unlimited {
  color: #909399;
}

📊 数据结构

名额信息数据格式

getScholarshipTypesWithQuota 接口返回的数据结构:

{
  "id": 1,
  "name": "国家奖学金",
  "amount": 8000,
  "levelType": "国家级",
  "typeCategory": "奖学金",
  "hasQuota": true,
  "totalQuota": 10,
  "usedQuota": 3,
  "availableQuota": 7,
  "quotaStatus": "available"
}

名额状态说明

  • available: 有可用名额 - 绿色标签
  • full: 名额已满 - 橙色标签
  • unlimited: 无名额限制 - 蓝色标签
  • 其他状态: 红色标签

🎨 用户体验优化

1. 视觉反馈

  • 绿色标签: 有可用名额,可以申请
  • 橙色标签: 名额已满,不建议申请
  • 蓝色标签: 无名额限制,可以申请
  • 红色标签: 异常状态

2. 交互优化

  • 名额不足的选项自动禁用
  • 选择名额不足类型时显示警告提示
  • 提交前进行完整的名额验证

3. 信息展示

  • 批次信息区域:简洁的名额概览
  • 学生选择区域:详细的名额信息和状态

🔍 测试要点

1. 功能测试

  • ✅ 批次选择后正确显示名额信息
  • ✅ 名额状态颜色正确显示
  • ✅ 名额不足时选项正确禁用
  • ✅ 提交时名额验证正确执行

2. 边界测试

  • ✅ 无班级ID时降级使用普通接口
  • ✅ API调用失败时的错误处理
  • ✅ 名额为0时的显示和禁用
  • ✅ 无名额限制时的正确显示

3. 用户体验测试

  • ✅ 名额信息显示清晰易懂
  • ✅ 颜色区分明显有效
  • ✅ 错误提示信息准确详细

📝 注意事项

1. API依赖

  • 依赖后端 getScholarshipTypesWithQuota 接口正常工作
  • 需要传入正确的批次ID和班级ID

2. 数据一致性

  • 名额信息实时性依赖后端数据更新
  • 并发申请时可能存在名额竞争问题

3. 权限控制

  • 确保班主任只能看到自己班级的名额信息
  • 接口需要进行适当的权限验证

✅ 完成状态

  • API调用优化
  • 名额显示逻辑实现
  • 界面显示优化
  • 名额验证逻辑
  • 样式优化
  • 用户体验优化
  • 变更记录文档

🔄 方案一优化实施 (2025-01-27 16:00:00)

📋 已完成的优化

1. 数据结构优化

  • ✅ 支持 classQuota 和 schoolQuota 双重名额结构
  • ✅ 优先显示班级名额,同时显示全校名额作为参考
  • ✅ 保持向后兼容性,支持旧数据格式

2. 显示逻辑优化

<!-- 批次信息区域 - 分层显示名额信息 -->
<div class="text-xs mt-1">
  <!-- 优先显示班级名额 -->
  <div v-if="type.classQuota && type.classQuota.hasQuota" 
       class="text-primary font-medium">
    班级名额: {{ type.classQuota.availableQuota }}/{{ type.classQuota.totalQuota }}
    <span v-if="type.classQuota.availableQuota === 0" class="text-warning ml-1">(已满)</span>
  </div>
  <!-- 显示全校名额 -->
  <div v-if="type.schoolQuota" class="text-gray-500">
    全校名额: {{ type.schoolQuota.availableQuota }}/{{ type.schoolQuota.totalQuota }}
  </div>
</div>

3. 选择器优化

<!-- 学生选择区域 - 详细名额信息 -->
<div class="flex-1">
  <div>{{ type.name }} (¥{{ type.amount }})</div>
  <div class="text-xs text-gray-500 mt-1">
    <div v-if="type.classQuota && type.classQuota.hasQuota" class="text-primary">
      班级: {{ type.classQuota.availableQuota }}/{{ type.classQuota.totalQuota }}
    </div>
    <div v-if="type.schoolQuota">
      全校: {{ type.schoolQuota.availableQuota }}/{{ type.schoolQuota.totalQuota }}
    </div>
  </div>
</div>

4. 验证逻辑优化

// 优先基于班级名额验证
if (scholarshipType.classQuota && scholarshipType.classQuota.hasQuota) {
  if (count > scholarshipType.classQuota.availableQuota) {
    ElMessage.error(`申请人数(${count})超过班级可用名额(${scholarshipType.classQuota.availableQuota})`);
    return;
  }
}

📊 新数据格式要求

后端API需要返回的数据结构:

{
  "id": 1,
  "name": "国家奖学金",
  "amount": 8000,
  "levelType": "国家级",
  
  "classQuota": {
    "hasQuota": true,
    "totalQuota": 5,
    "usedQuota": 2,
    "availableQuota": 3,
    "quotaStatus": "available"
  },
  
  "schoolQuota": {
    "hasQuota": true,
    "totalQuota": 100,
    "usedQuota": 30,
    "availableQuota": 70,
    "quotaStatus": "available"
  }
}

🎨 视觉效果

显示效果预览:

┌─────────────────────────────────────────┐
│ 国家奖学金 - 国家级 (¥8000)             │
│ 班级名额: 3/5                           │ <- 蓝色,优先显示
│ 全校名额: 70/100                        │ <- 灰色,参考信息
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│ 省政府奖学金 - 省级 (¥6000)             │
│ 班级名额: 0/2 (已满)                    │ <- 橙色警告
│ 全校名额: 15/50                         │ <- 灰色,参考信息
└─────────────────────────────────────────┘

📝 文档创建

  • ✅ 创建 docs/开发文档/API/后端API数据格式示例-班级名额显示.md
  • ✅ 包含完整的数据结构说明和后端实现建议
  • ✅ 提供SQL查询示例和Java实体类模板

🚨 需要后端支持

⚠️ 重要提醒

前端代码已完成适配,但需要后端实现以下内容:

  1. 修改 getScholarshipTypesWithQuota 接口

    • 返回包含 classQuota 和 schoolQuota 的数据结构
    • 计算班级维度的名额分配情况
    • 保持与全校名额的数据一致性
  2. 数据库设计

    • 可能需要添加班级名额分配表
    • 建立班级与奖学金类型的名额关联
  3. 权限验证

    • 确保班主任只能看到自己班级的名额信息
    • 验证 classId 参数的合法性

📋 部署步骤

  1. 前端部署: 当前修改可以直接部署,会自动兼容现有数据格式
  2. 后端开发: 根据文档中的数据格式要求实现API
  3. 测试验证: 确保新旧数据格式都能正确显示
  4. 生产部署: 先部署后端,再更新前端

🔄 后续优化建议

  1. 实时名额更新: 考虑添加名额信息的实时刷新机制
  2. 名额预警: 当名额即将用完时提供预警提示
  3. 历史名额统计: 显示历史批次的名额使用情况
  4. 名额分配可视化: 用图表形式展示名额分配情况

变更完成时间: 2025-01-27 16:15:00
前端状态: ✅ 已完成适配
后端状态: ⏳ 待实现新数据格式
部署状态: 🔄 需要后端配合完成