Files
martial-mini/pages/schedule/schedule-example.vue
2025-12-12 17:29:38 +08:00

449 lines
9.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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.totalParticipants || 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>