From d8730cc2c29cab441e413b866af2973b6dc97f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Wed, 17 Dec 2025 09:31:14 +0800 Subject: [PATCH] fix bugs --- doc/裁判邀请功能使用说明.md | 181 ++++++ doc/赛事列表加载问题排查指南.md | 260 +++++++++ src/api/martial/deduction.js | 4 +- src/api/martial/deductionItem.js | 4 +- src/api/martial/judgeInvite.js | 38 +- src/api/martial/project.js | 25 +- src/api/system/menu.js | 22 +- src/api/user.js | 24 +- src/views/martial/deduction/index.vue | 201 +++++-- src/views/martial/judgeInvite/index.vue | 588 +++++++++++--------- src/views/martial/judgeInvite/index.vue.old | Bin 33365 -> 0 bytes src/views/martial/order/index.vue | 1 - src/views/martial/project/index.vue | 46 +- src/views/martial/referee/index.vue | 12 +- src/views/martial/schedule/index.vue | 6 - 15 files changed, 1032 insertions(+), 380 deletions(-) create mode 100644 doc/裁判邀请功能使用说明.md create mode 100644 doc/赛事列表加载问题排查指南.md delete mode 100644 src/views/martial/judgeInvite/index.vue.old diff --git a/doc/裁判邀请功能使用说明.md b/doc/裁判邀请功能使用说明.md new file mode 100644 index 0000000..2f2a649 --- /dev/null +++ b/doc/裁判邀请功能使用说明.md @@ -0,0 +1,181 @@ +# 裁判邀请功能使用说明 + +## 功能概述 + +裁判邀请模块用于为武术赛事邀请裁判,通过生成邀请码的方式让裁判登录系统并回复邀请。 + +## 完整操作流程 + +### 1. 准备工作 + +#### 1.1 创建赛事 +- 进入"赛事管理"模块 +- 创建新的赛事 +- 确保赛事状态为"进行中" + +#### 1.2 添加裁判 +- 进入"评委管理"模块 +- 点击"新增评委"按钮 +- 填写裁判基本信息: + - 姓名、性别、手机号、身份证号 + - 裁判类型(主裁判/普通裁判) + - 等级/职称 + - 擅长项目 +- 保存裁判信息 + +### 2. 生成邀请码 + +#### 方式一:从评委库导入(推荐) + +这是为新裁判生成邀请码的主要方式。 + +**操作步骤:** +1. 进入"裁判邀请"页面 +2. 选择赛事(页面顶部下拉框) +3. 点击"从评委库导入"按钮 +4. 在弹出的对话框中: + - 可以搜索裁判(按姓名、手机号、类型) + - 勾选需要邀请的裁判(支持多选) + - 查看已选择的裁判数量 +5. 点击"确定导入"按钮 +6. 系统自动为选中的裁判批量生成邀请码 +7. 生成成功后,邀请列表会自动刷新 + +**特点:** +- ✅ 支持批量操作 +- ✅ 可以搜索和筛选裁判 +- ✅ 自动生成邀请码 +- ✅ 适合首次邀请裁判 + +#### 方式二:批量生成邀请码 + +用于为已有邀请记录但未生成邀请码的裁判批量生成。 + +**操作步骤:** +1. 在邀请列表中勾选需要生成邀请码的记录 +2. 点击"批量生成邀请码"按钮 +3. 确认操作 +4. 系统为选中的裁判生成邀请码 + +**注意:** +- ⚠️ 只能为已有邀请记录的裁判生成 +- ⚠️ 如果是新裁判,请使用"从评委库导入" + +#### 方式三:单个生成邀请码 + +用于为单个裁判生成或重新生成邀请码。 + +**操作步骤:** +1. 在邀请列表中找到目标裁判 +2. 如果未生成邀请码:点击"生成邀请码"按钮 +3. 如果已有邀请码:点击邀请码旁边的刷新图标"重新生成" +4. 邀请码会自动复制到剪贴板 + +### 3. 发送邀请 + +生成邀请码后,需要将邀请码发送给裁判: + +**发送方式:** +- 📧 邮件:将邀请码通过邮件发送 +- 📱 短信:将邀请码通过短信发送 +- 💬 微信/其他:通过即时通讯工具发送 + +**邀请码使用:** +- 裁判收到邀请码后,访问系统登录页面 +- 输入邀请码进行登录 +- 查看赛事信息并回复邀请(接受/拒绝) + +### 4. 管理邀请 + +#### 4.1 查看邀请状态 + +邀请状态说明: +- 🟡 **待回复**:已发送邀请,裁判尚未回复 +- 🟢 **已接受**:裁判已接受邀请 +- 🔴 **已拒绝**:裁判已拒绝邀请 +- ⚪ **已取消**:管理员已取消邀请 + +#### 4.2 邀请操作 + +**对于"待回复"状态的邀请:** +- **重发**:重新发送邀请通知 +- **提醒**:发送提醒消息催促裁判回复 +- **取消**:取消邀请(需填写取消原因) + +**对于"已接受"状态的邀请:** +- **确认**:确认裁判参与(可进行后续的场地、项目分配) + +**所有邀请:** +- **查看**:查看邀请详细信息 +- **复制邀请码**:点击邀请码即可复制 + +### 5. 统计信息 + +页面顶部显示四个统计卡片: +- 📊 **总邀请数**:已发送的邀请总数 +- ⏰ **待回复**:等待裁判回复的邀请数 +- ✅ **已接受**:裁判已接受的邀请数 +- ❌ **已拒绝**:裁判已拒绝的邀请数 + +### 6. 导出数据 + +点击"导出数据"按钮可以导出当前筛选条件下的邀请列表为Excel文件。 + +## 常见问题 + +### Q1: 为什么"批量生成邀请码"按钮是灰色的? +**A:** 可能的原因: +1. 未选择赛事 +2. 赛事列表正在加载 +3. 未勾选任何邀请记录 + +### Q2: 如何为新裁判生成邀请码? +**A:** 使用"从评委库导入"功能: +1. 先在"评委管理"中添加裁判 +2. 在"裁判邀请"页面点击"从评委库导入" +3. 选择裁判并确认导入 + +### Q3: 邀请码可以重复使用吗? +**A:** 不可以。每个邀请码只能使用一次。如果需要重新邀请,请使用"重新生成"功能。 + +### Q4: 邀请码有效期是多久? +**A:** 默认有效期为30天。过期后需要重新生成。 + +### Q5: 如何知道裁判是否收到邀请? +**A:** +- 查看邀请状态,如果裁判已登录并回复,状态会更新 +- 可以使用"提醒"功能发送提醒消息 +- 建议通过电话或其他方式确认裁判是否收到 + +## 技术说明 + +### 邀请码生成规则 +- 每个赛事+裁判组合生成唯一邀请码 +- 邀请码包含角色信息(主裁判/普通裁判) +- 可以预分配场地和项目 + +### 数据关联 +``` +赛事 (Competition) + ↓ +邀请记录 (JudgeInvite) + ↓ +裁判 (Judge) +``` + +### API接口 +- 生成邀请码:`POST /api/blade-martial/judgeInvite/generate` +- 批量生成:`POST /api/blade-martial/judgeInvite/generate/batch` +- 重新生成:`PUT /api/blade-martial/judgeInvite/regenerate/{inviteId}` +- 邀请列表:`GET /api/blade-martial/judgeInvite/list` +- 邀请统计:`GET /api/blade-martial/judgeInvite/statistics` + +## 更新日志 + +### 2025-12-13 +- ✨ 新增"从评委库导入"功能 +- ✨ 支持裁判搜索和筛选 +- ✨ 优化邀请码生成流程 +- 🐛 修复按钮禁用逻辑问题 +- 🐛 修复赛事选择初始化问题 +- 💄 优化用户界面和交互体验 diff --git a/doc/赛事列表加载问题排查指南.md b/doc/赛事列表加载问题排查指南.md new file mode 100644 index 0000000..32565b2 --- /dev/null +++ b/doc/赛事列表加载问题排查指南.md @@ -0,0 +1,260 @@ +# 赛事列表加载问题排查指南 + +## 问题现象 + +- 赛事下拉框显示"无数据" +- 所有按钮(发送邀请、批量生成邀请码等)无法点击 +- 页面显示"暂无数据" + +## 排查步骤 + +### 1. 检查浏览器控制台 + +打开浏览器开发者工具(F12),查看Console标签页: + +**查看日志输出:** +``` +赛事列表API返回: {...} +解析后的赛事列表: [...] +``` + +**可能的错误信息:** +- `加载赛事列表失败: Network Error` - 网络连接问题 +- `加载赛事列表失败: 404` - API路径错误 +- `加载赛事列表失败: 500` - 后端服务错误 + +### 2. 检查Network请求 + +在开发者工具的Network标签页中: + +1. 刷新页面 +2. 找到 `/api/martial/competition/list` 请求 +3. 查看请求状态: + - **200**: 请求成功,检查返回数据 + - **404**: API路径不存在 + - **500**: 后端服务错误 + - **Failed**: 网络连接失败 + +4. 查看Response数据结构: +```json +{ + "code": 200, + "data": { + "records": [...], // 赛事列表 + "total": 10 + } +} +``` + +### 3. 检查后端服务 + +#### 3.1 确认后端服务是否启动 + +**Windows:** +```bash +netstat -ano | findstr 8888 +``` + +**Linux/Mac:** +```bash +netstat -tuln | grep 8888 +``` + +如果没有输出,说明后端服务未启动。 + +#### 3.2 启动后端服务 + +进入后端项目目录: +```bash +cd martial-master +mvn spring-boot:run +``` + +或者使用IDE(IDEA/Eclipse)启动。 + +### 4. 检查数据库数据 + +#### 4.1 连接数据库 + +使用数据库客户端(Navicat、DBeaver等)连接: +- Host: 127.0.0.1 +- Port: 3306 +- Database: martial_db +- Username: root +- Password: 123456 + +#### 4.2 执行检查SQL + +运行项目根目录下的 `check_data.sql` 文件: + +```sql +-- 检查赛事数据 +SELECT * FROM martial_competition LIMIT 10; + +-- 检查赛事总数 +SELECT COUNT(*) FROM martial_competition; +``` + +**如果赛事表为空:** +需要先创建赛事数据。 + +### 5. 创建测试数据 + +#### 5.1 创建赛事 + +进入"赛事管理"页面,点击"新增赛事",填写: +- 赛事名称:测试赛事 +- 赛事编码:TEST001 +- 主办单位:测试单位 +- 地区:北京 +- 详细地点:测试地点 +- 报名时间:选择日期范围 +- 比赛时间:选择日期范围 + +保存后,赛事列表应该就能显示了。 + +#### 5.2 创建裁判 + +进入"评委管理"页面,点击"新增评委",填写: +- 姓名:张三 +- 性别:男 +- 手机号:13800138000 +- 身份证号:110101199001011234 +- 裁判类型:普通裁判 +- 等级/职称:一级 + +### 6. 检查API配置 + +#### 6.1 检查前端API配置 + +查看 `martial-web/src/axios/index.js` 或类似文件: + +```javascript +const baseURL = process.env.VUE_APP_API_BASE_URL || '/api' +``` + +确认API基础路径配置正确。 + +#### 6.2 检查代理配置 + +查看 `martial-web/vite.config.js` 或 `vue.config.js`: + +```javascript +proxy: { + '/api': { + target: 'http://localhost:8888', + changeOrigin: true + } +} +``` + +确认代理配置指向正确的后端地址。 + +### 7. 常见问题解决 + +#### 问题1: CORS跨域错误 + +**错误信息:** +``` +Access to XMLHttpRequest at 'http://localhost:8888/api/...' from origin 'http://localhost:5173' has been blocked by CORS policy +``` + +**解决方案:** +1. 检查后端CORS配置 +2. 使用代理配置 +3. 确保前后端端口配置正确 + +#### 问题2: 404 Not Found + +**可能原因:** +1. API路径错误 +2. 后端Controller路径配置错误 +3. 后端服务未启动 + +**解决方案:** +1. 检查API路径:`/api/martial/competition/list` +2. 检查后端Controller注解:`@RequestMapping("/martial/competition")` +3. 启动后端服务 + +#### 问题3: 数据结构不匹配 + +**现象:** +API返回数据,但前端解析失败。 + +**解决方案:** +查看控制台日志,确认数据结构: +```javascript +console.log('赛事列表API返回:', res) +console.log('解析后的赛事列表:', records) +``` + +如果数据结构不对,修改前端解析逻辑。 + +### 8. 快速测试 + +#### 8.1 使用Postman测试API + +**请求:** +``` +GET http://localhost:8888/api/martial/competition/list?current=1&size=10 +``` + +**期望返回:** +```json +{ + "code": 200, + "success": true, + "data": { + "records": [ + { + "id": 1, + "competitionName": "测试赛事", + "status": 1, + ... + } + ], + "total": 1 + } +} +``` + +#### 8.2 使用curl测试 + +```bash +curl -X GET "http://localhost:8888/api/martial/competition/list?current=1&size=10" +``` + +### 9. 临时解决方案 + +如果急需使用,可以临时修改代码,手动添加测试数据: + +```javascript +// 在 loadCompetitionList 函数中添加 +competitionList.value = [ + { + id: 1, + competitionName: '测试赛事', + status: 1 + } +] +queryParams.competitionId = 1 +``` + +**注意:** 这只是临时方案,不要提交到代码库。 + +### 10. 联系支持 + +如果以上步骤都无法解决问题,请提供以下信息: + +1. 浏览器控制台完整错误日志 +2. Network请求的详细信息(Request/Response) +3. 后端服务日志 +4. 数据库查询结果 + +## 修改记录 + +### 2025-12-13 +- ✨ 添加详细的日志输出 +- ✨ 优化数据解析逻辑,支持多种返回格式 +- ✨ 移除status=1的限制,查询所有赛事 +- 📝 创建排查指南文档 diff --git a/src/api/martial/deduction.js b/src/api/martial/deduction.js index 9ac8456..02b0906 100644 --- a/src/api/martial/deduction.js +++ b/src/api/martial/deduction.js @@ -45,7 +45,7 @@ export const getDeductionDetail = (id) => { */ export const addDeduction = (data) => { return request({ - url: '/api/blade-martial/deductionItem/save', + url: '/api/blade-martial/deductionItem/submit', method: 'post', data }) @@ -57,7 +57,7 @@ export const addDeduction = (data) => { */ export const updateDeduction = (data) => { return request({ - url: '/api/blade-martial/deductionItem/update', + url: '/api/blade-martial/deductionItem/submit', method: 'post', data }) diff --git a/src/api/martial/deductionItem.js b/src/api/martial/deductionItem.js index 4d9c023..c13df6e 100644 --- a/src/api/martial/deductionItem.js +++ b/src/api/martial/deductionItem.js @@ -45,7 +45,7 @@ export const getDeductionDetail = (id) => { */ export const addDeduction = (data) => { return request({ - url: '/api/blade-martial/deductionItem/save', + url: '/api/blade-martial/deductionItem/submit', method: 'post', data }) @@ -57,7 +57,7 @@ export const addDeduction = (data) => { */ export const updateDeduction = (data) => { return request({ - url: '/api/blade-martial/deductionItem/update', + url: '/api/blade-martial/deductionItem/submit', method: 'post', data }) diff --git a/src/api/martial/judgeInvite.js b/src/api/martial/judgeInvite.js index 6181874..42c8c87 100644 --- a/src/api/martial/judgeInvite.js +++ b/src/api/martial/judgeInvite.js @@ -14,7 +14,7 @@ import request from '@/axios'; */ export const getJudgeInviteList = (current, size, params) => { return request({ - url: '/api/blade-martial/judgeInvite/list', + url: '/api/martial/judgeInvite/list', method: 'get', params: { current, @@ -30,7 +30,7 @@ export const getJudgeInviteList = (current, size, params) => { */ export const getJudgeInviteDetail = (id) => { return request({ - url: '/api/blade-martial/judgeInvite/detail', + url: '/api/martial/judgeInvite/detail', method: 'get', params: { id } }) @@ -48,7 +48,7 @@ export const getJudgeInviteDetail = (id) => { */ export const sendInvite = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/send', + url: '/api/martial/judgeInvite/send', method: 'post', data }) @@ -60,7 +60,7 @@ export const sendInvite = (data) => { */ export const updateInvite = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/update', + url: '/api/martial/judgeInvite/submit', method: 'post', data }) @@ -72,7 +72,7 @@ export const updateInvite = (data) => { */ export const removeInvite = (ids) => { return request({ - url: '/api/blade-martial/judgeInvite/remove', + url: '/api/martial/judgeInvite/remove', method: 'post', params: { ids } }) @@ -87,7 +87,7 @@ export const removeInvite = (ids) => { */ export const batchSendInvites = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/batch-send', + url: '/api/martial/judgeInvite/batch-send', method: 'post', data }) @@ -99,7 +99,7 @@ export const batchSendInvites = (data) => { */ export const resendInvite = (id) => { return request({ - url: `/api/blade-martial/judgeInvite/resend/${id}`, + url: `/api/martial/judgeInvite/resend/${id}`, method: 'post' }) } @@ -114,7 +114,7 @@ export const resendInvite = (id) => { */ export const replyInvite = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/reply', + url: '/api/martial/judgeInvite/reply', method: 'post', data }) @@ -127,7 +127,7 @@ export const replyInvite = (data) => { */ export const cancelInvite = (id, reason) => { return request({ - url: `/api/blade-martial/judgeInvite/cancel/${id}`, + url: `/api/martial/judgeInvite/cancel/${id}`, method: 'post', params: { reason } }) @@ -139,7 +139,7 @@ export const cancelInvite = (id, reason) => { */ export const confirmInvite = (id) => { return request({ - url: `/api/blade-martial/judgeInvite/confirm/${id}`, + url: `/api/martial/judgeInvite/confirm/${id}`, method: 'post' }) } @@ -150,7 +150,7 @@ export const confirmInvite = (id) => { */ export const getInviteStatistics = (competitionId) => { return request({ - url: '/api/blade-martial/judgeInvite/statistics', + url: '/api/martial/judgeInvite/statistics', method: 'get', params: { competitionId } }) @@ -162,7 +162,7 @@ export const getInviteStatistics = (competitionId) => { */ export const getAcceptedJudges = (competitionId) => { return request({ - url: '/api/blade-martial/judgeInvite/accepted-judges', + url: '/api/martial/judgeInvite/accepted-judges', method: 'get', params: { competitionId } }) @@ -175,7 +175,7 @@ export const getAcceptedJudges = (competitionId) => { */ export const importFromJudgePool = (competitionId, judgeIds) => { return request({ - url: '/api/blade-martial/judgeInvite/import/pool', + url: '/api/martial/judgeInvite/import/pool', method: 'post', params: { competitionId, judgeIds } }) @@ -187,7 +187,7 @@ export const importFromJudgePool = (competitionId, judgeIds) => { */ export const exportInvites = (params) => { return request({ - url: '/api/blade-martial/judgeInvite/export', + url: '/api/martial/judgeInvite/export', method: 'get', params, responseType: 'blob' @@ -201,7 +201,7 @@ export const exportInvites = (params) => { */ export const sendReminder = (id, message) => { return request({ - url: `/api/blade-martial/judgeInvite/reminder/${id}`, + url: `/api/martial/judgeInvite/reminder/${id}`, method: 'post', params: { message } }) @@ -219,7 +219,7 @@ export const sendReminder = (id, message) => { */ export const generateInviteCode = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/generate', + url: '/api/martial/judgeInvite/generate', method: 'post', data }) @@ -235,7 +235,7 @@ export const generateInviteCode = (data) => { */ export const batchGenerateInviteCode = (data) => { return request({ - url: '/api/blade-martial/judgeInvite/generate/batch', + url: '/api/martial/judgeInvite/generate/batch', method: 'post', data }) @@ -247,7 +247,7 @@ export const batchGenerateInviteCode = (data) => { */ export const regenerateInviteCode = (inviteId) => { return request({ - url: `/api/blade-martial/judgeInvite/regenerate/${inviteId}`, + url: `/api/martial/judgeInvite/regenerate/${inviteId}`, method: 'put' }) } @@ -259,7 +259,7 @@ export const regenerateInviteCode = (inviteId) => { */ export const getInviteByJudge = (competitionId, judgeId) => { return request({ - url: '/api/blade-martial/judgeInvite/byJudge', + url: '/api/martial/judgeInvite/byJudge', method: 'get', params: { competitionId, judgeId } }) diff --git a/src/api/martial/project.js b/src/api/martial/project.js index d027d78..ed1cba7 100644 --- a/src/api/martial/project.js +++ b/src/api/martial/project.js @@ -9,7 +9,9 @@ import request from '@/axios'; * @param {Object} params - 查询参数 * @param {Number} params.competitionId - 赛事ID * @param {String} params.projectName - 项目名称(可选) - * @param {String} params.category - 分组类别(可选) + * @param {Number} params.category - 分组类别(可选) + * @param {Number} params.eventType - 项目类型(可选) + * @param {Number} params.participantType - 参赛类型(可选,1-单人,2-集体) */ export const getProjectList = (current, size, params) => { return request({ @@ -65,17 +67,16 @@ export const updateProject = (data) => { * @param {Number} data.competitionId - 赛事ID * @param {String} data.projectName - 项目名称 * @param {String} data.projectCode - 项目编码 - * @param {String} data.category - 组别(男子组/女子组) - * @param {Number} data.type - 类型(1-个人,2-双人,3-集体) - * @param {Number} data.minParticipants - 最少参赛人数 - * @param {Number} data.maxParticipants - 最多参赛人数 - * @param {Number} data.minAge - 最小年龄 - * @param {Number} data.maxAge - 最大年龄 - * @param {Number} data.genderLimit - 性别限制(0-不限,1-仅男,2-仅女) - * @param {Number} data.estimatedDuration - 预估时长(分钟) - * @param {Number} data.price - 报名费用 - * @param {Number} data.difficultyCoefficient - 难度系数 - * @param {String} data.description - 项目描述 + * @param {Number} data.category - 分组类别(1-男子,2-女子,3-团体,4-混合) + * @param {Number} data.eventType - 项目类型(1-套路,2-散打,3-器械,4-对练) + * @param {Number} data.participantType - 参赛类型(1-单人,2-集体) + * @param {Number} data.registrationFee - 报名费用 + * @param {String} data.registrationStartTime - 报名开始时间 + * @param {String} data.registrationEndTime - 报名结束时间 + * @param {Number} data.maxParticipants - 最大参赛人数 + * @param {Number} data.sortOrder - 排序序号 + * @param {String} data.rules - 比赛规则 + * @param {String} data.remark - 备注 */ export const submitProject = (data) => { return request({ diff --git a/src/api/system/menu.js b/src/api/system/menu.js index 7050c95..f5e26c2 100644 --- a/src/api/system/menu.js +++ b/src/api/system/menu.js @@ -2,7 +2,7 @@ import request from '@/axios'; export const getList = (current, size, params) => { return request({ - url: '/blade-system/menu/list', + url: '/api/blade-system/menu/list', method: 'get', params: { ...params, @@ -14,7 +14,7 @@ export const getList = (current, size, params) => { export const getLazyList = (parentId, params) => { return request({ - url: '/blade-system/menu/lazy-list', + url: '/api/blade-system/menu/lazy-list', method: 'get', params: { ...params, @@ -25,7 +25,7 @@ export const getLazyList = (parentId, params) => { export const getLazyMenuList = (parentId, params) => { return request({ - url: '/blade-system/menu/lazy-menu-list', + url: '/api/blade-system/menu/lazy-menu-list', method: 'get', params: { ...params, @@ -36,7 +36,7 @@ export const getLazyMenuList = (parentId, params) => { export const getMenuList = (current, size, params) => { return request({ - url: '/blade-system/menu/menu-list', + url: '/api/blade-system/menu/menu-list', method: 'get', params: { ...params, @@ -48,7 +48,7 @@ export const getMenuList = (current, size, params) => { export const getMenuTree = tenantId => { return request({ - url: '/blade-system/menu/tree', + url: '/api/blade-system/menu/tree', method: 'get', params: { tenantId, @@ -58,7 +58,7 @@ export const getMenuTree = tenantId => { export const remove = ids => { return request({ - url: '/blade-system/menu/remove', + url: '/api/blade-system/menu/remove', method: 'post', params: { ids, @@ -68,7 +68,7 @@ export const remove = ids => { export const add = row => { return request({ - url: '/blade-system/menu/submit', + url: '/api/blade-system/menu/submit', method: 'post', data: row, }); @@ -76,7 +76,7 @@ export const add = row => { export const update = row => { return request({ - url: '/blade-system/menu/submit', + url: '/api/blade-system/menu/submit', method: 'post', data: row, }); @@ -84,7 +84,7 @@ export const update = row => { export const getMenu = id => { return request({ - url: '/blade-system/menu/detail', + url: '/api/blade-system/menu/detail', method: 'get', params: { id, @@ -94,13 +94,13 @@ export const getMenu = id => { export const getTopMenu = () => request({ - url: '/blade-system/menu/top-menu', + url: '/api/blade-system/menu/top-menu', method: 'get', }); export const getRoutes = topMenuId => request({ - url: '/blade-system/menu/routes', + url: '/api/blade-system/menu/routes', method: 'get', params: { topMenuId, diff --git a/src/api/user.js b/src/api/user.js index 050b4aa..7595909 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -4,7 +4,7 @@ import website from '@/config/website'; export const loginByUsername = (tenantId, deptId, roleId, username, password, type, key, code) => request({ - url: '/blade-auth/oauth/token', + url: '/api/blade-auth/oauth/token', method: 'post', headers: { 'Tenant-Id': tenantId, @@ -25,7 +25,7 @@ export const loginByUsername = (tenantId, deptId, roleId, username, password, ty export const loginBySocial = (tenantId, source, code, state) => request({ - url: '/blade-auth/oauth/token', + url: '/api/blade-auth/oauth/token', method: 'post', headers: { 'Tenant-Id': tenantId, @@ -42,7 +42,7 @@ export const loginBySocial = (tenantId, source, code, state) => export const loginBySso = (state, code) => request({ - url: '/blade-auth/oauth/token', + url: '/api/blade-auth/oauth/token', method: 'post', headers: { 'Tenant-Id': state, @@ -58,7 +58,7 @@ export const loginBySso = (state, code) => export const refreshToken = (refresh_token, tenantId, deptId, roleId) => request({ - url: '/blade-auth/oauth/token', + url: '/api/blade-auth/oauth/token', method: 'post', headers: { 'Tenant-Id': tenantId, @@ -75,7 +75,7 @@ export const refreshToken = (refresh_token, tenantId, deptId, roleId) => export const registerUser = (tenantId, name, account, password, phone, email) => request({ - url: '/blade-auth/oauth/token', + url: '/api/blade-auth/oauth/token', method: 'post', headers: { 'Tenant-Id': tenantId, @@ -94,7 +94,7 @@ export const registerUser = (tenantId, name, account, password, phone, email) => export const registerGuest = (form, oauthId) => request({ - url: '/blade-system/user/register-guest', + url: '/api/blade-system/user/register-guest', method: 'post', params: { tenantId: form.tenantId, @@ -107,40 +107,40 @@ export const registerGuest = (form, oauthId) => export const getButtons = () => request({ - url: '/blade-system/menu/buttons', + url: '/api/blade-system/menu/buttons', method: 'get', }); export const getCaptcha = () => request({ - url: '/blade-auth/oauth/captcha', + url: '/api/blade-auth/oauth/captcha', method: 'get', authorization: false, }); export const logout = () => request({ - url: '/blade-auth/oauth/logout', + url: '/api/blade-auth/oauth/logout', method: 'get', authorization: false, }); export const getUserInfo = () => request({ - url: '/blade-auth/oauth/user-info', + url: '/api/blade-auth/oauth/user-info', method: 'get', }); export const sendLogs = list => request({ - url: '/blade-auth/oauth/logout', + url: '/api/blade-auth/oauth/logout', method: 'post', data: list, }); export const clearCache = () => request({ - url: '/blade-auth/oauth/clear-cache', + url: '/api/blade-auth/oauth/clear-cache', method: 'get', authorization: false, }); diff --git a/src/views/martial/deduction/index.vue b/src/views/martial/deduction/index.vue index f754b35..65a0d5c 100644 --- a/src/views/martial/deduction/index.vue +++ b/src/views/martial/deduction/index.vue @@ -3,12 +3,40 @@ + + + + + +
- + 新增扣分项 批量删除 - + + 导出模板
@@ -65,7 +103,12 @@ - +
@@ -135,9 +178,6 @@ 编辑 - - 克隆 - 删除 @@ -173,6 +213,22 @@ :rules="rules" label-width="120px" > + + + + + { +// 加载赛事列表 +const loadCompetitionList = async () => { + competitionLoading.value = true try { - const res = await getProjectList(1, 1000, {}) - if (res.data && res.data.records) { - projectList.value = res.data.records + const resCompe = await getCompetitionList(1, 1000, {}) + const recordsCompe = resCompe.data?.data?.records || [] + competitionList.value = recordsCompe + + if (recordsCompe.length === 0) { + console.warn('赛事列表为空,请先在"赛事管理"中创建赛事') + } + } catch (error) { + console.error('加载赛事列表失败:', error) + ElMessage.error('加载赛事列表失败') + } finally { + competitionLoading.value = false + } +} + +// 加载项目列表(根据赛事ID) +const loadProjectList = async (competitionId) => { + if (!competitionId) { + projectList.value = [] + return + } + + try { + const res = await getProjectList(1, 1000, { competitionId }) + // 根据axios响应拦截器的处理,数据在 res.data.data.records 中 + const records = res.data?.data?.records || [] + projectList.value = records + + if (records.length === 0) { + console.warn('该赛事下暂无项目,请先在"轮编管理"中创建项目') } } catch (error) { console.error('加载项目列表失败:', error) + ElMessage.error('加载项目列表失败') + } +} + +// 赛事选择变化 +const handleCompetitionChange = (competitionId) => { + // 清空项目选择和表格数据 + queryParams.projectId = null + tableData.value = [] + total.value = 0 + + // 加载该赛事下的项目 + if (competitionId) { + loadProjectList(competitionId) + // 自动查询数据 + fetchData() + } else { + projectList.value = [] } } // 查询数据 const fetchData = async () => { + // 必须选择赛事才能查询 + if (!queryParams.competitionId) { + ElMessage.warning('请先选择赛事') + return + } + loading.value = true try { const res = await getDeductionList( @@ -396,10 +509,10 @@ const fetchData = async () => { queryParams.size, queryParams ) - if (res.data) { - tableData.value = res.data.records || [] - total.value = res.data.total || 0 - } + // 根据axios响应拦截器的处理,数据在 res.data.data 中 + const data = res.data?.data || {} + tableData.value = data.records || [] + total.value = data.total || 0 } catch (error) { ElMessage.error('获取数据失败') console.error(error) @@ -416,19 +529,28 @@ const handleSearch = () => { // 重置 const handleReset = () => { + const competitionId = queryParams.competitionId Object.assign(queryParams, { current: 1, size: 10, - projectId: '', + competitionId: competitionId, + projectId: null, itemName: '' }) - fetchData() + if (competitionId) { + fetchData() + } } // 新增 const handleAdd = () => { + if (!queryParams.competitionId) { + ElMessage.warning('请先选择赛事') + return + } dialogTitle.value = '新增扣分项' resetForm() + form.competitionId = queryParams.competitionId dialogVisible.value = true } @@ -480,29 +602,6 @@ const handleBatchDelete = () => { .catch(() => {}) } -// 克隆单个扣分项 -const handleCloneSingle = (row) => { - cloneForm.sourceProjectId = row.projectId - cloneForm.sourceProjectName = row.projectName - cloneForm.targetProjectId = '' - cloneForm.itemCount = 1 - cloneDialogVisible.value = true -} - -// 克隆扣分项(批量) -const handleClone = () => { - if (!queryParams.projectId) { - ElMessage.warning('请先选择源项目(通过搜索筛选)') - return - } - - const sourceProject = projectList.value.find(p => p.id === queryParams.projectId) - cloneForm.sourceProjectId = queryParams.projectId - cloneForm.sourceProjectName = sourceProject?.projectName || '' - cloneForm.targetProjectId = '' - cloneForm.itemCount = total.value - cloneDialogVisible.value = true -} // 提交克隆 const handleCloneSubmit = async () => { @@ -568,7 +667,8 @@ const handleDialogClose = () => { const resetForm = () => { Object.assign(form, { id: null, - projectId: '', + competitionId: null, + projectId: null, itemName: '', deductionPoints: 0.5, sortOrder: 0, @@ -639,8 +739,7 @@ const formatDate = (date) => { // 生命周期 onMounted(() => { - loadProjectList() - fetchData() + loadCompetitionList() initSortable() }) diff --git a/src/views/martial/judgeInvite/index.vue b/src/views/martial/judgeInvite/index.vue index 41230e4..3950b8d 100644 --- a/src/views/martial/judgeInvite/index.vue +++ b/src/views/martial/judgeInvite/index.vue @@ -9,9 +9,19 @@ placeholder="请选择赛事" clearable filterable + :loading="competitionLoading" + :disabled="competitionLoading" style="width: 250px" @change="handleCompetitionChange" > + - - - - - - - - 搜索 @@ -63,82 +60,30 @@
- - - - -
-
- -
-
-
{{ statistics.totalInvites || 0 }}
-
总邀请数
-
-
-
-
- - -
-
- -
-
-
{{ statistics.pendingCount || 0 }}
-
待回复
-
-
-
-
- - -
-
- -
-
-
{{ statistics.acceptedCount || 0 }}
-
已接受
-
-
-
-
- - -
-
- -
-
-
{{ statistics.rejectedCount || 0 }}
-
已拒绝
-
-
-
-
-
-
- - 发送邀请 - - - 批量生成邀请码 - - + 从评委库导入 - + 导出数据
- +
@@ -154,15 +99,15 @@ @selection-change="handleSelectionChange" > - - + + - + - - - + - + + + - - - - - - - @@ -276,6 +177,108 @@ />
+ + + + + + + + + + + + + + + + + + + + 搜索 + + 重置 + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
@@ -285,36 +288,22 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { Search, Refresh, - Plus, Delete, Download, - DocumentCopy, FolderOpened, - Upload, View, - Close, - Bell, - CircleCheck, - CircleClose, - User, - Clock } from '@element-plus/icons-vue' import { getJudgeInviteList, - sendInvite, - batchSendInvites, - resendInvite, - cancelInvite, - confirmInvite, getInviteStatistics, - importFromJudgePool, exportInvites, - sendReminder, generateInviteCode, batchGenerateInviteCode, - regenerateInviteCode + regenerateInviteCode, + removeInvite } from '@/api/martial/judgeInvite' import { getCompetitionList } from '@/api/martial/competition' +import { getRefereeList } from '@/api/martial/referee' import dayjs from 'dayjs' // 数据状态 @@ -323,6 +312,21 @@ const tableData = ref([]) const total = ref(0) const selection = ref([]) const competitionList = ref([]) +const competitionLoading = ref(false) // 赛事列表加载状态 + +// 裁判选择对话框 +const judgeDialogVisible = ref(false) +const judgeLoading = ref(false) +const judgeList = ref([]) +const judgeTotal = ref(0) +const selectedJudges = ref([]) +const judgeQueryParams = reactive({ + current: 1, + size: 10, + name: '', + phone: '', + refereeType: null +}) // 统计数据 const statistics = ref({ @@ -336,7 +340,7 @@ const statistics = ref({ const queryParams = reactive({ current: 1, size: 10, - competitionId: '', + competitionId: null, judgeName: '', judgeLevel: '', inviteStatus: '' @@ -344,17 +348,33 @@ const queryParams = reactive({ // 加载赛事列表 const loadCompetitionList = async () => { + competitionLoading.value = true try { - const res = await getCompetitionList(1, 1000, { status: 1 }) - if (res.data && res.data.records) { - competitionList.value = res.data.records - if (competitionList.value.length > 0 && !queryParams.competitionId) { + // 查询所有赛事 + const res = await getCompetitionList(1, 1000, {}) + + // 根据axios响应拦截器的处理,数据结构为: res.data.data.records + // res.data = 后端返回的整个JSON对象 { code, success, data, msg } + // res.data.data = 后端返回的data字段 { records, total, size, current, pages } + // res.data.data.records = 实际的赛事列表数组 + const records = res.data?.data?.records || [] + + competitionList.value = records + + if (competitionList.value.length > 0) { + // 如果没有选中赛事,自动选择第一个 + if (queryParams.competitionId === null || queryParams.competitionId === '') { queryParams.competitionId = competitionList.value[0].id handleCompetitionChange(queryParams.competitionId) } + } else { + ElMessage.warning('暂无可用赛事,请先创建赛事') } } catch (error) { console.error('加载赛事列表失败:', error) + ElMessage.error(`加载赛事列表失败: ${error.response?.data?.msg || error.message || '请检查网络连接'}`) + } finally { + competitionLoading.value = false } } @@ -366,7 +386,7 @@ const handleCompetitionChange = (competitionId) => { // 加载统计数据 const loadStatistics = async () => { - if (!queryParams.competitionId) return + if (queryParams.competitionId === null || queryParams.competitionId === '') return try { const res = await getInviteStatistics(queryParams.competitionId) @@ -380,7 +400,7 @@ const loadStatistics = async () => { // 获取数据 const fetchData = async () => { - if (!queryParams.competitionId) return + if (queryParams.competitionId === null || queryParams.competitionId === '') return loading.value = true try { @@ -389,10 +409,10 @@ const fetchData = async () => { queryParams.size, queryParams ) - if (res.data) { - tableData.value = res.data.records || [] - total.value = res.data.total || 0 - } + // 根据axios响应拦截器的处理,数据在 res.data.data 中 + const data = res.data?.data || {} + tableData.value = data.records || [] + total.value = data.total || 0 } catch (error) { ElMessage.error('加载数据失败') console.error(error) @@ -419,96 +439,134 @@ const handleReset = () => { fetchData() } -// 发送邀请 -const handleSendInvite = async () => { - if (!queryParams.competitionId) { - ElMessage.warning('请先选择赛事') - return - } - // TODO: 打开发送邀请对话框 - ElMessage.info('请先生成邀请码,然后通过邮件/短信发送给评委') -} - // 从评委库导入 const handleImportFromPool = async () => { - if (!queryParams.competitionId) { + if (competitionLoading.value) { + ElMessage.warning('赛事列表加载中,请稍候...') + return + } + if (competitionList.value.length === 0) { + ElMessage.warning('暂无可用赛事,请先创建赛事') + return + } + if (queryParams.competitionId === null || queryParams.competitionId === '') { ElMessage.warning('请先选择赛事') return } + + // 打开裁判选择对话框 + judgeDialogVisible.value = true + selectedJudges.value = [] + loadJudgeList() +} + +// 加载裁判列表 +const loadJudgeList = async () => { + judgeLoading.value = true try { - // TODO: 打开评委选择对话框 - ElMessage.info('请先在评委管理中添加评委,然后在此处生成邀请码') + const params = {} + if (judgeQueryParams.name) { + params.name = judgeQueryParams.name + } + if (judgeQueryParams.phone) { + params.phone = judgeQueryParams.phone + } + if (judgeQueryParams.refereeType !== null && judgeQueryParams.refereeType !== '') { + params.refereeType = judgeQueryParams.refereeType + } + + const res = await getRefereeList( + judgeQueryParams.current, + judgeQueryParams.size, + params + ) + + if (res.data && res.data.data && res.data.data.records) { + judgeList.value = res.data.data.records + judgeTotal.value = res.data.data.total || 0 + } else if (res.data && res.data.records) { + judgeList.value = res.data.records + judgeTotal.value = res.data.total || 0 + } } catch (error) { - ElMessage.error('导入失败') + console.error('加载裁判列表失败:', error) + ElMessage.error('加载裁判列表失败') + } finally { + judgeLoading.value = false } } -// 重发 -const handleResend = async (row) => { - try { - await resendInvite(row.id) - ElMessage.success('重发成功') - fetchData() - } catch (error) { - ElMessage.error('重发失败') - } +// 裁判搜索 +const handleJudgeSearch = () => { + judgeQueryParams.current = 1 + loadJudgeList() } -// 提醒 -const handleReminder = async (row) => { - try { - await sendReminder(row.id, '请尽快回复邀请') - ElMessage.success('提醒发送成功') - } catch (error) { - ElMessage.error('提醒发送失败') - } +// 裁判搜索重置 +const handleJudgeReset = () => { + judgeQueryParams.name = '' + judgeQueryParams.phone = '' + judgeQueryParams.refereeType = null + judgeQueryParams.current = 1 + loadJudgeList() } -// 取消邀请 -const handleCancel = async (row) => { +// 裁判选择改变 +const handleJudgeSelectionChange = (val) => { + selectedJudges.value = val +} + +// 确认导入裁判 +const handleConfirmImport = async () => { + if (selectedJudges.value.length === 0) { + ElMessage.warning('请至少选择一位裁判') + return + } + try { - const { value: reason } = await ElMessageBox.prompt('请输入取消原因', '取消邀请', { - confirmButtonText: '确定', - cancelButtonText: '取消', - inputPlaceholder: '请输入取消原因', - inputValidator: (value) => { - if (!value || value.trim() === '') { - return '请输入取消原因' - } - return true + await ElMessageBox.confirm( + `确定为选中的 ${selectedJudges.value.length} 位裁判生成邀请码吗?`, + '确认导入', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'info' } + ) + + loading.value = true + const judgeIds = selectedJudges.value.map(item => item.id) + + const res = await batchGenerateInviteCode({ + competitionId: queryParams.competitionId, + judgeIds: judgeIds, + role: 'judge', + expireDays: 30 }) - await cancelInvite(row.id, reason) - ElMessage.success('取消成功') - fetchData() - loadStatistics() + // 根据axios响应拦截器的处理,数据在 res.data.data 中 + const invites = res.data?.data || [] + + if (Array.isArray(invites) && invites.length > 0) { + ElMessage.success(`成功为 ${invites.length} 位裁判生成邀请码`) + judgeDialogVisible.value = false + await fetchData() + await loadStatistics() + } else if (Array.isArray(invites) && invites.length === 0) { + ElMessage.warning('所有裁判都已有有效邀请码,无需重复生成') + } else { + ElMessage.error(res.data?.msg || '生成邀请码失败') + } } catch (error) { if (error !== 'cancel') { - ElMessage.error('取消失败') + console.error('导入裁判失败:', error) + ElMessage.error(error.response?.data?.msg || error.message || '导入裁判失败') } + } finally { + loading.value = false } } -// 确认 -const handleConfirm = async (row) => { - ElMessageBox.confirm('确认接受该评委的邀请回复?', '提示', { - confirmButtonText: '确认', - cancelButtonText: '取消', - type: 'warning' - }) - .then(async () => { - try { - await confirmInvite(row.id) - ElMessage.success('确认成功') - fetchData() - } catch (error) { - ElMessage.error('确认失败') - } - }) - .catch(() => {}) -} - // 查看 const handleView = async (row) => { try { @@ -547,13 +605,21 @@ const handleView = async (row) => { /** * 生成单个邀请码 + * 注意:此功能仅用于已有邀请记录但未生成邀请码的情况 + * 如果需要为新裁判生成邀请码,请使用"从评委库导入"功能 */ const handleGenerateCode = async (row) => { - if (!queryParams.competitionId) { + if (queryParams.competitionId === null || queryParams.competitionId === '') { ElMessage.warning('请先选择赛事') return } + // 检查是否有judgeId + if (!row.judgeId) { + ElMessage.error('数据异常:缺少裁判ID,请使用"从评委库导入"功能') + return + } + try { loading.value = true const res = await generateInviteCode({ @@ -622,52 +688,31 @@ const handleRegenerateCode = async (row) => { } /** - * 批量生成邀请码 + * 删除邀请记录 */ -const handleBatchGenerateCode = async () => { - if (!queryParams.competitionId) { - ElMessage.warning('请先选择赛事') - return - } - - if (selection.value.length === 0) { - ElMessage.warning('请先选择评委') - return - } - +const handleDelete = async (row) => { try { await ElMessageBox.confirm( - `确定为选中的 ${selection.value.length} 位评委批量生成邀请码吗?`, - '批量生成邀请码', + `确定要删除评委"${row.judgeName}"的邀请记录吗?`, + '删除确认', { confirmButtonText: '确定', cancelButtonText: '取消', - type: 'info' + type: 'warning' } ) loading.value = true - const judgeIds = selection.value.map(item => item.judgeId) + await removeInvite(row.id) + ElMessage.success('删除成功') - const res = await batchGenerateInviteCode({ - competitionId: queryParams.competitionId, - judgeIds: judgeIds, - role: 'judge', - expireDays: 30 - }) - - if (res.data && Array.isArray(res.data)) { - ElMessage.success(`成功生成 ${res.data.length} 个邀请码`) - // 刷新列表 - await fetchData() - await loadStatistics() - } else { - ElMessage.error(res.msg || '批量生成失败') - } + // 刷新列表和统计数据 + await fetchData() + await loadStatistics() } catch (error) { if (error !== 'cancel') { - console.error('批量生成邀请码失败:', error) - ElMessage.error(error.response?.data?.msg || error.message || '批量生成邀请码失败') + console.error('删除失败:', error) + ElMessage.error(error.response?.data?.msg || error.message || '删除失败') } } finally { loading.value = false @@ -854,4 +899,25 @@ onMounted(() => { justify-content: flex-end; } } + +// 裁判选择对话框样式 +.judge-search-form { + margin-bottom: 15px; + + .el-form-item { + margin-bottom: 0; + } +} + +.judge-pagination { + margin-top: 15px; + display: flex; + justify-content: flex-end; +} + +.dialog-footer { + display: flex; + align-items: center; + justify-content: flex-end; +} diff --git a/src/views/martial/judgeInvite/index.vue.old b/src/views/martial/judgeInvite/index.vue.old deleted file mode 100644 index 9102bf5cff7d448f37b18af2957bb614c1fccec2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33365 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 Z+$kt>U(>0TXj6y 调度 diff --git a/src/views/martial/project/index.vue b/src/views/martial/project/index.vue index c6f7cfe..7c44526 100644 --- a/src/views/martial/project/index.vue +++ b/src/views/martial/project/index.vue @@ -53,6 +53,17 @@ + + + + + + 查询 @@ -144,6 +155,12 @@ 对练 + + + + + + + + + + + + + 器械 对练 + + 单人 + 集体 + ¥{{ detailData.registrationFee || 0 }} @@ -490,7 +525,8 @@ const queryParams = reactive({ competitionId: '', projectName: '', category: '', - eventType: '' + eventType: '', + participantType: '' }) // 表单数据 @@ -501,6 +537,7 @@ const form = reactive({ projectName: '', category: null, eventType: null, + participantType: null, registrationFee: 0, registrationStartTime: '', registrationEndTime: '', @@ -539,6 +576,9 @@ const rules = { eventType: [ { required: true, message: '请选择项目类型', trigger: 'change' } ], + participantType: [ + { required: true, message: '请选择参赛类型', trigger: 'change' } + ], registrationFee: [ { required: true, message: '请输入报名费', trigger: 'blur' } ], @@ -610,7 +650,8 @@ const handleReset = () => { competitionId: '', projectName: '', category: '', - eventType: '' + eventType: '', + participantType: '' }) fetchData() } @@ -721,6 +762,7 @@ const resetForm = () => { projectName: '', category: null, eventType: null, + participantType: null, registrationFee: 0, registrationStartTime: '', registrationEndTime: '', diff --git a/src/views/martial/referee/index.vue b/src/views/martial/referee/index.vue index 779e270..c14cdd8 100644 --- a/src/views/martial/referee/index.vue +++ b/src/views/martial/referee/index.vue @@ -143,7 +143,17 @@ - + + + + + + diff --git a/src/views/martial/schedule/index.vue b/src/views/martial/schedule/index.vue index f7d4bfe..1accdb9 100644 --- a/src/views/martial/schedule/index.vue +++ b/src/views/martial/schedule/index.vue @@ -26,7 +26,6 @@ size="small" :type="activeTab === 'competition' ? 'primary' : ''" @click="activeTab = 'competition'" - :disabled="isScheduleCompleted" > 竞赛分组 @@ -34,7 +33,6 @@ size="small" :type="activeTab === 'venue' ? 'primary' : ''" @click="activeTab = 'venue'" - :disabled="isScheduleCompleted" > 场地 @@ -168,10 +166,6 @@ - -