This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
"allow": [
|
"allow": [
|
||||||
"Bash(dir:*)",
|
"Bash(dir:*)",
|
||||||
"Bash(npm run build:*)",
|
"Bash(npm run build:*)",
|
||||||
"Bash(findstr:*)"
|
"Bash(findstr:*)",
|
||||||
|
"WebFetch(domain:www.cmiassn.org)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
211
src/api/martial/activity.js
Normal file
211
src/api/martial/activity.js
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 活动日程管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活动日程分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.activityType - 活动类型(可选)
|
||||||
|
* @param {String} params.activityName - 活动名称(可选)
|
||||||
|
* @param {String} params.startDate - 开始日期(可选)
|
||||||
|
* @param {String} params.endDate - 结束日期(可选)
|
||||||
|
*/
|
||||||
|
export const getActivityScheduleList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活动日程详情
|
||||||
|
* @param {Number} id - 活动ID
|
||||||
|
*/
|
||||||
|
export const getActivityScheduleDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加活动日程
|
||||||
|
* @param {Object} data - 活动数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.activityType - 活动类型(opening_ceremony/closing_ceremony/competition/training/meeting/other)
|
||||||
|
* @param {String} data.activityName - 活动名称
|
||||||
|
* @param {String} data.description - 活动描述
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {String} data.location - 活动地点
|
||||||
|
* @param {Number} data.venueId - 场地ID(可选)
|
||||||
|
* @param {String} data.organizer - 组织者
|
||||||
|
* @param {String} data.participants - 参与人员(可选)
|
||||||
|
* @param {String} data.remarks - 备注(可选)
|
||||||
|
*/
|
||||||
|
export const addActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/add',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改活动日程
|
||||||
|
* @param {Object} data - 活动数据
|
||||||
|
*/
|
||||||
|
export const updateActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除活动日程
|
||||||
|
* @param {String} ids - 活动ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeActivity = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日历视图数据
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} month - 月份(格式: YYYY-MM)
|
||||||
|
*/
|
||||||
|
export const getActivityCalendar = (competitionId, month) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/calendar',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, month }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查活动时间冲突
|
||||||
|
* @param {Object} data - 活动数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {Number} data.venueId - 场地ID(可选)
|
||||||
|
* @param {Number} data.excludeId - 排除的活动ID(编辑时使用)
|
||||||
|
*/
|
||||||
|
export const checkActivityConflict = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/check-conflict',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制活动
|
||||||
|
* @param {Object} data - 复制数据
|
||||||
|
* @param {Number} data.sourceId - 源活动ID
|
||||||
|
* @param {String} data.newStartTime - 新开始时间
|
||||||
|
* @param {String} data.newEndTime - 新结束时间
|
||||||
|
*/
|
||||||
|
export const copyActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/copy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期查询活动
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} date - 日期(格式: YYYY-MM-DD)
|
||||||
|
*/
|
||||||
|
export const getActivitiesByDate = (competitionId, date) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/by-date',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, date }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活动统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getActivityStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按类型统计活动
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getActivityByType = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/statistics-by-type',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出活动日程
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportActivitySchedule = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入活动日程
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const importActivitySchedule = (file, competitionId) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加活动
|
||||||
|
* @param {Array} data - 活动数据数组
|
||||||
|
*/
|
||||||
|
export const batchAddActivities = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activity-schedule/batch-add',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
291
src/api/martial/activitySchedule.js
Normal file
291
src/api/martial/activitySchedule.js
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事活动日程管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活动日程分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.activityDate - 活动日期(可选)
|
||||||
|
* @param {Number} params.activityType - 活动类型(可选)
|
||||||
|
*/
|
||||||
|
export const getActivityScheduleList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活动日程详情
|
||||||
|
* @param {Number} id - 活动日程ID
|
||||||
|
*/
|
||||||
|
export const getActivityScheduleDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增活动日程
|
||||||
|
* @param {Object} data - 活动日程数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.activityDate - 活动日期
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {Number} data.activityType - 活动类型(1开幕式2闭幕式3比赛4培训5会议6其他)
|
||||||
|
* @param {String} data.activityName - 活动名称
|
||||||
|
* @param {String} data.activityLocation - 活动地点
|
||||||
|
* @param {String} data.activityDescription - 活动描述
|
||||||
|
* @param {String} data.organizer - 组织者
|
||||||
|
* @param {String} data.participants - 参与人员
|
||||||
|
* @param {Number} data.sortOrder - 排序序号
|
||||||
|
*/
|
||||||
|
export const addActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改活动日程
|
||||||
|
* @param {Object} data - 活动日程数据
|
||||||
|
*/
|
||||||
|
export const updateActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除活动日程
|
||||||
|
* @param {String} ids - 活动日程ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeActivity = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加活动日程
|
||||||
|
* @param {Array} data - 活动日程数据数组
|
||||||
|
*/
|
||||||
|
export const batchAddActivities = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/batch-save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某日期的活动日程
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} activityDate - 活动日期
|
||||||
|
*/
|
||||||
|
export const getActivitiesByDate = (competitionId, activityDate) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/list-by-date',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, activityDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日期范围内的活动日程
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} startDate - 开始日期
|
||||||
|
* @param {String} endDate - 结束日期
|
||||||
|
*/
|
||||||
|
export const getActivitiesByDateRange = (competitionId, startDate, endDate) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/list-by-range',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, startDate, endDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某类型的活动日程
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} activityType - 活动类型
|
||||||
|
*/
|
||||||
|
export const getActivitiesByType = (competitionId, activityType) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/list-by-type',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, activityType }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛事的所有活动日程(不分页)
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getAllActivities = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/all',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整活动日程顺序
|
||||||
|
* @param {Object} data - 调整参数
|
||||||
|
* @param {Number} data.id - 活动日程ID
|
||||||
|
* @param {Number} data.targetOrder - 目标顺序
|
||||||
|
*/
|
||||||
|
export const adjustActivityOrder = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/adjust-order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查活动时间冲突
|
||||||
|
* @param {Object} data - 检查参数
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.activityDate - 活动日期
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {String} data.activityLocation - 活动地点
|
||||||
|
* @param {Number} data.excludeId - 排除的活动ID(编辑时使用)
|
||||||
|
*/
|
||||||
|
export const checkActivityConflict = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/check-conflict',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制活动日程到其他日期
|
||||||
|
* @param {Object} data - 复制参数
|
||||||
|
* @param {Number} data.sourceActivityId - 源活动ID
|
||||||
|
* @param {String} data.targetDate - 目标日期
|
||||||
|
*/
|
||||||
|
export const copyActivity = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/copy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活动日程日历视图数据
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} month - 月份(格式:YYYY-MM)
|
||||||
|
*/
|
||||||
|
export const getActivityCalendar = (competitionId, month) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/calendar',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, month }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布活动日程
|
||||||
|
* @param {Number} id - 活动日程ID
|
||||||
|
*/
|
||||||
|
export const publishActivity = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/publish',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消活动日程
|
||||||
|
* @param {Number} id - 活动日程ID
|
||||||
|
* @param {String} cancelReason - 取消原因
|
||||||
|
*/
|
||||||
|
export const cancelActivity = (id, cancelReason) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/cancel',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { cancelReason }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成活动日程
|
||||||
|
* @param {Number} id - 活动日程ID
|
||||||
|
* @param {String} completionNote - 完成备注
|
||||||
|
*/
|
||||||
|
export const completeActivity = (id, completionNote) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/complete',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { completionNote }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出活动日程
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportActivities = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入活动日程
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
*/
|
||||||
|
export const importActivities = (competitionId, file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印活动日程表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const printActivitySchedule = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/activitySchedule/print',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
138
src/api/martial/deduction.js
Normal file
138
src/api/martial/deduction.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 扣分项管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扣分项分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.projectId - 项目ID
|
||||||
|
* @param {String} params.itemName - 扣分项名称(可选)
|
||||||
|
*/
|
||||||
|
export const getDeductionList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扣分项详情
|
||||||
|
* @param {Number} id - 扣分项ID
|
||||||
|
*/
|
||||||
|
export const getDeductionDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增扣分项
|
||||||
|
* @param {Object} data - 扣分项数据
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {String} data.itemName - 扣分项名称
|
||||||
|
* @param {Number} data.deductionPoints - 扣分值
|
||||||
|
* @param {String} data.description - 描述说明
|
||||||
|
* @param {Number} data.sortOrder - 排序序号
|
||||||
|
*/
|
||||||
|
export const addDeduction = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改扣分项
|
||||||
|
* @param {Object} data - 扣分项数据
|
||||||
|
*/
|
||||||
|
export const updateDeduction = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除扣分项
|
||||||
|
* @param {String} ids - 扣分项ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeDeduction = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目的扣分项列表(不分页)
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
*/
|
||||||
|
export const getDeductionsByProject = (projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/list-by-project',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 克隆扣分项
|
||||||
|
* @param {Object} data - 克隆数据
|
||||||
|
* @param {Number} data.sourceProjectId - 源项目ID
|
||||||
|
* @param {Number} data.targetProjectId - 目标项目ID
|
||||||
|
*/
|
||||||
|
export const cloneDeductions = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/clone',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新扣分项排序
|
||||||
|
* @param {Array} sortData - 排序数据数组 [{id, sortOrder}, ...]
|
||||||
|
*/
|
||||||
|
export const updateDeductionOrder = (sortData) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/update-order',
|
||||||
|
method: 'post',
|
||||||
|
data: sortData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出扣分项模板
|
||||||
|
*/
|
||||||
|
export const exportDeductionTemplate = () => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/export-template',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出扣分项列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const exportDeductions = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
118
src/api/martial/deductionItem.js
Normal file
118
src/api/martial/deductionItem.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事扣分项管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扣分项分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.projectId - 项目ID
|
||||||
|
* @param {String} params.itemName - 扣分项名称(可选)
|
||||||
|
*/
|
||||||
|
export const getDeductionList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扣分项详情
|
||||||
|
* @param {Number} id - 扣分项ID
|
||||||
|
*/
|
||||||
|
export const getDeductionDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增扣分项
|
||||||
|
* @param {Object} data - 扣分项数据
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {String} data.itemName - 扣分项名称
|
||||||
|
* @param {Number} data.deductionPoints - 扣分值
|
||||||
|
* @param {String} data.description - 描述
|
||||||
|
* @param {Number} data.sortOrder - 排序序号
|
||||||
|
*/
|
||||||
|
export const addDeduction = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改扣分项
|
||||||
|
* @param {Object} data - 扣分项数据
|
||||||
|
*/
|
||||||
|
export const updateDeduction = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除扣分项
|
||||||
|
* @param {String} ids - 扣分项ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeDeduction = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目的扣分项列表(不分页)
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
*/
|
||||||
|
export const getDeductionsByProject = (projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/list-by-project',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入扣分项
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
*/
|
||||||
|
export const importDeductions = (projectId, file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('projectId', projectId)
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出扣分项模板
|
||||||
|
*/
|
||||||
|
export const exportDeductionTemplate = () => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/deductionItem/export-template',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
262
src/api/martial/exception.js
Normal file
262
src/api/martial/exception.js
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事异常事件管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常事件分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.exceptionType - 异常类型(可选)
|
||||||
|
* @param {Number} params.severity - 严重程度(可选)
|
||||||
|
* @param {Number} params.status - 处理状态(可选)
|
||||||
|
*/
|
||||||
|
export const getExceptionList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常事件详情
|
||||||
|
* @param {Number} id - 异常事件ID
|
||||||
|
*/
|
||||||
|
export const getExceptionDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报异常事件
|
||||||
|
* @param {Object} data - 异常事件数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} data.venueId - 场地ID(可选)
|
||||||
|
* @param {Number} data.exceptionType - 异常类型(1设备故障2人员问题3时间冲突4评分异常5安全事故6其他)
|
||||||
|
* @param {Number} data.severity - 严重程度(1轻微2一般3严重4紧急)
|
||||||
|
* @param {String} data.title - 标题
|
||||||
|
* @param {String} data.description - 描述
|
||||||
|
* @param {String} data.reporterName - 上报人姓名
|
||||||
|
* @param {String} data.reporterPhone - 上报人电话
|
||||||
|
* @param {Array} data.images - 图片URL数组(可选)
|
||||||
|
*/
|
||||||
|
export const reportException = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/report',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改异常事件
|
||||||
|
* @param {Object} data - 异常事件数据
|
||||||
|
*/
|
||||||
|
export const updateException = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除异常事件
|
||||||
|
* @param {String} ids - 异常事件ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeException = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理异常事件
|
||||||
|
* @param {Object} data - 处理数据
|
||||||
|
* @param {Number} data.id - 异常事件ID
|
||||||
|
* @param {String} data.handlerName - 处理人姓名
|
||||||
|
* @param {String} data.handleResult - 处理结果
|
||||||
|
* @param {String} data.handleNote - 处理备注
|
||||||
|
*/
|
||||||
|
export const handleException = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/handle',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭异常事件
|
||||||
|
* @param {Number} id - 异常事件ID
|
||||||
|
* @param {String} closeReason - 关闭原因
|
||||||
|
*/
|
||||||
|
export const closeException = (id, closeReason) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/close',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { closeReason }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新打开异常事件
|
||||||
|
* @param {Number} id - 异常事件ID
|
||||||
|
* @param {String} reopenReason - 重开原因
|
||||||
|
*/
|
||||||
|
export const reopenException = (id, reopenReason) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/reopen',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { reopenReason }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配异常事件
|
||||||
|
* @param {Object} data - 分配数据
|
||||||
|
* @param {Number} data.id - 异常事件ID
|
||||||
|
* @param {String} data.assigneeName - 分配人姓名
|
||||||
|
* @param {String} data.assigneePhone - 分配人电话
|
||||||
|
*/
|
||||||
|
export const assignException = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取待处理异常事件列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} severity - 严重程度(可选)
|
||||||
|
*/
|
||||||
|
export const getPendingExceptions = (competitionId, severity) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/pending',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, severity }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的异常事件列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} assigneeName - 分配人姓名
|
||||||
|
*/
|
||||||
|
export const getMyExceptions = (competitionId, assigneeName) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/my-exceptions',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, assigneeName }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常事件统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getExceptionStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按类型统计异常事件
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getExceptionByType = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/statistics-by-type',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按严重程度统计异常事件
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getExceptionBySeverity = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/statistics-by-severity',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传异常事件图片
|
||||||
|
* @param {File} file - 图片文件
|
||||||
|
*/
|
||||||
|
export const uploadExceptionImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/upload-image',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加异常处理记录
|
||||||
|
* @param {Object} data - 处理记录数据
|
||||||
|
* @param {Number} data.exceptionId - 异常事件ID
|
||||||
|
* @param {String} data.operatorName - 操作人姓名
|
||||||
|
* @param {String} data.operationType - 操作类型
|
||||||
|
* @param {String} data.operationNote - 操作备注
|
||||||
|
*/
|
||||||
|
export const addExceptionLog = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/add-log',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常处理记录
|
||||||
|
* @param {Number} exceptionId - 异常事件ID
|
||||||
|
*/
|
||||||
|
export const getExceptionLogs = (exceptionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/logs',
|
||||||
|
method: 'get',
|
||||||
|
params: { exceptionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出异常事件报表
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportExceptions = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/exception/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
287
src/api/martial/export.js
Normal file
287
src/api/martial/export.js
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事导出打印接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出成绩单(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {String} params.category - 分组类别(可选)
|
||||||
|
* @param {Boolean} params.includeRanking - 是否包含排名(默认true)
|
||||||
|
* @param {Boolean} params.includeMedal - 是否包含奖牌(默认true)
|
||||||
|
*/
|
||||||
|
export const exportResults = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/results',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出运动员名单(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {String} params.category - 分组类别(可选)
|
||||||
|
* @param {String} params.teamName - 团队名称(可选)
|
||||||
|
* @param {Number} params.status - 报名状态(可选)
|
||||||
|
*/
|
||||||
|
export const exportAthletes = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/athletes',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出赛程表(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.venueId - 场地ID(可选)
|
||||||
|
* @param {String} params.scheduleDate - 赛程日期(可选)
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
*/
|
||||||
|
export const exportSchedule = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/schedule',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出评分表(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} params.judgeId - 裁判ID(可选)
|
||||||
|
* @param {String} params.scoreDate - 评分日期(可选)
|
||||||
|
*/
|
||||||
|
export const exportScores = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/scores',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出裁判名单(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.judgeLevel - 裁判等级(可选)
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
*/
|
||||||
|
export const exportReferees = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/referees',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出奖牌榜(Excel)
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.groupBy - 分组方式(team/region)
|
||||||
|
*/
|
||||||
|
export const exportMedalRanking = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/medal-ranking',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 证书生成接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成单个证书(HTML/PDF)
|
||||||
|
* @param {Number} resultId - 成绩ID
|
||||||
|
* @param {String} format - 格式(html/pdf)默认pdf
|
||||||
|
*/
|
||||||
|
export const generateCertificate = (resultId, format = 'pdf') => {
|
||||||
|
return request({
|
||||||
|
url: `/api/blade-martial/export/certificate/${resultId}`,
|
||||||
|
method: 'get',
|
||||||
|
params: { format },
|
||||||
|
responseType: format === 'pdf' ? 'blob' : 'json'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量生成证书
|
||||||
|
* @param {Object} data - 生成参数
|
||||||
|
* @param {Array} data.resultIds - 成绩ID数组
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选,为空时使用resultIds)
|
||||||
|
* @param {Number} data.competitionId - 赛事ID(可选,为空时使用resultIds)
|
||||||
|
* @param {String} data.format - 格式(html/pdf)默认pdf
|
||||||
|
* @param {Boolean} data.mergeFiles - 是否合并为一个文件(默认false)
|
||||||
|
*/
|
||||||
|
export const batchCertificates = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/certificates/batch',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取证书数据(用于预览)
|
||||||
|
* @param {Number} resultId - 成绩ID
|
||||||
|
*/
|
||||||
|
export const getCertificateData = (resultId) => {
|
||||||
|
return request({
|
||||||
|
url: `/api/blade-martial/export/certificate/data/${resultId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取证书模板列表
|
||||||
|
* @param {Number} competitionId - 赛事ID(可选)
|
||||||
|
*/
|
||||||
|
export const getCertificateTemplates = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/certificate/templates',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传证书模板
|
||||||
|
* @param {File} file - 模板文件
|
||||||
|
* @param {String} templateName - 模板名称
|
||||||
|
* @param {Number} competitionId - 赛事ID(可选)
|
||||||
|
*/
|
||||||
|
export const uploadCertificateTemplate = (file, templateName, competitionId) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('templateName', templateName)
|
||||||
|
if (competitionId) {
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
}
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/certificate/template/upload',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 报表导出接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出赛事统计报表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const exportCompetitionReport = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/competition-report',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出项目统计报表
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
*/
|
||||||
|
export const exportProjectReport = (projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/project-report',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出裁判工作量统计
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.startDate - 开始日期(可选)
|
||||||
|
* @param {String} params.endDate - 结束日期(可选)
|
||||||
|
*/
|
||||||
|
export const exportJudgeWorkload = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/judge-workload',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出报名统计报表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const exportRegistrationReport = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/registration-report',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 打印接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印签到表
|
||||||
|
* @param {Object} params - 打印参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {String} params.scheduleDate - 赛程日期(可选)
|
||||||
|
*/
|
||||||
|
export const printSignInSheet = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/print/sign-in',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印秩序册
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const printProgramBook = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/print/program-book',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印成绩公告
|
||||||
|
* @param {Object} params - 打印参数
|
||||||
|
* @param {Number} params.projectId - 项目ID
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const printResultAnnouncement = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/export/print/result-announcement',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
221
src/api/martial/info.js
Normal file
221
src/api/martial/info.js
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== <20>o<EFBFBD><03><06><> ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD>o<EFBFBD>h
|
||||||
|
* @param {Number} current - SMuؤ1
|
||||||
|
* @param {Number} size - <20>up<75>ؤ10
|
||||||
|
* @param {Object} params - <20><><EFBFBD>p
|
||||||
|
* @param {Number} params.competitionId - <20>[ID
|
||||||
|
* @param {String} params.infoType - <20>o{<7B>announcementlJ/notice<1A>/news<77><73>/rules<65>
|
||||||
|
* @param {String} params.title - <07>!<21>"<0C>
|
||||||
|
* @param {Number} params.publishStatus - <20><03>0<><30>/1<><31>/2<><0B><0C>
|
||||||
|
* @param {Boolean} params.isTop - /&nv<08>
|
||||||
|
* @param {Boolean} params.isImportant - /&́<08>
|
||||||
|
*/
|
||||||
|
export const getInfoPublishList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD>o<EFBFBD><03><>
|
||||||
|
* @param {Number} id - <20>oID
|
||||||
|
*/
|
||||||
|
export const getInfoPublishDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><03>o
|
||||||
|
* @param {Object} data - <20>opn
|
||||||
|
* @param {Number} data.competitionId - <20>[ID
|
||||||
|
* @param {String} data.infoType - <20>o{<7B>
|
||||||
|
* @param {String} data.title - <07>
|
||||||
|
* @param {String} data.coverImage - b<>URL<08>
|
||||||
|
* @param {String} data.summary - X<>
|
||||||
|
* @param {String} data.content - <20><>̇,
|
||||||
|
* @param {Array} data.attachments - D<>URLp<4C><08>
|
||||||
|
* @param {String} data.publishTime - <20><03><><08>
|
||||||
|
k<19>s<EFBFBD>
|
||||||
|
* @param {Boolean} data.isTop - /&nv
|
||||||
|
* @param {Boolean} data.isImportant - /&́
|
||||||
|
*/
|
||||||
|
export const publishInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD>o
|
||||||
|
* @param {Object} data - <20>opn
|
||||||
|
*/
|
||||||
|
export const updateInfoPublish = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* d<>o
|
||||||
|
* @param {String} ids - <20>oID*(<17><06>
|
||||||
|
*/
|
||||||
|
export const removeInfoPublish = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bnv<6E>
|
||||||
|
* @param {Number} id - <20>oID
|
||||||
|
* @param {Boolean} isTop - /&nv
|
||||||
|
*/
|
||||||
|
export const toggleTop = (id, isTop) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/toggle-top',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, isTop }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b́<62>
|
||||||
|
* @param {Number} id - <20>oID
|
||||||
|
* @param {Boolean} isImportant - /&́
|
||||||
|
*/
|
||||||
|
export const toggleImportant = (id, isImportant) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/toggle-important',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, isImportant }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
b<>
|
||||||
|
* @param {File} file - <20>G<EFBFBD><47>
|
||||||
|
*/
|
||||||
|
export const uploadCoverImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/upload-cover',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
D<>
|
||||||
|
* @param {File} file - D<><44><EFBFBD>
|
||||||
|
* @param {Function} onProgress -
|
||||||
|
ۦ<>
|
||||||
|
*/
|
||||||
|
export const uploadAttachment = (file, onProgress) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/upload-attachment',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
onUploadProgress: onProgress
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y<>
|
||||||
|
D<>
|
||||||
|
* @param {Array} files - D<><44><EFBFBD>p<EFBFBD>
|
||||||
|
*/
|
||||||
|
export const batchUploadAttachments = (files) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
files.forEach(file => {
|
||||||
|
formData.append('files', file)
|
||||||
|
})
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/batch-upload-attachments',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
<EFBFBD>/<0B><>o
|
||||||
|
* @param {Number} id - <20>oID
|
||||||
|
* @param {Number} publishStatus - <20><03>1<><31>/2<><0B>
|
||||||
|
*/
|
||||||
|
export const updatePublishStatus = (id, publishStatus) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/update-status',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, publishStatus }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD>oߡ
|
||||||
|
* @param {Number} competitionId - <20>[ID
|
||||||
|
*/
|
||||||
|
export const getInfoStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><05><>
|
||||||
|
* @param {Number} id - <20>oID
|
||||||
|
*/
|
||||||
|
export const incrementViewCount = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/increment-view',
|
||||||
|
method: 'post',
|
||||||
|
data: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD>o<EFBFBD><03>U
|
||||||
|
* @param {Object} params - <20><><EFBFBD>p
|
||||||
|
*/
|
||||||
|
export const exportInfoPublish = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/info-publish/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
259
src/api/martial/infoPublish.js
Normal file
259
src/api/martial/infoPublish.js
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事信息发布管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息发布分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.infoType - 信息类型(可选)
|
||||||
|
* @param {Number} params.publishStatus - 发布状态(可选)
|
||||||
|
* @param {String} params.title - 标题(可选)
|
||||||
|
*/
|
||||||
|
export const getInfoPublishList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取信息发布详情
|
||||||
|
* @param {Number} id - 信息ID
|
||||||
|
*/
|
||||||
|
export const getInfoPublishDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布信息
|
||||||
|
* @param {Object} data - 信息数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.infoType - 信息类型(1公告2通知3新闻4规则5其他)
|
||||||
|
* @param {String} data.title - 标题
|
||||||
|
* @param {String} data.content - 内容
|
||||||
|
* @param {String} data.summary - 摘要(可选)
|
||||||
|
* @param {String} data.coverImage - 封面图片(可选)
|
||||||
|
* @param {Array} data.attachments - 附件URL数组(可选)
|
||||||
|
* @param {Number} data.isTop - 是否置顶(0否1是)
|
||||||
|
* @param {Number} data.isImportant - 是否重要(0否1是)
|
||||||
|
* @param {String} data.publishTime - 发布时间(可选,为空则立即发布)
|
||||||
|
*/
|
||||||
|
export const publishInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改信息
|
||||||
|
* @param {Object} data - 信息数据
|
||||||
|
*/
|
||||||
|
export const updateInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除信息
|
||||||
|
* @param {String} ids - 信息ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeInfo = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回信息
|
||||||
|
* @param {Number} id - 信息ID
|
||||||
|
*/
|
||||||
|
export const withdrawInfo = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/withdraw',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 置顶/取消置顶信息
|
||||||
|
* @param {Number} id - 信息ID
|
||||||
|
* @param {Number} isTop - 是否置顶(0否1是)
|
||||||
|
*/
|
||||||
|
export const toggleTop = (id, isTop) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/toggle-top',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, isTop }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置/取消重要信息
|
||||||
|
* @param {Number} id - 信息ID
|
||||||
|
* @param {Number} isImportant - 是否重要(0否1是)
|
||||||
|
*/
|
||||||
|
export const toggleImportant = (id, isImportant) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/toggle-important',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, isImportant }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已发布信息列表(不分页)
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} infoType - 信息类型(可选)
|
||||||
|
* @param {Number} limit - 获取数量(默认10)
|
||||||
|
*/
|
||||||
|
export const getPublishedInfoList = (competitionId, infoType, limit = 10) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/published',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, infoType, limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取置顶信息列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getTopInfoList = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/top-info',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取重要信息列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getImportantInfoList = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/important-info',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索信息
|
||||||
|
* @param {Object} params - 搜索参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.keyword - 关键词
|
||||||
|
* @param {Number} params.infoType - 信息类型(可选)
|
||||||
|
* @param {String} params.startTime - 开始时间(可选)
|
||||||
|
* @param {String} params.endTime - 结束时间(可选)
|
||||||
|
*/
|
||||||
|
export const searchInfo = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/search',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加阅读量
|
||||||
|
* @param {Number} id - 信息ID
|
||||||
|
*/
|
||||||
|
export const increaseViewCount = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/view',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传封面图片
|
||||||
|
* @param {File} file - 图片文件
|
||||||
|
*/
|
||||||
|
export const uploadCoverImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/upload-cover',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传附件
|
||||||
|
* @param {File} file - 附件文件
|
||||||
|
*/
|
||||||
|
export const uploadAttachment = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/upload-attachment',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载附件
|
||||||
|
* @param {String} attachmentUrl - 附件URL
|
||||||
|
*/
|
||||||
|
export const downloadAttachment = (attachmentUrl) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/download',
|
||||||
|
method: 'get',
|
||||||
|
params: { attachmentUrl },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取信息统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getInfoStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量发布信息
|
||||||
|
* @param {Array} data - 信息数据数组
|
||||||
|
*/
|
||||||
|
export const batchPublishInfo = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/infoPublish/batch-publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
213
src/api/martial/judgeInvite.js
Normal file
213
src/api/martial/judgeInvite.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事裁判邀请管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 裁判邀请分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.judgeName - 裁判姓名(可选)
|
||||||
|
* @param {String} params.judgeLevel - 裁判等级(可选)
|
||||||
|
* @param {Number} params.inviteStatus - 邀请状态(可选)
|
||||||
|
*/
|
||||||
|
export const getJudgeInviteList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取裁判邀请详情
|
||||||
|
* @param {Number} id - 邀请ID
|
||||||
|
*/
|
||||||
|
export const getJudgeInviteDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送邀请
|
||||||
|
* @param {Object} data - 邀请数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.judgeName - 裁判姓名
|
||||||
|
* @param {String} data.judgeLevel - 裁判等级
|
||||||
|
* @param {String} data.contactPhone - 联系电话
|
||||||
|
* @param {String} data.contactEmail - 联系邮箱
|
||||||
|
* @param {String} data.inviteMessage - 邀请信息(可选)
|
||||||
|
*/
|
||||||
|
export const sendInvite = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/send',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改邀请
|
||||||
|
* @param {Object} data - 邀请数据
|
||||||
|
*/
|
||||||
|
export const updateInvite = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除邀请
|
||||||
|
* @param {String} ids - 邀请ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeInvite = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量发送邀请
|
||||||
|
* @param {Object} data - 批量邀请参数
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.judges - 裁判信息数组
|
||||||
|
* @param {String} data.inviteMessage - 邀请信息(可选)
|
||||||
|
*/
|
||||||
|
export const batchSendInvites = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/batch-send',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新发送邀请
|
||||||
|
* @param {Number} id - 邀请ID
|
||||||
|
*/
|
||||||
|
export const resendInvite = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/resend',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 裁判回复邀请
|
||||||
|
* @param {Object} data - 回复数据
|
||||||
|
* @param {Number} data.inviteId - 邀请ID
|
||||||
|
* @param {String} data.inviteToken - 邀请令牌
|
||||||
|
* @param {Number} data.replyStatus - 回复状态(1接受2拒绝)
|
||||||
|
* @param {String} data.replyNote - 回复备注
|
||||||
|
*/
|
||||||
|
export const replyInvite = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/reply',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消邀请
|
||||||
|
* @param {Number} id - 邀请ID
|
||||||
|
* @param {String} cancelReason - 取消原因
|
||||||
|
*/
|
||||||
|
export const cancelInvite = (id, cancelReason) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/cancel',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { cancelReason }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认邀请
|
||||||
|
* @param {Number} id - 邀请ID
|
||||||
|
*/
|
||||||
|
export const confirmInvite = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/confirm',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取邀请统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getInviteStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已接受邀请的裁判列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getAcceptedJudges = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/accepted-judges',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从裁判库导入
|
||||||
|
* @param {Object} data - 导入参数
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.judgeIds - 裁判ID数组(从裁判库选择)
|
||||||
|
*/
|
||||||
|
export const importFromJudgePool = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/import-from-pool',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出邀请名单
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportInvites = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送提醒消息
|
||||||
|
* @param {Number} id - 邀请ID
|
||||||
|
* @param {String} reminderMessage - 提醒消息
|
||||||
|
*/
|
||||||
|
export const sendReminder = (id, reminderMessage) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judgeInvite/send-reminder',
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
data: { reminderMessage }
|
||||||
|
})
|
||||||
|
}
|
||||||
248
src/api/martial/judgeProject.js
Normal file
248
src/api/martial/judgeProject.js
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事裁判-项目关联管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 裁判-项目关联分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.judgeId - 裁判ID(可选)
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {String} params.role - 角色(可选)
|
||||||
|
*/
|
||||||
|
export const getJudgeProjectList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取裁判-项目关联详情
|
||||||
|
* @param {Number} id - 关联ID
|
||||||
|
*/
|
||||||
|
export const getJudgeProjectDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配裁判到项目
|
||||||
|
* @param {Object} data - 分配数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.judgeId - 裁判ID
|
||||||
|
* @param {String} data.judgeName - 裁判姓名
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {String} data.projectName - 项目名称
|
||||||
|
* @param {String} data.role - 角色(主裁、副裁、执裁)
|
||||||
|
*/
|
||||||
|
export const assignJudge = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改分配
|
||||||
|
* @param {Object} data - 分配数据
|
||||||
|
*/
|
||||||
|
export const updateAssignment = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分配
|
||||||
|
* @param {String} ids - 关联ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeAssignment = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量分配裁判
|
||||||
|
* @param {Object} data - 批量分配参数
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.assignments - 分配信息数组
|
||||||
|
* @param {Number} data.assignments[].judgeId - 裁判ID
|
||||||
|
* @param {Number} data.assignments[].projectId - 项目ID
|
||||||
|
* @param {String} data.assignments[].role - 角色
|
||||||
|
*/
|
||||||
|
export const batchAssignJudges = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/batch-assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动分配裁判
|
||||||
|
* @param {Object} data - 自动分配参数
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选)
|
||||||
|
* @param {String} data.strategy - 分配策略(balance负载均衡/level按等级)
|
||||||
|
*/
|
||||||
|
export const autoAssignJudges = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/auto-assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目的裁判列表
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
*/
|
||||||
|
export const getJudgesByProject = (projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/judges-by-project',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取裁判的项目列表
|
||||||
|
* @param {Number} judgeId - 裁判ID
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getProjectsByJudge = (judgeId, competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/projects-by-judge',
|
||||||
|
method: 'get',
|
||||||
|
params: { judgeId, competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取裁判工作量统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getJudgeWorkload = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/workload',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查裁判冲突
|
||||||
|
* @param {Object} data - 检查参数
|
||||||
|
* @param {Number} data.judgeId - 裁判ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const checkJudgeConflict = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/check-conflict',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交换两个裁判的项目分配
|
||||||
|
* @param {Object} data - 交换参数
|
||||||
|
* @param {Number} data.assignmentId1 - 分配1的ID
|
||||||
|
* @param {Number} data.assignmentId2 - 分配2的ID
|
||||||
|
*/
|
||||||
|
export const swapJudgeAssignment = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/swap',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制分配到其他项目
|
||||||
|
* @param {Object} data - 复制参数
|
||||||
|
* @param {Number} data.sourceProjectId - 源项目ID
|
||||||
|
* @param {Number} data.targetProjectId - 目标项目ID
|
||||||
|
*/
|
||||||
|
export const copyAssignment = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/copy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可分配的裁判列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} projectId - 项目ID(可选)
|
||||||
|
*/
|
||||||
|
export const getAvailableJudges = (competitionId, projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/available-judges',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, projectId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取未分配裁判的项目列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getUnassignedProjects = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/unassigned-projects',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出裁判分配表
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportAssignments = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入裁判分配
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
*/
|
||||||
|
export const importAssignments = (competitionId, file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/judge-project/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
203
src/api/martial/live.js
Normal file
203
src/api/martial/live.js
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== f/[<5B><><EFBFBD><EFBFBD><06><> ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD><EFBFBD>u<><75>
|
||||||
|
* @param {Number} current - SMu,ؤ1
|
||||||
|
* @param {Number} size - <20>uap,ؤ10
|
||||||
|
* @param {Object} params - <20><><EFBFBD>p
|
||||||
|
* @param {Number} params.competitionId - [<5B>ID
|
||||||
|
* @param {String} params.updateType - <20><>{<7B>match[<5B>/result<10>/notice<1A>
|
||||||
|
* @param {Number} params.projectId - y<>ID<08>
|
||||||
|
* @param {Number} params.venueId - :0ID<08>
|
||||||
|
*/
|
||||||
|
export const getLiveUpdateList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
* @param {Number} id - <20><>ID
|
||||||
|
*/
|
||||||
|
export const getLiveUpdateDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><03><><EFBFBD><EFBFBD>
|
||||||
|
* @param {Object} data - <20><><EFBFBD><EFBFBD>pn
|
||||||
|
* @param {Number} data.competitionId - [<5B>ID
|
||||||
|
* @param {String} data.title - <07>
|
||||||
|
* @param {String} data.content - <20><>̇,
|
||||||
|
* @param {String} data.updateType - <20><>{<7B>
|
||||||
|
* @param {Number} data.projectId - y<>ID<08>
|
||||||
|
* @param {Number} data.venueId - :0ID<08>
|
||||||
|
* @param {Array} data.images - <20>GURLp<4C>
|
||||||
|
* @param {Array} data.videos - ƑURLp<4C>
|
||||||
|
* @param {Boolean} data.isTop - /&nv
|
||||||
|
*/
|
||||||
|
export const publishLiveUpdate = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20>9<EFBFBD><39><EFBFBD><EFBFBD>
|
||||||
|
* @param {Object} data - <20><><EFBFBD><EFBFBD>pn
|
||||||
|
*/
|
||||||
|
export const updateLiveUpdate = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* d<><64><EFBFBD><EFBFBD>
|
||||||
|
* @param {String} ids - <20><>ID,*(<17><06>
|
||||||
|
*/
|
||||||
|
export const removeLiveUpdate = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bnv<6E>
|
||||||
|
* @param {Number} id - <20><>ID
|
||||||
|
* @param {Boolean} isTop - /&nv
|
||||||
|
*/
|
||||||
|
export const toggleTop = (id, isTop) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/toggle-top',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, isTop }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
<20><><EFBFBD>G
|
||||||
|
* @param {File} file - <20>G<EFBFBD><47>
|
||||||
|
*/
|
||||||
|
export const uploadLiveImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/upload-image',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
<20><>Ƒ
|
||||||
|
* @param {File} file - Ƒ<><C691>
|
||||||
|
* @param {Function} onProgress -
|
||||||
|
ۦ<>
|
||||||
|
*/
|
||||||
|
export const uploadLiveVideo = (file, onProgress) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/upload-video',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
onUploadProgress: onProgress
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y<>
|
||||||
|
<20>G
|
||||||
|
* @param {Array} files - <20>G<EFBFBD><47>p<EFBFBD>
|
||||||
|
*/
|
||||||
|
export const batchUploadImages = (files) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
files.forEach(file => {
|
||||||
|
formData.append('files', file)
|
||||||
|
})
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/batch-upload-images',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD><EFBFBD>ߡpn
|
||||||
|
* @param {Number} competitionId - [<5B>ID
|
||||||
|
*/
|
||||||
|
export const getLiveStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20>^<5E><><EFBFBD><EFBFBD>
|
||||||
|
* @param {Number} id - <20><>ID
|
||||||
|
*/
|
||||||
|
export const likeLiveUpdate = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/like',
|
||||||
|
method: 'post',
|
||||||
|
data: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ֈ<>^
|
||||||
|
* @param {Number} id - <20><>ID
|
||||||
|
*/
|
||||||
|
export const unlikeLiveUpdate = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/unlike',
|
||||||
|
method: 'post',
|
||||||
|
data: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
* @param {Number} competitionId - [<5B>ID
|
||||||
|
* @param {Number} limit - p<>P6
|
||||||
|
*/
|
||||||
|
export const getHotLiveUpdates = (competitionId, limit = 10) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/live-update/hot',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
221
src/api/martial/liveUpdate.js
Normal file
221
src/api/martial/liveUpdate.js
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事直播更新管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播更新分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} params.venueId - 场地ID(可选)
|
||||||
|
* @param {Number} params.updateType - 更新类型(可选)
|
||||||
|
*/
|
||||||
|
export const getLiveUpdateList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取直播更新详情
|
||||||
|
* @param {Number} id - 更新ID
|
||||||
|
*/
|
||||||
|
export const getLiveUpdateDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布直播更新
|
||||||
|
* @param {Object} data - 更新数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} data.venueId - 场地ID(可选)
|
||||||
|
* @param {Number} data.updateType - 更新类型(1赛况2成绩3通知4其他)
|
||||||
|
* @param {String} data.title - 标题
|
||||||
|
* @param {String} data.content - 内容
|
||||||
|
* @param {Array} data.images - 图片URL数组(可选)
|
||||||
|
* @param {String} data.videoUrl - 视频URL(可选)
|
||||||
|
* @param {Number} data.isTop - 是否置顶(0否1是)
|
||||||
|
*/
|
||||||
|
export const publishLiveUpdate = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改直播更新
|
||||||
|
* @param {Object} data - 更新数据
|
||||||
|
*/
|
||||||
|
export const updateLiveUpdate = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除直播更新
|
||||||
|
* @param {String} ids - 更新ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeLiveUpdate = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量发布直播更新
|
||||||
|
* @param {Array} data - 更新数据数组
|
||||||
|
*/
|
||||||
|
export const batchPublishLiveUpdates = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/batch-publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 置顶/取消置顶直播更新
|
||||||
|
* @param {Number} id - 更新ID
|
||||||
|
* @param {Number} isTop - 是否置顶(0否1是)
|
||||||
|
*/
|
||||||
|
export const toggleTop = (id, isTop) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/toggle-top',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, isTop }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最新直播更新列表(不分页)
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} limit - 获取数量(默认10)
|
||||||
|
*/
|
||||||
|
export const getLatestUpdates = (competitionId, limit = 10) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/latest',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取置顶直播更新列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getTopUpdates = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/top-updates',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目的直播更新
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
* @param {Number} limit - 获取数量(默认20)
|
||||||
|
*/
|
||||||
|
export const getProjectUpdates = (projectId, limit = 20) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/project-updates',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId, limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地的直播更新
|
||||||
|
* @param {Number} venueId - 场地ID
|
||||||
|
* @param {Number} limit - 获取数量(默认20)
|
||||||
|
*/
|
||||||
|
export const getVenueUpdates = (venueId, limit = 20) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/venue-updates',
|
||||||
|
method: 'get',
|
||||||
|
params: { venueId, limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索直播更新
|
||||||
|
* @param {Object} params - 搜索参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.keyword - 关键词
|
||||||
|
* @param {Number} params.updateType - 更新类型(可选)
|
||||||
|
* @param {String} params.startTime - 开始时间(可选)
|
||||||
|
* @param {String} params.endTime - 结束时间(可选)
|
||||||
|
*/
|
||||||
|
export const searchLiveUpdates = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/search',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传直播图片
|
||||||
|
* @param {File} file - 图片文件
|
||||||
|
*/
|
||||||
|
export const uploadLiveImage = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/upload-image',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传直播视频
|
||||||
|
* @param {File} file - 视频文件
|
||||||
|
*/
|
||||||
|
export const uploadLiveVideo = (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/upload-video',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取直播统计
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getLiveStatistics = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/liveUpdate/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
138
src/api/martial/project.js
Normal file
138
src/api/martial/project.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事项目管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {String} params.projectName - 项目名称(可选)
|
||||||
|
* @param {String} params.category - 分组类别(可选)
|
||||||
|
* @param {String} params.eventType - 项目类型(可选)
|
||||||
|
*/
|
||||||
|
export const getProjectList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目详情
|
||||||
|
* @param {Number} id - 项目ID
|
||||||
|
*/
|
||||||
|
export const getProjectDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增项目
|
||||||
|
* @param {Object} data - 项目数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.projectName - 项目名称
|
||||||
|
* @param {String} data.projectCode - 项目编码
|
||||||
|
* @param {String} data.category - 分组类别(男子、女子、团体)
|
||||||
|
* @param {String} data.eventType - 项目类型(套路、散打等)
|
||||||
|
* @param {Number} data.registrationFee - 报名费
|
||||||
|
* @param {String} data.registrationStartTime - 报名开始时间
|
||||||
|
* @param {String} data.registrationEndTime - 报名结束时间
|
||||||
|
* @param {Number} data.maxParticipants - 最大参赛人数
|
||||||
|
* @param {String} data.rules - 比赛规则
|
||||||
|
*/
|
||||||
|
export const addProject = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改项目
|
||||||
|
* @param {Object} data - 项目数据
|
||||||
|
*/
|
||||||
|
export const updateProject = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除项目
|
||||||
|
* @param {String} ids - 项目ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeProject = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛事的项目列表(不分页)
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getProjectsByCompetition = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/list-by-competition',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入项目
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
*/
|
||||||
|
export const importProjects = (competitionId, file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出项目模板
|
||||||
|
*/
|
||||||
|
export const exportProjectTemplate = () => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/export-template',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出项目列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const exportProjects = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
234
src/api/martial/result.js
Normal file
234
src/api/martial/result.js
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事成绩管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成绩分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} params.athleteId - 运动员ID(可选)
|
||||||
|
* @param {String} params.playerName - 运动员姓名(可选)
|
||||||
|
* @param {String} params.teamName - 团队名称(可选)
|
||||||
|
* @param {Number} params.isFinal - 是否最终成绩(可选)
|
||||||
|
*/
|
||||||
|
export const getResultList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取成绩详情
|
||||||
|
* @param {Number} id - 成绩ID
|
||||||
|
*/
|
||||||
|
export const getResultDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增成绩
|
||||||
|
* @param {Object} data - 成绩数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.venueId - 场地ID
|
||||||
|
* @param {String} data.playerName - 运动员姓名
|
||||||
|
* @param {String} data.teamName - 团队名称
|
||||||
|
* @param {Number} data.totalScore - 总分
|
||||||
|
* @param {Number} data.difficultyCoefficient - 难度系数
|
||||||
|
* @param {Number} data.finalScore - 最终成绩
|
||||||
|
*/
|
||||||
|
export const addResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改成绩
|
||||||
|
* @param {Object} data - 成绩数据
|
||||||
|
*/
|
||||||
|
export const updateResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除成绩
|
||||||
|
* @param {String} ids - 成绩ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeResult = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 成绩高级功能接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算成绩
|
||||||
|
* @param {Object} data - 计算参数
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Boolean} data.autoRanking - 是否自动排名(可选)
|
||||||
|
*/
|
||||||
|
export const calculateResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/calculate',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量计算成绩
|
||||||
|
* @param {Object} data - 计算参数
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.athleteIds - 运动员ID数组(可选,为空时计算所有)
|
||||||
|
*/
|
||||||
|
export const batchCalculateResults = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/batch-calculate',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动排名
|
||||||
|
* @param {Object} data - 排名参数
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.category - 分组类别(可选)
|
||||||
|
*/
|
||||||
|
export const autoRanking = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/ranking',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配奖牌
|
||||||
|
* @param {Object} data - 奖牌分配参数
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.goldCount - 金牌数量
|
||||||
|
* @param {Number} data.silverCount - 银牌数量
|
||||||
|
* @param {Number} data.bronzeCount - 铜牌数量
|
||||||
|
*/
|
||||||
|
export const allocateMedals = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/medals',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成绩复核
|
||||||
|
* @param {Object} data - 复核参数
|
||||||
|
* @param {Number} data.resultId - 成绩ID
|
||||||
|
* @param {String} data.reviewNote - 复核说明
|
||||||
|
* @param {Number} data.reviewStatus - 复核状态(1通过2驳回)
|
||||||
|
*/
|
||||||
|
export const reviewResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/review',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布成绩
|
||||||
|
* @param {Object} data - 发布参数
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.resultIds - 成绩ID数组(可选)
|
||||||
|
* @param {Boolean} data.publishAll - 是否发布全部(可选)
|
||||||
|
*/
|
||||||
|
export const publishResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/publish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销发布成绩
|
||||||
|
* @param {Object} data - 撤销参数
|
||||||
|
* @param {Number} data.projectId - 项目ID(可选)
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Array} data.resultIds - 成绩ID数组(可选)
|
||||||
|
*/
|
||||||
|
export const unpublishResult = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/unpublish',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目成绩统计
|
||||||
|
* @param {Number} projectId - 项目ID
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getResultStatistics = (projectId, competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: { projectId, competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取奖牌榜
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {String} groupBy - 分组方式(team团队/region地区)
|
||||||
|
*/
|
||||||
|
export const getMedalRanking = (competitionId, groupBy = 'team') => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/medal-ranking',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, groupBy }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出成绩单
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const exportResults = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/result/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
239
src/api/martial/scheduleAthlete.js
Normal file
239
src/api/martial/scheduleAthlete.js
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事赛程选手关联管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赛程选手关联分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.scheduleId - 赛程ID
|
||||||
|
* @param {Number} params.schedulePlanId - 赛程计划ID(可选)
|
||||||
|
* @param {Number} params.athleteId - 运动员ID(可选)
|
||||||
|
* @param {Number} params.status - 状态(可选)
|
||||||
|
*/
|
||||||
|
export const getScheduleAthleteList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛程选手关联详情
|
||||||
|
* @param {Number} id - 关联ID
|
||||||
|
*/
|
||||||
|
export const getScheduleAthleteDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增赛程选手关联
|
||||||
|
* @param {Object} data - 关联数据
|
||||||
|
* @param {Number} data.scheduleId - 赛程ID
|
||||||
|
* @param {Number} data.schedulePlanId - 赛程计划ID
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {String} data.athleteName - 运动员姓名
|
||||||
|
* @param {Number} data.competitionOrder - 出场顺序
|
||||||
|
* @param {Number} data.status - 状态(0待出场1进行中2已完成)
|
||||||
|
*/
|
||||||
|
export const addScheduleAthlete = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改赛程选手关联
|
||||||
|
* @param {Object} data - 关联数据
|
||||||
|
*/
|
||||||
|
export const updateScheduleAthlete = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除赛程选手关联
|
||||||
|
* @param {String} ids - 关联ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeScheduleAthlete = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加赛程选手
|
||||||
|
* @param {Object} data - 批量添加参数
|
||||||
|
* @param {Number} data.schedulePlanId - 赛程计划ID
|
||||||
|
* @param {Array} data.athleteIds - 运动员ID数组
|
||||||
|
* @param {Boolean} data.autoOrder - 是否自动排序(默认true)
|
||||||
|
*/
|
||||||
|
export const batchAddScheduleAthletes = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/batch-save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动分配选手到赛程
|
||||||
|
* @param {Object} data - 分配参数
|
||||||
|
* @param {Number} data.scheduleId - 赛程ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {String} data.allocateStrategy - 分配策略(random随机/order顺序)
|
||||||
|
*/
|
||||||
|
export const autoAllocateAthletes = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/auto-allocate',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整选手出场顺序
|
||||||
|
* @param {Object} data - 调整参数
|
||||||
|
* @param {Number} data.id - 关联ID
|
||||||
|
* @param {Number} data.targetOrder - 目标顺序
|
||||||
|
*/
|
||||||
|
export const adjustAthleteOrder = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/adjust-order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交换两个选手的出场顺序
|
||||||
|
* @param {Object} data - 交换参数
|
||||||
|
* @param {Number} data.athleteId1 - 选手1的关联ID
|
||||||
|
* @param {Number} data.athleteId2 - 选手2的关联ID
|
||||||
|
*/
|
||||||
|
export const swapAthleteOrder = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/swap-order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机排序选手出场顺序
|
||||||
|
* @param {Number} schedulePlanId - 赛程计划ID
|
||||||
|
*/
|
||||||
|
export const randomizeAthleteOrder = (schedulePlanId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/randomize',
|
||||||
|
method: 'post',
|
||||||
|
params: { schedulePlanId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新选手状态
|
||||||
|
* @param {Object} data - 状态更新参数
|
||||||
|
* @param {Number} data.id - 关联ID
|
||||||
|
* @param {Number} data.status - 状态(0待出场1进行中2已完成)
|
||||||
|
*/
|
||||||
|
export const updateAthleteStatus = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/update-status',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛程计划的选手列表(不分页)
|
||||||
|
* @param {Number} schedulePlanId - 赛程计划ID
|
||||||
|
*/
|
||||||
|
export const getAthletesBySchedulePlan = (schedulePlanId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/list-by-plan',
|
||||||
|
method: 'get',
|
||||||
|
params: { schedulePlanId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取运动员的赛程安排
|
||||||
|
* @param {Number} athleteId - 运动员ID
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getAthleteSchedules = (athleteId, competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/athlete-schedules',
|
||||||
|
method: 'get',
|
||||||
|
params: { athleteId, competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查选手时间冲突
|
||||||
|
* @param {Object} data - 检查参数
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {Number} data.schedulePlanId - 赛程计划ID
|
||||||
|
*/
|
||||||
|
export const checkAthleteConflict = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/check-conflict',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到选手
|
||||||
|
* @param {Number} id - 关联ID
|
||||||
|
*/
|
||||||
|
export const checkInAthlete = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/check-in',
|
||||||
|
method: 'post',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量签到选手
|
||||||
|
* @param {Array} ids - 关联ID数组
|
||||||
|
*/
|
||||||
|
export const batchCheckInAthletes = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/batch-check-in',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出赛程选手名单
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportScheduleAthletes = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/scheduleAthlete/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
196
src/api/martial/schedulePlan.js
Normal file
196
src/api/martial/schedulePlan.js
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 武术赛事赛程计划管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赛程计划分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.scheduleId - 赛程ID
|
||||||
|
* @param {Number} params.venueId - 场地ID(可选)
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
* @param {String} params.planDate - 计划日期(可选)
|
||||||
|
*/
|
||||||
|
export const getSchedulePlanList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛程计划详情
|
||||||
|
* @param {Number} id - 赛程计划ID
|
||||||
|
*/
|
||||||
|
export const getSchedulePlanDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增赛程计划
|
||||||
|
* @param {Object} data - 赛程计划数据
|
||||||
|
* @param {Number} data.scheduleId - 赛程ID
|
||||||
|
* @param {String} data.planDate - 计划日期
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {Number} data.venueId - 场地ID
|
||||||
|
* @param {String} data.venueName - 场地名称
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {String} data.projectName - 项目名称
|
||||||
|
* @param {Number} data.planOrder - 计划顺序
|
||||||
|
*/
|
||||||
|
export const addSchedulePlan = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改赛程计划
|
||||||
|
* @param {Object} data - 赛程计划数据
|
||||||
|
*/
|
||||||
|
export const updateSchedulePlan = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除赛程计划
|
||||||
|
* @param {String} ids - 赛程计划ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeSchedulePlan = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加赛程计划
|
||||||
|
* @param {Array} data - 赛程计划数据数组
|
||||||
|
*/
|
||||||
|
export const batchAddSchedulePlans = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/batch-save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动编排赛程
|
||||||
|
* @param {Object} data - 编排参数
|
||||||
|
* @param {Number} data.scheduleId - 赛程ID
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.startDate - 开始日期
|
||||||
|
* @param {String} data.endDate - 结束日期
|
||||||
|
* @param {Array} data.venueIds - 场地ID数组
|
||||||
|
* @param {Array} data.projectIds - 项目ID数组
|
||||||
|
*/
|
||||||
|
export const autoArrangeSchedule = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/auto-arrange',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整赛程计划顺序
|
||||||
|
* @param {Object} data - 调整参数
|
||||||
|
* @param {Number} data.id - 赛程计划ID
|
||||||
|
* @param {Number} data.targetOrder - 目标顺序
|
||||||
|
*/
|
||||||
|
export const adjustScheduleOrder = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/adjust-order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某日期的赛程计划
|
||||||
|
* @param {Number} scheduleId - 赛程ID
|
||||||
|
* @param {String} planDate - 计划日期
|
||||||
|
*/
|
||||||
|
export const getSchedulePlansByDate = (scheduleId, planDate) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/list-by-date',
|
||||||
|
method: 'get',
|
||||||
|
params: { scheduleId, planDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取某场地的赛程计划
|
||||||
|
* @param {Number} venueId - 场地ID
|
||||||
|
* @param {String} planDate - 计划日期(可选)
|
||||||
|
*/
|
||||||
|
export const getSchedulePlansByVenue = (venueId, planDate) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/list-by-venue',
|
||||||
|
method: 'get',
|
||||||
|
params: { venueId, planDate }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查时间冲突
|
||||||
|
* @param {Object} data - 检查参数
|
||||||
|
* @param {Number} data.venueId - 场地ID
|
||||||
|
* @param {String} data.planDate - 计划日期
|
||||||
|
* @param {String} data.startTime - 开始时间
|
||||||
|
* @param {String} data.endTime - 结束时间
|
||||||
|
* @param {Number} data.excludeId - 排除的赛程计划ID(编辑时使用)
|
||||||
|
*/
|
||||||
|
export const checkTimeConflict = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/check-conflict',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制赛程计划到其他日期
|
||||||
|
* @param {Object} data - 复制参数
|
||||||
|
* @param {Number} data.sourcePlanId - 源赛程计划ID
|
||||||
|
* @param {String} data.targetDate - 目标日期
|
||||||
|
*/
|
||||||
|
export const copySchedulePlan = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/copy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出赛程计划
|
||||||
|
* @param {Object} params - 导出参数
|
||||||
|
*/
|
||||||
|
export const exportSchedulePlans = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/schedule-plan/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -82,3 +82,94 @@ export const getProjectList = (competitionId, venueId) => {
|
|||||||
params: { competitionId, venueId }
|
params: { competitionId, venueId }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 评分提交接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交评分
|
||||||
|
* @param {Object} data - 评分数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.scheduleId - 赛程ID
|
||||||
|
* @param {Number} data.venueId - 场地ID
|
||||||
|
* @param {Number} data.judgeId - 裁判ID
|
||||||
|
* @param {String} data.judgeName - 裁判姓名
|
||||||
|
* @param {Number} data.score - 评分
|
||||||
|
* @param {Number} data.originalScore - 原始分
|
||||||
|
* @param {Array} data.deductionItems - 扣分项ID数组
|
||||||
|
* @param {String} data.note - 备注
|
||||||
|
*/
|
||||||
|
export const submitScore = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/submit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除评分
|
||||||
|
* @param {String} ids - 评分ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeScore = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常评分列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {Number} params.competitionId - 赛事ID
|
||||||
|
* @param {Number} params.projectId - 项目ID(可选)
|
||||||
|
*/
|
||||||
|
export const getAnomalies = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/anomalies',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证评分
|
||||||
|
* @param {Object} data - 验证数据
|
||||||
|
* @param {Number} data.athleteId - 运动员ID
|
||||||
|
* @param {Number} data.projectId - 项目ID
|
||||||
|
* @param {Number} data.score - 评分
|
||||||
|
*/
|
||||||
|
export const validateScores = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/validate',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量提交评分
|
||||||
|
* @param {Array} data - 评分数据数组
|
||||||
|
*/
|
||||||
|
export const batchSubmitScores = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/batch-submit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取裁判待评分列表
|
||||||
|
* @param {Number} judgeId - 裁判ID
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getPendingScores = (judgeId, competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/pending',
|
||||||
|
method: 'get',
|
||||||
|
params: { judgeId, competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
186
src/components/martial/CompetitionSelector.vue
Normal file
186
src/components/martial/CompetitionSelector.vue
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<el-select
|
||||||
|
v-model="selectedValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:multiple="multiple"
|
||||||
|
:disabled="disabled"
|
||||||
|
:filterable="filterable"
|
||||||
|
:clearable="clearable"
|
||||||
|
:loading="loading"
|
||||||
|
@change="handleChange"
|
||||||
|
class="competition-selector"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in filteredList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
>
|
||||||
|
<span class="option-label">{{ item.name }}</span>
|
||||||
|
<span class="option-status">
|
||||||
|
<el-tag :type="getStatusType(item.status)" size="small">
|
||||||
|
{{ getStatusText(item.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CompetitionSelector',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: [Number, Array],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择赛事'
|
||||||
|
},
|
||||||
|
filterable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 过滤状态: 0-未开始, 1-报名中, 2-进行中, 3-已结束
|
||||||
|
status: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
competitionList: [],
|
||||||
|
selectedValue: this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredList() {
|
||||||
|
if (!this.status || this.status.length === 0) {
|
||||||
|
return this.competitionList
|
||||||
|
}
|
||||||
|
return this.competitionList.filter(item =>
|
||||||
|
this.status.includes(item.status)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(newVal) {
|
||||||
|
this.selectedValue = newVal
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.loadCompetitions()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadCompetitions() {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getCompetitionList({
|
||||||
|
current: 1,
|
||||||
|
size: 1000
|
||||||
|
})
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
this.competitionList = res.data.records.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
status: item.status,
|
||||||
|
disabled: false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载赛事列表失败:', error)
|
||||||
|
this.$message.error('加载赛事列表失败')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChange(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
|
||||||
|
// 获取选中的选项
|
||||||
|
let selectedOptions = null
|
||||||
|
if (this.multiple) {
|
||||||
|
selectedOptions = this.competitionList.filter(item =>
|
||||||
|
value.includes(item.id)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
selectedOptions = this.competitionList.find(item =>
|
||||||
|
item.id === value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('change', value, selectedOptions)
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusType(status) {
|
||||||
|
const typeMap = {
|
||||||
|
0: 'info',
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning',
|
||||||
|
3: 'info'
|
||||||
|
}
|
||||||
|
return typeMap[status] || 'info'
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusText(status) {
|
||||||
|
const textMap = {
|
||||||
|
0: '未开始',
|
||||||
|
1: '报名中',
|
||||||
|
2: '进行中',
|
||||||
|
3: '已结束'
|
||||||
|
}
|
||||||
|
return textMap[status] || '未知'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.competition-selector {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-select-dropdown__item) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-label {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-status {
|
||||||
|
margin-left: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
330
src/components/martial/MedalDialog.vue
Normal file
330
src/components/martial/MedalDialog.vue
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
title="分配奖牌"
|
||||||
|
width="800px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<!-- 赛事和项目信息 -->
|
||||||
|
<div class="medal-info">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="赛事名称">{{ competitionName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目名称">{{ projectName }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 奖牌分配表单 -->
|
||||||
|
<div class="medal-form">
|
||||||
|
<!-- 金牌 -->
|
||||||
|
<div class="medal-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="medal-icon gold">🥇</span>
|
||||||
|
<span class="medal-title">金牌</span>
|
||||||
|
</div>
|
||||||
|
<el-select
|
||||||
|
v-model="form.goldMedal"
|
||||||
|
placeholder="请选择金牌获得者"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
class="medal-select"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in athleteList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="`${item.name} (${item.number}) - ${item.score}分`"
|
||||||
|
:value="item.id"
|
||||||
|
:disabled="isDisabled(item.id, 'gold')"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 银牌 -->
|
||||||
|
<div class="medal-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="medal-icon silver">🥈</span>
|
||||||
|
<span class="medal-title">银牌</span>
|
||||||
|
</div>
|
||||||
|
<el-select
|
||||||
|
v-model="form.silverMedal"
|
||||||
|
placeholder="请选择银牌获得者"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
class="medal-select"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in athleteList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="`${item.name} (${item.number}) - ${item.score}分`"
|
||||||
|
:value="item.id"
|
||||||
|
:disabled="isDisabled(item.id, 'silver')"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 铜牌 -->
|
||||||
|
<div class="medal-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="medal-icon bronze">🥉</span>
|
||||||
|
<span class="medal-title">铜牌</span>
|
||||||
|
</div>
|
||||||
|
<el-select
|
||||||
|
v-model="form.bronzeMedals"
|
||||||
|
placeholder="请选择铜牌获得者(可多选)"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
class="medal-select"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in athleteList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="`${item.name} (${item.number}) - ${item.score}分`"
|
||||||
|
:value="item.id"
|
||||||
|
:disabled="isDisabled(item.id, 'bronze')"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 自动分配建议 -->
|
||||||
|
<el-alert
|
||||||
|
v-if="autoSuggestion"
|
||||||
|
:title="autoSuggestion"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
class="auto-suggestion"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<div>{{ autoSuggestion }}</div>
|
||||||
|
<el-button type="primary" size="small" @click="applyAutoSuggestion">
|
||||||
|
应用建议
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitting" @click="handleSubmit">
|
||||||
|
确定分配
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { allocateMedals } from '@/api/martial/result'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MedalDialog',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
competitionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
competitionName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
projectName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
athleteList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['update:modelValue', 'success'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: this.modelValue,
|
||||||
|
submitting: false,
|
||||||
|
form: {
|
||||||
|
goldMedal: null,
|
||||||
|
silverMedal: null,
|
||||||
|
bronzeMedals: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
autoSuggestion() {
|
||||||
|
if (this.athleteList.length < 3) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按分数排序
|
||||||
|
const sorted = [...this.athleteList].sort((a, b) => b.score - a.score)
|
||||||
|
|
||||||
|
return `建议: 金牌-${sorted[0].name}(${sorted[0].score}分), 银牌-${sorted[1].name}(${sorted[1].score}分), 铜牌-${sorted[2].name}(${sorted[2].score}分)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(newVal) {
|
||||||
|
this.visible = newVal
|
||||||
|
if (newVal) {
|
||||||
|
this.resetForm()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
visible(newVal) {
|
||||||
|
this.$emit('update:modelValue', newVal)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isDisabled(athleteId, medalType) {
|
||||||
|
// 同一选手不能获得多个奖牌
|
||||||
|
if (medalType === 'gold') {
|
||||||
|
return this.form.silverMedal === athleteId ||
|
||||||
|
this.form.bronzeMedals.includes(athleteId)
|
||||||
|
}
|
||||||
|
if (medalType === 'silver') {
|
||||||
|
return this.form.goldMedal === athleteId ||
|
||||||
|
this.form.bronzeMedals.includes(athleteId)
|
||||||
|
}
|
||||||
|
if (medalType === 'bronze') {
|
||||||
|
return this.form.goldMedal === athleteId ||
|
||||||
|
this.form.silverMedal === athleteId
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
applyAutoSuggestion() {
|
||||||
|
if (this.athleteList.length < 3) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...this.athleteList].sort((a, b) => b.score - a.score)
|
||||||
|
|
||||||
|
this.form.goldMedal = sorted[0].id
|
||||||
|
this.form.silverMedal = sorted[1].id
|
||||||
|
this.form.bronzeMedals = [sorted[2].id]
|
||||||
|
|
||||||
|
this.$message.success('已应用自动分配建议')
|
||||||
|
},
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
this.form = {
|
||||||
|
goldMedal: null,
|
||||||
|
silverMedal: null,
|
||||||
|
bronzeMedals: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClose() {
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleSubmit() {
|
||||||
|
// 验证
|
||||||
|
if (!this.form.goldMedal && !this.form.silverMedal && this.form.bronzeMedals.length === 0) {
|
||||||
|
this.$message.warning('请至少分配一个奖牌')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitting = true
|
||||||
|
try {
|
||||||
|
await allocateMedals({
|
||||||
|
competitionId: this.competitionId,
|
||||||
|
projectId: this.projectId,
|
||||||
|
goldMedal: this.form.goldMedal,
|
||||||
|
silverMedal: this.form.silverMedal,
|
||||||
|
bronzeMedals: this.form.bronzeMedals
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$message.success('奖牌分配成功')
|
||||||
|
this.$emit('success')
|
||||||
|
this.handleClose()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('奖牌分配失败:', error)
|
||||||
|
this.$message.error('奖牌分配失败')
|
||||||
|
} finally {
|
||||||
|
this.submitting = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.medal-info {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medal-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medal-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medal-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
&.gold {
|
||||||
|
filter: drop-shadow(0 0 5px rgba(212, 175, 55, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.silver {
|
||||||
|
filter: drop-shadow(0 0 5px rgba(192, 192, 192, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bronze {
|
||||||
|
filter: drop-shadow(0 0 5px rgba(205, 127, 50, 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.medal-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medal-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-suggestion {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
:deep(.el-alert__content) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
186
src/components/martial/ProjectSelector.vue
Normal file
186
src/components/martial/ProjectSelector.vue
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<el-select
|
||||||
|
v-model="selectedValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:multiple="multiple"
|
||||||
|
:disabled="disabled"
|
||||||
|
:filterable="filterable"
|
||||||
|
:clearable="clearable"
|
||||||
|
:loading="loading"
|
||||||
|
@change="handleChange"
|
||||||
|
class="project-selector"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in filteredList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
>
|
||||||
|
<span class="option-label">{{ item.name }}</span>
|
||||||
|
<span class="option-type" v-if="item.type">
|
||||||
|
<el-tag size="small">{{ item.type }}</el-tag>
|
||||||
|
</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getProjectList } from '@/api/martial/project'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectSelector',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: [Number, Array],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择项目'
|
||||||
|
},
|
||||||
|
filterable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 赛事ID,用于过滤项目
|
||||||
|
competitionId: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 项目类型过滤
|
||||||
|
projectType: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
projectList: [],
|
||||||
|
selectedValue: this.modelValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredList() {
|
||||||
|
let list = this.projectList
|
||||||
|
|
||||||
|
if (this.projectType) {
|
||||||
|
list = list.filter(item => item.type === this.projectType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(newVal) {
|
||||||
|
this.selectedValue = newVal
|
||||||
|
},
|
||||||
|
|
||||||
|
competitionId(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.loadProjects()
|
||||||
|
} else {
|
||||||
|
this.projectList = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.competitionId) {
|
||||||
|
this.loadProjects()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadProjects() {
|
||||||
|
if (!this.competitionId) {
|
||||||
|
this.projectList = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getProjectList({
|
||||||
|
competitionId: this.competitionId,
|
||||||
|
current: 1,
|
||||||
|
size: 1000
|
||||||
|
})
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
this.projectList = res.data.records.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
type: item.type,
|
||||||
|
disabled: false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
this.$message.error('加载项目列表失败')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChange(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
|
||||||
|
// 获取选中的选项
|
||||||
|
let selectedOptions = null
|
||||||
|
if (this.multiple) {
|
||||||
|
selectedOptions = this.projectList.filter(item =>
|
||||||
|
value.includes(item.id)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
selectedOptions = this.projectList.find(item =>
|
||||||
|
item.id === value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('change', value, selectedOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.project-selector {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-select-dropdown__item) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-label {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-type {
|
||||||
|
margin-left: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
268
src/components/martial/ScoreInput.vue
Normal file
268
src/components/martial/ScoreInput.vue
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<template>
|
||||||
|
<div class="score-input">
|
||||||
|
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
|
||||||
|
<!-- 基础分 -->
|
||||||
|
<el-form-item label="基础分" prop="baseScore">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.baseScore"
|
||||||
|
:min="0"
|
||||||
|
:max="10"
|
||||||
|
:step="0.1"
|
||||||
|
:precision="2"
|
||||||
|
controls-position="right"
|
||||||
|
@change="handleCalculate"
|
||||||
|
></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 扣分项 -->
|
||||||
|
<el-form-item label="扣分项">
|
||||||
|
<el-tag
|
||||||
|
v-for="(deduction, index) in form.deductions"
|
||||||
|
:key="index"
|
||||||
|
closable
|
||||||
|
@close="handleRemoveDeduction(index)"
|
||||||
|
class="deduction-tag"
|
||||||
|
>
|
||||||
|
{{ deduction.name }} (-{{ deduction.score }}分)
|
||||||
|
</el-tag>
|
||||||
|
<el-button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="showDeductionSelector"
|
||||||
|
>
|
||||||
|
添加扣分项
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 总扣分 -->
|
||||||
|
<el-form-item label="总扣分">
|
||||||
|
<el-tag type="danger">-{{ totalDeduction }}分</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 最终得分 -->
|
||||||
|
<el-form-item label="最终得分">
|
||||||
|
<el-tag :type="getScoreType(finalScore)" size="large" class="final-score-tag">
|
||||||
|
{{ finalScore }}分
|
||||||
|
</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 备注 -->
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 扣分项选择器弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="deductionDialogVisible"
|
||||||
|
title="选择扣分项"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<el-checkbox-group v-model="selectedDeductions">
|
||||||
|
<div class="deduction-list">
|
||||||
|
<el-checkbox
|
||||||
|
v-for="item in deductionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.id"
|
||||||
|
class="deduction-item"
|
||||||
|
>
|
||||||
|
<span class="deduction-name">{{ item.name }}</span>
|
||||||
|
<span class="deduction-score">-{{ item.score }}分</span>
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-checkbox-group>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="deductionDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleConfirmDeductions">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDeductionList } from '@/api/martial/deduction'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ScoreInput',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
baseScore: 10,
|
||||||
|
deductions: [],
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['update:modelValue', 'change'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
baseScore: 10,
|
||||||
|
deductions: [],
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
baseScore: [
|
||||||
|
{ required: true, message: '请输入基础分', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
deductionDialogVisible: false,
|
||||||
|
deductionList: [],
|
||||||
|
selectedDeductions: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
totalDeduction() {
|
||||||
|
return this.form.deductions.reduce((sum, item) => sum + item.score, 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
finalScore() {
|
||||||
|
const score = this.form.baseScore - this.totalDeduction
|
||||||
|
return Math.max(0, parseFloat(score.toFixed(2)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.form = { ...newVal }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
|
||||||
|
projectId: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.loadDeductions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadDeductions() {
|
||||||
|
try {
|
||||||
|
const res = await getDeductionList({
|
||||||
|
projectId: this.projectId
|
||||||
|
})
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
this.deductionList = res.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载扣分项失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCalculate() {
|
||||||
|
this.emitChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
showDeductionSelector() {
|
||||||
|
this.selectedDeductions = this.form.deductions.map(item => item.id)
|
||||||
|
this.deductionDialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
handleConfirmDeductions() {
|
||||||
|
this.form.deductions = this.deductionList.filter(item =>
|
||||||
|
this.selectedDeductions.includes(item.id)
|
||||||
|
).map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
score: item.score
|
||||||
|
}))
|
||||||
|
|
||||||
|
this.deductionDialogVisible = false
|
||||||
|
this.emitChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleRemoveDeduction(index) {
|
||||||
|
this.form.deductions.splice(index, 1)
|
||||||
|
this.emitChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
emitChange() {
|
||||||
|
const value = {
|
||||||
|
...this.form,
|
||||||
|
totalDeduction: this.totalDeduction,
|
||||||
|
finalScore: this.finalScore
|
||||||
|
}
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
this.$emit('change', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
getScoreType(score) {
|
||||||
|
if (score >= 9) return 'success'
|
||||||
|
if (score >= 8) return 'warning'
|
||||||
|
if (score >= 7) return 'info'
|
||||||
|
return 'danger'
|
||||||
|
},
|
||||||
|
|
||||||
|
async validate() {
|
||||||
|
return await this.$refs.formRef.validate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.score-input {
|
||||||
|
.deduction-tag {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.final-score-tag {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deduction-score {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -53,23 +53,13 @@ export default [
|
|||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/competition/list.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/competition/index.vue'),
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'competition/create',
|
|
||||||
name: '赛事详情',
|
|
||||||
meta: {
|
|
||||||
keepAlive: false,
|
|
||||||
menu: false,
|
|
||||||
},
|
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/competition/create.vue'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'registration/detail',
|
path: 'registration/detail',
|
||||||
name: '报名详情',
|
name: '报名详情',
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
menu: false,
|
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/registration/index.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/registration/index.vue'),
|
||||||
},
|
},
|
||||||
@@ -127,16 +117,106 @@ export default [
|
|||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/participant/list.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/participant/index.vue'),
|
||||||
|
},
|
||||||
|
// 新增页面 - P0核心页面
|
||||||
|
{
|
||||||
|
path: 'project/list',
|
||||||
|
name: '项目管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/project/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'participant/manage',
|
path: 'deduction/list',
|
||||||
name: '选手详情',
|
name: '扣分项管理',
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: true,
|
||||||
menu: false,
|
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/participant/manage.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/deduction/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'result/list',
|
||||||
|
name: '成绩管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/result/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'export/index',
|
||||||
|
name: '导出中心',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/export/index.vue'),
|
||||||
|
},
|
||||||
|
// P1重要页面
|
||||||
|
{
|
||||||
|
path: 'schedulePlan/list',
|
||||||
|
name: '赛程计划',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/schedulePlan/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'scheduleAthlete/list',
|
||||||
|
name: '选手关联',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/scheduleAthlete/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'judgeInvite/list',
|
||||||
|
name: '裁判邀请',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/judgeInvite/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'judgeProject/list',
|
||||||
|
name: '裁判分配',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/judgeProject/index.vue'),
|
||||||
|
},
|
||||||
|
// P2增强页面
|
||||||
|
{
|
||||||
|
path: 'live/list',
|
||||||
|
name: '直播管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/live/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'info/list',
|
||||||
|
name: '信息发布',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/info/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'exception/list',
|
||||||
|
name: '异常事件',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/exception/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'activity/list',
|
||||||
|
name: '活动日程',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/activity/index.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
1249
src/views/martial/activity/index.vue
Normal file
1249
src/views/martial/activity/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="linkUrl" label="跳转链接" min-width="180" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="linkUrl" label="跳转链接" min-width="180" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column prop="sortOrder" label="排序" width="80" align="center"></el-table-column>
|
<el-table-column prop="sortOrder" label="排序" width="80" align="center"></el-table-column>
|
||||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
<el-table-column prop="status" label="状态" width="150" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="scope.row.status"
|
v-model="scope.row.status"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -105,6 +105,309 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 新增/编辑/查看弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="900px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
top="5vh"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
:disabled="dialogMode === 'view'"
|
||||||
|
class="competition-form"
|
||||||
|
>
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
基本信息
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="赛事名称" prop="competitionName">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.competitionName"
|
||||||
|
placeholder="请输入赛事名称"
|
||||||
|
maxlength="100"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="主办单位" prop="organizer">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.organizer"
|
||||||
|
placeholder="请输入主办单位"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="举办地点" prop="location">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.location"
|
||||||
|
placeholder="请输入举办地点"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="比赛场馆" prop="venue">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.venue"
|
||||||
|
placeholder="请输入比赛场馆"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="报名时间" prop="registrationTime">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.registrationTime"
|
||||||
|
placeholder="例如:2025-01-01 至 2025-02-28"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="比赛时间" prop="competitionTime">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.competitionTime"
|
||||||
|
placeholder="例如:2025-03-15 至 2025-03-20"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="赛事状态" prop="status">
|
||||||
|
<el-select v-model="formData.status" placeholder="请选择状态" style="width: 100%">
|
||||||
|
<el-option label="未开始" :value="1" />
|
||||||
|
<el-option label="报名中" :value="2" />
|
||||||
|
<el-option label="进行中" :value="3" />
|
||||||
|
<el-option label="已结束" :value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 信息发布 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-reading"></i>
|
||||||
|
信息发布
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item label="赛事简介" prop="introduction">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入赛事简介"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系人" prop="contactPerson">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.contactPerson"
|
||||||
|
placeholder="请输入联系人姓名"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.contactPhone"
|
||||||
|
placeholder="请输入联系电话"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系邮箱" prop="contactEmail">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.contactEmail"
|
||||||
|
placeholder="请输入联系邮箱"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 赛事规程 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-document-checked"></i>
|
||||||
|
赛事规程
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item label="比赛规则" prop="rules">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.rules"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入比赛规则"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="参赛要求" prop="requirements">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.requirements"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入参赛要求"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="奖项设置" prop="awards">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.awards"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入奖项设置"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 活动日程 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-date"></i>
|
||||||
|
活动日程
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-button
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
size="small"
|
||||||
|
@click="handleAddSchedule"
|
||||||
|
>
|
||||||
|
添加日程
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="formData.schedule"
|
||||||
|
border
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
label="日期"
|
||||||
|
width="150"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
v-model="scope.row.date"
|
||||||
|
placeholder="2025-03-15"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.date }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="时间"
|
||||||
|
width="150"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
v-model="scope.row.time"
|
||||||
|
placeholder="09:00-12:00"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.time }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="赛事项目"
|
||||||
|
min-width="200"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
v-model="scope.row.event"
|
||||||
|
placeholder="请输入赛事项目"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.event }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="比赛场馆"
|
||||||
|
width="150"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
v-model="scope.row.venue"
|
||||||
|
placeholder="请输入场馆"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ scope.row.venue }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
label="操作"
|
||||||
|
width="80"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="handleDeleteSchedule(scope.$index)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
type="primary"
|
||||||
|
:loading="submitLoading"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
@click="switchToEdit"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -184,9 +487,51 @@ export default {
|
|||||||
{ date: '2025-05-21', time: '09:00-18:00', event: '初赛', venue: '比赛馆' },
|
{ date: '2025-05-21', time: '09:00-18:00', event: '初赛', venue: '比赛馆' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogMode: 'create', // create, edit, view
|
||||||
|
submitLoading: false,
|
||||||
|
currentCompetitionId: null,
|
||||||
|
formData: {
|
||||||
|
competitionName: '',
|
||||||
|
organizer: '',
|
||||||
|
location: '',
|
||||||
|
venue: '',
|
||||||
|
registrationTime: '',
|
||||||
|
competitionTime: '',
|
||||||
|
status: 1,
|
||||||
|
introduction: '',
|
||||||
|
contactPerson: '',
|
||||||
|
contactPhone: '',
|
||||||
|
contactEmail: '',
|
||||||
|
rules: '',
|
||||||
|
requirements: '',
|
||||||
|
awards: '',
|
||||||
|
schedule: []
|
||||||
|
},
|
||||||
|
formRules: {
|
||||||
|
competitionName: [
|
||||||
|
{ required: true, message: '请输入赛事名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
organizer: [
|
||||||
|
{ required: true, message: '请输入主办单位', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
location: [
|
||||||
|
{ required: true, message: '请输入举办地点', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
dialogTitle() {
|
||||||
|
const titleMap = {
|
||||||
|
create: '新建赛事',
|
||||||
|
edit: '编辑赛事',
|
||||||
|
view: '查看赛事'
|
||||||
|
};
|
||||||
|
return titleMap[this.dialogMode] || '赛事信息';
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadCompetitionList();
|
this.loadCompetitionList();
|
||||||
},
|
},
|
||||||
@@ -231,24 +576,28 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleCreate() {
|
handleCreate() {
|
||||||
this.$router.push({
|
this.dialogMode = 'create';
|
||||||
path: '/martial/competition/create',
|
this.currentCompetitionId = null;
|
||||||
query: { mode: 'create' }
|
this.resetFormData();
|
||||||
});
|
this.dialogVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleView(row) {
|
handleView(row) {
|
||||||
this.$router.push({
|
this.dialogMode = 'view';
|
||||||
path: '/martial/competition/create',
|
this.currentCompetitionId = row.id;
|
||||||
query: { mode: 'view', id: row.id }
|
this.formData = { ...row };
|
||||||
});
|
this.dialogVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEdit(row) {
|
handleEdit(row) {
|
||||||
this.$router.push({
|
this.dialogMode = 'edit';
|
||||||
path: '/martial/competition/create',
|
this.currentCompetitionId = row.id;
|
||||||
query: { mode: 'edit', id: row.id }
|
this.formData = { ...row };
|
||||||
});
|
this.dialogVisible = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
switchToEdit() {
|
||||||
|
this.dialogMode = 'edit';
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
@@ -264,6 +613,85 @@ export default {
|
|||||||
this.$message.success('删除成功');
|
this.$message.success('删除成功');
|
||||||
}
|
}
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAddSchedule() {
|
||||||
|
this.formData.schedule.push({
|
||||||
|
date: '',
|
||||||
|
time: '',
|
||||||
|
event: '',
|
||||||
|
venue: ''
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteSchedule(index) {
|
||||||
|
this.formData.schedule.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.dialogMode === 'create') {
|
||||||
|
// 新建
|
||||||
|
const newId = this.competitionList.length > 0
|
||||||
|
? Math.max(...this.competitionList.map(item => item.id)) + 1
|
||||||
|
: 1;
|
||||||
|
const newCompetition = {
|
||||||
|
...this.formData,
|
||||||
|
id: newId,
|
||||||
|
posterImages: [],
|
||||||
|
regulationFiles: []
|
||||||
|
};
|
||||||
|
this.competitionList.push(newCompetition);
|
||||||
|
this.$message.success('创建成功');
|
||||||
|
} else if (this.dialogMode === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
const index = this.competitionList.findIndex(item => item.id === this.currentCompetitionId);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.competitionList[index] = {
|
||||||
|
...this.competitionList[index],
|
||||||
|
...this.formData
|
||||||
|
};
|
||||||
|
this.$message.success('保存成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveCompetitionList();
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.submitLoading = false;
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善必填信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDialogClose() {
|
||||||
|
this.$refs.formRef.resetFields();
|
||||||
|
this.resetFormData();
|
||||||
|
},
|
||||||
|
|
||||||
|
resetFormData() {
|
||||||
|
this.formData = {
|
||||||
|
competitionName: '',
|
||||||
|
organizer: '',
|
||||||
|
location: '',
|
||||||
|
venue: '',
|
||||||
|
registrationTime: '',
|
||||||
|
competitionTime: '',
|
||||||
|
status: 1,
|
||||||
|
introduction: '',
|
||||||
|
contactPerson: '',
|
||||||
|
contactPhone: '',
|
||||||
|
contactEmail: '',
|
||||||
|
rules: '',
|
||||||
|
requirements: '',
|
||||||
|
awards: '',
|
||||||
|
schedule: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -306,4 +734,49 @@ export default {
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.competition-form {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #dc2626;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
.el-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
698
src/views/martial/deduction/index.vue
Normal file
698
src/views/martial/deduction/index.vue
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
<template>
|
||||||
|
<div class="deduction-container">
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form :inline="true" :model="queryParams" class="search-form">
|
||||||
|
<el-form-item label="所属项目">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.projectId"
|
||||||
|
placeholder="请选择项目"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 250px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="扣分项名称">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.itemName"
|
||||||
|
placeholder="请输入扣分项名称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :icon="Search" @click="handleSearch">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<el-card shadow="never" class="toolbar-card">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="handleAdd">
|
||||||
|
新增扣分项
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Delete"
|
||||||
|
:disabled="!selection.length"
|
||||||
|
@click="handleBatchDelete"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</el-button>
|
||||||
|
<el-button type="success" :icon="DocumentCopy" @click="handleClone">
|
||||||
|
克隆扣分项
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" :icon="Download" @click="handleExport">
|
||||||
|
导出模板
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-tooltip content="拖拽行可调整顺序" placement="top">
|
||||||
|
<el-icon :size="20" color="#409EFF">
|
||||||
|
<InfoFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip content="刷新" placement="top">
|
||||||
|
<el-button circle :icon="Refresh" @click="fetchData" />
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card shadow="never" class="table-card">
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
row-key="id"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column label="排序" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-icon class="drag-handle" :size="20">
|
||||||
|
<Rank />
|
||||||
|
</el-icon>
|
||||||
|
<span style="margin-left: 5px">{{ row.sortOrder }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="itemName"
|
||||||
|
label="扣分项名称"
|
||||||
|
min-width="200"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="projectName"
|
||||||
|
label="所属项目"
|
||||||
|
min-width="180"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="deductionPoints"
|
||||||
|
label="扣分值(分)"
|
||||||
|
width="120"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag type="danger" effect="dark">
|
||||||
|
-{{ row.deductionPoints }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="description"
|
||||||
|
label="描述说明"
|
||||||
|
min-width="250"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="createTime"
|
||||||
|
label="创建时间"
|
||||||
|
width="160"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" :icon="Edit" @click="handleEdit(row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="success" :icon="DocumentCopy" @click="handleCloneSingle(row)">
|
||||||
|
克隆
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="danger" :icon="Delete" @click="handleDelete(row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.current"
|
||||||
|
v-model:page-size="queryParams.size"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="fetchData"
|
||||||
|
@current-change="fetchData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<el-form-item label="所属项目" prop="projectId">
|
||||||
|
<el-select
|
||||||
|
v-model="form.projectId"
|
||||||
|
placeholder="请选择所属项目"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="扣分项名称" prop="itemName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.itemName"
|
||||||
|
placeholder="请输入扣分项名称"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="扣分值(分)" prop="deductionPoints">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.deductionPoints"
|
||||||
|
:min="0.1"
|
||||||
|
:max="10"
|
||||||
|
:precision="1"
|
||||||
|
:step="0.1"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">扣分范围:0.1 - 10.0 分</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序序号" prop="sortOrder">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.sortOrder"
|
||||||
|
:min="0"
|
||||||
|
:max="999"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">数字越小越靠前</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述说明" prop="description">
|
||||||
|
<el-input
|
||||||
|
v-model="form.description"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入描述说明"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 克隆扣分项弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="cloneDialogVisible"
|
||||||
|
title="克隆扣分项"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="cloneFormRef"
|
||||||
|
:model="cloneForm"
|
||||||
|
:rules="cloneRules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<el-form-item label="源项目">
|
||||||
|
<el-input :value="cloneForm.sourceProjectName" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="目标项目" prop="targetProjectId">
|
||||||
|
<el-select
|
||||||
|
v-model="cloneForm.targetProjectId"
|
||||||
|
placeholder="请选择目标项目"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList.filter(p => p.id !== cloneForm.sourceProjectId)"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-alert
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
将复制 <strong>{{ cloneForm.itemCount }}</strong> 个扣分项到目标项目
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="cloneDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="cloneLoading" @click="handleCloneSubmit">
|
||||||
|
确定克隆
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Refresh,
|
||||||
|
Plus,
|
||||||
|
Delete,
|
||||||
|
Edit,
|
||||||
|
Download,
|
||||||
|
DocumentCopy,
|
||||||
|
InfoFilled,
|
||||||
|
Rank
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import {
|
||||||
|
getDeductionList,
|
||||||
|
addDeduction,
|
||||||
|
updateDeduction,
|
||||||
|
removeDeduction,
|
||||||
|
cloneDeductions,
|
||||||
|
updateDeductionOrder
|
||||||
|
} from '@/api/martial/deduction'
|
||||||
|
import { getProjectList } from '@/api/martial/project'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
|
||||||
|
// 数据定义
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
const cloneLoading = ref(false)
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const selection = ref([])
|
||||||
|
const projectList = ref([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const cloneDialogVisible = ref(false)
|
||||||
|
const dialogTitle = ref('')
|
||||||
|
const formRef = ref(null)
|
||||||
|
const cloneFormRef = ref(null)
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
projectId: '',
|
||||||
|
itemName: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: null,
|
||||||
|
projectId: '',
|
||||||
|
itemName: '',
|
||||||
|
deductionPoints: 0.5,
|
||||||
|
sortOrder: 0,
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 克隆表单
|
||||||
|
const cloneForm = reactive({
|
||||||
|
sourceProjectId: '',
|
||||||
|
sourceProjectName: '',
|
||||||
|
targetProjectId: '',
|
||||||
|
itemCount: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
projectId: [
|
||||||
|
{ required: true, message: '请选择所属项目', trigger: 'change' }
|
||||||
|
],
|
||||||
|
itemName: [
|
||||||
|
{ required: true, message: '请输入扣分项名称', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
deductionPoints: [
|
||||||
|
{ required: true, message: '请输入扣分值', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
sortOrder: [
|
||||||
|
{ required: true, message: '请输入排序序号', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 克隆表单验证规则
|
||||||
|
const cloneRules = {
|
||||||
|
targetProjectId: [
|
||||||
|
{ required: true, message: '请选择目标项目', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载项目列表
|
||||||
|
const loadProjectList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getProjectList(1, 1000, {})
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
projectList.value = res.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getDeductionList(
|
||||||
|
queryParams.current,
|
||||||
|
queryParams.size,
|
||||||
|
queryParams
|
||||||
|
)
|
||||||
|
if (res.data) {
|
||||||
|
tableData.value = res.data.records || []
|
||||||
|
total.value = res.data.total || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('获取数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
queryParams.current = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(queryParams, {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
projectId: '',
|
||||||
|
itemName: ''
|
||||||
|
})
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
dialogTitle.value = '新增扣分项'
|
||||||
|
resetForm()
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row) => {
|
||||||
|
dialogTitle.value = '编辑扣分项'
|
||||||
|
Object.keys(form).forEach((key) => {
|
||||||
|
form[key] = row[key]
|
||||||
|
})
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除该扣分项吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
await removeDeduction(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const handleBatchDelete = () => {
|
||||||
|
ElMessageBox.confirm(`确定要删除选中的 ${selection.value.length} 个扣分项吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const ids = selection.value.map((item) => item.id).join(',')
|
||||||
|
await removeDeduction(ids)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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 () => {
|
||||||
|
if (!cloneFormRef.value) return
|
||||||
|
|
||||||
|
await cloneFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
cloneLoading.value = true
|
||||||
|
try {
|
||||||
|
await cloneDeductions({
|
||||||
|
sourceProjectId: cloneForm.sourceProjectId,
|
||||||
|
targetProjectId: cloneForm.targetProjectId
|
||||||
|
})
|
||||||
|
ElMessage.success('克隆成功')
|
||||||
|
cloneDialogVisible.value = false
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('克隆失败')
|
||||||
|
} finally {
|
||||||
|
cloneLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
await formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
if (form.id) {
|
||||||
|
await updateDeduction(form)
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await addDeduction(form)
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(form.id ? '修改失败' : '新增失败')
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
selection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const handleDialogClose = () => {
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(form, {
|
||||||
|
id: null,
|
||||||
|
projectId: '',
|
||||||
|
itemName: '',
|
||||||
|
deductionPoints: 0.5,
|
||||||
|
sortOrder: 0,
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
if (formRef.value) {
|
||||||
|
formRef.value.clearValidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出模板
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
const csvContent = [
|
||||||
|
['项目ID', '扣分项名称', '扣分值', '描述说明', '排序序号'],
|
||||||
|
['1', '示例扣分项', '0.5', '这是一个示例', '1']
|
||||||
|
].map(row => row.join(',')).join('\n')
|
||||||
|
|
||||||
|
const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' })
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `扣分项导入模板_${dayjs().format('YYYYMMDDHHmmss')}.csv`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化拖拽排序
|
||||||
|
const initSortable = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const tbody = document.querySelector('.el-table__body-wrapper tbody')
|
||||||
|
if (!tbody) return
|
||||||
|
|
||||||
|
Sortable.create(tbody, {
|
||||||
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
onEnd: async ({ oldIndex, newIndex }) => {
|
||||||
|
if (oldIndex === newIndex) return
|
||||||
|
|
||||||
|
const movedItem = tableData.value.splice(oldIndex, 1)[0]
|
||||||
|
tableData.value.splice(newIndex, 0, movedItem)
|
||||||
|
|
||||||
|
// 更新排序
|
||||||
|
try {
|
||||||
|
const sortData = tableData.value.map((item, index) => ({
|
||||||
|
id: item.id,
|
||||||
|
sortOrder: index
|
||||||
|
}))
|
||||||
|
await updateDeductionOrder(sortData)
|
||||||
|
ElMessage.success('排序已更新')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('排序更新失败')
|
||||||
|
fetchData() // 重新加载数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
if (!date) return '-'
|
||||||
|
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
loadProjectList()
|
||||||
|
fetchData()
|
||||||
|
initSortable()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.deduction-container {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.search-card,
|
||||||
|
.toolbar-card,
|
||||||
|
.table-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-right {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: move;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -154,7 +154,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
this.$router.push('/martial/order/list')
|
this.$router.go(-1)
|
||||||
},
|
},
|
||||||
setViewMode(index, mode) {
|
setViewMode(index, mode) {
|
||||||
this.dispatchGroups[index].viewMode = mode
|
this.dispatchGroups[index].viewMode = mode
|
||||||
|
|||||||
BIN
src/views/martial/exception/index.vue
Normal file
BIN
src/views/martial/exception/index.vue
Normal file
Binary file not shown.
698
src/views/martial/export/index.vue
Normal file
698
src/views/martial/export/index.vue
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
<template>
|
||||||
|
<div class="export-container">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<!-- Tab 1: 成绩导出 -->
|
||||||
|
<el-tab-pane label="成绩导出" name="result">
|
||||||
|
<div class="tab-content">
|
||||||
|
<el-form :model="resultForm" label-width="120px" style="max-width: 800px">
|
||||||
|
<el-form-item label="选择赛事" required>
|
||||||
|
<el-select
|
||||||
|
v-model="resultForm.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleCompetitionChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择项目">
|
||||||
|
<el-select
|
||||||
|
v-model="resultForm.projectId"
|
||||||
|
placeholder="请选择项目(不选则导出全部)"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分组类别">
|
||||||
|
<el-select
|
||||||
|
v-model="resultForm.category"
|
||||||
|
placeholder="请选择分组类别(可选)"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option label="男子" value="1" />
|
||||||
|
<el-option label="女子" value="2" />
|
||||||
|
<el-option label="团体" value="3" />
|
||||||
|
<el-option label="混合" value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导出内容">
|
||||||
|
<el-checkbox-group v-model="resultForm.exportFields">
|
||||||
|
<el-checkbox label="ranking">排名</el-checkbox>
|
||||||
|
<el-checkbox label="athleteName">选手姓名</el-checkbox>
|
||||||
|
<el-checkbox label="teamName">团队名称</el-checkbox>
|
||||||
|
<el-checkbox label="projectName">项目</el-checkbox>
|
||||||
|
<el-checkbox label="totalScore">总分</el-checkbox>
|
||||||
|
<el-checkbox label="finalScore">最终成绩</el-checkbox>
|
||||||
|
<el-checkbox label="medal">奖牌</el-checkbox>
|
||||||
|
<el-checkbox label="judgeScores">裁判评分明细</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导出格式">
|
||||||
|
<el-radio-group v-model="resultForm.format">
|
||||||
|
<el-radio label="xlsx">Excel (xlsx)</el-radio>
|
||||||
|
<el-radio label="pdf">PDF</el-radio>
|
||||||
|
<el-radio label="csv">CSV</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Download"
|
||||||
|
:loading="resultLoading"
|
||||||
|
@click="handleExportResult"
|
||||||
|
>
|
||||||
|
导出成绩单
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- Tab 2: 名单导出 -->
|
||||||
|
<el-tab-pane label="名单导出" name="list">
|
||||||
|
<div class="tab-content">
|
||||||
|
<el-form :model="listForm" label-width="120px" style="max-width: 800px">
|
||||||
|
<el-form-item label="选择赛事" required>
|
||||||
|
<el-select
|
||||||
|
v-model="listForm.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名单类型" required>
|
||||||
|
<el-radio-group v-model="listForm.type">
|
||||||
|
<el-radio label="athlete">运动员名单</el-radio>
|
||||||
|
<el-radio label="referee">裁判名单</el-radio>
|
||||||
|
<el-radio label="staff">工作人员名单</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导出格式">
|
||||||
|
<el-radio-group v-model="listForm.format">
|
||||||
|
<el-radio label="xlsx">Excel (xlsx)</el-radio>
|
||||||
|
<el-radio label="pdf">PDF</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Download"
|
||||||
|
:loading="listLoading"
|
||||||
|
@click="handleExportList"
|
||||||
|
>
|
||||||
|
导出名单
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- Tab 3: 证书生成 -->
|
||||||
|
<el-tab-pane label="证书生成" name="certificate">
|
||||||
|
<div class="tab-content">
|
||||||
|
<el-form :model="certForm" label-width="120px" style="max-width: 800px">
|
||||||
|
<el-form-item label="选择赛事" required>
|
||||||
|
<el-select
|
||||||
|
v-model="certForm.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleCertCompetitionChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择项目">
|
||||||
|
<el-select
|
||||||
|
v-model="certForm.projectId"
|
||||||
|
placeholder="请选择项目"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in certProjectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="证书类型" required>
|
||||||
|
<el-radio-group v-model="certForm.certType">
|
||||||
|
<el-radio label="medal">获奖证书(金银铜牌)</el-radio>
|
||||||
|
<el-radio label="participation">参赛证书</el-radio>
|
||||||
|
<el-radio label="excellent">优秀运动员证书</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="证书模板" required>
|
||||||
|
<el-select
|
||||||
|
v-model="certForm.templateId"
|
||||||
|
placeholder="请选择证书模板"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in certificateTemplates"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.templateName"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
<span style="float: left">{{ item.templateName }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">
|
||||||
|
{{ item.description }}
|
||||||
|
</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导出格式">
|
||||||
|
<el-radio-group v-model="certForm.format">
|
||||||
|
<el-radio label="pdf">PDF</el-radio>
|
||||||
|
<el-radio label="jpg">JPG图片</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="批量生成">
|
||||||
|
<el-switch v-model="certForm.batch" />
|
||||||
|
<span style="margin-left: 10px; color: #909399; font-size: 13px">
|
||||||
|
开启后将根据筛选条件批量生成
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="DocumentAdd"
|
||||||
|
:loading="certLoading"
|
||||||
|
@click="handleGenerateCertificate"
|
||||||
|
>
|
||||||
|
生成证书
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:icon="View"
|
||||||
|
@click="handlePreviewCertificate"
|
||||||
|
>
|
||||||
|
预览模板
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 证书列表 -->
|
||||||
|
<el-divider />
|
||||||
|
<h4>已生成证书</h4>
|
||||||
|
<el-table :data="certificateList" border stripe>
|
||||||
|
<el-table-column prop="athleteName" label="选手姓名" width="120" />
|
||||||
|
<el-table-column prop="projectName" label="项目" min-width="150" />
|
||||||
|
<el-table-column prop="ranking" label="排名" width="80" align="center" />
|
||||||
|
<el-table-column prop="medal" label="奖牌" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.medal === 'gold'" type="danger">🏅 金牌</el-tag>
|
||||||
|
<el-tag v-else-if="row.medal === 'silver'" type="warning">🥈 银牌</el-tag>
|
||||||
|
<el-tag v-else-if="row.medal === 'bronze'" type="success">🥉 铜牌</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="generateTime" label="生成时间" width="160" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.generateTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" :icon="View" @click="handleViewCertificate(row)">
|
||||||
|
预览
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="success" :icon="Download" @click="handleDownloadCertificate(row)">
|
||||||
|
下载
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- Tab 4: 报表导出 -->
|
||||||
|
<el-tab-pane label="报表导出" name="report">
|
||||||
|
<div class="tab-content">
|
||||||
|
<el-form :model="reportForm" label-width="120px" style="max-width: 800px">
|
||||||
|
<el-form-item label="报表类型" required>
|
||||||
|
<el-select
|
||||||
|
v-model="reportForm.reportType"
|
||||||
|
placeholder="请选择报表类型"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleReportTypeChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
label="赛事统计报表"
|
||||||
|
value="competition"
|
||||||
|
description="包含赛事基本信息、参赛人数、项目数量等统计"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="项目统计报表"
|
||||||
|
value="project"
|
||||||
|
description="各项目报名人数、参赛人数、获奖分布等"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="裁判工作量报表"
|
||||||
|
value="referee"
|
||||||
|
description="裁判评分数量、工作时长等统计"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="报名统计报表"
|
||||||
|
value="registration"
|
||||||
|
description="报名趋势、地域分布、团队排名等"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="奖牌榜报表"
|
||||||
|
value="medal"
|
||||||
|
description="团队/地区奖牌榜"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择赛事" required>
|
||||||
|
<el-select
|
||||||
|
v-model="reportForm.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="导出格式">
|
||||||
|
<el-radio-group v-model="reportForm.format">
|
||||||
|
<el-radio label="xlsx">Excel (xlsx)</el-radio>
|
||||||
|
<el-radio label="pdf">PDF</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="包含图表">
|
||||||
|
<el-switch v-model="reportForm.includeCharts" />
|
||||||
|
<span style="margin-left: 10px; color: #909399; font-size: 13px">
|
||||||
|
导出PDF时包含统计图表
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Download"
|
||||||
|
:loading="reportLoading"
|
||||||
|
@click="handleExportReport"
|
||||||
|
>
|
||||||
|
导出报表
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 报表说明 -->
|
||||||
|
<el-divider />
|
||||||
|
<el-alert type="info" :closable="false" show-icon>
|
||||||
|
<template #title>
|
||||||
|
<strong>报表说明</strong>
|
||||||
|
</template>
|
||||||
|
<div style="margin-top: 10px">
|
||||||
|
<p><strong>赛事统计报表:</strong>包含赛事基本信息、时间、地点、参赛人数、项目数量、裁判数量等综合统计信息</p>
|
||||||
|
<p><strong>项目统计报表:</strong>各项目报名人数、实际参赛人数、获奖分布、平均分、最高分、最低分等详细数据</p>
|
||||||
|
<p><strong>裁判工作量报表:</strong>每位裁判的评分数量、负责项目、工作时长、评分准确性等统计</p>
|
||||||
|
<p><strong>报名统计报表:</strong>报名趋势分析、地域分布、团队排名、报名费收入统计等</p>
|
||||||
|
<p><strong>奖牌榜报表:</strong>按团队或地区统计金银铜牌数量,包含总积分排名</p>
|
||||||
|
</div>
|
||||||
|
</el-alert>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 证书预览弹窗 -->
|
||||||
|
<el-dialog v-model="previewVisible" title="证书预览" width="900px">
|
||||||
|
<div class="certificate-preview">
|
||||||
|
<img v-if="previewUrl" :src="previewUrl" style="width: 100%" />
|
||||||
|
<el-empty v-else description="暂无预览" />
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import {
|
||||||
|
Download,
|
||||||
|
DocumentAdd,
|
||||||
|
View
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import {
|
||||||
|
exportResults,
|
||||||
|
exportAwardList,
|
||||||
|
generateCertificate,
|
||||||
|
batchGenerateCertificates
|
||||||
|
} from '@/api/martial/result'
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
import { getProjectsByCompetition } from '@/api/martial/project'
|
||||||
|
import {
|
||||||
|
exportAthletes,
|
||||||
|
exportReferees,
|
||||||
|
exportStaff
|
||||||
|
} from '@/api/martial/export'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
// 数据定义
|
||||||
|
const activeTab = ref('result')
|
||||||
|
const resultLoading = ref(false)
|
||||||
|
const listLoading = ref(false)
|
||||||
|
const certLoading = ref(false)
|
||||||
|
const reportLoading = ref(false)
|
||||||
|
const previewVisible = ref(false)
|
||||||
|
const previewUrl = ref('')
|
||||||
|
|
||||||
|
const competitionList = ref([])
|
||||||
|
const projectList = ref([])
|
||||||
|
const certProjectList = ref([])
|
||||||
|
const certificateList = ref([])
|
||||||
|
const certificateTemplates = ref([
|
||||||
|
{ id: 1, templateName: '标准证书模板', description: '简洁大方' },
|
||||||
|
{ id: 2, templateName: '豪华证书模板', description: '精美设计' },
|
||||||
|
{ id: 3, templateName: '传统证书模板', description: '古典风格' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 成绩导出表单
|
||||||
|
const resultForm = reactive({
|
||||||
|
competitionId: '',
|
||||||
|
projectId: '',
|
||||||
|
category: '',
|
||||||
|
exportFields: ['ranking', 'athleteName', 'teamName', 'projectName', 'finalScore', 'medal'],
|
||||||
|
format: 'xlsx'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 名单导出表单
|
||||||
|
const listForm = reactive({
|
||||||
|
competitionId: '',
|
||||||
|
type: 'athlete',
|
||||||
|
format: 'xlsx'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 证书生成表单
|
||||||
|
const certForm = reactive({
|
||||||
|
competitionId: '',
|
||||||
|
projectId: '',
|
||||||
|
certType: 'medal',
|
||||||
|
templateId: null,
|
||||||
|
format: 'pdf',
|
||||||
|
batch: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 报表导出表单
|
||||||
|
const reportForm = reactive({
|
||||||
|
reportType: 'competition',
|
||||||
|
competitionId: '',
|
||||||
|
format: 'xlsx',
|
||||||
|
includeCharts: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载赛事列表
|
||||||
|
const loadCompetitionList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getCompetitionList(1, 1000, { status: 1 })
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
competitionList.value = res.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载赛事列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赛事变化
|
||||||
|
const handleCompetitionChange = async (competitionId) => {
|
||||||
|
resultForm.projectId = ''
|
||||||
|
projectList.value = []
|
||||||
|
|
||||||
|
if (competitionId) {
|
||||||
|
try {
|
||||||
|
const res = await getProjectsByCompetition(competitionId)
|
||||||
|
if (res.data) {
|
||||||
|
projectList.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 证书赛事变化
|
||||||
|
const handleCertCompetitionChange = async (competitionId) => {
|
||||||
|
certForm.projectId = ''
|
||||||
|
certProjectList.value = []
|
||||||
|
|
||||||
|
if (competitionId) {
|
||||||
|
try {
|
||||||
|
const res = await getProjectsByCompetition(competitionId)
|
||||||
|
if (res.data) {
|
||||||
|
certProjectList.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 报表类型变化
|
||||||
|
const handleReportTypeChange = (type) => {
|
||||||
|
// 可根据不同报表类型显示不同的配置项
|
||||||
|
console.log('报表类型:', type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成绩
|
||||||
|
const handleExportResult = async () => {
|
||||||
|
if (!resultForm.competitionId) {
|
||||||
|
ElMessage.warning('请选择赛事')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resultLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await exportResults(resultForm)
|
||||||
|
const blob = new Blob([res], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `成绩单_${dayjs().format('YYYYMMDDHHmmss')}.${resultForm.format}`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
} finally {
|
||||||
|
resultLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出名单
|
||||||
|
const handleExportList = async () => {
|
||||||
|
if (!listForm.competitionId) {
|
||||||
|
ElMessage.warning('请选择赛事')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listLoading.value = true
|
||||||
|
try {
|
||||||
|
let exportFunc
|
||||||
|
switch (listForm.type) {
|
||||||
|
case 'athlete':
|
||||||
|
exportFunc = exportAthletes
|
||||||
|
break
|
||||||
|
case 'referee':
|
||||||
|
exportFunc = exportReferees
|
||||||
|
break
|
||||||
|
case 'staff':
|
||||||
|
exportFunc = exportStaff
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await exportFunc({
|
||||||
|
competitionId: listForm.competitionId,
|
||||||
|
format: listForm.format
|
||||||
|
})
|
||||||
|
|
||||||
|
const blob = new Blob([res], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `${listForm.type}_名单_${dayjs().format('YYYYMMDDHHmmss')}.${listForm.format}`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
} finally {
|
||||||
|
listLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成证书
|
||||||
|
const handleGenerateCertificate = async () => {
|
||||||
|
if (!certForm.competitionId) {
|
||||||
|
ElMessage.warning('请选择赛事')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!certForm.templateId) {
|
||||||
|
ElMessage.warning('请选择证书模板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certLoading.value = true
|
||||||
|
try {
|
||||||
|
let res
|
||||||
|
if (certForm.batch) {
|
||||||
|
res = await batchGenerateCertificates(certForm)
|
||||||
|
ElMessage.success('批量生成成功,正在下载...')
|
||||||
|
} else {
|
||||||
|
res = await generateCertificate(certForm)
|
||||||
|
ElMessage.success('生成成功,正在下载...')
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = new Blob([res], { type: 'application/pdf' })
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `证书_${dayjs().format('YYYYMMDDHHmmss')}.${certForm.format}`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('生成失败')
|
||||||
|
} finally {
|
||||||
|
certLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览证书模板
|
||||||
|
const handlePreviewCertificate = () => {
|
||||||
|
if (!certForm.templateId) {
|
||||||
|
ElMessage.warning('请先选择证书模板')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 这里可以加载模板预览图
|
||||||
|
previewUrl.value = `/static/templates/cert_${certForm.templateId}.jpg`
|
||||||
|
previewVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看证书
|
||||||
|
const handleViewCertificate = (row) => {
|
||||||
|
previewUrl.value = row.certificateUrl
|
||||||
|
previewVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载证书
|
||||||
|
const handleDownloadCertificate = (row) => {
|
||||||
|
window.open(row.certificateUrl, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出报表
|
||||||
|
const handleExportReport = async () => {
|
||||||
|
if (!reportForm.competitionId) {
|
||||||
|
ElMessage.warning('请选择赛事')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reportLoading.value = true
|
||||||
|
try {
|
||||||
|
// 根据不同的报表类型调用不同的API
|
||||||
|
// 这里简化处理,实际应该有不同的API接口
|
||||||
|
const res = await exportResults({
|
||||||
|
...reportForm,
|
||||||
|
reportType: reportForm.reportType
|
||||||
|
})
|
||||||
|
|
||||||
|
const blob = new Blob([res], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `${reportForm.reportType}_报表_${dayjs().format('YYYYMMDDHHmmss')}.${reportForm.format}`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
} finally {
|
||||||
|
reportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
if (!date) return '-'
|
||||||
|
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
loadCompetitionList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.export-container {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-preview {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox-group) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-alert) {
|
||||||
|
p {
|
||||||
|
margin: 5px 0;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1194
src/views/martial/info/index.vue
Normal file
1194
src/views/martial/info/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/views/martial/judgeInvite/index.vue
Normal file
BIN
src/views/martial/judgeInvite/index.vue
Normal file
Binary file not shown.
1722
src/views/martial/judgeProject/index.vue
Normal file
1722
src/views/martial/judgeProject/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/views/martial/live/index.vue
Normal file
BIN
src/views/martial/live/index.vue
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -32,11 +32,12 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button size="small" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="participantList"
|
:data="displayList"
|
||||||
border
|
border
|
||||||
stripe
|
stripe
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@@ -150,6 +151,190 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 新增/编辑/查看弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="800px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
top="5vh"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
:disabled="dialogMode === 'view'"
|
||||||
|
class="participant-form"
|
||||||
|
>
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
|
基本信息
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="选手姓名" prop="playerName">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.playerName"
|
||||||
|
placeholder="请输入选手姓名"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="性别" prop="gender">
|
||||||
|
<el-radio-group v-model="formData.gender">
|
||||||
|
<el-radio :label="1">男</el-radio>
|
||||||
|
<el-radio :label="2">女</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="年龄" prop="age">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.age"
|
||||||
|
:min="6"
|
||||||
|
:max="100"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="身份证号" prop="idCard">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.idCard"
|
||||||
|
placeholder="请输入身份证号"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.contactPhone"
|
||||||
|
placeholder="请输入联系电话"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="所属单位" prop="organization">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.organization"
|
||||||
|
placeholder="请输入所属单位"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 赛事信息 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-trophy"></i>
|
||||||
|
赛事信息
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所属赛事" prop="competitionId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleCompetitionChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="参赛项目" prop="projectName">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.projectName"
|
||||||
|
placeholder="请输入参赛项目"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="组别" prop="category">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.category"
|
||||||
|
placeholder="例如:成年男子组"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="出场顺序" prop="orderNum">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.orderNum"
|
||||||
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 其他信息 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
其他信息
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item label="选手简介" prop="introduction">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入选手简介"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="dialogMode !== 'view'"
|
||||||
|
type="primary"
|
||||||
|
:loading="submitLoading"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
@click="switchToEdit"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -182,6 +367,7 @@ export default {
|
|||||||
category: '成年男子组',
|
category: '成年男子组',
|
||||||
orderNum: 1,
|
orderNum: 1,
|
||||||
introduction: '国家一级运动员',
|
introduction: '国家一级运动员',
|
||||||
|
remark: '',
|
||||||
attachments: []
|
attachments: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -198,6 +384,7 @@ export default {
|
|||||||
category: '成年女子组',
|
category: '成年女子组',
|
||||||
orderNum: 2,
|
orderNum: 2,
|
||||||
introduction: '国家二级运动员',
|
introduction: '国家二级运动员',
|
||||||
|
remark: '',
|
||||||
attachments: []
|
attachments: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -214,6 +401,7 @@ export default {
|
|||||||
category: '少年男子组',
|
category: '少年男子组',
|
||||||
orderNum: 1,
|
orderNum: 1,
|
||||||
introduction: '市级青少年冠军',
|
introduction: '市级青少年冠军',
|
||||||
|
remark: '',
|
||||||
attachments: []
|
attachments: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -230,6 +418,7 @@ export default {
|
|||||||
category: '少年女子组',
|
category: '少年女子组',
|
||||||
orderNum: 2,
|
orderNum: 2,
|
||||||
introduction: '省级青少年亚军',
|
introduction: '省级青少年亚军',
|
||||||
|
remark: '',
|
||||||
attachments: []
|
attachments: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -246,11 +435,80 @@ export default {
|
|||||||
category: '中年组',
|
category: '中年组',
|
||||||
orderNum: 1,
|
orderNum: 1,
|
||||||
introduction: '武当第十五代传人',
|
introduction: '武当第十五代传人',
|
||||||
|
remark: '',
|
||||||
attachments: []
|
attachments: []
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogMode: 'create', // create, edit, view
|
||||||
|
submitLoading: false,
|
||||||
|
currentParticipantId: null,
|
||||||
|
formData: {
|
||||||
|
competitionId: null,
|
||||||
|
competitionName: '',
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '',
|
||||||
|
remark: '',
|
||||||
|
attachments: []
|
||||||
|
},
|
||||||
|
formRules: {
|
||||||
|
playerName: [
|
||||||
|
{ required: true, message: '请输入选手姓名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
gender: [
|
||||||
|
{ required: true, message: '请选择性别', trigger: 'change' }
|
||||||
|
],
|
||||||
|
age: [
|
||||||
|
{ required: true, message: '请输入年龄', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
contactPhone: [
|
||||||
|
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
competitionId: [
|
||||||
|
{ required: true, message: '请选择赛事', trigger: 'change' }
|
||||||
|
],
|
||||||
|
projectName: [
|
||||||
|
{ required: true, message: '请输入参赛项目', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
dialogTitle() {
|
||||||
|
const titleMap = {
|
||||||
|
create: '添加参赛选手',
|
||||||
|
edit: '编辑参赛选手',
|
||||||
|
view: '查看参赛选手'
|
||||||
|
};
|
||||||
|
return titleMap[this.dialogMode] || '参赛选手信息';
|
||||||
|
},
|
||||||
|
displayList() {
|
||||||
|
let list = [...this.participantList];
|
||||||
|
|
||||||
|
// 关键词搜索
|
||||||
|
if (this.searchForm.keyword) {
|
||||||
|
list = list.filter(item =>
|
||||||
|
item.playerName.includes(this.searchForm.keyword)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赛事筛选
|
||||||
|
if (this.searchForm.competitionId) {
|
||||||
|
list = list.filter(item => item.competitionId === this.searchForm.competitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadParticipantList();
|
this.loadParticipantList();
|
||||||
},
|
},
|
||||||
@@ -285,29 +543,39 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSearch() {
|
handleSearch() {
|
||||||
// 实现搜索逻辑
|
// 搜索逻辑已在 computed 中实现
|
||||||
this.loadParticipantList();
|
},
|
||||||
|
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
keyword: '',
|
||||||
|
competitionId: null
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleCreate() {
|
handleCreate() {
|
||||||
this.$router.push({
|
this.dialogMode = 'create';
|
||||||
path: '/martial/participant/manage',
|
this.currentParticipantId = null;
|
||||||
query: { mode: 'create' }
|
this.resetFormData();
|
||||||
});
|
this.dialogVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleView(row) {
|
handleView(row) {
|
||||||
this.$router.push({
|
this.dialogMode = 'view';
|
||||||
path: '/martial/participant/manage',
|
this.currentParticipantId = row.id;
|
||||||
query: { mode: 'view', id: row.id }
|
this.formData = { ...row };
|
||||||
});
|
this.dialogVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEdit(row) {
|
handleEdit(row) {
|
||||||
this.$router.push({
|
this.dialogMode = 'edit';
|
||||||
path: '/martial/participant/manage',
|
this.currentParticipantId = row.id;
|
||||||
query: { mode: 'edit', id: row.id }
|
this.formData = { ...row };
|
||||||
});
|
this.dialogVisible = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
switchToEdit() {
|
||||||
|
this.dialogMode = 'edit';
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
@@ -323,6 +591,84 @@ export default {
|
|||||||
this.$message.success('删除成功');
|
this.$message.success('删除成功');
|
||||||
}
|
}
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCompetitionChange(competitionId) {
|
||||||
|
const competition = this.competitionOptions.find(item => item.id === competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true;
|
||||||
|
|
||||||
|
// 确保有赛事名称
|
||||||
|
if (!this.formData.competitionName) {
|
||||||
|
const competition = this.competitionOptions.find(item => item.id === this.formData.competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.dialogMode === 'create') {
|
||||||
|
// 新建
|
||||||
|
const newId = this.participantList.length > 0
|
||||||
|
? Math.max(...this.participantList.map(item => item.id)) + 1
|
||||||
|
: 1;
|
||||||
|
const newParticipant = {
|
||||||
|
...this.formData,
|
||||||
|
id: newId
|
||||||
|
};
|
||||||
|
this.participantList.push(newParticipant);
|
||||||
|
this.$message.success('添加成功');
|
||||||
|
} else if (this.dialogMode === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
const index = this.participantList.findIndex(item => item.id === this.currentParticipantId);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.participantList[index] = {
|
||||||
|
...this.participantList[index],
|
||||||
|
...this.formData
|
||||||
|
};
|
||||||
|
this.$message.success('保存成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveParticipantList();
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.submitLoading = false;
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善必填信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDialogClose() {
|
||||||
|
this.$refs.formRef.resetFields();
|
||||||
|
this.resetFormData();
|
||||||
|
},
|
||||||
|
|
||||||
|
resetFormData() {
|
||||||
|
this.formData = {
|
||||||
|
competitionId: null,
|
||||||
|
competitionName: '',
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '',
|
||||||
|
remark: '',
|
||||||
|
attachments: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -369,4 +715,43 @@ export default {
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.participant-form {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #dc2626;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
837
src/views/martial/project/index.vue
Normal file
837
src/views/martial/project/index.vue
Normal file
@@ -0,0 +1,837 @@
|
|||||||
|
<template>
|
||||||
|
<div class="project-container">
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form :inline="true" :model="queryParams" class="search-form">
|
||||||
|
<el-form-item label="赛事">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.projectName"
|
||||||
|
placeholder="请输入项目名称"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分组类别">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.category"
|
||||||
|
placeholder="请选择分组类别"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<el-option label="男子" value="1" />
|
||||||
|
<el-option label="女子" value="2" />
|
||||||
|
<el-option label="团体" value="3" />
|
||||||
|
<el-option label="混合" value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目类型">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.eventType"
|
||||||
|
placeholder="请选择项目类型"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<el-option label="套路" value="1" />
|
||||||
|
<el-option label="散打" value="2" />
|
||||||
|
<el-option label="器械" value="3" />
|
||||||
|
<el-option label="对练" value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :icon="Search" @click="handleSearch">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<el-card shadow="never" class="toolbar-card">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="handleAdd">
|
||||||
|
新增项目
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Delete"
|
||||||
|
:disabled="!selection.length"
|
||||||
|
@click="handleBatchDelete"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</el-button>
|
||||||
|
<el-upload
|
||||||
|
:action="uploadUrl"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:on-success="handleUploadSuccess"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:show-file-list="false"
|
||||||
|
accept=".xlsx,.xls"
|
||||||
|
>
|
||||||
|
<el-button type="success" :icon="Upload">导入Excel</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-button type="warning" :icon="Download" @click="handleExport">
|
||||||
|
导出Excel
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-tooltip content="刷新" placement="top">
|
||||||
|
<el-button circle :icon="Refresh" @click="fetchData" />
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card shadow="never" class="table-card">
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column
|
||||||
|
prop="projectCode"
|
||||||
|
label="项目编码"
|
||||||
|
width="120"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="projectName"
|
||||||
|
label="项目名称"
|
||||||
|
min-width="180"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="competitionName"
|
||||||
|
label="所属赛事"
|
||||||
|
min-width="150"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column prop="category" label="分组类别" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.category === 1" type="primary">男子</el-tag>
|
||||||
|
<el-tag v-else-if="row.category === 2" type="danger">女子</el-tag>
|
||||||
|
<el-tag v-else-if="row.category === 3" type="success">团体</el-tag>
|
||||||
|
<el-tag v-else-if="row.category === 4" type="warning">混合</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="eventType" label="项目类型" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.eventType === 1">套路</span>
|
||||||
|
<span v-else-if="row.eventType === 2">散打</span>
|
||||||
|
<span v-else-if="row.eventType === 3">器械</span>
|
||||||
|
<span v-else-if="row.eventType === 4">对练</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="registrationFee"
|
||||||
|
label="报名费(元)"
|
||||||
|
width="110"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="color: #f56c6c">¥{{ row.registrationFee || 0 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="报名时间" width="180" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.registrationStartTime && row.registrationEndTime">
|
||||||
|
<div style="font-size: 12px">
|
||||||
|
{{ formatDate(row.registrationStartTime) }}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px">至</div>
|
||||||
|
<div style="font-size: 12px">
|
||||||
|
{{ formatDate(row.registrationEndTime) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span v-else style="color: #909399">未设置</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="报名人数" width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :style="{ color: row.currentCount >= row.maxParticipants ? '#f56c6c' : '#67c23a' }">
|
||||||
|
{{ row.currentCount || 0 }}
|
||||||
|
</span>
|
||||||
|
/ {{ row.maxParticipants || 0 }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="createTime"
|
||||||
|
label="创建时间"
|
||||||
|
width="160"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" :icon="Edit" @click="handleEdit(row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="primary" :icon="View" @click="handleView(row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="danger" :icon="Delete" @click="handleDelete(row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.current"
|
||||||
|
v-model:page-size="queryParams.size"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="fetchData"
|
||||||
|
@current-change="fetchData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="800px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所属赛事" prop="competitionId">
|
||||||
|
<el-select
|
||||||
|
v-model="form.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="项目编码" prop="projectCode">
|
||||||
|
<el-input
|
||||||
|
v-model="form.projectCode"
|
||||||
|
placeholder="请输入项目编码"
|
||||||
|
maxlength="50"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.projectName"
|
||||||
|
placeholder="请输入项目名称"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="分组类别" prop="category">
|
||||||
|
<el-select
|
||||||
|
v-model="form.category"
|
||||||
|
placeholder="请选择分组类别"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option label="男子" :value="1" />
|
||||||
|
<el-option label="女子" :value="2" />
|
||||||
|
<el-option label="团体" :value="3" />
|
||||||
|
<el-option label="混合" :value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="项目类型" prop="eventType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.eventType"
|
||||||
|
placeholder="请选择项目类型"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option label="套路" :value="1" />
|
||||||
|
<el-option label="散打" :value="2" />
|
||||||
|
<el-option label="器械" :value="3" />
|
||||||
|
<el-option label="对练" :value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="报名费(元)" prop="registrationFee">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.registrationFee"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:step="10"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="报名开始时间" prop="registrationStartTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.registrationStartTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择开始时间"
|
||||||
|
style="width: 100%"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="报名结束时间" prop="registrationEndTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.registrationEndTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择结束时间"
|
||||||
|
style="width: 100%"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="最大参赛人数" prop="maxParticipants">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.maxParticipants"
|
||||||
|
:min="1"
|
||||||
|
:max="1000"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="排序序号" prop="sortOrder">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.sortOrder"
|
||||||
|
:min="0"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item label="比赛规则" prop="rules">
|
||||||
|
<el-input
|
||||||
|
v-model="form.rules"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入比赛规则说明"
|
||||||
|
maxlength="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
maxlength="200"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 查看详情弹窗 -->
|
||||||
|
<el-dialog v-model="detailVisible" title="项目详情" width="700px">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="项目编码">
|
||||||
|
{{ detailData.projectCode }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目名称">
|
||||||
|
{{ detailData.projectName }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所属赛事">
|
||||||
|
{{ detailData.competitionName }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分组类别">
|
||||||
|
<el-tag v-if="detailData.category === 1" type="primary">男子</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.category === 2" type="danger">女子</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.category === 3" type="success">团体</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.category === 4" type="warning">混合</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目类型">
|
||||||
|
<span v-if="detailData.eventType === 1">套路</span>
|
||||||
|
<span v-else-if="detailData.eventType === 2">散打</span>
|
||||||
|
<span v-else-if="detailData.eventType === 3">器械</span>
|
||||||
|
<span v-else-if="detailData.eventType === 4">对练</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="报名费">
|
||||||
|
<span style="color: #f56c6c; font-weight: bold">
|
||||||
|
¥{{ detailData.registrationFee || 0 }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="报名开始时间">
|
||||||
|
{{ formatDate(detailData.registrationStartTime) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="报名结束时间">
|
||||||
|
{{ formatDate(detailData.registrationEndTime) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最大参赛人数">
|
||||||
|
{{ detailData.maxParticipants }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前报名人数">
|
||||||
|
<span :style="{
|
||||||
|
color: detailData.currentCount >= detailData.maxParticipants ? '#f56c6c' : '#67c23a',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}">
|
||||||
|
{{ detailData.currentCount || 0 }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间" :span="2">
|
||||||
|
{{ formatDate(detailData.createTime) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="比赛规则" :span="2">
|
||||||
|
{{ detailData.rules || '暂无' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注" :span="2">
|
||||||
|
{{ detailData.remark || '暂无' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, computed } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Refresh,
|
||||||
|
Plus,
|
||||||
|
Delete,
|
||||||
|
Edit,
|
||||||
|
View,
|
||||||
|
Upload,
|
||||||
|
Download
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import {
|
||||||
|
getProjectList,
|
||||||
|
addProject,
|
||||||
|
updateProject,
|
||||||
|
removeProject,
|
||||||
|
importProjects,
|
||||||
|
exportProjects
|
||||||
|
} from '@/api/martial/project'
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
import { getToken } from '@/util/auth'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
// 数据定义
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const selection = ref([])
|
||||||
|
const competitionList = ref([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const detailVisible = ref(false)
|
||||||
|
const dialogTitle = ref('')
|
||||||
|
const formRef = ref(null)
|
||||||
|
const detailData = ref({})
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
competitionId: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
eventType: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: null,
|
||||||
|
competitionId: '',
|
||||||
|
projectCode: '',
|
||||||
|
projectName: '',
|
||||||
|
category: null,
|
||||||
|
eventType: null,
|
||||||
|
registrationFee: 0,
|
||||||
|
registrationStartTime: '',
|
||||||
|
registrationEndTime: '',
|
||||||
|
maxParticipants: 100,
|
||||||
|
sortOrder: 0,
|
||||||
|
rules: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 上传配置
|
||||||
|
const uploadUrl = computed(() => {
|
||||||
|
return import.meta.env.VITE_APP_BASE_URL + '/api/blade-martial/project/import'
|
||||||
|
})
|
||||||
|
const uploadHeaders = computed(() => {
|
||||||
|
return {
|
||||||
|
'Blade-Auth': 'bearer ' + getToken()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
competitionId: [
|
||||||
|
{ required: true, message: '请选择所属赛事', trigger: 'change' }
|
||||||
|
],
|
||||||
|
projectCode: [
|
||||||
|
{ required: true, message: '请输入项目编码', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
projectName: [
|
||||||
|
{ required: true, message: '请输入项目名称', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
category: [
|
||||||
|
{ required: true, message: '请选择分组类别', trigger: 'change' }
|
||||||
|
],
|
||||||
|
eventType: [
|
||||||
|
{ required: true, message: '请选择项目类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
registrationFee: [
|
||||||
|
{ required: true, message: '请输入报名费', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
registrationStartTime: [
|
||||||
|
{ required: true, message: '请选择报名开始时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
registrationEndTime: [
|
||||||
|
{ required: true, message: '请选择报名结束时间', trigger: 'change' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value && form.registrationStartTime && value <= form.registrationStartTime) {
|
||||||
|
callback(new Error('结束时间必须大于开始时间'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
maxParticipants: [
|
||||||
|
{ required: true, message: '请输入最大参赛人数', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载赛事列表
|
||||||
|
const loadCompetitionList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getCompetitionList(1, 1000, { status: 1 })
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
competitionList.value = res.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载赛事列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getProjectList(
|
||||||
|
queryParams.current,
|
||||||
|
queryParams.size,
|
||||||
|
queryParams
|
||||||
|
)
|
||||||
|
if (res.data) {
|
||||||
|
tableData.value = res.data.records || []
|
||||||
|
total.value = res.data.total || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('获取数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
queryParams.current = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(queryParams, {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
competitionId: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
eventType: ''
|
||||||
|
})
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
dialogTitle.value = '新增项目'
|
||||||
|
resetForm()
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row) => {
|
||||||
|
dialogTitle.value = '编辑项目'
|
||||||
|
Object.keys(form).forEach((key) => {
|
||||||
|
form[key] = row[key]
|
||||||
|
})
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看
|
||||||
|
const handleView = (row) => {
|
||||||
|
detailData.value = { ...row }
|
||||||
|
detailVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = (row) => {
|
||||||
|
ElMessageBox.confirm('确定要删除该项目吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
await removeProject(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const handleBatchDelete = () => {
|
||||||
|
ElMessageBox.confirm(`确定要删除选中的 ${selection.value.length} 个项目吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const ids = selection.value.map((item) => item.id).join(',')
|
||||||
|
await removeProject(ids)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
await formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
if (form.id) {
|
||||||
|
await updateProject(form)
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await addProject(form)
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(form.id ? '修改失败' : '新增失败')
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
selection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const handleDialogClose = () => {
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(form, {
|
||||||
|
id: null,
|
||||||
|
competitionId: '',
|
||||||
|
projectCode: '',
|
||||||
|
projectName: '',
|
||||||
|
category: null,
|
||||||
|
eventType: null,
|
||||||
|
registrationFee: 0,
|
||||||
|
registrationStartTime: '',
|
||||||
|
registrationEndTime: '',
|
||||||
|
maxParticipants: 100,
|
||||||
|
sortOrder: 0,
|
||||||
|
rules: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
if (formRef.value) {
|
||||||
|
formRef.value.clearValidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传前检查
|
||||||
|
const beforeUpload = (file) => {
|
||||||
|
if (!queryParams.competitionId) {
|
||||||
|
ElMessage.warning('请先选择赛事')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const isExcel =
|
||||||
|
file.type === 'application/vnd.ms-excel' ||
|
||||||
|
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
if (!isExcel) {
|
||||||
|
ElMessage.error('只能上传 Excel 文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5
|
||||||
|
if (!isLt5M) {
|
||||||
|
ElMessage.error('文件大小不能超过 5MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传成功
|
||||||
|
const handleUploadSuccess = (response) => {
|
||||||
|
if (response.success) {
|
||||||
|
ElMessage.success('导入成功')
|
||||||
|
fetchData()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.msg || '导入失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败
|
||||||
|
const handleUploadError = () => {
|
||||||
|
ElMessage.error('导入失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
const res = await exportProjects(queryParams)
|
||||||
|
const blob = new Blob([res], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `项目列表_${dayjs().format('YYYYMMDDHHmmss')}.xlsx`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
if (!date) return '-'
|
||||||
|
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
loadCompetitionList()
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.project-container {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.search-card,
|
||||||
|
.toolbar-card,
|
||||||
|
.table-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="martial-registration-detail">
|
<div class="martial-registration-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
||||||
<h2 class="page-title">报名详情</h2>
|
<h2 class="page-title">报名详情</h2>
|
||||||
@@ -273,7 +273,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
this.$router.push('/martial/order/list')
|
this.$router.go(-1)
|
||||||
},
|
},
|
||||||
handleExport() {
|
handleExport() {
|
||||||
this.$message.success('导出功能开发中')
|
this.$message.success('导出功能开发中')
|
||||||
@@ -283,7 +283,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.martial-registration-detail {
|
.martial-registration-container {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
|
|||||||
786
src/views/martial/result/index.vue
Normal file
786
src/views/martial/result/index.vue
Normal file
@@ -0,0 +1,786 @@
|
|||||||
|
<template>
|
||||||
|
<div class="result-container">
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form :inline="true" :model="queryParams" class="search-form">
|
||||||
|
<el-form-item label="赛事">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.competitionId"
|
||||||
|
placeholder="请选择赛事"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handleCompetitionChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.projectId"
|
||||||
|
placeholder="请选择项目"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选手姓名">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.athleteName"
|
||||||
|
placeholder="请输入选手姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="团队名称">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.teamName"
|
||||||
|
placeholder="请输入团队名称"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否最终成绩">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.isFinal"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
style="width: 130px"
|
||||||
|
>
|
||||||
|
<el-option label="是" :value="1" />
|
||||||
|
<el-option label="否" :value="0" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :icon="Search" @click="handleSearch">
|
||||||
|
查询
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<el-row :gutter="20" class="stats-row">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stats-card">
|
||||||
|
<div class="stats-content">
|
||||||
|
<div class="stats-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
|
||||||
|
<el-icon :size="30"><User /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-info">
|
||||||
|
<div class="stats-value">{{ statistics.totalCount || 0 }}</div>
|
||||||
|
<div class="stats-label">总参赛人数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stats-card">
|
||||||
|
<div class="stats-content">
|
||||||
|
<div class="stats-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
|
||||||
|
<el-icon :size="30"><Finished /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-info">
|
||||||
|
<div class="stats-value">{{ statistics.calculatedCount || 0 }}</div>
|
||||||
|
<div class="stats-label">已计算成绩</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stats-card">
|
||||||
|
<div class="stats-content">
|
||||||
|
<div class="stats-icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
|
||||||
|
<el-icon :size="30"><Trophy /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-info">
|
||||||
|
<div class="stats-value">{{ statistics.medalCount || 0 }}</div>
|
||||||
|
<div class="stats-label">已分配奖牌</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card shadow="hover" class="stats-card">
|
||||||
|
<div class="stats-content">
|
||||||
|
<div class="stats-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)">
|
||||||
|
<el-icon :size="30"><Document /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-info">
|
||||||
|
<div class="stats-value">{{ statistics.publishedCount || 0 }}</div>
|
||||||
|
<div class="stats-label">已发布成绩</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<el-card shadow="never" class="toolbar-card">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Operation"
|
||||||
|
:disabled="!selection.length"
|
||||||
|
@click="handleBatchCalculate"
|
||||||
|
>
|
||||||
|
批量计算成绩
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:icon="Sort"
|
||||||
|
:disabled="!queryParams.projectId"
|
||||||
|
@click="handleAutoRanking"
|
||||||
|
>
|
||||||
|
自动排名
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
:icon="Medal"
|
||||||
|
:disabled="!queryParams.projectId"
|
||||||
|
@click="handleAllocateMedals"
|
||||||
|
>
|
||||||
|
分配奖牌
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="info"
|
||||||
|
:icon="Promotion"
|
||||||
|
:disabled="!selection.length"
|
||||||
|
@click="handlePublish"
|
||||||
|
>
|
||||||
|
发布成绩
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:icon="Remove"
|
||||||
|
:disabled="!selection.length"
|
||||||
|
@click="handleUnpublish"
|
||||||
|
>
|
||||||
|
撤销发布
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Download"
|
||||||
|
@click="handleExport"
|
||||||
|
>
|
||||||
|
导出成绩单
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-tooltip content="刷新" placement="top">
|
||||||
|
<el-button circle :icon="Refresh" @click="fetchData" />
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card shadow="never" class="table-card">
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column prop="ranking" label="排名" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.ranking === 1" type="danger" effect="dark">🥇 {{ row.ranking }}</el-tag>
|
||||||
|
<el-tag v-else-if="row.ranking === 2" type="warning" effect="dark">🥈 {{ row.ranking }}</el-tag>
|
||||||
|
<el-tag v-else-if="row.ranking === 3" type="success" effect="dark">🥉 {{ row.ranking }}</el-tag>
|
||||||
|
<span v-else>{{ row.ranking || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="athleteName" label="选手姓名" width="120" />
|
||||||
|
<el-table-column prop="teamName" label="团队名称" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="projectName" label="项目" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="totalScore" label="总分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.totalScore !== null ? row.totalScore.toFixed(2) : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="highestScore" label="最高分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="color: #f56c6c">{{ row.highestScore !== null ? row.highestScore.toFixed(2) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="lowestScore" label="最低分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="color: #909399">{{ row.lowestScore !== null ? row.lowestScore.toFixed(2) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="validScoreCount" label="有效评分数" width="110" align="center" />
|
||||||
|
<el-table-column prop="difficultyCoefficient" label="难度系数" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
×{{ row.difficultyCoefficient || 1 }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="finalScore" label="最终成绩" width="110" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.finalScore !== null" style="color: #409eff; font-weight: bold; font-size: 16px">
|
||||||
|
{{ row.finalScore.toFixed(2) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #909399">未计算</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="medal" label="奖牌" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.medal === 'gold'" type="danger" effect="dark">🏅 金牌</el-tag>
|
||||||
|
<el-tag v-else-if="row.medal === 'silver'" type="warning" effect="dark">🥈 银牌</el-tag>
|
||||||
|
<el-tag v-else-if="row.medal === 'bronze'" type="success" effect="dark">🥉 铜牌</el-tag>
|
||||||
|
<span v-else style="color: #909399">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isPublished" label="状态" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.isPublished === 1" type="success">已发布</el-tag>
|
||||||
|
<el-tag v-else type="info">未发布</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="publishTime" label="发布时间" width="160" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.publishTime ? formatDate(row.publishTime) : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" :icon="View" @click="handleView(row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="success" :icon="Edit" @click="handleCalculate(row)">
|
||||||
|
重新计算
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.current"
|
||||||
|
v-model:page-size="queryParams.size"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="fetchData"
|
||||||
|
@current-change="fetchData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 成绩详情弹窗 -->
|
||||||
|
<el-dialog v-model="detailVisible" title="成绩详情" width="900px">
|
||||||
|
<el-descriptions :column="3" border>
|
||||||
|
<el-descriptions-item label="选手姓名">{{ detailData.athleteName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="团队名称">{{ detailData.teamName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目">{{ detailData.projectName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="排名">
|
||||||
|
<el-tag v-if="detailData.ranking === 1" type="danger" effect="dark">🥇 第1名</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.ranking === 2" type="warning" effect="dark">🥈 第2名</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.ranking === 3" type="success" effect="dark">🥉 第3名</el-tag>
|
||||||
|
<span v-else>第{{ detailData.ranking }}名</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="总分">{{ detailData.totalScore?.toFixed(2) || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最终成绩">
|
||||||
|
<span style="color: #409eff; font-weight: bold; font-size: 18px">
|
||||||
|
{{ detailData.finalScore?.toFixed(2) || '-' }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最高分">
|
||||||
|
<span style="color: #f56c6c">{{ detailData.highestScore?.toFixed(2) || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="最低分">
|
||||||
|
<span style="color: #909399">{{ detailData.lowestScore?.toFixed(2) || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="有效评分数">{{ detailData.validScoreCount || 0 }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="难度系数">×{{ detailData.difficultyCoefficient || 1 }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="奖牌">
|
||||||
|
<el-tag v-if="detailData.medal === 'gold'" type="danger" effect="dark">🏅 金牌</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.medal === 'silver'" type="warning" effect="dark">🥈 银牌</el-tag>
|
||||||
|
<el-tag v-else-if="detailData.medal === 'bronze'" type="success" effect="dark">🥉 铜牌</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="发布状态">
|
||||||
|
<el-tag v-if="detailData.isPublished === 1" type="success">已发布</el-tag>
|
||||||
|
<el-tag v-else type="info">未发布</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<h4>评分明细</h4>
|
||||||
|
<el-table :data="detailData.scoreList" border size="small">
|
||||||
|
<el-table-column prop="judgeName" label="裁判" width="120" />
|
||||||
|
<el-table-column prop="score" label="评分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.score?.toFixed(2) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="deductionPoints" label="扣分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="color: #f56c6c">-{{ row.deductionPoints?.toFixed(2) || 0 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isExcluded" label="是否去除" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.isExcluded" type="info" size="small">已去除</el-tag>
|
||||||
|
<el-tag v-else type="success" size="small">有效</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="scoreTime" label="评分时间" width="160" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDate(row.scoreTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 奖牌分配弹窗 -->
|
||||||
|
<el-dialog v-model="medalDialogVisible" title="奖牌分配" width="500px">
|
||||||
|
<el-form :model="medalForm" label-width="100px">
|
||||||
|
<el-form-item label="金牌数量">
|
||||||
|
<el-input-number v-model="medalForm.goldCount" :min="0" :max="20" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="银牌数量">
|
||||||
|
<el-input-number v-model="medalForm.silverCount" :min="0" :max="20" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="铜牌数量">
|
||||||
|
<el-input-number v-model="medalForm.bronzeCount" :min="0" :max="20" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-alert type="info" :closable="false" show-icon>
|
||||||
|
<template #title>
|
||||||
|
将根据当前项目的排名自动分配奖牌
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="medalDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="medalLoading" @click="handleMedalSubmit">
|
||||||
|
确认分配
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Refresh,
|
||||||
|
View,
|
||||||
|
Edit,
|
||||||
|
Operation,
|
||||||
|
Sort,
|
||||||
|
Medal,
|
||||||
|
Promotion,
|
||||||
|
Remove,
|
||||||
|
Download,
|
||||||
|
User,
|
||||||
|
Finished,
|
||||||
|
Trophy,
|
||||||
|
Document
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
import {
|
||||||
|
getResultList,
|
||||||
|
calculateResult,
|
||||||
|
batchCalculateResults,
|
||||||
|
autoRanking,
|
||||||
|
allocateMedals,
|
||||||
|
publishResult,
|
||||||
|
unpublishResult,
|
||||||
|
getResultDetail,
|
||||||
|
getResultStatistics,
|
||||||
|
exportResults
|
||||||
|
} from '@/api/martial/result'
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
import { getProjectsByCompetition } from '@/api/martial/project'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
// 数据定义
|
||||||
|
const loading = ref(false)
|
||||||
|
const medalLoading = ref(false)
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const selection = ref([])
|
||||||
|
const competitionList = ref([])
|
||||||
|
const projectList = ref([])
|
||||||
|
const detailVisible = ref(false)
|
||||||
|
const medalDialogVisible = ref(false)
|
||||||
|
const detailData = ref({})
|
||||||
|
const statistics = ref({
|
||||||
|
totalCount: 0,
|
||||||
|
calculatedCount: 0,
|
||||||
|
medalCount: 0,
|
||||||
|
publishedCount: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
competitionId: '',
|
||||||
|
projectId: '',
|
||||||
|
athleteName: '',
|
||||||
|
teamName: '',
|
||||||
|
isFinal: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 奖牌分配表单
|
||||||
|
const medalForm = reactive({
|
||||||
|
goldCount: 1,
|
||||||
|
silverCount: 2,
|
||||||
|
bronzeCount: 3
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载赛事列表
|
||||||
|
const loadCompetitionList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getCompetitionList(1, 1000, { status: 1 })
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
competitionList.value = res.data.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载赛事列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赛事变化
|
||||||
|
const handleCompetitionChange = async (competitionId) => {
|
||||||
|
queryParams.projectId = ''
|
||||||
|
projectList.value = []
|
||||||
|
|
||||||
|
if (competitionId) {
|
||||||
|
try {
|
||||||
|
const res = await getProjectsByCompetition(competitionId)
|
||||||
|
if (res.data) {
|
||||||
|
projectList.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载统计数据
|
||||||
|
const loadStatistics = async () => {
|
||||||
|
if (!queryParams.projectId && !queryParams.competitionId) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getResultStatistics({
|
||||||
|
projectId: queryParams.projectId,
|
||||||
|
competitionId: queryParams.competitionId
|
||||||
|
})
|
||||||
|
if (res.data) {
|
||||||
|
statistics.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载统计数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getResultList(
|
||||||
|
queryParams.current,
|
||||||
|
queryParams.size,
|
||||||
|
queryParams
|
||||||
|
)
|
||||||
|
if (res.data) {
|
||||||
|
tableData.value = res.data.records || []
|
||||||
|
total.value = res.data.total || 0
|
||||||
|
}
|
||||||
|
await loadStatistics()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('获取数据失败')
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
queryParams.current = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(queryParams, {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
competitionId: '',
|
||||||
|
projectId: '',
|
||||||
|
athleteName: '',
|
||||||
|
teamName: '',
|
||||||
|
isFinal: ''
|
||||||
|
})
|
||||||
|
projectList.value = []
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const handleView = async (row) => {
|
||||||
|
try {
|
||||||
|
const res = await getResultDetail(row.id)
|
||||||
|
if (res.data) {
|
||||||
|
detailData.value = res.data
|
||||||
|
detailVisible.value = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('获取详情失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算单个成绩
|
||||||
|
const handleCalculate = async (row) => {
|
||||||
|
try {
|
||||||
|
await calculateResult({ resultId: row.id })
|
||||||
|
ElMessage.success('计算成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('计算失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量计算成绩
|
||||||
|
const handleBatchCalculate = () => {
|
||||||
|
ElMessageBox.confirm(`确定要计算选中的 ${selection.value.length} 个选手的成绩吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const ids = selection.value.map((item) => item.id)
|
||||||
|
await batchCalculateResults({ resultIds: ids })
|
||||||
|
ElMessage.success('批量计算成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('批量计算失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动排名
|
||||||
|
const handleAutoRanking = () => {
|
||||||
|
if (!queryParams.projectId) {
|
||||||
|
ElMessage.warning('请先选择项目')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessageBox.confirm('确定要对当前项目的所有选手自动排名吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
await autoRanking({ projectId: queryParams.projectId })
|
||||||
|
ElMessage.success('自动排名成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('自动排名失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配奖牌
|
||||||
|
const handleAllocateMedals = () => {
|
||||||
|
if (!queryParams.projectId) {
|
||||||
|
ElMessage.warning('请先选择项目')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
medalDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交奖牌分配
|
||||||
|
const handleMedalSubmit = async () => {
|
||||||
|
medalLoading.value = true
|
||||||
|
try {
|
||||||
|
await allocateMedals({
|
||||||
|
projectId: queryParams.projectId,
|
||||||
|
...medalForm
|
||||||
|
})
|
||||||
|
ElMessage.success('奖牌分配成功')
|
||||||
|
medalDialogVisible.value = false
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('奖牌分配失败')
|
||||||
|
} finally {
|
||||||
|
medalLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布成绩
|
||||||
|
const handlePublish = () => {
|
||||||
|
ElMessageBox.confirm(`确定要发布选中的 ${selection.value.length} 个选手的成绩吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const ids = selection.value.map((item) => item.id)
|
||||||
|
await publishResult({ resultIds: ids })
|
||||||
|
ElMessage.success('发布成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('发布失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销发布
|
||||||
|
const handleUnpublish = () => {
|
||||||
|
ElMessageBox.confirm(`确定要撤销选中的 ${selection.value.length} 个选手的成绩发布吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const ids = selection.value.map((item) => item.id)
|
||||||
|
await unpublishResult({ resultIds: ids })
|
||||||
|
ElMessage.success('撤销成功')
|
||||||
|
fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('撤销失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成绩单
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
const res = await exportResults(queryParams)
|
||||||
|
const blob = new Blob([res], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
})
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `成绩单_${dayjs().format('YYYYMMDDHHmmss')}.xlsx`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (val) => {
|
||||||
|
selection.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
if (!date) return '-'
|
||||||
|
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
loadCompetitionList()
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.result-container {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.search-card,
|
||||||
|
.toolbar-card,
|
||||||
|
.table-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.stats-card {
|
||||||
|
.stats-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.stats-icon {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.stats-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -241,7 +241,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
this.$router.push('/martial/order/list')
|
this.$router.go(-1)
|
||||||
},
|
},
|
||||||
handleVenueCommand(command, groupIndex) {
|
handleVenueCommand(command, groupIndex) {
|
||||||
const venueName = command === 'venue1' ? '一号场地' : '二号场地'
|
const venueName = command === 'venue1' ? '一号场地' : '二号场地'
|
||||||
|
|||||||
1231
src/views/martial/scheduleAthlete/index.vue
Normal file
1231
src/views/martial/scheduleAthlete/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/views/martial/schedulePlan/index.vue
Normal file
BIN
src/views/martial/schedulePlan/index.vue
Normal file
Binary file not shown.
@@ -21,11 +21,11 @@ export default ({ mode, command }) => {
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: path => path.replace(/^\/api/, ''),
|
rewrite: path => path.replace(/^\/api/, ''),
|
||||||
},
|
},
|
||||||
// 武术业务模块保留 /api 前缀
|
// // 武术业务模块保留 /api 前缀
|
||||||
'/api': {
|
// '/api': {
|
||||||
target: 'http://localhost:8123',
|
// target: 'http://localhost:8123',
|
||||||
changeOrigin: true,
|
// changeOrigin: true,
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user