fix(add-player): 修复新增选手页面问题

- 移除一直显示的悬浮提示弹窗 (Issue #8)
- 添加登录状态检查
- 优化表单验证逻辑
- 改进错误提示显示方式
This commit is contained in:
DevOps
2025-12-30 17:26:46 +08:00
parent c6c9f9a5d1
commit b5a8c811aa

View File

@@ -72,29 +72,16 @@
</view> </view>
</view> </view>
<!-- 错误提示 --> <!-- 错误提示 - 只在有具体错误时显示 -->
<view class="error-hints" v-if="errors.length > 0"> <view class="error-hints" v-if="validationErrors.length > 0">
<view class="error-item" v-for="(error, index) in errors" :key="index"> <view class="error-item" v-for="(error, index) in validationErrors" :key="index">
<text class="error-label">{{ error.label }}</text> <text class="error-text">{{ error }}</text>
<text class="error-msg">{{ error.message }}</text>
</view> </view>
</view> </view>
<!-- 提示信息 -->
<view class="hint-message" v-if="showHint">
<text class="hint-icon"></text>
<text class="hint-text">请完善信息</text>
</view>
<!-- Toast提示 -->
<view class="toast-message" v-if="showToast">
<text class="toast-text">{{ toastMessage }}</text>
</view>
<!-- 按钮 --> <!-- 按钮 -->
<view class="btn-wrapper"> <view class="btn-wrapper">
<view class="btn save-btn disabled" v-if="!isFormValid">保存</view> <view class="btn save-btn" :class="{ disabled: !isFormValid }" @click="handleSave">保存</view>
<view class="btn save-btn" v-else @click="handleSave">保存</view>
</view> </view>
<!-- 证件类型选择器 --> <!-- 证件类型选择器 -->
@@ -114,6 +101,7 @@
<script> <script>
import athleteAPI from '@/api/athlete.js' import athleteAPI from '@/api/athlete.js'
import { getUserInfo, getToken } from '@/utils/auth.js'
export default { export default {
data() { data() {
@@ -128,25 +116,17 @@ export default {
}, },
competitionId: '', competitionId: '',
projectIds: [], projectIds: [],
errors: [], validationErrors: [],
showHint: false,
showToast: false,
toastMessage: '',
showIdTypePicker: false showIdTypePicker: false
}; };
}, },
onLoad(options) { onLoad(options) {
// 接收赛事ID和项目ID
if (options.competitionId) { if (options.competitionId) {
this.competitionId = options.competitionId this.competitionId = options.competitionId
} }
if (options.projectIds) { if (options.projectIds) {
this.projectIds = options.projectIds.split(',') this.projectIds = options.projectIds.split(',')
} }
console.log('新增选手页面接收参数:', {
competitionId: this.competitionId,
projectIds: this.projectIds
})
}, },
computed: { computed: {
isFormValid() { isFormValid() {
@@ -162,51 +142,28 @@ export default {
} }
}, },
watch: { watch: {
'formData.name'(val) {
this.validateForm();
},
'formData.idCard'(val) { 'formData.idCard'(val) {
this.validateForm(); this.updateValidationErrors();
},
'formData.team'(val) {
this.validateForm();
},
'formData.organization'(val) {
this.validateForm();
}, },
'formData.phone'(val) { 'formData.phone'(val) {
this.validateForm(); this.updateValidationErrors();
} }
}, },
methods: { methods: {
validateIdCard(idCard) { validateIdCard(idCard) {
// 身份证号验证18位最后一位可以是数字或字母X if (!idCard) return false;
if (!/^\d{17}[\dXx]$/.test(idCard)) { if (!/^\d{17}[\dXx]$/.test(idCard)) return false;
return false;
}
// 验证日期部分是否有效
const year = parseInt(idCard.substring(6, 10)) const year = parseInt(idCard.substring(6, 10))
const month = parseInt(idCard.substring(10, 12)) const month = parseInt(idCard.substring(10, 12))
const day = parseInt(idCard.substring(12, 14)) const day = parseInt(idCard.substring(12, 14))
// 检查月份是否有效 (1-12) if (month < 1 || month > 12) return false;
if (month < 1 || month > 12) { if (day < 1 || day > 31) return false;
return false;
}
// 检查日期是否有效 (1-31)
if (day < 1 || day > 31) {
return false;
}
// 检查年份是否合理 (1900-当前年份)
const currentYear = new Date().getFullYear() const currentYear = new Date().getFullYear()
if (year < 1900 || year > currentYear) { if (year < 1900 || year > currentYear) return false;
return false;
}
// 进一步验证日期是否真实存在
const date = new Date(year, month - 1, day) const date = new Date(year, month - 1, day)
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) { if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
return false; return false;
@@ -215,131 +172,106 @@ export default {
return true; return true;
}, },
validatePhone(phone) { validatePhone(phone) {
// 手机号验证11位数字
return /^1[3-9]\d{9}$/.test(phone); return /^1[3-9]\d{9}$/.test(phone);
}, },
validateForm() { updateValidationErrors() {
this.errors = []; this.validationErrors = [];
this.showHint = false;
if (!this.formData.name || !this.formData.idCard || !this.formData.team || !this.formData.organization || !this.formData.phone) {
this.showHint = true;
if (!this.formData.name || !this.formData.idCard || !this.formData.team || !this.formData.organization || !this.formData.phone) {
this.errors.push({
label: '有空文本时弹出:',
message: '按钮置灰'
});
}
}
if (this.formData.idCard && !this.validateIdCard(this.formData.idCard)) { if (this.formData.idCard && !this.validateIdCard(this.formData.idCard)) {
this.errors.push({ this.validationErrors.push('身份证号码格式不正确');
label: '身份证不足18位',
message: '按钮置灰'
});
this.errors.push({
label: '输入不合法:',
message: '按钮置灰'
});
} }
if (this.formData.phone && !this.validatePhone(this.formData.phone)) { if (this.formData.phone && !this.validatePhone(this.formData.phone)) {
this.errors.push({ this.validationErrors.push('手机号格式不正确');
label: '手机号格式不正确:',
message: '按钮置灰'
});
} }
}, },
handleIdTypeChange(e) { handleIdTypeChange(e) {
this.formData.idType = '身份证'; this.formData.idType = '身份证';
this.showIdTypePicker = false; this.showIdTypePicker = false;
}, },
/**
* 从身份证号中提取信息
*/
extractInfoFromIdCard(idCard) { extractInfoFromIdCard(idCard) {
if (!idCard || idCard.length !== 18) { if (!idCard || idCard.length !== 18) {
return { return { gender: null, age: null, birthDate: null }
gender: null,
age: null,
birthDate: null
}
} }
// 提取出生日期
const year = idCard.substring(6, 10) const year = idCard.substring(6, 10)
const month = idCard.substring(10, 12) const month = idCard.substring(10, 12)
const day = idCard.substring(12, 14) const day = idCard.substring(12, 14)
const birthDate = `${year}-${month}-${day}` const birthDate = `${year}-${month}-${day}`
// 计算年龄
const birthYear = parseInt(year) const birthYear = parseInt(year)
const currentYear = new Date().getFullYear() const currentYear = new Date().getFullYear()
const age = currentYear - birthYear const age = currentYear - birthYear
// 提取性别(倒数第二位,奇数为男,偶数为女)
const genderCode = parseInt(idCard.substring(16, 17)) const genderCode = parseInt(idCard.substring(16, 17))
const gender = genderCode % 2 === 1 ? 1 : 2 const gender = genderCode % 2 === 1 ? 1 : 2
return { return { gender, age, birthDate }
gender,
age,
birthDate
}
}, },
async handleSave() { async handleSave() {
if (!this.isFormValid) { if (!this.isFormValid) {
uni.showToast({
title: '请完善信息',
icon: 'none'
})
return;
}
// Check login status
const token = getToken()
if (!token) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/login/login'
})
}, 1500)
return; return;
} }
try { try {
// 从身份证号中提取信息
const info = this.extractInfoFromIdCard(this.formData.idCard) const info = this.extractInfoFromIdCard(this.formData.idCard)
// 调用API保存选手信息使用后端实体类的字段名
const submitData = { const submitData = {
playerName: this.formData.name, playerName: this.formData.name,
idCard: this.formData.idCard, idCard: this.formData.idCard,
teamName: this.formData.team, teamName: this.formData.team,
organization: this.formData.organization, organization: this.formData.organization,
contactPhone: this.formData.phone, contactPhone: this.formData.phone,
idCardType: 1, // 身份证类型固定为1 idCardType: 1,
gender: info.gender, gender: info.gender,
age: info.age, age: info.age,
birthDate: info.birthDate birthDate: info.birthDate
} }
// 如果有赛事ID和项目ID一起提交
if (this.competitionId) { if (this.competitionId) {
submitData.competitionId = this.competitionId submitData.competitionId = this.competitionId
} }
if (this.projectIds && this.projectIds.length > 0) { if (this.projectIds && this.projectIds.length > 0) {
// 如果有多个项目取第一个项目ID
submitData.projectId = this.projectIds[0] submitData.projectId = this.projectIds[0]
} }
console.log('提交选手数据:', submitData) console.log('提交选手数据:', submitData)
await athleteAPI.submitAthlete(submitData) await athleteAPI.submitAthlete(submitData)
// 保存成功
uni.showToast({ uni.showToast({
title: '保存成功', title: '保存成功',
icon: 'success' icon: 'success'
}) })
// 延迟返回上一页
setTimeout(() => { setTimeout(() => {
uni.navigateBack() uni.navigateBack()
}, 1500) }, 1500)
} catch (err) { } catch (err) {
console.error('保存选手失败:', err) console.error('保存选手失败:', err)
// 显示错误提示 uni.showToast({
this.toastMessage = '保存失败,请重试' title: err.message || '保存失败,请重试',
this.showToast = true icon: 'none'
setTimeout(() => { })
this.showToast = false
}, 2000)
} }
} }
} }
@@ -401,64 +333,23 @@ export default {
} }
.error-hints { .error-hints {
margin: 30rpx; margin: 0 30rpx;
padding: 30rpx; padding: 20rpx;
background-color: #fff; background-color: #fff3f3;
border-radius: 16rpx; border-radius: 8rpx;
} }
.error-item { .error-item {
margin-bottom: 15rpx; margin-bottom: 10rpx;
} }
.error-label { .error-item:last-child {
margin-bottom: 0;
}
.error-text {
font-size: 26rpx; font-size: 26rpx;
color: #C93639; color: #C93639;
margin-right: 10rpx;
}
.error-msg {
font-size: 26rpx;
color: #C93639;
}
.hint-message {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 30rpx 50rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
gap: 15rpx;
z-index: 9999;
}
.hint-icon {
font-size: 40rpx;
}
.hint-text {
font-size: 28rpx;
}
.toast-message {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 30rpx 50rpx;
border-radius: 16rpx;
z-index: 9999;
}
.toast-text {
font-size: 28rpx;
} }
.btn-wrapper { .btn-wrapper {