449 lines
9.1 KiB
Vue
449 lines
9.1 KiB
Vue
<template>
|
||
<view class="schedule-page">
|
||
<view class="header">
|
||
<text class="title">赛程编排管理</text>
|
||
</view>
|
||
|
||
<!-- 状态显示 -->
|
||
<view class="status-card">
|
||
<view class="status-item">
|
||
<text class="label">编排状态:</text>
|
||
<text :class="['status-text', getStatusClass()]">{{ getStatusText() }}</text>
|
||
</view>
|
||
<view class="status-item">
|
||
<text class="label">分组数:</text>
|
||
<text class="value">{{ scheduleData.totalGroups || 0 }}</text>
|
||
</view>
|
||
<view class="status-item">
|
||
<text class="label">参赛人数:</text>
|
||
<text class="value">{{ scheduleData.registerCount || 0 }}</text>
|
||
</view>
|
||
<view class="status-item" v-if="scheduleData.lastAutoScheduleTime">
|
||
<text class="label">最后编排时间:</text>
|
||
<text class="value">{{ scheduleData.lastAutoScheduleTime }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons">
|
||
<!-- 触发自动编排 -->
|
||
<button
|
||
class="btn btn-primary"
|
||
@click="handleAutoArrange"
|
||
:disabled="loading || scheduleData.scheduleStatus === 2"
|
||
>
|
||
{{ loading ? '编排中...' : '自动编排' }}
|
||
</button>
|
||
|
||
<!-- 刷新编排结果 -->
|
||
<button
|
||
class="btn btn-default"
|
||
@click="loadScheduleResult"
|
||
:disabled="loading"
|
||
>
|
||
刷新结果
|
||
</button>
|
||
|
||
<!-- 保存并锁定 -->
|
||
<button
|
||
class="btn btn-success"
|
||
@click="handleSaveAndLock"
|
||
:disabled="loading || scheduleData.scheduleStatus === 2 || scheduleData.scheduleStatus === 0"
|
||
>
|
||
{{ scheduleData.scheduleStatus === 2 ? '已锁定' : '保存并锁定' }}
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 编排结果列表 -->
|
||
<view class="schedule-list" v-if="scheduleData.scheduleGroups && scheduleData.scheduleGroups.length > 0">
|
||
<view class="list-title">编排结果</view>
|
||
<view
|
||
class="schedule-item"
|
||
v-for="group in scheduleData.scheduleGroups"
|
||
:key="group.groupId"
|
||
>
|
||
<view class="group-header">
|
||
<text class="group-name">{{ group.groupName }}</text>
|
||
<text class="participant-count">{{ group.participants.length }}人</text>
|
||
</view>
|
||
<view class="group-info">
|
||
<text class="info-text">场地:{{ group.venueName }}</text>
|
||
<text class="info-text">时间:{{ group.scheduleDate }} {{ group.scheduleTime }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="empty-state" v-else>
|
||
<text class="empty-text">暂无编排数据</text>
|
||
<text class="empty-hint">点击"自动编排"开始编排</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import scheduleAPI from '@/api/schedule.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
competitionId: null, // 赛事ID,从页面参数获取
|
||
loading: false,
|
||
scheduleData: {
|
||
scheduleStatus: 0, // 0-未编排, 1-已编排, 2-已锁定
|
||
totalGroups: 0,
|
||
totalParticipants: 0,
|
||
lastAutoScheduleTime: null,
|
||
scheduleGroups: []
|
||
}
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
// 从页面参数获取赛事ID
|
||
if (options.competitionId) {
|
||
this.competitionId = parseInt(options.competitionId)
|
||
this.loadScheduleResult()
|
||
} else {
|
||
uni.showToast({
|
||
title: '缺少赛事ID',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
methods: {
|
||
/**
|
||
* 加载赛程编排结果
|
||
*/
|
||
async loadScheduleResult() {
|
||
if (!this.competitionId) return
|
||
|
||
this.loading = true
|
||
try {
|
||
const result = await scheduleAPI.getScheduleResult(this.competitionId)
|
||
this.scheduleData = result
|
||
console.log('编排结果:', result)
|
||
} catch (error) {
|
||
console.error('加载编排结果失败:', error)
|
||
uni.showToast({
|
||
title: error.message || '加载失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 触发自动编排
|
||
*/
|
||
async handleAutoArrange() {
|
||
if (!this.competitionId) return
|
||
|
||
// 确认提示
|
||
const [err, res] = await uni.showModal({
|
||
title: '确认编排',
|
||
content: '确定要执行自动编排吗?',
|
||
confirmText: '确定',
|
||
cancelText: '取消'
|
||
})
|
||
|
||
if (err || !res.confirm) return
|
||
|
||
this.loading = true
|
||
uni.showLoading({
|
||
title: '编排中...',
|
||
mask: true
|
||
})
|
||
|
||
try {
|
||
await scheduleAPI.triggerAutoArrange(this.competitionId)
|
||
|
||
uni.showToast({
|
||
title: '编排成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 延迟1秒后刷新结果
|
||
setTimeout(() => {
|
||
this.loadScheduleResult()
|
||
}, 1000)
|
||
} catch (error) {
|
||
console.error('自动编排失败:', error)
|
||
uni.showToast({
|
||
title: error.message || '编排失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.loading = false
|
||
uni.hideLoading()
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 保存并锁定编排
|
||
*/
|
||
async handleSaveAndLock() {
|
||
if (!this.competitionId) return
|
||
|
||
// 确认提示
|
||
const [err, res] = await uni.showModal({
|
||
title: '确认锁定',
|
||
content: '锁定后将无法再修改编排,确定要锁定吗?',
|
||
confirmText: '确定锁定',
|
||
cancelText: '取消'
|
||
})
|
||
|
||
if (err || !res.confirm) return
|
||
|
||
this.loading = true
|
||
uni.showLoading({
|
||
title: '保存中...',
|
||
mask: true
|
||
})
|
||
|
||
try {
|
||
await scheduleAPI.saveAndLockSchedule(this.competitionId)
|
||
|
||
uni.showToast({
|
||
title: '锁定成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 刷新结果
|
||
setTimeout(() => {
|
||
this.loadScheduleResult()
|
||
}, 1000)
|
||
} catch (error) {
|
||
console.error('保存锁定失败:', error)
|
||
uni.showToast({
|
||
title: error.message || '锁定失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.loading = false
|
||
uni.hideLoading()
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取状态文本
|
||
*/
|
||
getStatusText() {
|
||
const statusMap = {
|
||
0: '未编排',
|
||
1: '已编排',
|
||
2: '已锁定'
|
||
}
|
||
return statusMap[this.scheduleData.scheduleStatus] || '未知'
|
||
},
|
||
|
||
/**
|
||
* 获取状态样式类
|
||
*/
|
||
getStatusClass() {
|
||
const classMap = {
|
||
0: 'status-pending',
|
||
1: 'status-draft',
|
||
2: 'status-locked'
|
||
}
|
||
return classMap[this.scheduleData.scheduleStatus] || ''
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.schedule-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.header {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.status-card {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.status-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
min-width: 180rpx;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 8rpx;
|
||
|
||
&.status-pending {
|
||
color: #999;
|
||
background: #f0f0f0;
|
||
}
|
||
|
||
&.status-draft {
|
||
color: #1890ff;
|
||
background: #e6f7ff;
|
||
}
|
||
|
||
&.status-locked {
|
||
color: #52c41a;
|
||
background: #f6ffed;
|
||
}
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
|
||
&.btn-primary {
|
||
background: #1890ff;
|
||
color: #fff;
|
||
|
||
&:disabled {
|
||
background: #d9d9d9;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
&.btn-default {
|
||
background: #fff;
|
||
color: #333;
|
||
border: 2rpx solid #d9d9d9;
|
||
|
||
&:disabled {
|
||
background: #f5f5f5;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
&.btn-success {
|
||
background: #52c41a;
|
||
color: #fff;
|
||
|
||
&:disabled {
|
||
background: #d9d9d9;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
.schedule-list {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.list-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.schedule-item {
|
||
padding: 24rpx;
|
||
background: #f9f9f9;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.group-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.group-name {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.participant-count {
|
||
font-size: 24rpx;
|
||
color: #1890ff;
|
||
background: #e6f7ff;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 6rpx;
|
||
}
|
||
|
||
.group-info {
|
||
display: flex;
|
||
gap: 30rpx;
|
||
}
|
||
|
||
.info-text {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 120rpx 0;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 32rpx;
|
||
color: #999;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.empty-hint {
|
||
font-size: 26rpx;
|
||
color: #ccc;
|
||
}
|
||
</style>
|