fix bugs
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(dir:*)",
|
"Bash(dir:*)",
|
||||||
"Bash(npm run build:*)"
|
"Bash(npm run build:*)",
|
||||||
|
"Bash(findstr:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
BIN
doc/image/异常错误页面/微信图片_20251129185716_267_2.png
Normal file
BIN
doc/image/异常错误页面/微信图片_20251129185716_267_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
BIN
doc/image/异常错误页面/微信图片_20251130112538_268_2.png
Normal file
BIN
doc/image/异常错误页面/微信图片_20251130112538_268_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
doc/image/异常错误页面/微信图片_20251130112801_269_2.png
Normal file
BIN
doc/image/异常错误页面/微信图片_20251130112801_269_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
BIN
doc/image/异常错误页面/微信图片_20251130113116_270_2.png
Normal file
BIN
doc/image/异常错误页面/微信图片_20251130113116_270_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 225 KiB |
92
src/api/martial/banner.js
Normal file
92
src/api/martial/banner.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 轮播图管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播图分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const getBannerList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取轮播图详情
|
||||||
|
* @param {Number} id - 轮播图主键ID
|
||||||
|
*/
|
||||||
|
export const getBannerDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增轮播图
|
||||||
|
* @param {Object} data - 轮播图数据
|
||||||
|
*/
|
||||||
|
export const addBanner = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改轮播图
|
||||||
|
* @param {Object} data - 轮播图数据
|
||||||
|
*/
|
||||||
|
export const updateBanner = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除轮播图
|
||||||
|
* @param {String} ids - 轮播图ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeBanner = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取启用的轮播图列表(小程序端使用)
|
||||||
|
*/
|
||||||
|
export const getActiveBannerList = () => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/active-list',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改轮播图状态
|
||||||
|
* @param {Number} id - 轮播图ID
|
||||||
|
* @param {Number} status - 状态(0-禁用 1-启用)
|
||||||
|
*/
|
||||||
|
export const updateBannerStatus = (id, status) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/banner/update-status',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, status }
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -255,3 +255,89 @@ export const removeVenue = (ids) => {
|
|||||||
params: { ids }
|
params: { ids }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 赛事管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增赛事
|
||||||
|
* @param {Object} data - 赛事数据
|
||||||
|
* @param {String} data.competitionName - 赛事名称
|
||||||
|
* @param {String} data.organizer - 主办单位
|
||||||
|
* @param {String} data.location - 地区
|
||||||
|
* @param {String} data.venue - 详细地点
|
||||||
|
* @param {String} data.registrationStartTime - 报名开始时间
|
||||||
|
* @param {String} data.registrationEndTime - 报名结束时间
|
||||||
|
* @param {String} data.competitionStartTime - 比赛开始时间
|
||||||
|
* @param {String} data.competitionEndTime - 比赛结束时间
|
||||||
|
* @param {String} data.introduction - 赛事简介
|
||||||
|
* @param {Array} data.posterImages - 宣传图片
|
||||||
|
* @param {String} data.contactPerson - 联系人
|
||||||
|
* @param {String} data.contactPhone - 联系电话
|
||||||
|
* @param {String} data.contactEmail - 联系邮箱
|
||||||
|
* @param {String} data.rules - 竞赛规则
|
||||||
|
* @param {String} data.requirements - 参赛要求
|
||||||
|
* @param {String} data.awards - 奖项设置
|
||||||
|
* @param {Array} data.regulationFiles - 规程文件
|
||||||
|
* @param {Array} data.schedule - 活动日程
|
||||||
|
*/
|
||||||
|
export const addCompetition = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/competition/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赛事列表查询
|
||||||
|
* @param {Number} current - 当前页
|
||||||
|
* @param {Number} size - 每页条数
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const getCompetitionList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/competition/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取赛事详情
|
||||||
|
* @param {Number} id - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getCompetitionDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/competition/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改赛事
|
||||||
|
* @param {Object} data - 赛事数据
|
||||||
|
*/
|
||||||
|
export const updateCompetition = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/competition/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除赛事
|
||||||
|
* @param {String} ids - 赛事ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeCompetition = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/competition/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
142
src/api/martial/participant.js
Normal file
142
src/api/martial/participant.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 参赛选手管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参赛选手分页查询
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const getParticipantList = (competitionId, current, size, params = {}) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
competitionId,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参赛选手详情
|
||||||
|
* @param {Number} id - 选手主键ID
|
||||||
|
*/
|
||||||
|
export const getParticipantDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增参赛选手
|
||||||
|
* @param {Object} data - 选手数据
|
||||||
|
* @param {Number} data.competitionId - 赛事ID
|
||||||
|
* @param {String} data.playerName - 选手姓名
|
||||||
|
* @param {Number} data.gender - 性别(1-男,2-女)
|
||||||
|
* @param {Number} data.age - 年龄
|
||||||
|
* @param {String} data.contactPhone - 联系电话
|
||||||
|
* @param {String} data.organization - 所属单位
|
||||||
|
* @param {String} data.idCard - 身份证号
|
||||||
|
* @param {String} data.projectName - 参赛项目
|
||||||
|
* @param {String} data.category - 组别
|
||||||
|
* @param {Number} data.orderNum - 出场顺序
|
||||||
|
* @param {String} data.introduction - 选手简介
|
||||||
|
* @param {Array} data.attachments - 附件列表
|
||||||
|
* @param {String} data.remark - 备注
|
||||||
|
*/
|
||||||
|
export const addParticipant = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改参赛选手
|
||||||
|
* @param {Object} data - 选手数据
|
||||||
|
*/
|
||||||
|
export const updateParticipant = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除参赛选手
|
||||||
|
* @param {String} ids - 选手ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeParticipant = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新出场顺序
|
||||||
|
* @param {Number} id - 选手ID
|
||||||
|
* @param {Number} orderNum - 出场顺序
|
||||||
|
*/
|
||||||
|
export const updateOrder = (id, orderNum) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/update-order',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, orderNum }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入参赛选手
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {File} file - Excel文件
|
||||||
|
*/
|
||||||
|
export const importParticipants = (competitionId, file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('competitionId', competitionId)
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出参赛选手名单
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const exportParticipants = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/export',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新出场顺序
|
||||||
|
* @param {Array} data - 选手顺序数据 [{id: 1, orderNum: 1}, {id: 2, orderNum: 2}]
|
||||||
|
*/
|
||||||
|
export const batchUpdateOrder = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/participant/batch-update-order',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
91
src/api/martial/referee.js
Normal file
91
src/api/martial/referee.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 评委管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评委分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {String} params.keyword - 关键词搜索(姓名/手机号)
|
||||||
|
* @param {Number} params.refereeType - 裁判类型(1-主裁判,2-普通裁判)
|
||||||
|
*/
|
||||||
|
export const getRefereeList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评委详情
|
||||||
|
* @param {Number} id - 评委主键ID
|
||||||
|
*/
|
||||||
|
export const getRefereeDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增评委
|
||||||
|
* @param {Object} data - 评委数据
|
||||||
|
* @param {String} data.name - 姓名
|
||||||
|
* @param {Number} data.gender - 性别(1-男,2-女)
|
||||||
|
* @param {String} data.phone - 手机号
|
||||||
|
* @param {String} data.idCard - 身份证号
|
||||||
|
* @param {Number} data.refereeType - 裁判类型(1-主裁判,2-普通裁判)
|
||||||
|
* @param {String} data.level - 等级/职称
|
||||||
|
* @param {String} data.specialty - 擅长项目
|
||||||
|
* @param {String} data.remark - 备注
|
||||||
|
*/
|
||||||
|
export const addReferee = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/save',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改评委
|
||||||
|
* @param {Object} data - 评委数据
|
||||||
|
*/
|
||||||
|
export const updateReferee = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除评委
|
||||||
|
* @param {String} ids - 评委ID,多个用逗号分隔
|
||||||
|
*/
|
||||||
|
export const removeReferee = (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可用评委列表(用于下拉选择)
|
||||||
|
* @param {Number} refereeType - 裁判类型(可选)
|
||||||
|
*/
|
||||||
|
export const getAvailableReferees = (refereeType) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/referee/available',
|
||||||
|
method: 'get',
|
||||||
|
params: { refereeType }
|
||||||
|
})
|
||||||
|
}
|
||||||
84
src/api/martial/score.js
Normal file
84
src/api/martial/score.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
// ==================== 评分管理接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评分分页查询
|
||||||
|
* @param {Number} current - 当前页,默认1
|
||||||
|
* @param {Number} size - 每页条数,默认10
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const getScoreList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评分详情
|
||||||
|
* @param {Number} id - 评分主键ID
|
||||||
|
*/
|
||||||
|
export const getScoreDetail = (id) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选手的所有裁判评分
|
||||||
|
* @param {Number} playerId - 选手ID
|
||||||
|
* @param {Number} projectId - 比赛项目ID
|
||||||
|
*/
|
||||||
|
export const getPlayerScores = (playerId, projectId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/player-scores',
|
||||||
|
method: 'get',
|
||||||
|
params: { playerId, projectId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出评分数据
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
*/
|
||||||
|
export const exportScores = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/score/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取场地列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const getVenueList = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/venue/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取比赛项目列表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
* @param {Number} venueId - 场地ID(可选)
|
||||||
|
*/
|
||||||
|
export const getProjectList = (competitionId, venueId) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/blade-martial/project/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId, venueId }
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,44 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-container" ref="login" @keyup.enter="handleLogin">
|
<div class="login-container" ref="login" @keyup.enter="handleLogin">
|
||||||
<div class="stars"></div>
|
<div class="login-wrapper animated bounceInDown">
|
||||||
<div class="stars2"></div>
|
<!-- 左侧图形装饰区 -->
|
||||||
<div class="stars3"></div>
|
|
||||||
<div class="login-weaper animated bounceInDown">
|
|
||||||
<div class="login-left">
|
<div class="login-left">
|
||||||
<div class="login-time">
|
<div class="decoration-bg">
|
||||||
{{ time }}
|
<div class="circle circle-1"></div>
|
||||||
|
<div class="circle circle-2"></div>
|
||||||
|
<div class="circle circle-3"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-left-content">
|
|
||||||
<h1 class="title">武术赛事通</h1>
|
<div class="content-wrapper">
|
||||||
<p class="subtitle">专业的武术赛事管理平台</p>
|
<div class="logo-area">
|
||||||
<div class="feature-box">
|
<div class="logo-circle">
|
||||||
<div class="feature-item">
|
<div class="logo-icon">武</div>
|
||||||
<span>赛事报名管理</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-item">
|
<h1 class="brand-title">武术赛事通</h1>
|
||||||
<span>赛程编排</span>
|
<p class="brand-subtitle">专业的武术赛事管理平台</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-points">
|
||||||
|
<div class="point-item">
|
||||||
|
<i class="el-icon-trophy"></i>
|
||||||
|
<span>专业赛事管理</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-item">
|
<div class="point-item">
|
||||||
<span>赛事调度</span>
|
<i class="el-icon-s-flag"></i>
|
||||||
|
<span>智能编排调度</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-item">
|
<div class="point-item">
|
||||||
<span>数据统计分析</span>
|
<i class="el-icon-medal"></i>
|
||||||
|
<span>权威评分系统</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-border">
|
|
||||||
<div class="login-main">
|
<!-- 右侧登录表单区 -->
|
||||||
<h4 class="login-title">
|
<div class="login-right">
|
||||||
登录 武术赛事通
|
<div class="login-form-container">
|
||||||
|
<h2 class="login-title">
|
||||||
|
系统登录
|
||||||
<top-lang></top-lang>
|
<top-lang></top-lang>
|
||||||
</h4>
|
</h2>
|
||||||
<userLogin></userLogin>
|
<userLogin></userLogin>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="copyright">
|
||||||
|
© 2025 武术赛事通 All Rights Reserved
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import userLogin from './userlogin.vue';
|
import userLogin from './userlogin.vue';
|
||||||
import codeLogin from './codelogin.vue';
|
import codeLogin from './codelogin.vue';
|
||||||
@@ -117,7 +131,6 @@ export default {
|
|||||||
.dispatch('LoginBySocial', this.socialForm)
|
.dispatch('LoginBySocial', this.socialForm)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location.href = topUrl.split(redirectUrl)[0];
|
window.location.href = topUrl.split(redirectUrl)[0];
|
||||||
//加载工作流路由集
|
|
||||||
this.loadFlowRoutes();
|
this.loadFlowRoutes();
|
||||||
this.$router.push(this.tagWel);
|
this.$router.push(this.tagWel);
|
||||||
loading.close();
|
loading.close();
|
||||||
@@ -139,7 +152,6 @@ export default {
|
|||||||
.dispatch('LoginBySso', this.socialForm)
|
.dispatch('LoginBySso', this.socialForm)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location.href = topUrl.split(ssoCode)[0];
|
window.location.href = topUrl.split(ssoCode)[0];
|
||||||
//加载工作流路由集
|
|
||||||
this.loadFlowRoutes();
|
this.loadFlowRoutes();
|
||||||
this.$router.push(this.tagWel);
|
this.$router.push(this.tagWel);
|
||||||
loading.close();
|
loading.close();
|
||||||
@@ -156,220 +168,256 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.login-container {
|
.login-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
|
background: #f5f5f5;
|
||||||
position: relative;
|
padding: 20px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@function multiple-box-shadow($n) {
|
.login-wrapper {
|
||||||
$value: '#{random(2000)}px #{random(2000)}px #FFF';
|
display: flex;
|
||||||
@for $i from 2 through $n {
|
|
||||||
$value: '#{$value} , #{random(2000)}px #{random(2000)}px #FFF';
|
|
||||||
}
|
|
||||||
@return unquote($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$shadows-small: multiple-box-shadow(700);
|
|
||||||
$shadows-medium: multiple-box-shadow(200);
|
|
||||||
$shadows-big: multiple-box-shadow(100);
|
|
||||||
|
|
||||||
.stars {
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-small;
|
|
||||||
animation: animateStars 50s linear infinite;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
top: 2000px;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stars2 {
|
|
||||||
width: 2px;
|
|
||||||
height: 2px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-medium;
|
|
||||||
animation: animateStars 100s linear infinite;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
top: 2000px;
|
|
||||||
width: 2px;
|
|
||||||
height: 2px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-medium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stars3 {
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-big;
|
|
||||||
animation: animateStars 150s linear infinite;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
top: 2000px;
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: $shadows-big;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animateStars {
|
|
||||||
from {
|
|
||||||
transform: translateY(0px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateY(-2000px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-weaper {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
display: flex;
|
max-width: 100%;
|
||||||
border-radius: 8px;
|
min-height: 600px;
|
||||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.3);
|
background: #fff;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.12);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 左侧图形装饰区
|
||||||
.login-left {
|
.login-left {
|
||||||
|
position: relative;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
background: linear-gradient(135deg, rgba(27, 39, 53, 0.95) 0%, rgba(9, 10, 15, 0.95) 100%);
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
padding: 40px;
|
|
||||||
position: relative;
|
|
||||||
color: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: radial-gradient(circle at 50% 50%, rgba(33, 147, 176, 0.3) 0%, transparent 50%),
|
|
||||||
radial-gradient(circle at 20% 80%, rgba(40, 60, 190, 0.3) 0%, transparent 50%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-time {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-left-content {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
justify-content: center;
|
||||||
}
|
padding: 60px 40px;
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 16px;
|
|
||||||
opacity: 0.8;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-box {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 15px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-item {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
padding: 15px 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
backdrop-filter: blur(5px);
|
}
|
||||||
|
|
||||||
&::before {
|
.decoration-bg {
|
||||||
content: '';
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.circle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
border-radius: 50%;
|
||||||
left: -100%;
|
background: rgba(255, 255, 255, 0.08);
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
|
||||||
transition: 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&.circle-1 {
|
||||||
background: rgba(255, 255, 255, 0.15);
|
width: 400px;
|
||||||
border-color: rgba(255, 255, 255, 0.3);
|
height: 400px;
|
||||||
transform: translateY(-2px);
|
top: -100px;
|
||||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
right: -100px;
|
||||||
|
animation: float 8s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
&::before {
|
&.circle-2 {
|
||||||
left: 100%;
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
bottom: -80px;
|
||||||
|
left: -80px;
|
||||||
|
animation: float 10s ease-in-out infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.circle-3 {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
top: 50%;
|
||||||
|
left: 20%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
animation: float 6s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-border {
|
@keyframes float {
|
||||||
width: 50%;
|
0%, 100% {
|
||||||
padding: 40px;
|
transform: translateY(0) scale(1);
|
||||||
background: #fff;
|
}
|
||||||
display: flex;
|
50% {
|
||||||
align-items: center;
|
transform: translateY(-20px) scale(1.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-main {
|
.content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 360px;
|
}
|
||||||
|
|
||||||
|
.logo-area {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-circle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
font-size: 56px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-title {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-points {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
max-width: 280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.point-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
transform: translateX(8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧登录表单区
|
||||||
|
.login-right {
|
||||||
|
width: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 60px 50px 30px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
font-size: 20px;
|
font-size: 28px;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 30px;
|
font-weight: 700;
|
||||||
|
margin: 0 0 40px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 12px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -15px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60px;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.login-wrapper {
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-left,
|
||||||
|
.login-right {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-left {
|
||||||
|
padding: 40px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-right {
|
||||||
|
padding: 40px 30px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-circle {
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
font-size: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-title {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-area {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-points {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -412,4 +412,102 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style lang="scss" scoped>
|
||||||
|
.login-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input) {
|
||||||
|
.el-input__wrapper {
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
box-shadow: 0 0 0 1px #e0e0e0 inset;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 0 1px #dc2626 inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
box-shadow: 0 0 0 2px #dc2626 inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__prefix {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__suffix {
|
||||||
|
.el-icon-view {
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #dc2626;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code {
|
||||||
|
height: 40px;
|
||||||
|
margin-left: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #dc2626;
|
||||||
|
box-shadow: 0 2px 8px rgba(220, 38, 38, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-submit {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(220, 38, 38, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -47,13 +47,39 @@ export default [
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/martial/order/list',
|
redirect: '/martial/order/list',
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'competition/list',
|
||||||
|
name: '赛事管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/competition/list.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/detail.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/registration/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'order/list',
|
||||||
|
name: '订单管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/order/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'schedule/list',
|
path: 'schedule/list',
|
||||||
@@ -61,7 +87,7 @@ export default [
|
|||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/schedule/list.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/schedule/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'dispatch/list',
|
path: 'dispatch/list',
|
||||||
@@ -69,7 +95,48 @@ export default [
|
|||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
},
|
},
|
||||||
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/dispatch/list.vue'),
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/dispatch/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'banner/index',
|
||||||
|
name: '轮播图管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/banner/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'referee/list',
|
||||||
|
name: '评委管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/referee/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'score/index',
|
||||||
|
name: '评分管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/score/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'participant/list',
|
||||||
|
name: '参赛选手管理',
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/participant/list.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'participant/manage',
|
||||||
|
name: '选手详情',
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
menu: false,
|
||||||
|
},
|
||||||
|
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/participant/manage.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
// Element Plus 主题色覆盖 - 改为红色主题
|
||||||
|
:root {
|
||||||
|
--el-color-primary: #dc2626;
|
||||||
|
--el-color-primary-light-3: #ef4444;
|
||||||
|
--el-color-primary-light-5: #f87171;
|
||||||
|
--el-color-primary-light-7: #fca5a5;
|
||||||
|
--el-color-primary-light-8: #fecaca;
|
||||||
|
--el-color-primary-light-9: #fee2e2;
|
||||||
|
--el-color-primary-dark-2: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
.el-card.is-always-shadow {
|
.el-card.is-always-shadow {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #031527;
|
background: linear-gradient(180deg, #1a1a1a 0%, #2d2d2d 100%);
|
||||||
transition: width .2s;
|
transition: width .2s;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
box-shadow: 2px 0 6px rgba(0, 0, 0, .15);
|
||||||
|
|
||||||
.el-scrollbar__wrap {
|
.el-scrollbar__wrap {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -60,11 +60,11 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
background: #409eff;
|
background: linear-gradient(180deg, #dc2626 0%, #991b1b 100%);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, .8);
|
background-color: rgba(220, 38, 38, .12);
|
||||||
|
|
||||||
i, span {
|
i, span {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
color: #ccc;
|
color: #ccc;
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
color: #409EFF;
|
color: #dc2626;
|
||||||
border-bottom: 3px solid #409EFF;
|
border-bottom: 3px solid #dc2626;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #409EFF;
|
background-color: #dc2626;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,11 +116,11 @@
|
|||||||
.avue-logo {
|
.avue-logo {
|
||||||
height: $top_height;
|
height: $top_height;
|
||||||
line-height: $top_height;
|
line-height: $top_height;
|
||||||
background-color: #031527;
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 0 2px 4px 0 rgba(220, 38, 38, 0.2);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&_title {
|
&_title {
|
||||||
|
|||||||
434
src/views/martial/banner/index.vue
Normal file
434
src/views/martial/banner/index.vue
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
<template>
|
||||||
|
<div class="banner-container">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2 class="page-title">轮播图管理</h2>
|
||||||
|
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">
|
||||||
|
新增轮播图
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.keyword"
|
||||||
|
placeholder="搜索轮播图标题"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="searchForm.status" placeholder="状态" clearable size="small" style="width: 120px">
|
||||||
|
<el-option label="全部" :value="null"></el-option>
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button size="small" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="title" label="轮播图标题" min-width="150"></el-table-column>
|
||||||
|
<el-table-column prop="imageUrl" label="轮播图图片" width="150" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-image
|
||||||
|
v-if="scope.row.imageUrl"
|
||||||
|
:src="scope.row.imageUrl"
|
||||||
|
:preview-src-list="[scope.row.imageUrl]"
|
||||||
|
style="width: 100px; height: 50px; cursor: pointer;"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
<span v-else>暂无图片</span>
|
||||||
|
</template>
|
||||||
|
</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="status" label="状态" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.status"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="0"
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="禁用"
|
||||||
|
@change="handleStatusChange(scope.row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="160"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pagination.current"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pagination.size"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
:total="pagination.total"
|
||||||
|
small
|
||||||
|
></el-pagination>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
>
|
||||||
|
<el-form ref="bannerForm" :model="formData" :rules="rules" label-width="100px">
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input v-model="formData.title" placeholder="请输入轮播图标题" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="图片链接" prop="imageUrl">
|
||||||
|
<el-input v-model="formData.imageUrl" placeholder="请输入图片链接地址" clearable>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="formData.imageUrl = 'https://via.placeholder.com/800x400'">使用示例</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<div v-if="formData.imageUrl" style="margin-top: 10px;">
|
||||||
|
<el-image :src="formData.imageUrl" style="width: 200px; height: 100px;" fit="cover" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="跳转链接" prop="linkUrl">
|
||||||
|
<el-input v-model="formData.linkUrl" placeholder="选填,点击轮播图跳转的链接地址" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="排序" prop="sortOrder">
|
||||||
|
<el-input-number v-model="formData.sortOrder" :min="0" :max="999" style="width: 100%"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-radio-group v-model="formData.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="0">禁用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BannerList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitLoading: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogTitle: '新增轮播图',
|
||||||
|
isEdit: false,
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: null
|
||||||
|
},
|
||||||
|
allTableData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: '2025年全国武术锦标赛',
|
||||||
|
imageUrl: 'https://via.placeholder.com/800x400/dc2626/ffffff?text=2025年全国武术锦标赛',
|
||||||
|
linkUrl: '/martial/competition/list',
|
||||||
|
sortOrder: 1,
|
||||||
|
status: 1,
|
||||||
|
createTime: '2025-11-20 10:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '青少年武术大赛',
|
||||||
|
imageUrl: 'https://via.placeholder.com/800x400/991b1b/ffffff?text=青少年武术大赛',
|
||||||
|
linkUrl: '/martial/competition/list',
|
||||||
|
sortOrder: 2,
|
||||||
|
status: 1,
|
||||||
|
createTime: '2025-11-21 11:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '传统武术邀请赛',
|
||||||
|
imageUrl: 'https://via.placeholder.com/800x400/dc2626/ffffff?text=传统武术邀请赛',
|
||||||
|
linkUrl: '/martial/competition/list',
|
||||||
|
sortOrder: 3,
|
||||||
|
status: 0,
|
||||||
|
createTime: '2025-11-22 14:00:00'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tableData: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
id: null,
|
||||||
|
title: '',
|
||||||
|
imageUrl: '',
|
||||||
|
linkUrl: '',
|
||||||
|
sortOrder: 0,
|
||||||
|
status: 1
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
title: [
|
||||||
|
{ required: true, message: '请输入轮播图标题', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
imageUrl: [
|
||||||
|
{ required: true, message: '请输入图片链接', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
sortOrder: [
|
||||||
|
{ required: true, message: '请输入排序', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
status: [
|
||||||
|
{ required: true, message: '请选择状态', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadBannerList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
loadBannerList() {
|
||||||
|
const savedData = localStorage.getItem('bannerList')
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.allTableData = JSON.parse(savedData)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载轮播图数据失败', e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 首次加载,保存默认数据
|
||||||
|
this.saveBannerList()
|
||||||
|
}
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存数据到 localStorage
|
||||||
|
saveBannerList() {
|
||||||
|
localStorage.setItem('bannerList', JSON.stringify(this.allTableData))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
fetchData() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 过滤数据
|
||||||
|
let filteredData = [...this.allTableData]
|
||||||
|
|
||||||
|
// 搜索过滤
|
||||||
|
if (this.searchForm.keyword) {
|
||||||
|
const keyword = this.searchForm.keyword.toLowerCase()
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.title.toLowerCase().includes(keyword)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态过滤
|
||||||
|
if (this.searchForm.status !== null && this.searchForm.status !== '') {
|
||||||
|
filteredData = filteredData.filter(item => item.status === this.searchForm.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按排序字段排序
|
||||||
|
filteredData.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||||
|
|
||||||
|
this.pagination.total = filteredData.length
|
||||||
|
|
||||||
|
// 分页处理
|
||||||
|
const start = (this.pagination.current - 1) * this.pagination.size
|
||||||
|
const end = start + this.pagination.size
|
||||||
|
this.tableData = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
handleSearch() {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
keyword: '',
|
||||||
|
status: null
|
||||||
|
}
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCurrentChange(current) {
|
||||||
|
this.pagination.current = current
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
handleAdd() {
|
||||||
|
this.dialogTitle = '新增轮播图'
|
||||||
|
this.isEdit = false
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
handleEdit(row) {
|
||||||
|
this.dialogTitle = '编辑轮播图'
|
||||||
|
this.isEdit = true
|
||||||
|
this.formData = { ...row }
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定删除该轮播图吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const index = this.allTableData.findIndex(item => item.id === row.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.allTableData.splice(index, 1)
|
||||||
|
this.saveBannerList()
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 状态变更
|
||||||
|
handleStatusChange(row) {
|
||||||
|
const index = this.allTableData.findIndex(item => item.id === row.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.allTableData[index].status = row.status
|
||||||
|
this.saveBannerList()
|
||||||
|
this.$message.success('状态更新成功')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.bannerForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.isEdit) {
|
||||||
|
// 编辑
|
||||||
|
const index = this.allTableData.findIndex(item => item.id === this.formData.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.allTableData[index] = { ...this.formData }
|
||||||
|
this.$message.success('修改成功')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
const newId = this.allTableData.length > 0
|
||||||
|
? Math.max(...this.allTableData.map(item => item.id)) + 1
|
||||||
|
: 1
|
||||||
|
const now = new Date()
|
||||||
|
const createTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
|
||||||
|
|
||||||
|
this.allTableData.push({
|
||||||
|
...this.formData,
|
||||||
|
id: newId,
|
||||||
|
createTime
|
||||||
|
})
|
||||||
|
this.$message.success('新增成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveBannerList()
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.fetchData()
|
||||||
|
this.submitLoading = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
handleDialogClose() {
|
||||||
|
this.$refs.bannerForm.resetFields()
|
||||||
|
this.formData = {
|
||||||
|
id: null,
|
||||||
|
title: '',
|
||||||
|
imageUrl: '',
|
||||||
|
linkUrl: '',
|
||||||
|
sortOrder: 0,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.banner-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
513
src/views/martial/competition/create.vue
Normal file
513
src/views/martial/competition/create.vue
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
<template>
|
||||||
|
<div class="competition-create">
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-arrow-left"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
|
返回列表
|
||||||
|
</el-button>
|
||||||
|
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||||
|
<div class="header-actions" v-if="mode !== 'view'">
|
||||||
|
<el-button @click="handleBack">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">
|
||||||
|
{{ mode === 'create' ? '创建' : '保存' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions" v-else>
|
||||||
|
<el-button type="primary" @click="switchToEdit">编辑</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
:disabled="mode === '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="mode !== '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="mode !== '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="mode !== '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="mode !== '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="mode !== '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="mode !== '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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CompetitionCreate',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mode: 'create', // create, edit, view
|
||||||
|
competitionId: 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: {
|
||||||
|
pageTitle() {
|
||||||
|
const titleMap = {
|
||||||
|
create: '新建赛事',
|
||||||
|
edit: '编辑赛事',
|
||||||
|
view: '查看赛事'
|
||||||
|
};
|
||||||
|
return titleMap[this.mode] || '赛事信息';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initPage();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initPage() {
|
||||||
|
const { mode, id } = this.$route.query;
|
||||||
|
this.mode = mode || 'create';
|
||||||
|
this.competitionId = id ? parseInt(id) : null;
|
||||||
|
|
||||||
|
if (this.mode !== 'create' && this.competitionId) {
|
||||||
|
this.loadCompetitionData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadCompetitionData() {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
const savedData = localStorage.getItem('competitionList');
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
const list = JSON.parse(savedData);
|
||||||
|
const competition = list.find(item => item.id === this.competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData = { ...competition };
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载数据失败', e);
|
||||||
|
this.$message.error('加载数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAddSchedule() {
|
||||||
|
this.formData.schedule.push({
|
||||||
|
date: '',
|
||||||
|
time: '',
|
||||||
|
event: '',
|
||||||
|
venue: ''
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeleteSchedule(index) {
|
||||||
|
this.formData.schedule.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const savedData = localStorage.getItem('competitionList');
|
||||||
|
let list = savedData ? JSON.parse(savedData) : [];
|
||||||
|
|
||||||
|
if (this.mode === 'create') {
|
||||||
|
// 新建
|
||||||
|
const newId = list.length > 0 ? Math.max(...list.map(item => item.id)) + 1 : 1;
|
||||||
|
const newCompetition = {
|
||||||
|
...this.formData,
|
||||||
|
id: newId,
|
||||||
|
posterImages: [],
|
||||||
|
regulationFiles: []
|
||||||
|
};
|
||||||
|
list.push(newCompetition);
|
||||||
|
this.$message.success('创建成功');
|
||||||
|
} else if (this.mode === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
const index = list.findIndex(item => item.id === this.competitionId);
|
||||||
|
if (index !== -1) {
|
||||||
|
list[index] = {
|
||||||
|
...list[index],
|
||||||
|
...this.formData
|
||||||
|
};
|
||||||
|
this.$message.success('保存成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('competitionList', JSON.stringify(list));
|
||||||
|
|
||||||
|
// 返回列表页
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleBack();
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善必填信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
switchToEdit() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/competition/create',
|
||||||
|
query: { mode: 'edit', id: this.competitionId }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBack() {
|
||||||
|
this.$router.push('/martial/competition/list');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.competition-create {
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 2px solid #f0f0f0;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.competition-form {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
padding: 24px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #dc2626;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
.el-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
497
src/views/martial/competition/index.vue
Normal file
497
src/views/martial/competition/index.vue
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
<template>
|
||||||
|
<div class="competition-create-container">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
||||||
|
<h2 class="page-title">新建赛事</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-steps :active="currentStep" align-center class="steps-container" finish-status="success">
|
||||||
|
<el-step title="基本信息"></el-step>
|
||||||
|
<el-step title="信息发布"></el-step>
|
||||||
|
<el-step title="赛事规程"></el-step>
|
||||||
|
<el-step title="活动日程"></el-step>
|
||||||
|
</el-steps>
|
||||||
|
|
||||||
|
<el-form ref="competitionForm" :model="formData" :rules="rules" label-width="120px" class="form-container">
|
||||||
|
<!-- Step 1: 基本信息 -->
|
||||||
|
<div v-show="currentStep === 0" class="form-step">
|
||||||
|
<h3 class="step-title">赛事基本信息</h3>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="赛事名称" prop="competitionName">
|
||||||
|
<el-input v-model="formData.competitionName" placeholder="请输入赛事名称" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="主办单位" prop="organizer">
|
||||||
|
<el-input v-model="formData.organizer" placeholder="请输入主办单位" clearable></el-input>
|
||||||
|
</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="例如:四川省 成都市" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="详细地点" prop="venue">
|
||||||
|
<el-input v-model="formData.venue" placeholder="请输入详细地点" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="报名时间" prop="registrationTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.registrationTime"
|
||||||
|
type="datetimerange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="比赛时间" prop="competitionTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.competitionTime"
|
||||||
|
type="datetimerange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 2: 信息发布 -->
|
||||||
|
<div v-show="currentStep === 1" class="form-step">
|
||||||
|
<h3 class="step-title">信息发布</h3>
|
||||||
|
|
||||||
|
<el-form-item label="赛事简介" prop="introduction">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="8"
|
||||||
|
placeholder="请输入赛事简介,支持详细描述赛事背景、特色等信息"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="宣传图片" prop="posterImages">
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
action="#"
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="handlePosterChange"
|
||||||
|
:on-remove="handlePosterRemove"
|
||||||
|
:file-list="posterFileList"
|
||||||
|
list-type="picture-card"
|
||||||
|
accept="image/*"
|
||||||
|
>
|
||||||
|
<i class="el-icon-plus"></i>
|
||||||
|
<div slot="tip" class="el-upload__tip">建议上传jpg/png文件,且不超过2MB</div>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系人" prop="contactPerson">
|
||||||
|
<el-input v-model="formData.contactPerson" placeholder="请输入联系人姓名" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
|
<el-input v-model="formData.contactPhone" placeholder="请输入联系电话" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="联系邮箱" prop="contactEmail">
|
||||||
|
<el-input v-model="formData.contactEmail" placeholder="请输入联系邮箱" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 3: 赛事规程 -->
|
||||||
|
<div v-show="currentStep === 2" class="form-step">
|
||||||
|
<h3 class="step-title">赛事规程</h3>
|
||||||
|
|
||||||
|
<el-form-item label="竞赛规则" prop="rules">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.rules"
|
||||||
|
type="textarea"
|
||||||
|
:rows="6"
|
||||||
|
placeholder="请输入竞赛规则,包括比赛项目、评分标准等"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="参赛要求" prop="requirements">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.requirements"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入参赛要求,如年龄限制、资格要求等"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="奖项设置" prop="awards">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.awards"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入奖项设置,如一等奖、二等奖等"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="规程文件" prop="regulationFiles">
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
action="#"
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="handleRegulationChange"
|
||||||
|
:on-remove="handleRegulationRemove"
|
||||||
|
:file-list="regulationFileList"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
>
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
<div slot="tip" class="el-upload__tip">支持上传PDF、Word文件,单个文件不超过10MB</div>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 4: 活动日程 -->
|
||||||
|
<div v-show="currentStep === 3" class="form-step">
|
||||||
|
<h3 class="step-title">活动日程</h3>
|
||||||
|
|
||||||
|
<div class="schedule-header">
|
||||||
|
<el-button type="primary" size="small" icon="el-icon-plus" @click="addScheduleItem">添加日程</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="formData.schedule" border stripe size="small" class="schedule-table">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column label="日期时间" min-width="200">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="scope.row.date"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择日期时间"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
></el-date-picker>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="活动项目" min-width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input v-model="scope.row.eventName" placeholder="请输入活动项目" size="small"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="地点" min-width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input v-model="scope.row.venue" placeholder="请输入地点" size="small"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="备注" min-width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input v-model="scope.row.remark" placeholder="请输入备注" size="small"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="danger" size="small" icon="el-icon-delete" @click="deleteScheduleItem(scope.$index)" circle></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div v-if="formData.schedule.length === 0" class="empty-schedule">
|
||||||
|
<i class="el-icon-calendar"></i>
|
||||||
|
<p>暂无日程安排,点击上方"添加日程"按钮开始添加</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<div class="form-footer">
|
||||||
|
<el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
|
||||||
|
<el-button v-if="currentStep < 3" type="primary" @click="nextStep">下一步</el-button>
|
||||||
|
<el-button v-if="currentStep === 3" type="success" :loading="submitLoading" @click="handleSubmit">提交</el-button>
|
||||||
|
<el-button @click="goBack">取消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { addCompetition } from '@/api/martial/competition'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CompetitionCreate',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentStep: 0,
|
||||||
|
submitLoading: false,
|
||||||
|
posterFileList: [],
|
||||||
|
regulationFileList: [],
|
||||||
|
formData: {
|
||||||
|
// 基本信息
|
||||||
|
competitionName: '',
|
||||||
|
organizer: '',
|
||||||
|
location: '',
|
||||||
|
venue: '',
|
||||||
|
registrationTime: [],
|
||||||
|
competitionTime: [],
|
||||||
|
|
||||||
|
// 信息发布
|
||||||
|
introduction: '',
|
||||||
|
posterImages: [],
|
||||||
|
contactPerson: '',
|
||||||
|
contactPhone: '',
|
||||||
|
contactEmail: '',
|
||||||
|
|
||||||
|
// 赛事规程
|
||||||
|
rules: '',
|
||||||
|
requirements: '',
|
||||||
|
awards: '',
|
||||||
|
regulationFiles: [],
|
||||||
|
|
||||||
|
// 活动日程
|
||||||
|
schedule: []
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
competitionName: [
|
||||||
|
{ required: true, message: '请输入赛事名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
organizer: [
|
||||||
|
{ required: true, message: '请输入主办单位', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
location: [
|
||||||
|
{ required: true, message: '请输入地区', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
venue: [
|
||||||
|
{ required: true, message: '请输入详细地点', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
registrationTime: [
|
||||||
|
{ required: true, message: '请选择报名时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
competitionTime: [
|
||||||
|
{ required: true, message: '请选择比赛时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
contactPhone: [
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
contactEmail: [
|
||||||
|
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 上一步
|
||||||
|
prevStep() {
|
||||||
|
if (this.currentStep > 0) {
|
||||||
|
this.currentStep--
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
nextStep() {
|
||||||
|
// 验证当前步骤的表单
|
||||||
|
this.$refs.competitionForm.validateField(this.getStepFields(this.currentStep), (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
this.currentStep++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取每个步骤需要验证的字段
|
||||||
|
getStepFields(step) {
|
||||||
|
const fieldsMap = {
|
||||||
|
0: ['competitionName', 'organizer', 'location', 'venue', 'registrationTime', 'competitionTime'],
|
||||||
|
1: ['contactPhone', 'contactEmail'],
|
||||||
|
2: [],
|
||||||
|
3: []
|
||||||
|
}
|
||||||
|
return fieldsMap[step] || []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
goBack() {
|
||||||
|
this.$router.go(-1)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 添加日程项
|
||||||
|
addScheduleItem() {
|
||||||
|
this.formData.schedule.push({
|
||||||
|
date: '',
|
||||||
|
eventName: '',
|
||||||
|
venue: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除日程项
|
||||||
|
deleteScheduleItem(index) {
|
||||||
|
this.$confirm('确定删除该日程吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.formData.schedule.splice(index, 1)
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理宣传图片上传
|
||||||
|
handlePosterChange(file, fileList) {
|
||||||
|
this.posterFileList = fileList
|
||||||
|
this.formData.posterImages = fileList.map(f => f.raw || f)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理宣传图片删除
|
||||||
|
handlePosterRemove(file, fileList) {
|
||||||
|
this.posterFileList = fileList
|
||||||
|
this.formData.posterImages = fileList.map(f => f.raw || f)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理规程文件上传
|
||||||
|
handleRegulationChange(file, fileList) {
|
||||||
|
this.regulationFileList = fileList
|
||||||
|
this.formData.regulationFiles = fileList.map(f => f.raw || f)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理规程文件删除
|
||||||
|
handleRegulationRemove(file, fileList) {
|
||||||
|
this.regulationFileList = fileList
|
||||||
|
this.formData.regulationFiles = fileList.map(f => f.raw || f)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.competitionForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
|
||||||
|
// 构建提交数据
|
||||||
|
const submitData = {
|
||||||
|
...this.formData,
|
||||||
|
registrationStartTime: this.formData.registrationTime[0],
|
||||||
|
registrationEndTime: this.formData.registrationTime[1],
|
||||||
|
competitionStartTime: this.formData.competitionTime[0],
|
||||||
|
competitionEndTime: this.formData.competitionTime[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除临时字段
|
||||||
|
delete submitData.registrationTime
|
||||||
|
delete submitData.competitionTime
|
||||||
|
|
||||||
|
// 调用API
|
||||||
|
addCompetition(submitData).then(res => {
|
||||||
|
this.$message.success('赛事创建成功')
|
||||||
|
this.$router.push('/martial/order/list')
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err.message || '创建失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$message.warning('请完善必填信息')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.competition-create-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
min-height: 400px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.form-step {
|
||||||
|
.step-title {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #409eff;
|
||||||
|
border-left: 4px solid #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-header {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-table {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-schedule {
|
||||||
|
padding: 60px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 64px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-footer {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #dcdfe6;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
309
src/views/martial/competition/list.vue
Normal file
309
src/views/martial/competition/list.vue
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
<template>
|
||||||
|
<div class="competition-list">
|
||||||
|
<div class="list-header">
|
||||||
|
<h2 class="page-title">赛事管理</h2>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||||
|
新建赛事
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="competitionList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
style="width: 100%"
|
||||||
|
class="data-table"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="index"
|
||||||
|
label="序号"
|
||||||
|
width="60"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="competitionName"
|
||||||
|
label="赛事名称"
|
||||||
|
min-width="200"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="organizer"
|
||||||
|
label="主办单位"
|
||||||
|
min-width="150"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="location"
|
||||||
|
label="举办地点"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="competitionTime"
|
||||||
|
label="比赛时间"
|
||||||
|
width="180"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="registrationTime"
|
||||||
|
label="报名时间"
|
||||||
|
width="180"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="状态"
|
||||||
|
width="100"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getStatusType(scope.row.status)"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ getStatusText(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
width="220"
|
||||||
|
fixed="right"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-view"
|
||||||
|
@click="handleView(scope.row)"
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="handleEdit(scope.row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CompetitionList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
competitionList: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
competitionName: '2025年全国武术锦标赛',
|
||||||
|
organizer: '国家体育总局武术运动管理中心',
|
||||||
|
location: '北京',
|
||||||
|
venue: '国家奥林匹克体育中心',
|
||||||
|
registrationTime: '2025-01-01 至 2025-02-28',
|
||||||
|
competitionTime: '2025-03-15 至 2025-03-20',
|
||||||
|
status: 1, // 1-未开始 2-报名中 3-进行中 4-已结束
|
||||||
|
introduction: '全国最高水平的武术竞技赛事',
|
||||||
|
posterImages: [],
|
||||||
|
contactPerson: '张主任',
|
||||||
|
contactPhone: '010-12345678',
|
||||||
|
contactEmail: 'contact@wushu.cn',
|
||||||
|
rules: '参赛选手必须持有国家二级运动员及以上证书',
|
||||||
|
requirements: '年龄18-35岁,身体健康',
|
||||||
|
awards: '冠军奖金10万元,亚军5万元,季军3万元',
|
||||||
|
regulationFiles: [],
|
||||||
|
schedule: [
|
||||||
|
{ date: '2025-03-15', time: '09:00-12:00', event: '开幕式', venue: '主场馆' },
|
||||||
|
{ date: '2025-03-16', time: '09:00-18:00', event: '太极拳比赛', venue: 'A馆' },
|
||||||
|
{ date: '2025-03-17', time: '09:00-18:00', event: '长拳比赛', venue: 'B馆' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
competitionName: '2025年青少年武术大赛',
|
||||||
|
organizer: '中国武术协会',
|
||||||
|
location: '上海',
|
||||||
|
venue: '上海体育馆',
|
||||||
|
registrationTime: '2025-02-01 至 2025-03-31',
|
||||||
|
competitionTime: '2025-04-10 至 2025-04-15',
|
||||||
|
status: 2,
|
||||||
|
introduction: '面向青少年的武术竞技赛事',
|
||||||
|
posterImages: [],
|
||||||
|
contactPerson: '李教练',
|
||||||
|
contactPhone: '021-87654321',
|
||||||
|
contactEmail: 'youth@wushu.org',
|
||||||
|
rules: '年龄限制6-18岁',
|
||||||
|
requirements: '需提供学生证明',
|
||||||
|
awards: '设金银铜奖及优秀奖',
|
||||||
|
regulationFiles: [],
|
||||||
|
schedule: [
|
||||||
|
{ date: '2025-04-10', time: '09:00-12:00', event: '开幕式', venue: '主场馆' },
|
||||||
|
{ date: '2025-04-11', time: '09:00-18:00', event: '少年组比赛', venue: 'A馆' },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
competitionName: '2025年传统武术邀请赛',
|
||||||
|
organizer: '中华武术联合会',
|
||||||
|
location: '杭州',
|
||||||
|
venue: '杭州国际博览中心',
|
||||||
|
registrationTime: '2025-03-01 至 2025-04-30',
|
||||||
|
competitionTime: '2025-05-20 至 2025-05-25',
|
||||||
|
status: 1,
|
||||||
|
introduction: '传统武术项目展示与竞技',
|
||||||
|
posterImages: [],
|
||||||
|
contactPerson: '王馆长',
|
||||||
|
contactPhone: '0571-23456789',
|
||||||
|
contactEmail: 'traditional@wushu.com',
|
||||||
|
rules: '限传统武术门派参赛',
|
||||||
|
requirements: '需提供师承证明',
|
||||||
|
awards: '金银铜奖及最佳表演奖',
|
||||||
|
regulationFiles: [],
|
||||||
|
schedule: [
|
||||||
|
{ date: '2025-05-20', time: '14:00-17:00', event: '报到', venue: '接待中心' },
|
||||||
|
{ date: '2025-05-21', time: '09:00-18:00', event: '初赛', venue: '比赛馆' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadCompetitionList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadCompetitionList() {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
const savedData = localStorage.getItem('competitionList');
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.competitionList = JSON.parse(savedData);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载数据失败', e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 首次加载,保存默认数据
|
||||||
|
this.saveCompetitionList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCompetitionList() {
|
||||||
|
localStorage.setItem('competitionList', JSON.stringify(this.competitionList));
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusText(status) {
|
||||||
|
const statusMap = {
|
||||||
|
1: '未开始',
|
||||||
|
2: '报名中',
|
||||||
|
3: '进行中',
|
||||||
|
4: '已结束'
|
||||||
|
};
|
||||||
|
return statusMap[status] || '未知';
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusType(status) {
|
||||||
|
const typeMap = {
|
||||||
|
1: 'info',
|
||||||
|
2: 'success',
|
||||||
|
3: 'warning',
|
||||||
|
4: 'danger'
|
||||||
|
};
|
||||||
|
return typeMap[status] || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreate() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/competition/create',
|
||||||
|
query: { mode: 'create' }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleView(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/competition/create',
|
||||||
|
query: { mode: 'view', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEdit(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/competition/create',
|
||||||
|
query: { mode: 'edit', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定要删除该赛事吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const index = this.competitionList.findIndex(item => item.id === row.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.competitionList.splice(index, 1);
|
||||||
|
this.saveCompetitionList();
|
||||||
|
this.$message.success('删除成功');
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.competition-list {
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 2px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
:deep(.el-table__header) {
|
||||||
|
th {
|
||||||
|
background-color: #fafafa;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button--text) {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -265,6 +265,9 @@ export default {
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
536
src/views/martial/participant/index.vue
Normal file
536
src/views/martial/participant/index.vue
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
<template>
|
||||||
|
<div class="participant-container">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
||||||
|
<h2 class="page-title">维护参赛选手 - {{ competitionInfo.competitionName }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 赛事基本信息 -->
|
||||||
|
<div class="competition-info">
|
||||||
|
<el-descriptions :column="3" border size="small">
|
||||||
|
<el-descriptions-item label="赛事名称">{{ competitionInfo.competitionName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="主办单位">{{ competitionInfo.organizer }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="比赛时间">{{ competitionInfo.competitionTime }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮区 -->
|
||||||
|
<div class="action-bar">
|
||||||
|
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">添加选手</el-button>
|
||||||
|
<el-button type="success" size="small" icon="el-icon-upload" @click="handleImport">导入选手</el-button>
|
||||||
|
<el-button type="warning" size="small" icon="el-icon-sort" @click="handleBatchSort">批量排序</el-button>
|
||||||
|
<el-button type="info" size="small" icon="el-icon-download" @click="handleExport">导出名单</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 参赛选手列表 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
size="small"
|
||||||
|
row-key="id"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="orderNum" label="出场顺序" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number
|
||||||
|
v-model="scope.row.orderNum"
|
||||||
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
|
size="small"
|
||||||
|
@change="handleOrderChange(scope.row)"
|
||||||
|
></el-input-number>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="playerName" label="选手姓名" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="gender" label="性别" width="60" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.gender === 1 ? '男' : '女' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="age" label="年龄" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="organization" label="所属单位" min-width="150" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="参赛项目" min-width="120" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="category" label="组别" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="contactPhone" label="联系电话" width="120"></el-table-column>
|
||||||
|
<el-table-column label="附件" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachments && scope.row.attachments.length > 0"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewAttachments(scope.row)"
|
||||||
|
>
|
||||||
|
查看({{ scope.row.attachments.length }})
|
||||||
|
</el-button>
|
||||||
|
<span v-else style="color: #999;">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pagination.current"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pagination.size"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
:total="pagination.total"
|
||||||
|
small
|
||||||
|
></el-pagination>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑选手弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="700px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
>
|
||||||
|
<el-form ref="playerForm" :model="formData" :rules="rules" label-width="100px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="选手姓名" prop="playerName">
|
||||||
|
<el-input v-model="formData.playerName" placeholder="请输入选手姓名" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="年龄" prop="age">
|
||||||
|
<el-input-number v-model="formData.age" :min="1" :max="150" style="width: 100%"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
|
<el-input v-model="formData.contactPhone" placeholder="请输入联系电话" clearable maxlength="11"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所属单位" prop="organization">
|
||||||
|
<el-input v-model="formData.organization" placeholder="请输入所属单位" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="身份证号" prop="idCard">
|
||||||
|
<el-input v-model="formData.idCard" placeholder="请输入身份证号" clearable maxlength="18"></el-input>
|
||||||
|
</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="formData.projectName" placeholder="请输入参赛项目" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="组别" prop="category">
|
||||||
|
<el-select v-model="formData.category" placeholder="请选择组别" style="width: 100%">
|
||||||
|
<el-option label="少儿组" value="少儿组"></el-option>
|
||||||
|
<el-option label="少年组" value="少年组"></el-option>
|
||||||
|
<el-option label="青年组" value="青年组"></el-option>
|
||||||
|
<el-option label="成人组" value="成人组"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="出场顺序" prop="orderNum">
|
||||||
|
<el-input-number v-model="formData.orderNum" :min="1" :max="9999" style="width: 100%"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="选手简介" prop="introduction">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入选手简介、成绩、特长等信息"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="上传附件">
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
action="#"
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="handleFileChange"
|
||||||
|
:on-remove="handleFileRemove"
|
||||||
|
:file-list="fileList"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
<div slot="tip" class="el-upload__tip">支持上传PDF、Word、图片等文件,单个文件不超过5MB</div>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 查看附件弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="附件列表"
|
||||||
|
:visible.sync="attachmentDialogVisible"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<el-table :data="currentAttachments" border stripe size="small">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="size" label="大小" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatFileSize(scope.row.size) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="text" size="small" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getParticipantList, addParticipant, updateParticipant, removeParticipant, updateOrder } from '@/api/martial/participant'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ParticipantManage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
competitionId: null,
|
||||||
|
loading: false,
|
||||||
|
submitLoading: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
attachmentDialogVisible: false,
|
||||||
|
dialogTitle: '添加选手',
|
||||||
|
isEdit: false,
|
||||||
|
competitionInfo: {
|
||||||
|
competitionName: '',
|
||||||
|
organizer: '',
|
||||||
|
competitionTime: ''
|
||||||
|
},
|
||||||
|
tableData: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
id: null,
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '',
|
||||||
|
attachments: [],
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
fileList: [],
|
||||||
|
currentAttachments: [],
|
||||||
|
rules: {
|
||||||
|
playerName: [
|
||||||
|
{ required: true, message: '请输入选手姓名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
gender: [
|
||||||
|
{ required: true, message: '请选择性别', trigger: 'change' }
|
||||||
|
],
|
||||||
|
contactPhone: [
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
idCard: [
|
||||||
|
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/, message: '请输入正确的身份证号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
projectName: [
|
||||||
|
{ required: true, message: '请输入参赛项目', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
category: [
|
||||||
|
{ required: true, message: '请选择组别', trigger: 'change' }
|
||||||
|
],
|
||||||
|
orderNum: [
|
||||||
|
{ required: true, message: '请输入出场顺序', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.competitionId = this.$route.query.competitionId
|
||||||
|
if (this.competitionId) {
|
||||||
|
this.loadCompetitionInfo()
|
||||||
|
this.fetchData()
|
||||||
|
} else {
|
||||||
|
this.$message.error('缺少赛事ID参数')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 加载赛事信息
|
||||||
|
loadCompetitionInfo() {
|
||||||
|
// 这里应该调用API获取赛事信息,暂时使用模拟数据
|
||||||
|
this.competitionInfo = {
|
||||||
|
competitionName: '第三十届武术大赛',
|
||||||
|
organizer: '武术协会',
|
||||||
|
competitionTime: '2025-11-28 至 2025-12-01'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取参赛选手列表
|
||||||
|
fetchData() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
getParticipantList(this.competitionId, this.pagination.current, this.pagination.size)
|
||||||
|
.then(res => {
|
||||||
|
this.tableData = res.data.records || []
|
||||||
|
this.pagination.total = res.data.total || 0
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$message.error(err.message || '获取数据失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
goBack() {
|
||||||
|
this.$router.go(-1)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 添加选手
|
||||||
|
handleAdd() {
|
||||||
|
this.dialogTitle = '添加选手'
|
||||||
|
this.isEdit = false
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 编辑选手
|
||||||
|
handleEdit(row) {
|
||||||
|
this.dialogTitle = '编辑选手'
|
||||||
|
this.isEdit = true
|
||||||
|
this.formData = { ...row }
|
||||||
|
this.fileList = row.attachments || []
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除选手
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定删除该选手吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
removeParticipant(row.id).then(() => {
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err.message || '删除失败')
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 出场顺序变更
|
||||||
|
handleOrderChange(row) {
|
||||||
|
updateOrder(row.id, row.orderNum).then(() => {
|
||||||
|
this.$message.success('顺序更新成功')
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err.message || '更新失败')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.playerForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
|
||||||
|
const submitData = {
|
||||||
|
...this.formData,
|
||||||
|
competitionId: this.competitionId,
|
||||||
|
attachments: this.fileList
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiMethod = this.isEdit ? updateParticipant : addParticipant
|
||||||
|
const successMsg = this.isEdit ? '修改成功' : '添加成功'
|
||||||
|
|
||||||
|
apiMethod(submitData)
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success(successMsg)
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.fetchData()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$message.error(err.message || '操作失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
handleDialogClose() {
|
||||||
|
this.$refs.playerForm.resetFields()
|
||||||
|
this.formData = {
|
||||||
|
id: null,
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectName: '',
|
||||||
|
category: '',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '',
|
||||||
|
attachments: [],
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
|
this.fileList = []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件上传
|
||||||
|
handleFileChange(file, fileList) {
|
||||||
|
this.fileList = fileList
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFileRemove(file, fileList) {
|
||||||
|
this.fileList = fileList
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查看附件
|
||||||
|
handleViewAttachments(row) {
|
||||||
|
this.currentAttachments = row.attachments || []
|
||||||
|
this.attachmentDialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下载附件
|
||||||
|
handleDownload(file) {
|
||||||
|
// 实现文件下载逻辑
|
||||||
|
this.$message.info('下载功能开发中')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化文件大小
|
||||||
|
formatFileSize(bytes) {
|
||||||
|
if (bytes === 0) return '0 B'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导入选手
|
||||||
|
handleImport() {
|
||||||
|
this.$message.info('批量导入功能开发中')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 批量排序
|
||||||
|
handleBatchSort() {
|
||||||
|
this.$message.info('批量排序功能开发中')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出名单
|
||||||
|
handleExport() {
|
||||||
|
this.$message.info('导出功能开发中')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCurrentChange(current) {
|
||||||
|
this.pagination.current = current
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.participant-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.competition-info {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
372
src/views/martial/participant/list.vue
Normal file
372
src/views/martial/participant/list.vue
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
<template>
|
||||||
|
<div class="participant-list">
|
||||||
|
<div class="list-header">
|
||||||
|
<h2 class="page-title">参赛选手管理</h2>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||||
|
添加选手
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.keyword"
|
||||||
|
placeholder="搜索选手姓名"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="searchForm.competitionId" placeholder="选择赛事" clearable size="small" style="width: 200px">
|
||||||
|
<el-option label="全部赛事" :value="null" />
|
||||||
|
<el-option
|
||||||
|
v-for="item in competitionOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.competitionName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="participantList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
style="width: 100%"
|
||||||
|
class="data-table"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="index"
|
||||||
|
label="序号"
|
||||||
|
width="60"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="playerName"
|
||||||
|
label="选手姓名"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="competitionName"
|
||||||
|
label="所属赛事"
|
||||||
|
min-width="180"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="projectName"
|
||||||
|
label="参赛项目"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="category"
|
||||||
|
label="组别"
|
||||||
|
width="100"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="性别"
|
||||||
|
width="80"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.gender === 1 ? 'primary' : 'danger'" size="small">
|
||||||
|
{{ scope.row.gender === 1 ? '男' : '女' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="age"
|
||||||
|
label="年龄"
|
||||||
|
width="80"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="organization"
|
||||||
|
label="所属单位"
|
||||||
|
min-width="150"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="contactPhone"
|
||||||
|
label="联系电话"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="orderNum"
|
||||||
|
label="出场顺序"
|
||||||
|
width="100"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
width="180"
|
||||||
|
fixed="right"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-view"
|
||||||
|
@click="handleView(scope.row)"
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
@click="handleEdit(scope.row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ParticipantList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
competitionId: null
|
||||||
|
},
|
||||||
|
competitionOptions: [
|
||||||
|
{ id: 1, competitionName: '2025年全国武术锦标赛' },
|
||||||
|
{ id: 2, competitionName: '2025年青少年武术大赛' },
|
||||||
|
{ id: 3, competitionName: '2025年传统武术邀请赛' }
|
||||||
|
],
|
||||||
|
participantList: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
competitionId: 1,
|
||||||
|
competitionName: '2025年全国武术锦标赛',
|
||||||
|
playerName: '张伟',
|
||||||
|
gender: 1,
|
||||||
|
age: 25,
|
||||||
|
contactPhone: '13800138001',
|
||||||
|
organization: '北京武术队',
|
||||||
|
idCard: '110101199001011234',
|
||||||
|
projectName: '太极拳',
|
||||||
|
category: '成年男子组',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '国家一级运动员',
|
||||||
|
attachments: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
competitionId: 1,
|
||||||
|
competitionName: '2025年全国武术锦标赛',
|
||||||
|
playerName: '李娜',
|
||||||
|
gender: 2,
|
||||||
|
age: 22,
|
||||||
|
contactPhone: '13800138002',
|
||||||
|
organization: '上海武术队',
|
||||||
|
idCard: '310101199201011234',
|
||||||
|
projectName: '长拳',
|
||||||
|
category: '成年女子组',
|
||||||
|
orderNum: 2,
|
||||||
|
introduction: '国家二级运动员',
|
||||||
|
attachments: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
competitionId: 2,
|
||||||
|
competitionName: '2025年青少年武术大赛',
|
||||||
|
playerName: '王小明',
|
||||||
|
gender: 1,
|
||||||
|
age: 16,
|
||||||
|
contactPhone: '13800138003',
|
||||||
|
organization: '广州市体校',
|
||||||
|
idCard: '440101200801011234',
|
||||||
|
projectName: '剑术',
|
||||||
|
category: '少年男子组',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '市级青少年冠军',
|
||||||
|
attachments: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
competitionId: 2,
|
||||||
|
competitionName: '2025年青少年武术大赛',
|
||||||
|
playerName: '赵小红',
|
||||||
|
gender: 2,
|
||||||
|
age: 15,
|
||||||
|
contactPhone: '13800138004',
|
||||||
|
organization: '深圳市体校',
|
||||||
|
idCard: '440301200901011234',
|
||||||
|
projectName: '刀术',
|
||||||
|
category: '少年女子组',
|
||||||
|
orderNum: 2,
|
||||||
|
introduction: '省级青少年亚军',
|
||||||
|
attachments: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
competitionId: 3,
|
||||||
|
competitionName: '2025年传统武术邀请赛',
|
||||||
|
playerName: '孙师傅',
|
||||||
|
gender: 1,
|
||||||
|
age: 45,
|
||||||
|
contactPhone: '13800138005',
|
||||||
|
organization: '武当派',
|
||||||
|
idCard: '420101197901011234',
|
||||||
|
projectName: '太极剑',
|
||||||
|
category: '中年组',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '武当第十五代传人',
|
||||||
|
attachments: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadParticipantList() {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
const savedData = localStorage.getItem('participantList');
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.participantList = JSON.parse(savedData);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载数据失败', e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 首次加载,保存默认数据
|
||||||
|
this.saveParticipantList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载赛事选项
|
||||||
|
const competitionData = localStorage.getItem('competitionList');
|
||||||
|
if (competitionData) {
|
||||||
|
try {
|
||||||
|
this.competitionOptions = JSON.parse(competitionData);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载赛事数据失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveParticipantList() {
|
||||||
|
localStorage.setItem('participantList', JSON.stringify(this.participantList));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSearch() {
|
||||||
|
// 实现搜索逻辑
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreate() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/manage',
|
||||||
|
query: { mode: 'create' }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleView(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/manage',
|
||||||
|
query: { mode: 'view', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEdit(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/manage',
|
||||||
|
query: { mode: 'edit', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定要删除该选手吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const index = this.participantList.findIndex(item => item.id === row.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.participantList.splice(index, 1);
|
||||||
|
this.saveParticipantList();
|
||||||
|
this.$message.success('删除成功');
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.participant-list {
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 2px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
:deep(.el-table__header) {
|
||||||
|
th {
|
||||||
|
background-color: #fafafa;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button--text) {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
412
src/views/martial/participant/manage.vue
Normal file
412
src/views/martial/participant/manage.vue
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
<template>
|
||||||
|
<div class="participant-manage">
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-arrow-left"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
|
返回列表
|
||||||
|
</el-button>
|
||||||
|
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||||
|
<div class="header-actions" v-if="mode !== 'view'">
|
||||||
|
<el-button @click="handleBack">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">
|
||||||
|
{{ mode === 'create' ? '添加' : '保存' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions" v-else>
|
||||||
|
<el-button type="primary" @click="switchToEdit">编辑</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="120px"
|
||||||
|
:disabled="mode === '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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ParticipantManage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mode: 'create', // create, edit, view
|
||||||
|
participantId: null,
|
||||||
|
competitionOptions: [],
|
||||||
|
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: {
|
||||||
|
pageTitle() {
|
||||||
|
const titleMap = {
|
||||||
|
create: '添加参赛选手',
|
||||||
|
edit: '编辑参赛选手',
|
||||||
|
view: '查看参赛选手'
|
||||||
|
};
|
||||||
|
return titleMap[this.mode] || '参赛选手信息';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initPage();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initPage() {
|
||||||
|
const { mode, id } = this.$route.query;
|
||||||
|
this.mode = mode || 'create';
|
||||||
|
this.participantId = id ? parseInt(id) : null;
|
||||||
|
|
||||||
|
// 加载赛事选项
|
||||||
|
this.loadCompetitionOptions();
|
||||||
|
|
||||||
|
if (this.mode !== 'create' && this.participantId) {
|
||||||
|
this.loadParticipantData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadCompetitionOptions() {
|
||||||
|
const savedData = localStorage.getItem('competitionList');
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.competitionOptions = JSON.parse(savedData);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载赛事数据失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadParticipantData() {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
const savedData = localStorage.getItem('participantList');
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
const list = JSON.parse(savedData);
|
||||||
|
const participant = list.find(item => item.id === this.participantId);
|
||||||
|
if (participant) {
|
||||||
|
this.formData = { ...participant };
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载数据失败', e);
|
||||||
|
this.$message.error('加载数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCompetitionChange(competitionId) {
|
||||||
|
const competition = this.competitionOptions.find(item => item.id === competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const savedData = localStorage.getItem('participantList');
|
||||||
|
let list = savedData ? JSON.parse(savedData) : [];
|
||||||
|
|
||||||
|
// 确保有赛事名称
|
||||||
|
if (!this.formData.competitionName) {
|
||||||
|
const competition = this.competitionOptions.find(item => item.id === this.formData.competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mode === 'create') {
|
||||||
|
// 新建
|
||||||
|
const newId = list.length > 0 ? Math.max(...list.map(item => item.id)) + 1 : 1;
|
||||||
|
const newParticipant = {
|
||||||
|
...this.formData,
|
||||||
|
id: newId
|
||||||
|
};
|
||||||
|
list.push(newParticipant);
|
||||||
|
this.$message.success('添加成功');
|
||||||
|
} else if (this.mode === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
const index = list.findIndex(item => item.id === this.participantId);
|
||||||
|
if (index !== -1) {
|
||||||
|
list[index] = {
|
||||||
|
...list[index],
|
||||||
|
...this.formData
|
||||||
|
};
|
||||||
|
this.$message.success('保存成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('participantList', JSON.stringify(list));
|
||||||
|
|
||||||
|
// 返回列表页
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleBack();
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善必填信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
switchToEdit() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/manage',
|
||||||
|
query: { mode: 'edit', id: this.participantId }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBack() {
|
||||||
|
this.$router.push('/martial/participant/list');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.participant-manage {
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 2px solid #f0f0f0;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.participant-form {
|
||||||
|
max-width: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
padding: 24px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #dc2626;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
482
src/views/martial/referee/index.vue
Normal file
482
src/views/martial/referee/index.vue
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
<template>
|
||||||
|
<div class="referee-container">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2 class="page-title">评委管理</h2>
|
||||||
|
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">新增评委</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.keyword"
|
||||||
|
placeholder="搜索姓名/手机号"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="searchForm.refereeType" placeholder="裁判类型" clearable size="small" style="width: 180px">
|
||||||
|
<el-option label="全部" :value="null"></el-option>
|
||||||
|
<el-option label="主裁判" :value="1"></el-option>
|
||||||
|
<el-option label="普通裁判" :value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button size="small" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="姓名" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="gender" label="性别" width="60" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.gender === 1 ? '男' : '女' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="phone" label="手机号" width="130"></el-table-column>
|
||||||
|
<el-table-column prop="idCard" label="身份证号" width="180" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="refereeType" label="裁判类型" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="scope.row.refereeType === 1 ? 'danger' : 'primary'" size="small">
|
||||||
|
{{ scope.row.refereeType === 1 ? '主裁判' : '普通裁判' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="level" label="等级/职称" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="specialty" label="擅长项目" min-width="150" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="160"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pagination.current"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pagination.size"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
:total="pagination.total"
|
||||||
|
small
|
||||||
|
></el-pagination>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleDialogClose"
|
||||||
|
>
|
||||||
|
<el-form ref="refereeForm" :model="formData" :rules="rules" label-width="100px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="姓名" prop="name">
|
||||||
|
<el-input v-model="formData.name" placeholder="请输入姓名" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手机号" prop="phone">
|
||||||
|
<el-input v-model="formData.phone" placeholder="请输入手机号" clearable maxlength="11"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="身份证号" prop="idCard">
|
||||||
|
<el-input v-model="formData.idCard" placeholder="请输入身份证号" clearable maxlength="18"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="裁判类型" prop="refereeType">
|
||||||
|
<el-select v-model="formData.refereeType" placeholder="请选择裁判类型" style="width: 100%">
|
||||||
|
<el-option label="主裁判" :value="1"></el-option>
|
||||||
|
<el-option label="普通裁判" :value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="等级/职称" prop="level">
|
||||||
|
<el-input v-model="formData.level" placeholder="请输入等级/职称" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="擅长项目" prop="specialty">
|
||||||
|
<el-input v-model="formData.specialty" placeholder="请输入擅长项目,多个用逗号分隔" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.remark"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RefereeList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitLoading: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogTitle: '新增评委',
|
||||||
|
isEdit: false,
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
refereeType: null
|
||||||
|
},
|
||||||
|
allTableData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '王大伟',
|
||||||
|
gender: 1,
|
||||||
|
phone: '13800138001',
|
||||||
|
idCard: '110101197001011234',
|
||||||
|
refereeType: 1,
|
||||||
|
level: '国家一级裁判',
|
||||||
|
specialty: '太极拳、长拳',
|
||||||
|
remark: '经验丰富,专业能力强',
|
||||||
|
createTime: '2025-11-20 10:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '李美丽',
|
||||||
|
gender: 2,
|
||||||
|
phone: '13800138002',
|
||||||
|
idCard: '110101198001011234',
|
||||||
|
refereeType: 2,
|
||||||
|
level: '国家二级裁判',
|
||||||
|
specialty: '剑术、刀术',
|
||||||
|
remark: '认真负责',
|
||||||
|
createTime: '2025-11-21 11:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '张强',
|
||||||
|
gender: 1,
|
||||||
|
phone: '13800138003',
|
||||||
|
idCard: '110101197501011234',
|
||||||
|
refereeType: 2,
|
||||||
|
level: '国家一级裁判',
|
||||||
|
specialty: '棍术、枪术',
|
||||||
|
remark: '',
|
||||||
|
createTime: '2025-11-22 14:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: '刘芳',
|
||||||
|
gender: 2,
|
||||||
|
phone: '13800138004',
|
||||||
|
idCard: '110101198501011234',
|
||||||
|
refereeType: 1,
|
||||||
|
level: '国际级裁判',
|
||||||
|
specialty: '太极拳、太极剑',
|
||||||
|
remark: '国际武术裁判',
|
||||||
|
createTime: '2025-11-23 15:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: '陈建国',
|
||||||
|
gender: 1,
|
||||||
|
phone: '13800138005',
|
||||||
|
idCard: '110101197801011234',
|
||||||
|
refereeType: 2,
|
||||||
|
level: '国家二级裁判',
|
||||||
|
specialty: '长拳、南拳',
|
||||||
|
remark: '',
|
||||||
|
createTime: '2025-11-24 16:00:00'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tableData: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
gender: 1,
|
||||||
|
phone: '',
|
||||||
|
idCard: '',
|
||||||
|
refereeType: null,
|
||||||
|
level: '',
|
||||||
|
specialty: '',
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入姓名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
gender: [
|
||||||
|
{ required: true, message: '请选择性别', trigger: 'change' }
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
idCard: [
|
||||||
|
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/, message: '请输入正确的身份证号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
refereeType: [
|
||||||
|
{ required: true, message: '请选择裁判类型', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadRefereeList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
loadRefereeList() {
|
||||||
|
const savedData = localStorage.getItem('refereeList')
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.allTableData = JSON.parse(savedData)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载评委数据失败', e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 首次加载,保存默认数据
|
||||||
|
this.saveRefereeList()
|
||||||
|
}
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存数据到 localStorage
|
||||||
|
saveRefereeList() {
|
||||||
|
localStorage.setItem('refereeList', JSON.stringify(this.allTableData))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
fetchData() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 过滤数据
|
||||||
|
let filteredData = [...this.allTableData]
|
||||||
|
|
||||||
|
// 搜索过滤
|
||||||
|
if (this.searchForm.keyword) {
|
||||||
|
const keyword = this.searchForm.keyword.toLowerCase()
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.name.toLowerCase().includes(keyword) ||
|
||||||
|
item.phone.includes(keyword)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型过滤
|
||||||
|
if (this.searchForm.refereeType !== null && this.searchForm.refereeType !== '') {
|
||||||
|
filteredData = filteredData.filter(item => item.refereeType === this.searchForm.refereeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pagination.total = filteredData.length
|
||||||
|
|
||||||
|
// 分页处理
|
||||||
|
const start = (this.pagination.current - 1) * this.pagination.size
|
||||||
|
const end = start + this.pagination.size
|
||||||
|
this.tableData = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
handleSearch() {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
keyword: '',
|
||||||
|
refereeType: null
|
||||||
|
}
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCurrentChange(current) {
|
||||||
|
this.pagination.current = current
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
handleAdd() {
|
||||||
|
this.dialogTitle = '新增评委'
|
||||||
|
this.isEdit = false
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
handleEdit(row) {
|
||||||
|
this.dialogTitle = '编辑评委'
|
||||||
|
this.isEdit = true
|
||||||
|
this.formData = { ...row }
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定删除该评委吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const index = this.allTableData.findIndex(item => item.id === row.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.allTableData.splice(index, 1)
|
||||||
|
this.saveRefereeList()
|
||||||
|
this.$message.success('删除成功')
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.refereeForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.isEdit) {
|
||||||
|
// 编辑
|
||||||
|
const index = this.allTableData.findIndex(item => item.id === this.formData.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.allTableData[index] = { ...this.formData }
|
||||||
|
this.$message.success('修改成功')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
const newId = this.allTableData.length > 0
|
||||||
|
? Math.max(...this.allTableData.map(item => item.id)) + 1
|
||||||
|
: 1
|
||||||
|
const now = new Date()
|
||||||
|
const createTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
|
||||||
|
|
||||||
|
this.allTableData.push({
|
||||||
|
...this.formData,
|
||||||
|
id: newId,
|
||||||
|
createTime
|
||||||
|
})
|
||||||
|
this.$message.success('新增成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveRefereeList()
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.fetchData()
|
||||||
|
this.submitLoading = false
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
handleDialogClose() {
|
||||||
|
this.$refs.refereeForm.resetFields()
|
||||||
|
this.formData = {
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
gender: 1,
|
||||||
|
phone: '',
|
||||||
|
idCard: '',
|
||||||
|
refereeType: null,
|
||||||
|
level: '',
|
||||||
|
specialty: '',
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.referee-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -181,7 +181,6 @@ export default {
|
|||||||
{
|
{
|
||||||
schoolUnit: '清河小学',
|
schoolUnit: '清河小学',
|
||||||
category: '集体',
|
category: '集体',
|
||||||
hint: '剩余功能在提现上,显示栏标签',
|
|
||||||
individual: 1,
|
individual: 1,
|
||||||
dual: 1,
|
dual: 1,
|
||||||
team1101: 1,
|
team1101: 1,
|
||||||
507
src/views/martial/score/index.vue
Normal file
507
src/views/martial/score/index.vue
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
<template>
|
||||||
|
<div class="score-management-container">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2 class="page-title">评分管理</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 查询栏 -->
|
||||||
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||||
|
<el-form-item label="比赛项目">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.projectId"
|
||||||
|
placeholder="请选择比赛项目"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
size="small"
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handleSearch"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="场地">
|
||||||
|
<el-select
|
||||||
|
v-model="searchForm.venueId"
|
||||||
|
placeholder="请选择场地"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
size="small"
|
||||||
|
style="width: 150px"
|
||||||
|
@change="handleSearch"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in venueList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="比赛人员">
|
||||||
|
<el-input
|
||||||
|
v-model="searchForm.playerName"
|
||||||
|
placeholder="搜索选手姓名"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 180px"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<i class="el-input__icon el-icon-search"></i>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button size="small" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
:max-height="600"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" fixed></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="比赛项目" min-width="180" show-overflow-tooltip fixed></el-table-column>
|
||||||
|
<el-table-column prop="venueName" label="场地" width="120" align="center" fixed></el-table-column>
|
||||||
|
<el-table-column prop="playerName" label="比赛人员" width="120" align="center" fixed></el-table-column>
|
||||||
|
|
||||||
|
<!-- 动态裁判评分列 -->
|
||||||
|
<el-table-column
|
||||||
|
v-for="(judge, index) in judgeColumns"
|
||||||
|
:key="'judge-' + index"
|
||||||
|
:label="'裁判' + (index + 1) + '分数'"
|
||||||
|
width="110"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<span :class="getScoreClass(scope.row.judgeScores[index])">
|
||||||
|
{{ formatScore(scope.row.judgeScores[index]) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="总裁判分数" width="120" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="total-score">{{ formatScore(scope.row.totalScore) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" size="small" @click="handleViewDetail(scope.row)">查看详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="pagination.current"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pagination.size"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
:total="pagination.total"
|
||||||
|
small
|
||||||
|
></el-pagination>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 评分详情弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="detailDialogVisible"
|
||||||
|
title="评分详情"
|
||||||
|
width="700px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div v-if="currentDetail" class="detail-content">
|
||||||
|
<el-descriptions :column="2" border size="small">
|
||||||
|
<el-descriptions-item label="比赛项目">{{ currentDetail.projectName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="场地">{{ currentDetail.venueName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="选手姓名">{{ currentDetail.playerName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="队伍">{{ currentDetail.teamName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="身份证">{{ currentDetail.idCard }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="编号">{{ currentDetail.playerNo }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="score-section">
|
||||||
|
<h4>裁判评分明细</h4>
|
||||||
|
<el-table :data="currentDetail.scoreDetails" border size="small" style="margin-top: 10px">
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="judgeName" label="裁判姓名" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="score" label="评分" align="center" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="score-value">{{ formatScore(scope.row.score) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="deductions" label="扣分项" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="note" label="备注" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="scoreTime" label="评分时间" width="150"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="total-score-display">
|
||||||
|
<span class="label">总分:</span>
|
||||||
|
<span class="value">{{ formatScore(currentDetail.totalScore) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ScoreManagement',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
searchForm: {
|
||||||
|
projectId: null,
|
||||||
|
venueId: null,
|
||||||
|
playerName: ''
|
||||||
|
},
|
||||||
|
projectList: [
|
||||||
|
{ id: 1, name: '女子组长拳' },
|
||||||
|
{ id: 2, name: '男子组陈氏太极拳' },
|
||||||
|
{ id: 3, name: '女子组双剑(含长穗双剑)' },
|
||||||
|
{ id: 4, name: '男子组杨氏太极拳' },
|
||||||
|
{ id: 5, name: '女子组刀术' },
|
||||||
|
{ id: 6, name: '男子组棍术' },
|
||||||
|
{ id: 7, name: '女子组枪术' },
|
||||||
|
{ id: 8, name: '男子组剑术' }
|
||||||
|
],
|
||||||
|
venueList: [
|
||||||
|
{ id: 1, name: '第一场地' },
|
||||||
|
{ id: 2, name: '第二场地' },
|
||||||
|
{ id: 3, name: '第三场地' },
|
||||||
|
{ id: 4, name: '第四场地' },
|
||||||
|
{ id: 5, name: '第五场地' }
|
||||||
|
],
|
||||||
|
allTableData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
projectName: '男子组陈氏太极拳',
|
||||||
|
venueName: '第一场地',
|
||||||
|
playerName: '张三',
|
||||||
|
teamName: '少林寺武术大学院',
|
||||||
|
idCard: '123456789000000000',
|
||||||
|
playerNo: '123-4567898275',
|
||||||
|
judgeScores: [8.906, 8.905, 8.908, 8.907, 8.906],
|
||||||
|
totalScore: 8.907
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
projectName: '女子组长拳',
|
||||||
|
venueName: '第一场地',
|
||||||
|
playerName: '李四',
|
||||||
|
teamName: '武当武术学院',
|
||||||
|
idCard: '123456789000000001',
|
||||||
|
playerNo: '123-4567898276',
|
||||||
|
judgeScores: [9.125, 9.130, 9.128, 9.126, 9.129],
|
||||||
|
totalScore: 9.128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
projectName: '男子组陈氏太极拳',
|
||||||
|
venueName: '第二场地',
|
||||||
|
playerName: '王五',
|
||||||
|
teamName: '峨眉武术协会',
|
||||||
|
idCard: '123456789000000002',
|
||||||
|
playerNo: '123-4567898277',
|
||||||
|
judgeScores: [8.550, 8.548, 8.552, 8.551, 8.549],
|
||||||
|
totalScore: 8.550
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
projectName: '女子组双剑(含长穗双剑)',
|
||||||
|
venueName: '第一场地',
|
||||||
|
playerName: '赵六',
|
||||||
|
teamName: '昆仑武术馆',
|
||||||
|
idCard: '123456789000000003',
|
||||||
|
playerNo: '123-4567898278',
|
||||||
|
judgeScores: [9.245, 9.248, 9.246, 9.247, 9.249],
|
||||||
|
totalScore: 9.247
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
projectName: '男子组杨氏太极拳',
|
||||||
|
venueName: '第三场地',
|
||||||
|
playerName: '孙七',
|
||||||
|
teamName: '华山武术学校',
|
||||||
|
idCard: '123456789000000004',
|
||||||
|
playerNo: '123-4567898279',
|
||||||
|
judgeScores: [8.785, 8.788, 8.786, 8.787, 8.785],
|
||||||
|
totalScore: 8.786
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
projectName: '女子组刀术',
|
||||||
|
venueName: '第二场地',
|
||||||
|
playerName: '周八',
|
||||||
|
teamName: '少林寺武术大学院',
|
||||||
|
idCard: '123456789000000005',
|
||||||
|
playerNo: '123-4567898280',
|
||||||
|
judgeScores: [8.925, 8.928, 8.926, 8.927, 8.925],
|
||||||
|
totalScore: 8.926
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
projectName: '男子组棍术',
|
||||||
|
venueName: '第四场地',
|
||||||
|
playerName: '吴九',
|
||||||
|
teamName: '武当武术学院',
|
||||||
|
idCard: '123456789000000006',
|
||||||
|
playerNo: '123-4567898281',
|
||||||
|
judgeScores: [9.015, 9.018, 9.016, 9.017, 9.015],
|
||||||
|
totalScore: 9.016
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
projectName: '女子组枪术',
|
||||||
|
venueName: '第三场地',
|
||||||
|
playerName: '郑十',
|
||||||
|
teamName: '峨眉武术协会',
|
||||||
|
idCard: '123456789000000007',
|
||||||
|
playerNo: '123-4567898282',
|
||||||
|
judgeScores: [8.665, 8.668, 8.666, 8.667, 8.665],
|
||||||
|
totalScore: 8.666
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tableData: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
judgeColumns: Array(5).fill(null), // 5个裁判
|
||||||
|
detailDialogVisible: false,
|
||||||
|
currentDetail: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadScoreList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 从 localStorage 加载数据
|
||||||
|
loadScoreList() {
|
||||||
|
const savedData = localStorage.getItem('scoreList')
|
||||||
|
if (savedData) {
|
||||||
|
try {
|
||||||
|
this.allTableData = JSON.parse(savedData)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载评分数据失败', e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 首次加载,保存默认数据
|
||||||
|
this.saveScoreList()
|
||||||
|
}
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存数据到 localStorage
|
||||||
|
saveScoreList() {
|
||||||
|
localStorage.setItem('scoreList', JSON.stringify(this.allTableData))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取评分数据
|
||||||
|
fetchData() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 过滤数据
|
||||||
|
let filteredData = [...this.allTableData]
|
||||||
|
|
||||||
|
if (this.searchForm.projectId) {
|
||||||
|
const project = this.projectList.find(p => p.id === this.searchForm.projectId)
|
||||||
|
if (project) {
|
||||||
|
filteredData = filteredData.filter(item => item.projectName === project.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.searchForm.venueId) {
|
||||||
|
const venue = this.venueList.find(v => v.id === this.searchForm.venueId)
|
||||||
|
if (venue) {
|
||||||
|
filteredData = filteredData.filter(item => item.venueName === venue.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.searchForm.playerName) {
|
||||||
|
filteredData = filteredData.filter(item =>
|
||||||
|
item.playerName.includes(this.searchForm.playerName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pagination.total = filteredData.length
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const start = (this.pagination.current - 1) * this.pagination.size
|
||||||
|
const end = start + this.pagination.size
|
||||||
|
this.tableData = filteredData.slice(start, end)
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
handleSearch() {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
projectId: null,
|
||||||
|
venueId: null,
|
||||||
|
playerName: ''
|
||||||
|
}
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分页大小变化
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 当前页变化
|
||||||
|
handleCurrentChange(current) {
|
||||||
|
this.pagination.current = current
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
handleViewDetail(row) {
|
||||||
|
this.currentDetail = {
|
||||||
|
...row,
|
||||||
|
scoreDetails: [
|
||||||
|
{ judgeName: '裁判1', score: row.judgeScores[0], deductions: '无', note: '', scoreTime: '2025-11-29 10:30:00' },
|
||||||
|
{ judgeName: '裁判2', score: row.judgeScores[1], deductions: '无', note: '', scoreTime: '2025-11-29 10:30:05' },
|
||||||
|
{ judgeName: '裁判3', score: row.judgeScores[2], deductions: '扣分项描述', note: '动作不规范', scoreTime: '2025-11-29 10:30:10' },
|
||||||
|
{ judgeName: '裁判4', score: row.judgeScores[3], deductions: '无', note: '', scoreTime: '2025-11-29 10:30:15' },
|
||||||
|
{ judgeName: '裁判5', score: row.judgeScores[4], deductions: '无', note: '表现优秀', scoreTime: '2025-11-29 10:30:20' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
this.detailDialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化分数
|
||||||
|
formatScore(score) {
|
||||||
|
if (score === null || score === undefined) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
return Number(score).toFixed(3)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分数样式
|
||||||
|
getScoreClass(score) {
|
||||||
|
if (!score) return ''
|
||||||
|
if (score >= 9.0) return 'high-score'
|
||||||
|
if (score >= 8.5) return 'medium-score'
|
||||||
|
return 'low-score'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.score-management-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
::v-deep(.el-form-item__label) {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.high-score {
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium-score {
|
||||||
|
color: #e6a23c;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.low-score {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-score {
|
||||||
|
color: #1b7c5e;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
.score-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
color: #1b7c5e;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-score-display {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
background: linear-gradient(135deg, #e8f5f2 0%, #f0f9f6 100%);
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1b7c5e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,26 +1,621 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wel-container">
|
<div class="wel-container">
|
||||||
|
<!-- 顶部横幅 -->
|
||||||
|
<div class="welcome-banner">
|
||||||
|
<div class="banner-bg">
|
||||||
|
<div class="bg-pattern"></div>
|
||||||
|
</div>
|
||||||
|
<div class="banner-content">
|
||||||
|
<div class="logo-wrapper">
|
||||||
|
<div class="logo-icon">武</div>
|
||||||
|
</div>
|
||||||
|
<h1 class="banner-title">武术赛事通</h1>
|
||||||
|
<p class="banner-subtitle">专业的武术赛事管理平台</p>
|
||||||
|
<div class="banner-time">
|
||||||
|
<i class="el-icon-time"></i>
|
||||||
|
{{ currentTime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<!-- 快速入口 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="header-line"></div>
|
||||||
|
<h2 class="section-title">快速入口</h2>
|
||||||
|
<div class="header-line"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-access">
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/competition/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-s-flag"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>赛事管理</h3>
|
||||||
|
<p>管理武术赛事</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/participant/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>参赛选手</h3>
|
||||||
|
<p>管理参赛选手信息</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/order/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-s-order"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>订单管理</h3>
|
||||||
|
<p>查看和管理订单</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/schedule/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-date"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>赛程编排</h3>
|
||||||
|
<p>比赛赛程安排</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/dispatch/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-s-promotion"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>赛事调度</h3>
|
||||||
|
<p>实时进度跟踪</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/referee/list')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-user-solid"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>评委管理</h3>
|
||||||
|
<p>裁判人员管理</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="access-card" @click="navigateTo('/martial/score/index')">
|
||||||
|
<div class="card-bg"></div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<i class="el-icon-medal"></i>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<h3>评分管理</h3>
|
||||||
|
<p>比赛评分系统</p>
|
||||||
|
</div>
|
||||||
|
<i class="card-arrow el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 数据统计 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="header-line"></div>
|
||||||
|
<h2 class="section-title">数据统计</h2>
|
||||||
|
<div class="header-line"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon red">
|
||||||
|
<i class="el-icon-s-order"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-label">总订单数</div>
|
||||||
|
<div class="stat-value">{{ stats.totalOrders }}</div>
|
||||||
|
<div class="stat-trend up">
|
||||||
|
<i class="el-icon-top"></i>
|
||||||
|
<span>12%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon orange">
|
||||||
|
<i class="el-icon-s-custom"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-label">参赛人数</div>
|
||||||
|
<div class="stat-value">{{ stats.totalParticipants }}</div>
|
||||||
|
<div class="stat-trend up">
|
||||||
|
<i class="el-icon-top"></i>
|
||||||
|
<span>25%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon blue">
|
||||||
|
<i class="el-icon-s-flag"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-label">进行中赛事</div>
|
||||||
|
<div class="stat-value">{{ stats.ongoingEvents }}</div>
|
||||||
|
<div class="stat-trend">
|
||||||
|
<i class="el-icon-minus"></i>
|
||||||
|
<span>0%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon green">
|
||||||
|
<i class="el-icon-s-data"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-label">已完成赛事</div>
|
||||||
|
<div class="stat-value">{{ stats.completedEvents }}</div>
|
||||||
|
<div class="stat-trend up">
|
||||||
|
<i class="el-icon-top"></i>
|
||||||
|
<span>8%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Wel',
|
name: 'Wel',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
currentTime: '',
|
||||||
|
stats: {
|
||||||
|
totalOrders: 0,
|
||||||
|
totalParticipants: 0,
|
||||||
|
ongoingEvents: 0,
|
||||||
|
completedEvents: 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.updateTime();
|
||||||
|
setInterval(this.updateTime, 1000);
|
||||||
|
this.loadStats();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
updateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(now.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(now.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||||
|
this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
},
|
||||||
|
navigateTo(path) {
|
||||||
|
this.$router.push(path);
|
||||||
|
},
|
||||||
|
loadStats() {
|
||||||
|
// TODO: 从API加载真实统计数据
|
||||||
|
this.stats = {
|
||||||
|
totalOrders: 128,
|
||||||
|
totalParticipants: 456,
|
||||||
|
ongoingEvents: 3,
|
||||||
|
completedEvents: 12
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.wel-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
.welcome-banner {
|
||||||
|
position: relative;
|
||||||
|
height: 280px;
|
||||||
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 70% 50%, rgba(0, 0, 0, 0.1) 0%, transparent 50%);
|
||||||
|
animation: bgRotate 30s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bgRotate {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-pattern {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.03) 0px, rgba(255, 255, 255, 0.03) 1px, transparent 1px, transparent 60px),
|
||||||
|
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.03) 0px, rgba(255, 255, 255, 0.03) 1px, transparent 1px, transparent 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrapper {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #fff;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||||
|
animation: logoFloat 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logoFloat {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-8px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-title {
|
||||||
|
font-size: 42px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
letter-spacing: 6px;
|
||||||
|
text-shadow: 0 2px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
opacity: 0.95;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-time {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 24px;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-line {
|
||||||
|
flex: 1;
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(90deg, transparent, #dc2626, transparent);
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a1a;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: #dc2626;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-access {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.access-card {
|
||||||
|
position: relative;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 28px 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(180deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
transform: scaleY(0);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bg {
|
||||||
|
position: absolute;
|
||||||
|
right: -20px;
|
||||||
|
top: -20px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background: radial-gradient(circle, rgba(220, 38, 38, 0.05) 0%, transparent 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
box-shadow: 0 12px 32px rgba(220, 38, 38, 0.2);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-bg {
|
||||||
|
transform: scale(1.5);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.1) rotate(5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-arrow {
|
||||||
|
transform: translateX(6px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fef2f2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 26px;
|
||||||
|
color: #dc2626;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 6px 0;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-arrow {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #dc2626;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 28px 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
transform: scale(1.1) rotate(-5deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30px;
|
||||||
|
color: #fff;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.orange {
|
||||||
|
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a1a;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-trend {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
&.up {
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.banner-title {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-access {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default ({ mode, command }) => {
|
|||||||
port: 2888,
|
port: 2888,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:82',
|
target: 'http://localhost:8123',
|
||||||
// target: 'http://120.197.149.12:8480',
|
// target: 'http://120.197.149.12:8480',
|
||||||
// target: 'https://2df1-223-74-180-188.ngrok-free.app ',
|
// target: 'https://2df1-223-74-180-188.ngrok-free.app ',
|
||||||
//target: 'https://saber3.bladex.cn/api',
|
//target: 'https://saber3.bladex.cn/api',
|
||||||
|
|||||||
Reference in New Issue
Block a user