Files
martial-admin-mini/pages/score-detail/score-detail.vue

766 lines
15 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="container">
<!-- 自定义导航栏 -->
<view class="nav-bar">
<view class="nav-left" @click="goBack">
<text class="back-icon"></text>
</view>
<view class="nav-title">评分详情</view>
<view class="nav-right">
<view class="icon-menu">···</view>
<view class="icon-close"></view>
</view>
</view>
<!-- 选手信息 -->
<view class="player-info-section">
<view class="player-name">{{ player.name }}</view>
<view class="player-details">
<view class="detail-item">身份证{{ player.idCard }}</view>
<view class="detail-item">队伍{{ player.team }}</view>
<view class="detail-item">编号{{ player.number }}</view>
</view>
</view>
<!-- 评分提示 -->
<view class="score-tip">
点击分数填写或拖动滑块打分5-10
</view>
<!-- 分数调整 -->
<view class="score-control">
<view class="control-btn decrease" @click="decreaseScore">
<text class="btn-symbol"></text>
</view>
<view class="score-display" @click="showScoreInput">
<text class="current-score">{{ currentScore.toFixed(3) }}</text>
<text class="edit-hint">点击编辑</text>
</view>
<view class="control-btn increase" @click="increaseScore">
<text class="btn-symbol"></text>
</view>
</view>
<!-- 扣分项 -->
<view class="deduction-section">
<view class="deduction-header">
<text class="deduction-label">扣分项</text>
</view>
<view class="deduction-list">
<view
v-for="(item, index) in deductions"
:key="item.deductionId"
class="deduction-item"
@click="toggleDeduction(index)"
>
<view :class="['checkbox', item.checked ? 'checked' : '']">
<text v-if="item.checked" class="check-icon"></text>
</view>
<text class="deduction-text">{{ item.deductionName }}</text>
</view>
</view>
</view>
<!-- 备注 -->
<view class="note-section">
<view class="note-label">
<text>备注</text>
</view>
<view class="note-input-wrapper">
<textarea
class="note-input"
placeholder="请输入修改备注"
v-model="note"
maxlength="200"
/>
</view>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" @click="handleSubmit">提交</button>
<!-- 分数输入弹窗 -->
<view v-if="showInputModal" class="modal-overlay" @click="hideScoreInput">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">输入分数</text>
</view>
<view class="modal-body">
<input
type="digit"
class="score-input"
v-model="inputScore"
placeholder="请输入5-10之间的分数"
:focus="showInputModal"
@confirm="confirmScoreInput"
/>
<text class="input-hint">分数范围{{ minScore }} - {{ maxScore }}保留3位小数</text>
</view>
<view class="modal-footer">
<button class="modal-btn cancel" @click="hideScoreInput">取消</button>
<button class="modal-btn confirm" @click="confirmScoreInput">确定</button>
</view>
</view>
</view>
</view>
</template>
<script>
import dataAdapter from '@/utils/dataAdapter.js'
import config from '@/config/env.config.js'
export default {
data() {
return {
player: {
athleteId: '',
name: '',
idCard: '',
team: '',
number: ''
},
judgeId: '',
projectId: '',
competitionId: '',
venueId: '',
currentScore: 8.000,
note: '',
minScore: 5.0,
maxScore: 10.0,
deductions: [],
showInputModal: false,
inputScore: ''
}
},
async onLoad() {
const app = getApp()
const globalData = app.globalData || {}
const currentAthlete = globalData.currentAthlete || {}
this.player = {
athleteId: currentAthlete.athleteId || '',
name: currentAthlete.name || '选手姓名',
idCard: currentAthlete.idCard || '',
team: currentAthlete.team || '',
number: currentAthlete.number || ''
}
if (currentAthlete.scored && currentAthlete.myScore) {
this.currentScore = currentAthlete.myScore
}
this.judgeId = globalData.judgeId
this.projectId = globalData.currentProjectId || ''
this.competitionId = globalData.matchId || globalData.matchCode || ''
this.venueId = globalData.currentVenueId || globalData.venueId || ''
if (config.debug) {
console.log('评分详情页加载:', {
athlete: this.player,
judgeId: this.judgeId,
projectId: this.projectId,
competitionId: this.competitionId,
venueId: this.venueId,
initialScore: this.currentScore
})
}
await this.loadDeductions()
},
methods: {
async loadDeductions() {
try {
const response = await dataAdapter.getData('getDeductions', {
projectId: this.projectId
})
const records = response.data && response.data.records ? response.data.records : []
this.deductions = records.map(item => ({
deductionId: item.id,
deductionName: item.itemName,
deductionScore: parseFloat(item.deductionPoint || 0),
checked: false
}))
if (config.debug) {
console.log('扣分项加载成功:', this.deductions)
}
} catch (error) {
console.error('加载扣分项失败:', error)
uni.showToast({
title: '加载扣分项失败',
icon: 'none'
})
}
},
goBack() {
if (config.debug) {
console.log('返回上一页')
}
uni.navigateBack({
delta: 1,
fail: (err) => {
console.error('返回失败:', err)
uni.redirectTo({
url: '/pages/score-list/score-list'
})
}
})
},
decreaseScore() {
if (this.currentScore > this.minScore) {
this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3))
}
},
increaseScore() {
if (this.currentScore < this.maxScore) {
this.currentScore = parseFloat((this.currentScore + 0.001).toFixed(3))
}
},
showScoreInput() {
this.inputScore = this.currentScore.toFixed(3)
this.showInputModal = true
},
hideScoreInput() {
this.showInputModal = false
this.inputScore = ''
},
confirmScoreInput() {
const score = parseFloat(this.inputScore)
if (isNaN(score)) {
uni.showToast({
title: '请输入有效的数字',
icon: 'none'
})
return
}
if (score < this.minScore || score > this.maxScore) {
uni.showToast({
title: `分数必须在${this.minScore}-${this.maxScore}之间`,
icon: 'none'
})
return
}
this.currentScore = parseFloat(score.toFixed(3))
this.hideScoreInput()
},
toggleDeduction(index) {
this.deductions[index].checked = !this.deductions[index].checked
},
async handleSubmit() {
if (this.currentScore < this.minScore || this.currentScore > this.maxScore) {
uni.showToast({
title: `评分必须在${this.minScore}-${this.maxScore}分之间`,
icon: 'none'
})
return
}
if (!this.competitionId) {
uni.showToast({
title: '缺少比赛ID请重新登录',
icon: 'none'
})
return
}
if (!this.projectId) {
uni.showToast({
title: '缺少项目ID请返回重新选择',
icon: 'none'
})
return
}
const selectedDeductions = this.deductions
.filter(item => item.checked)
.map(item => item.deductionId)
try {
uni.showLoading({
title: '提交中...',
mask: true
})
const submitData = {
athleteId: this.player.athleteId,
judgeId: this.judgeId,
projectId: this.projectId,
competitionId: this.competitionId,
venueId: this.venueId,
score: this.currentScore,
deductions: selectedDeductions,
note: this.note
}
if (config.debug) {
console.log('准备提交评分数据:', submitData)
}
const response = await dataAdapter.getData('submitScore', submitData)
uni.hideLoading()
if (config.debug) {
console.log('评分提交成功:', {
athleteId: this.player.athleteId,
score: this.currentScore,
deductions: selectedDeductions,
response: response
})
}
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 1500
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (error) {
uni.hideLoading()
console.error('提交评分失败:', error)
uni.showToast({
title: error.message || '提交失败,请重试',
icon: 'none',
duration: 2000
})
}
}
}
}
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: #F5F5F5;
padding-bottom: 40rpx;
}
/* 导航栏 */
.nav-bar {
height: 90rpx;
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 0 30rpx;
}
.nav-left {
position: absolute;
left: 0;
top: 0;
width: 120rpx;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
cursor: pointer;
}
.nav-left:active {
opacity: 0.6;
}
.back-icon {
font-size: 60rpx;
color: #FFFFFF;
font-weight: 300;
line-height: 1;
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #FFFFFF;
letter-spacing: 2rpx;
}
.nav-right {
position: absolute;
right: 30rpx;
display: flex;
align-items: center;
gap: 30rpx;
}
.icon-menu,
.icon-close {
width: 60rpx;
height: 60rpx;
background-color: rgba(255, 255, 255, 0.25);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #FFFFFF;
font-weight: bold;
}
/* 选手信息 */
.player-info-section {
margin: 30rpx;
}
.player-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
margin-bottom: 20rpx;
}
.player-details {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.detail-item {
font-size: 26rpx;
color: #CD8B6F;
line-height: 1.5;
}
/* 评分提示 */
.score-tip {
padding: 0 30rpx;
font-size: 26rpx;
color: #666666;
margin-bottom: 30rpx;
}
/* 分数控制 */
.score-control {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 60rpx;
margin-bottom: 20rpx;
}
.control-btn {
width: 140rpx;
height: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #F5F5F5;
border-radius: 12rpx;
}
.control-btn.decrease {
background-color: #FFE5E5;
}
.control-btn.increase {
background-color: #E5F5F0;
}
.btn-symbol {
font-size: 48rpx;
font-weight: 300;
}
.control-btn.decrease .btn-symbol {
color: #FF4D6A;
}
.control-btn.increase .btn-symbol {
color: #1B7C5E;
}
.btn-value {
font-size: 24rpx;
margin-top: 8rpx;
}
.control-btn.decrease .btn-value {
color: #FF4D6A;
}
.control-btn.increase .btn-value {
color: #1B7C5E;
}
.score-display {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
padding: 20rpx;
border-radius: 16rpx;
transition: background-color 0.2s;
}
.score-display:active {
background-color: rgba(27, 124, 94, 0.1);
}
.current-score {
font-size: 80rpx;
font-weight: 600;
color: #1B7C5E;
}
.edit-hint {
font-size: 22rpx;
color: #999999;
margin-top: 8rpx;
}
.judge-tip {
padding: 0 30rpx;
font-size: 24rpx;
color: #FF4D6A;
text-align: center;
line-height: 1.6;
margin-bottom: 30rpx;
}
/* 扣分项 */
.deduction-section {
margin: 30rpx;
}
.deduction-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.deduction-label {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.deduction-hint {
font-size: 24rpx;
color: #FF4D6A;
}
.deduction-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.deduction-item {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #FFFFFF;
border-radius: 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #CCCCCC;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
flex-shrink: 0;
background-color: #F5F5F5;
}
.checkbox.checked {
background-color: #1B7C5E;
border-color: #1B7C5E;
}
.check-icon {
color: #FFFFFF;
font-size: 28rpx;
font-weight: bold;
}
.deduction-text {
font-size: 26rpx;
color: #333333;
line-height: 1.4;
flex: 1;
}
/* 备注 */
.note-section {
margin: 30rpx;
}
.note-label {
font-size: 28rpx;
color: #333333;
margin-bottom: 20rpx;
}
.note-input-wrapper {
background-color: #FFFFFF;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
position: relative;
}
.note-input {
width: 100%;
min-height: 120rpx;
font-size: 28rpx;
color: #333333;
line-height: 1.6;
}
.note-input::placeholder {
color: #CCCCCC;
}
.optional-text {
position: absolute;
right: 30rpx;
bottom: 30rpx;
font-size: 24rpx;
color: #FF4D6A;
}
/* 提交按钮 */
.submit-btn {
margin: 30rpx;
height: 90rpx;
background: linear-gradient(135deg, #1B7C5E 0%, #2A9D7E 100%);
border-radius: 16rpx;
font-size: 32rpx;
font-weight: 600;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(27, 124, 94, 0.3);
}
.submit-btn:active {
opacity: 0.9;
}
/* 分数输入弹窗 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
width: 600rpx;
background-color: #FFFFFF;
border-radius: 24rpx;
overflow: hidden;
}
.modal-header {
padding: 40rpx 30rpx 20rpx;
text-align: center;
}
.modal-title {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.modal-body {
padding: 20rpx 30rpx 30rpx;
}
.score-input {
width: 100%;
height: 90rpx;
border: 2rpx solid #E0E0E0;
border-radius: 12rpx;
padding: 0 24rpx;
font-size: 36rpx;
text-align: center;
color: #1B7C5E;
font-weight: 600;
}
.score-input:focus {
border-color: #1B7C5E;
}
.input-hint {
display: block;
margin-top: 16rpx;
font-size: 24rpx;
color: #999999;
text-align: center;
}
.modal-footer {
display: flex;
border-top: 1rpx solid #E0E0E0;
}
.modal-btn {
flex: 1;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
background: none;
border: none;
border-radius: 0;
}
.modal-btn.cancel {
color: #666666;
border-right: 1rpx solid #E0E0E0;
}
.modal-btn.confirm {
color: #1B7C5E;
font-weight: 600;
}
.modal-btn:active {
background-color: #F5F5F5;
}
</style>