实现赛程表导出功能
- 添加 exportSchedule API 调用后端导出接口 - 实现 handleExport 方法,支持下载 Excel 文件 - 文件名格式:赛程表_赛事名称.xlsx 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -183,3 +183,16 @@ export const saveDispatch = (data) => {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出赛程表
|
||||||
|
* @param {Number} competitionId - 赛事ID
|
||||||
|
*/
|
||||||
|
export const exportSchedule = (competitionId) => {
|
||||||
|
return request({
|
||||||
|
url: '/martial/export/schedule',
|
||||||
|
method: 'get',
|
||||||
|
params: { competitionId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
355
src/views/martial/order/index.vue.bak
Normal file
355
src/views/martial/order/index.vue.bak
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
<template>
|
||||||
|
<div class="martial-order-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>
|
||||||
|
<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: 180px"
|
||||||
|
>
|
||||||
|
<el-option label="未开始" :value="1"></el-option>
|
||||||
|
<el-option label="报名中" :value="2"></el-option>
|
||||||
|
<el-option label="进行中" :value="3"></el-option>
|
||||||
|
<el-option label="已结束" :value="4"></el-option>
|
||||||
|
</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
|
||||||
|
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="competitionName" label="赛事名称" min-width="200" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="competitionCode" label="赛事编号" width="150"></el-table-column>
|
||||||
|
<el-table-column prop="organizer" label="主办单位" min-width="150" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="location" label="举办地点" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="registrationTime" label="报名时间" width="180" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDateRange(scope.row.registrationStartTime, scope.row.registrationEndTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="competitionTime" label="比赛时间" width="180" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDateRange(scope.row.competitionStartTime, scope.row.competitionEndTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="90" 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="320" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" size="small" @click="handleRegistrationDetail(scope.row)">报名详情</el-button>
|
||||||
|
<el-button type="success" size="small" @click="handleSchedule(scope.row)">编排</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
size="small"
|
||||||
|
@click="handleDispatch(scope.row)"
|
||||||
|
:title="isScheduleCompleted(scope.row.id) ? '进入调度' : '请先完成编排'"
|
||||||
|
>
|
||||||
|
调度
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
import { getScheduleResult } from '@/api/martial/activitySchedule'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MartialOrderList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: null
|
||||||
|
},
|
||||||
|
tableData: [],
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
scheduleStatusMap: {} // 存储每个赛事的编排状态
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadCompetitionList()
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
// 当页面被激活时(从其他页面返回),重新加载编排状态
|
||||||
|
if (this.tableData.length > 0) {
|
||||||
|
this.loadScheduleStatus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 加载赛事列表
|
||||||
|
loadCompetitionList() {
|
||||||
|
this.loading = true
|
||||||
|
const params = {}
|
||||||
|
|
||||||
|
if (this.searchForm.keyword) {
|
||||||
|
params.competitionName = this.searchForm.keyword
|
||||||
|
}
|
||||||
|
if (this.searchForm.status !== null && this.searchForm.status !== '') {
|
||||||
|
params.status = this.searchForm.status
|
||||||
|
}
|
||||||
|
|
||||||
|
getCompetitionList(this.pagination.current, this.pagination.size, params)
|
||||||
|
.then(res => {
|
||||||
|
console.log('赛事列表返回数据:', res)
|
||||||
|
const responseData = res.data?.data
|
||||||
|
if (responseData && responseData.records) {
|
||||||
|
// 处理赛事数据,兼容驼峰和下划线命名
|
||||||
|
this.tableData = responseData.records.map(competition => ({
|
||||||
|
id: competition.id,
|
||||||
|
competitionName: competition.competitionName || competition.competition_name,
|
||||||
|
competitionCode: competition.competitionCode || competition.competition_code,
|
||||||
|
organizer: competition.organizer,
|
||||||
|
location: competition.location,
|
||||||
|
venue: competition.venue,
|
||||||
|
registrationStartTime: competition.registrationStartTime || competition.registration_start_time,
|
||||||
|
registrationEndTime: competition.registrationEndTime || competition.registration_end_time,
|
||||||
|
competitionStartTime: competition.competitionStartTime || competition.competition_start_time,
|
||||||
|
competitionEndTime: competition.competitionEndTime || competition.competition_end_time,
|
||||||
|
status: competition.status,
|
||||||
|
createTime: competition.createTime || competition.create_time
|
||||||
|
}))
|
||||||
|
this.pagination.total = responseData.total || 0
|
||||||
|
|
||||||
|
// 加载每个赛事的编排状态
|
||||||
|
this.loadScheduleStatus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载赛事列表失败', err)
|
||||||
|
this.$message.error('加载赛事列表失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载编排状态
|
||||||
|
async loadScheduleStatus() {
|
||||||
|
for (const competition of this.tableData) {
|
||||||
|
try {
|
||||||
|
const res = await getScheduleResult(competition.id)
|
||||||
|
if (res.data?.data) {
|
||||||
|
this.$set(this.scheduleStatusMap, competition.id, res.data.data.isCompleted || false)
|
||||||
|
} else {
|
||||||
|
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 如果获取失败,默认为未完成
|
||||||
|
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查编排是否完成
|
||||||
|
isScheduleCompleted(competitionId) {
|
||||||
|
return this.scheduleStatusMap[competitionId] === true
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSearch() {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.loadCompetitionList()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.loadCompetitionList()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCurrentChange(current) {
|
||||||
|
this.pagination.current = current
|
||||||
|
this.loadCompetitionList()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查看报名详情 - 传递赛事ID
|
||||||
|
handleRegistrationDetail(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/registration/detail',
|
||||||
|
query: { competitionId: row.id }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 编排 - 传递赛事ID
|
||||||
|
handleSchedule(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/schedule/list',
|
||||||
|
query: { competitionId: row.id }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 调度 - 传递赛事ID
|
||||||
|
handleDispatch(row) {
|
||||||
|
// 检查编排是否完成
|
||||||
|
if (!this.isScheduleCompleted(row.id)) {
|
||||||
|
this.$message.warning('请先完成编排后再进行调度')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/dispatch/list',
|
||||||
|
query: { competitionId: row.id }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 格式化日期范围
|
||||||
|
formatDateRange(startTime, endTime) {
|
||||||
|
if (!startTime || !endTime) return '-'
|
||||||
|
// 简单格式化,只显示日期部分
|
||||||
|
const start = startTime.split(' ')[0]
|
||||||
|
const end = endTime.split(' ')[0]
|
||||||
|
return `${start} ~ ${end}`
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusType(status) {
|
||||||
|
const statusMap = {
|
||||||
|
1: 'info', // 未开始
|
||||||
|
2: 'success', // 报名中
|
||||||
|
3: 'warning', // 进行中
|
||||||
|
4: 'info' // 已结束
|
||||||
|
}
|
||||||
|
return statusMap[status] || 'info'
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusText(status) {
|
||||||
|
const statusMap = {
|
||||||
|
1: '未开始',
|
||||||
|
2: '报名中',
|
||||||
|
3: '进行中',
|
||||||
|
4: '已结束'
|
||||||
|
}
|
||||||
|
return statusMap[status] || '未知'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.martial-order-container {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-message {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: linear-gradient(90deg, #ffd54f 0%, #ffecb3 100%);
|
||||||
|
border: 1px solid #ffc107;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(255, 193, 7, 0.2);
|
||||||
|
|
||||||
|
.tip-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.tip-number {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background: #ff9800;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-content {
|
||||||
|
flex: 1;
|
||||||
|
color: #5d4037;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
.tip-subtitle {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #6d4c41;
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-text {
|
||||||
|
color: #e6a23c;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
990
src/views/martial/participant/index.vue.bak
Normal file
990
src/views/martial/participant/index.vue.bak
Normal file
@@ -0,0 +1,990 @@
|
|||||||
|
<template>
|
||||||
|
<div class="participant-container">
|
||||||
|
<!-- 列表视图 -->
|
||||||
|
<div v-if="currentView === 'list'" class="list-view">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<i class="el-input__icon el-icon-search"></i>
|
||||||
|
</template>
|
||||||
|
</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 allCompetitionOptions"
|
||||||
|
: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-button size="small" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="displayList"
|
||||||
|
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="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>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
v-if="pagination.total > 0"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handlePageChange"
|
||||||
|
:current-page="pagination.current"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pagination.size"
|
||||||
|
:total="pagination.total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
style="margin-top: 20px; text-align: right"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单视图 -->
|
||||||
|
<div v-else class="form-view">
|
||||||
|
<el-card shadow="hover" v-loading="loading">
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-arrow-left"
|
||||||
|
@click="backToList"
|
||||||
|
>
|
||||||
|
返回列表
|
||||||
|
</el-button>
|
||||||
|
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||||
|
<div class="header-actions" v-if="currentView !== 'view'">
|
||||||
|
<el-button @click="backToList">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">
|
||||||
|
{{ currentView === '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="currentView === '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 availableCompetitionOptions"
|
||||||
|
: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="projectId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.projectId"
|
||||||
|
placeholder="请选择参赛项目"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleProjectChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in projectOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.projectName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</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>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getCompetitionList } from '@/api/martial/competition'
|
||||||
|
import { getInfoPublishList } from '@/api/martial/infoPublish'
|
||||||
|
import { getProjectsByCompetition } from '@/api/martial/project'
|
||||||
|
import {
|
||||||
|
getParticipantList,
|
||||||
|
getParticipantDetail,
|
||||||
|
addParticipant,
|
||||||
|
updateParticipant,
|
||||||
|
removeParticipant
|
||||||
|
} from '@/api/martial/participant'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ParticipantManagement',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
currentView: 'list', // list, create, edit, view
|
||||||
|
participantId: null,
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
competitionId: null
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
competitionOptions: [], // 已发布的可报名赛事列表(用于新建)
|
||||||
|
allCompetitionOptions: [], // 所有赛事列表(用于搜索和编辑)
|
||||||
|
projectOptions: [], // 项目列表
|
||||||
|
participantList: [],
|
||||||
|
formData: {
|
||||||
|
orderId: null,
|
||||||
|
competitionId: null,
|
||||||
|
competitionName: '',
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectId: null,
|
||||||
|
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' }
|
||||||
|
],
|
||||||
|
projectId: [
|
||||||
|
{ required: true, message: '请选择参赛项目', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageTitle() {
|
||||||
|
const titleMap = {
|
||||||
|
create: '添加参赛选手',
|
||||||
|
edit: '编辑参赛选手',
|
||||||
|
view: '查看参赛选手'
|
||||||
|
};
|
||||||
|
return titleMap[this.currentView] || '参赛选手信息';
|
||||||
|
},
|
||||||
|
displayList() {
|
||||||
|
return this.participantList;
|
||||||
|
},
|
||||||
|
// 根据不同模式返回不同的赛事选项
|
||||||
|
availableCompetitionOptions() {
|
||||||
|
// 编辑和查看模式:显示所有赛事(因为可能编辑已过报名期的选手)
|
||||||
|
if (this.currentView === 'edit' || this.currentView === 'view') {
|
||||||
|
return this.allCompetitionOptions;
|
||||||
|
}
|
||||||
|
// 新建模式:只显示可报名的赛事
|
||||||
|
return this.competitionOptions;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query': {
|
||||||
|
handler(query) {
|
||||||
|
this.initPage();
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadAvailableCompetitions();
|
||||||
|
this.loadAllCompetitions();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initPage() {
|
||||||
|
const { mode, id } = this.$route.query;
|
||||||
|
this.currentView = mode || 'list';
|
||||||
|
// 不使用 parseInt,保持 ID 为字符串避免精度丢失
|
||||||
|
this.participantId = id || null;
|
||||||
|
|
||||||
|
if (this.currentView === 'list') {
|
||||||
|
this.loadParticipantList();
|
||||||
|
} else if (this.currentView !== 'list' && this.participantId) {
|
||||||
|
this.loadParticipantData();
|
||||||
|
} else if (this.currentView === 'create') {
|
||||||
|
this.resetFormData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载可报名的赛事(从已发布的信息中获取)
|
||||||
|
loadAvailableCompetitions() {
|
||||||
|
getInfoPublishList(1, 100, { isPublished: 1 })
|
||||||
|
.then(res => {
|
||||||
|
console.log('已发布信息列表返回数据:', res);
|
||||||
|
const responseData = res.data?.data;
|
||||||
|
if (responseData && responseData.records) {
|
||||||
|
const publishedCompetitionIds = new Set(
|
||||||
|
responseData.records
|
||||||
|
.filter(item => item.competitionId)
|
||||||
|
.map(item => item.competitionId)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('已发布的赛事ID列表:', Array.from(publishedCompetitionIds));
|
||||||
|
|
||||||
|
if (publishedCompetitionIds.size > 0) {
|
||||||
|
this.loadPublishedCompetitions(Array.from(publishedCompetitionIds));
|
||||||
|
} else {
|
||||||
|
// 如果没有发布信息,直接加载所有赛事作为可报名赛事
|
||||||
|
console.log('没有已发布信息,加载所有赛事');
|
||||||
|
this.loadPublishedCompetitions([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载已发布信息列表失败', err);
|
||||||
|
// 出错时也加载所有赛事
|
||||||
|
this.loadPublishedCompetitions([]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载已发布的赛事详细信息,并过滤出可报名的赛事
|
||||||
|
loadPublishedCompetitions(competitionIds) {
|
||||||
|
getCompetitionList(1, 100, {})
|
||||||
|
.then(res => {
|
||||||
|
console.log('赛事列表返回数据:', res);
|
||||||
|
const responseData = res.data?.data;
|
||||||
|
if (responseData && responseData.records) {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
this.competitionOptions = responseData.records
|
||||||
|
.filter(item => {
|
||||||
|
// 如果没有发布信息(competitionIds为空数组),则显示所有在报名期内的赛事
|
||||||
|
if (competitionIds.length > 0 && !competitionIds.includes(item.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查报名时间
|
||||||
|
if (!item.registrationStartTime || !item.registrationEndTime) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regStart = new Date(item.registrationStartTime);
|
||||||
|
const regEnd = new Date(item.registrationEndTime);
|
||||||
|
return now >= regStart && now <= regEnd;
|
||||||
|
})
|
||||||
|
.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
competitionName: item.competitionName,
|
||||||
|
registrationStartTime: item.registrationStartTime,
|
||||||
|
registrationEndTime: item.registrationEndTime
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('可报名的赛事列表:', this.competitionOptions);
|
||||||
|
|
||||||
|
if (this.competitionOptions.length === 0) {
|
||||||
|
console.log('当前没有可以报名的赛事(报名时间范围外)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载赛事列表失败', err);
|
||||||
|
this.$message.error('加载赛事列表失败');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载所有赛事(用于搜索过滤)
|
||||||
|
loadAllCompetitions() {
|
||||||
|
getCompetitionList(1, 100, {})
|
||||||
|
.then(res => {
|
||||||
|
const responseData = res.data?.data;
|
||||||
|
if (responseData && responseData.records) {
|
||||||
|
this.allCompetitionOptions = responseData.records.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
competitionName: item.competitionName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载所有赛事失败', err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadParticipantList() {
|
||||||
|
this.loading = true;
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (this.searchForm.keyword) {
|
||||||
|
params.playerName = this.searchForm.keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.searchForm.competitionId) {
|
||||||
|
params.competitionId = this.searchForm.competitionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getParticipantList(null, this.pagination.current, this.pagination.size, params)
|
||||||
|
.then(res => {
|
||||||
|
console.log('参赛人员列表返回数据:', res);
|
||||||
|
const responseData = res.data?.data;
|
||||||
|
if (responseData && responseData.records) {
|
||||||
|
this.participantList = responseData.records;
|
||||||
|
this.pagination.total = responseData.total || 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载参赛人员列表失败', err);
|
||||||
|
this.$message.error('加载参赛人员列表失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadParticipantData() {
|
||||||
|
if (!this.participantId) return;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
getParticipantDetail(this.participantId)
|
||||||
|
.then(res => {
|
||||||
|
const detailData = res.data?.data;
|
||||||
|
if (detailData) {
|
||||||
|
this.formData = { ...detailData };
|
||||||
|
|
||||||
|
// 将 attachments 字符串转换为数组(前端需要数组格式)
|
||||||
|
if (typeof this.formData.attachments === 'string') {
|
||||||
|
try {
|
||||||
|
this.formData.attachments = JSON.parse(this.formData.attachments);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析 attachments 失败,使用空数组', e);
|
||||||
|
this.formData.attachments = [];
|
||||||
|
}
|
||||||
|
} else if (!this.formData.attachments) {
|
||||||
|
this.formData.attachments = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载该赛事的项目列表
|
||||||
|
if (detailData.competitionId) {
|
||||||
|
this.loadProjectsByCompetition(detailData.competitionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载参赛人员详情失败', err);
|
||||||
|
this.$message.error('加载参赛人员详情失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handlePageChange(current) {
|
||||||
|
this.pagination.current = current;
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pagination.size = size;
|
||||||
|
this.pagination.current = 1;
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSearch() {
|
||||||
|
this.pagination.current = 1;
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleReset() {
|
||||||
|
this.searchForm = {
|
||||||
|
keyword: '',
|
||||||
|
competitionId: null
|
||||||
|
};
|
||||||
|
this.pagination.current = 1;
|
||||||
|
this.loadParticipantList();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreate() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/list',
|
||||||
|
query: { mode: 'create' }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleView(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/list',
|
||||||
|
query: { mode: 'view', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEdit(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/list',
|
||||||
|
query: { mode: 'edit', id: row.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
switchToEdit() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/list',
|
||||||
|
query: { mode: 'edit', id: this.participantId }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确定要删除该选手吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.loading = true;
|
||||||
|
removeParticipant(row.id.toString())
|
||||||
|
.then(res => {
|
||||||
|
this.$message.success('删除成功');
|
||||||
|
this.loadParticipantList();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('删除失败', err);
|
||||||
|
this.$message.error('删除失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}).catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCompetitionChange(competitionId) {
|
||||||
|
// 从可用的选项列表中查找赛事
|
||||||
|
const competition = this.availableCompetitionOptions.find(item => item.id === competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
// 加载该赛事的项目列表
|
||||||
|
this.loadProjectsByCompetition(competitionId);
|
||||||
|
// 清空已选项目
|
||||||
|
this.formData.projectId = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleProjectChange(projectId) {
|
||||||
|
const project = this.projectOptions.find(item => item.id === projectId);
|
||||||
|
if (project) {
|
||||||
|
// 自动填充组别信息
|
||||||
|
if (project.category && !this.formData.category) {
|
||||||
|
this.formData.category = project.category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadProjectsByCompetition(competitionId) {
|
||||||
|
if (!competitionId) {
|
||||||
|
this.projectOptions = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('加载赛事项目,赛事ID:', competitionId);
|
||||||
|
|
||||||
|
getProjectsByCompetition(competitionId)
|
||||||
|
.then(res => {
|
||||||
|
console.log('项目列表返回数据:', res);
|
||||||
|
const responseData = res.data?.data;
|
||||||
|
|
||||||
|
// 兼容两种数据格式:分页数据(有records)和直接数组
|
||||||
|
let projectList = [];
|
||||||
|
if (responseData) {
|
||||||
|
if (Array.isArray(responseData)) {
|
||||||
|
// 直接是数组
|
||||||
|
projectList = responseData;
|
||||||
|
console.log('返回的是直接数组,长度:', projectList.length);
|
||||||
|
} else if (responseData.records && Array.isArray(responseData.records)) {
|
||||||
|
// 分页数据
|
||||||
|
projectList = responseData.records;
|
||||||
|
console.log('返回的是分页数据,记录数:', projectList.length);
|
||||||
|
} else {
|
||||||
|
console.warn('未知的数据格式:', responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectList.length > 0) {
|
||||||
|
this.projectOptions = projectList.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
projectName: item.projectName,
|
||||||
|
projectCode: item.projectCode,
|
||||||
|
category: item.category
|
||||||
|
}));
|
||||||
|
console.log('可选项目列表:', this.projectOptions);
|
||||||
|
} else {
|
||||||
|
this.projectOptions = [];
|
||||||
|
console.log('该赛事没有项目数据');
|
||||||
|
this.$message.warning('该赛事还没有配置项目,请先添加项目');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('加载项目列表失败', err);
|
||||||
|
this.$message.error('加载项目列表失败: ' + (err.message || '未知错误'));
|
||||||
|
this.projectOptions = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSave() {
|
||||||
|
this.$refs.formRef.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
// 确保有赛事名称
|
||||||
|
if (!this.formData.competitionName) {
|
||||||
|
const competition = this.availableCompetitionOptions.find(item => item.id === this.formData.competitionId);
|
||||||
|
if (competition) {
|
||||||
|
this.formData.competitionName = competition.competitionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitData = { ...this.formData };
|
||||||
|
|
||||||
|
console.log('=== 提交前的 formData ===', this.formData);
|
||||||
|
console.log('formData.orderId:', this.formData.orderId);
|
||||||
|
|
||||||
|
// 将 attachments 数组转换为 JSON 字符串(后端需要 String 类型)
|
||||||
|
if (Array.isArray(submitData.attachments)) {
|
||||||
|
submitData.attachments = JSON.stringify(submitData.attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 临时方案: 如果没有 orderId,使用 competitionId 作为 orderId
|
||||||
|
// 警告: 这是临时解决方案,后续应修改数据库表结构或后端逻辑
|
||||||
|
if (!submitData.orderId && submitData.competitionId) {
|
||||||
|
submitData.orderId = submitData.competitionId;
|
||||||
|
console.warn('⚠️ 临时方案: 使用 competitionId 作为 orderId', submitData.competitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('=== 提交的数据 submitData ===', submitData);
|
||||||
|
console.log('submitData.orderId:', submitData.orderId);
|
||||||
|
|
||||||
|
if (this.currentView === 'create') {
|
||||||
|
// 新建
|
||||||
|
addParticipant(submitData)
|
||||||
|
.then(res => {
|
||||||
|
this.$message.success('添加成功');
|
||||||
|
this.backToList();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('添加失败', err);
|
||||||
|
this.$message.error('添加失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
} else if (this.currentView === 'edit') {
|
||||||
|
// 编辑
|
||||||
|
submitData.id = this.participantId;
|
||||||
|
updateParticipant(submitData)
|
||||||
|
.then(res => {
|
||||||
|
this.$message.success('保存成功');
|
||||||
|
this.backToList();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('保存失败', err);
|
||||||
|
this.$message.error('保存失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善必填信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
backToList() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/martial/participant/list'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
resetFormData() {
|
||||||
|
this.formData = {
|
||||||
|
orderId: null,
|
||||||
|
competitionId: null,
|
||||||
|
competitionName: '',
|
||||||
|
playerName: '',
|
||||||
|
gender: 1,
|
||||||
|
age: null,
|
||||||
|
contactPhone: '',
|
||||||
|
organization: '',
|
||||||
|
idCard: '',
|
||||||
|
projectId: null,
|
||||||
|
category: '',
|
||||||
|
orderNum: 1,
|
||||||
|
introduction: '',
|
||||||
|
remark: '',
|
||||||
|
attachments: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.participant-container {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #dc2626;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -380,7 +380,7 @@
|
|||||||
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue'
|
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue'
|
||||||
import { getVenuesByCompetition } from '@/api/martial/venue'
|
import { getVenuesByCompetition } from '@/api/martial/venue'
|
||||||
import { getCompetitionDetail } from '@/api/martial/competition'
|
import { getCompetitionDetail } from '@/api/martial/competition'
|
||||||
import { getScheduleResult, saveAndLockSchedule, saveDraftSchedule, triggerAutoArrange, moveScheduleGroup } from '@/api/martial/activitySchedule'
|
import { getScheduleResult, saveAndLockSchedule, saveDraftSchedule, triggerAutoArrange, moveScheduleGroup, exportSchedule } from '@/api/martial/activitySchedule'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MartialScheduleList',
|
name: 'MartialScheduleList',
|
||||||
@@ -1113,8 +1113,23 @@ export default {
|
|||||||
group.items.splice(itemIndex + 1, 0, temp)
|
group.items.splice(itemIndex + 1, 0, temp)
|
||||||
this.$message.success('下移成功')
|
this.$message.success('下移成功')
|
||||||
},
|
},
|
||||||
handleExport() {
|
async handleExport() {
|
||||||
this.$message.success('导出功能开发中')
|
try {
|
||||||
|
this.loading = true
|
||||||
|
const res = await exportSchedule(this.competitionId)
|
||||||
|
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = `赛程表_${this.competitionInfo.competitionName || this.competitionId}.xlsx`
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
this.$message.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('导出失败:', error)
|
||||||
|
this.$message.error('导出失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleConfirm() {
|
handleConfirm() {
|
||||||
this.confirmDialogVisible = true
|
this.confirmDialogVisible = true
|
||||||
|
|||||||
1019
src/views/martial/schedule/index.vue.bak
Normal file
1019
src/views/martial/schedule/index.vue.bak
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user