feat: add contact management and various bug fixes

- Add contact API methods in athlete.js
- Add contact list display in common-info.vue
- Update add-contact.vue for contact creation
- Create edit-contact page for contact editing
- Fix event-register.vue with contact picker modal
- Fix home.vue registration status display
- Fix my-registration.vue cert modal display

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
DevOps
2026-01-05 15:12:04 +08:00
parent 4eddc5a194
commit 13eb311575
8 changed files with 720 additions and 489 deletions

View File

@@ -41,9 +41,6 @@ export default {
// ========== 集体/团队相关 API ========== // ========== 集体/团队相关 API ==========
/**
* 获取集体列表
*/
getTeamList(params = {}) { getTeamList(params = {}) {
return request.get('/martial/team/list', { return request.get('/martial/team/list', {
current: params.current || 1, current: params.current || 1,
@@ -52,24 +49,37 @@ export default {
}) })
}, },
/**
* 获取集体详情
*/
getTeamDetail(id) { getTeamDetail(id) {
return request.get('/martial/team/detail', { id }) return request.get('/martial/team/detail', { id })
}, },
/**
* 保存集体
*/
saveTeam(data) { saveTeam(data) {
return request.post('/martial/team/submit', data) return request.post('/martial/team/submit', data)
}, },
/**
* 删除集体
*/
removeTeam(id) { removeTeam(id) {
return request.post('/martial/team/remove?id=' + id, {}) return request.post('/martial/team/remove?id=' + id, {})
},
// ========== 联系人相关 API ==========
getContactList(params = {}) {
return request.get('/martial/contact/list', {
current: params.current || 1,
size: params.size || 100,
...params
})
},
getContactDetail(id) {
return request.get('/martial/contact/detail', { id })
},
saveContact(data) {
return request.post('/martial/contact/submit', data)
},
removeContact(id) {
return request.post('/martial/contact/remove?ids=' + id, {})
} }
} }

View File

@@ -1,252 +1,35 @@
{ {
"pages": [ "pages": [
{ {"path": "pages/login/login", "style": {"navigationBarTitleText": "登录", "navigationStyle": "custom"}},
"path": "pages/login/login", {"path": "pages/register/register", "style": {"navigationBarTitleText": "注册", "navigationStyle": "custom"}},
"style": { {"path": "pages/home/home", "style": {"navigationBarTitleText": "武术赛事通", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTitleText": "登录", {"path": "pages/profile/profile", "style": {"navigationBarTitleText": "个人中心", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationStyle": "custom" {"path": "pages/change-password/change-password", "style": {"navigationBarTitleText": "修改密码", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
} {"path": "pages/common-info/common-info", "style": {"navigationBarTitleText": "常用信息", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
}, {"path": "pages/add-player/add-player", "style": {"navigationBarTitleText": "新增选手", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
{ {"path": "pages/add-team/add-team", "style": {"navigationBarTitleText": "新增集体", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"path": "pages/register/register", {"path": "pages/edit-team/edit-team", "style": {"navigationBarTitleText": "编辑集体", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"style": { {"path": "pages/edit-player/edit-player", "style": {"navigationBarTitleText": "编辑选手", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTitleText": "注册", {"path": "pages/add-contact/add-contact", "style": {"navigationBarTitleText": "新增联系人", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationStyle": "custom" {"path": "pages/edit-contact/edit-contact", "style": {"navigationBarTitleText": "编辑联系人", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
} {"path": "pages/my-registration/my-registration", "style": {"navigationBarTitleText": "我的报名", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
}, {"path": "pages/event-list/event-list", "style": {"navigationBarTitleText": "全部赛事列表", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
{ {"path": "pages/event-detail/event-detail", "style": {"navigationBarTitleText": "赛事详情", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"path": "pages/home/home", {"path": "pages/select-event/select-event", "style": {"navigationBarTitleText": "选择报名项目", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"style": { {"path": "pages/event-register/event-register", "style": {"navigationBarTitleText": "赛事报名", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTitleText": "武术赛事通", {"path": "pages/register-type/register-type", "style": {"navigationBarTitleText": "选择报名", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarBackgroundColor": "#C93639", {"path": "pages/event-info/event-info", "style": {"navigationBarTitleText": "信息发布", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTextStyle": "white" {"path": "pages/event-info-detail/event-info-detail", "style": {"navigationBarTitleText": "信息详情", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
} {"path": "pages/event-rules/event-rules", "style": {"navigationBarTitleText": "赛事规程", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
}, {"path": "pages/event-schedule/event-schedule", "style": {"navigationBarTitleText": "活动日程", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
{ {"path": "pages/event-players/event-players", "style": {"navigationBarTitleText": "参赛选手", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"path": "pages/profile/profile", {"path": "pages/event-live/event-live", "style": {"navigationBarTitleText": "比赛实况", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"style": { {"path": "pages/event-lineup/event-lineup", "style": {"navigationBarTitleText": "出场顺序", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTitleText": "个人中心", {"path": "pages/event-score/event-score", "style": {"navigationBarTitleText": "成绩", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarBackgroundColor": "#C93639", {"path": "pages/event-medals/event-medals", "style": {"navigationBarTitleText": "奖牌榜", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
"navigationBarTextStyle": "white" {"path": "pages/attachment-view/attachment-view", "style": {"navigationBarTitleText": "附件查看", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}},
} {"path": "pages/event-photos/event-photos", "style": {"navigationBarTitleText": "图片直播", "navigationBarBackgroundColor": "#C93639", "navigationBarTextStyle": "white"}}
},
{
"path": "pages/change-password/change-password",
"style": {
"navigationBarTitleText": "修改密码",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/common-info/common-info",
"style": {
"navigationBarTitleText": "常用信息",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/add-player/add-player",
"style": {
"navigationBarTitleText": "新增选手",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/add-team/add-team",
"style": {
"navigationBarTitleText": "新增集体",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/edit-team/edit-team",
"style": {
"navigationBarTitleText": "编辑集体",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/edit-player/edit-player",
"style": {
"navigationBarTitleText": "编辑选手",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/add-contact/add-contact",
"style": {
"navigationBarTitleText": "新增联系人",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/my-registration/my-registration",
"style": {
"navigationBarTitleText": "我的报名",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-list/event-list",
"style": {
"navigationBarTitleText": "全部赛事列表",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-detail/event-detail",
"style": {
"navigationBarTitleText": "赛事详情",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/select-event/select-event",
"style": {
"navigationBarTitleText": "选择报名项目",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-register/event-register",
"style": {
"navigationBarTitleText": "赛事报名",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/register-type/register-type",
"style": {
"navigationBarTitleText": "选择报名",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-info/event-info",
"style": {
"navigationBarTitleText": "信息发布",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-info-detail/event-info-detail",
"style": {
"navigationBarTitleText": "信息详情",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-rules/event-rules",
"style": {
"navigationBarTitleText": "赛事规程",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-schedule/event-schedule",
"style": {
"navigationBarTitleText": "活动日程",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-players/event-players",
"style": {
"navigationBarTitleText": "参赛选手",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-live/event-live",
"style": {
"navigationBarTitleText": "比赛实况",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-lineup/event-lineup",
"style": {
"navigationBarTitleText": "出场顺序",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-score/event-score",
"style": {
"navigationBarTitleText": "成绩",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-medals/event-medals",
"style": {
"navigationBarTitleText": "奖牌榜",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/attachment-view/attachment-view",
"style": {
"navigationBarTitleText": "附件查看",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/event-photos/event-photos",
"style": {
"navigationBarTitleText": "图片直播",
"navigationBarBackgroundColor": "#C93639",
"navigationBarTextStyle": "white"
}
}
], ],
"globalStyle": { "globalStyle": {"navigationBarTextStyle": "white", "navigationBarTitleText": "武术赛事", "navigationBarBackgroundColor": "#C93639", "backgroundColor": "#F8F8F8"},
"navigationBarTextStyle": "white", "tabBar": {"color": "#999999", "selectedColor": "#C93639", "backgroundColor": "#ffffff", "borderStyle": "black", "list": [{"pagePath": "pages/home/home", "text": "首页", "iconPath": "static/images/首页灰@3x.png", "selectedIconPath": "static/images/首页亮@3x.png"}, {"pagePath": "pages/profile/profile", "text": "个人中心", "iconPath": "static/images/个人中心灰@3x.png", "selectedIconPath": "static/images/个人中心亮@3x.png"}]}
"navigationBarTitleText": "武术赛事",
"navigationBarBackgroundColor": "#C93639",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#999999",
"selectedColor": "#C93639",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "static/images/首页灰@3x.png",
"selectedIconPath": "static/images/首页亮@3x.png"
},
{
"pagePath": "pages/profile/profile",
"text": "个人中心",
"iconPath": "static/images/个人中心灰@3x.png",
"selectedIconPath": "static/images/个人中心亮@3x.png"
}
]
}
} }

View File

@@ -13,82 +13,46 @@
<view class="form-item"> <view class="form-item">
<view class="form-label">姓名</view> <view class="form-label">姓名</view>
<view class="form-value"> <view class="form-value">
<input <input class="form-input" v-model="formData.name" placeholder="请输入姓名" placeholder-class="placeholder" />
class="form-input"
v-model="formData.name"
placeholder="请输入姓名"
placeholder-class="placeholder"
/>
</view> </view>
</view> </view>
<view class="form-item"> <view class="form-item">
<view class="form-label">证件号码</view> <view class="form-label">证件号码</view>
<view class="form-value"> <view class="form-value">
<input <input class="form-input" v-model="formData.idCard" placeholder="请输入身份证号码" placeholder-class="placeholder" />
class="form-input"
v-model="formData.idCard"
placeholder="请输入身份证号码"
placeholder-class="placeholder"
/>
</view> </view>
</view> </view>
<view class="form-item"> <view class="form-item">
<view class="form-label">手机号码</view> <view class="form-label">手机号码</view>
<view class="form-value"> <view class="form-value">
<input <input class="form-input" v-model="formData.phone" placeholder="请输入手机号码" placeholder-class="placeholder" type="number" />
class="form-input"
v-model="formData.phone"
placeholder="请输入手机号码"
placeholder-class="placeholder"
type="number"
/>
</view> </view>
</view> </view>
<view class="form-item"> <view class="form-item">
<view class="form-label">邮箱</view> <view class="form-label">邮箱</view>
<view class="form-value"> <view class="form-value">
<input <input class="form-input" v-model="formData.email" placeholder="请输入邮箱" placeholder-class="placeholder" />
class="form-input"
v-model="formData.email"
placeholder="请输入邮箱"
placeholder-class="placeholder"
/>
</view> </view>
</view> </view>
<view class="form-item"> <view class="form-item">
<view class="form-label">地址</view> <view class="form-label">地址</view>
<view class="form-value"> <view class="form-value">
<input <input class="form-input" v-model="formData.address" placeholder="请输入地址" placeholder-class="placeholder" />
class="form-input"
v-model="formData.address"
placeholder="请输入地址"
placeholder-class="placeholder"
/>
</view> </view>
</view> </view>
<view class="form-item switch-item"> <view class="form-item switch-item">
<view class="form-label">设置为默认联系人</view> <view class="form-label">设置为默认联系人</view>
<view class="form-value"> <view class="form-value">
<switch <switch :checked="formData.isDefault" @change="handleSwitchChange" color="#C93639" />
:checked="formData.isDefault"
@change="handleSwitchChange"
color="#C93639"
/>
</view> </view>
</view> </view>
</view> </view>
<!-- 提示信息 -->
<view class="hint-message" v-if="showHint">
<text class="hint-icon"></text>
<text class="hint-text">{{ hintText }}</text>
</view>
<!-- Toast提示 --> <!-- Toast提示 -->
<view class="toast-message" v-if="showToast"> <view class="toast-message" v-if="showToast">
<text class="toast-text">{{ toastMessage }}</text> <text class="toast-text">{{ toastMessage }}</text>
@@ -96,13 +60,16 @@
<!-- 按钮 --> <!-- 按钮 -->
<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 || saving }" @click="handleSave">
<view class="btn save-btn" v-else @click="handleSave">保存</view> {{ saving ? '保存中...' : '保存' }}
</view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import athleteAPI from '@/api/athlete.js';
export default { export default {
data() { data() {
return { return {
@@ -115,27 +82,20 @@ export default {
address: '', address: '',
isDefault: false isDefault: false
}, },
showHint: false,
hintText: '',
showToast: false, showToast: false,
toastMessage: '', toastMessage: '',
showIdTypePicker: false saving: false
}; };
}, },
computed: { computed: {
isFormValid() { isFormValid() {
return ( return this.formData.name && this.formData.idCard && this.formData.phone &&
this.formData.name && this.validateIdCard(this.formData.idCard) && this.validatePhone(this.formData.phone);
this.formData.idCard &&
this.formData.phone &&
this.validateIdCard(this.formData.idCard) &&
this.validatePhone(this.formData.phone)
);
} }
}, },
methods: { methods: {
validateIdCard(idCard) { validateIdCard(idCard) {
return /^\d{18}$/.test(idCard); return /^\d{17}[\dXx]$/.test(idCard);
}, },
validatePhone(phone) { validatePhone(phone) {
return /^1\d{10}$/.test(phone); return /^1\d{10}$/.test(phone);
@@ -143,38 +103,34 @@ export default {
handleSwitchChange(e) { handleSwitchChange(e) {
this.formData.isDefault = e.detail.value; this.formData.isDefault = e.detail.value;
}, },
handleSave() { showToastMsg(msg) {
if (!this.isFormValid) { this.toastMessage = msg;
if (this.formData.phone && !this.validatePhone(this.formData.phone)) {
this.toastMessage = '手机号码格式不正确';
this.showToast = true; this.showToast = true;
setTimeout(() => { setTimeout(() => { this.showToast = false; }, 2000);
this.showToast = false; },
}, 2000); async handleSave() {
} else if (this.formData.idCard && !this.validateIdCard(this.formData.idCard)) { if (!this.isFormValid || this.saving) return;
this.toastMessage = '身份证号码格式不正确';
this.showToast = true; if (!this.validatePhone(this.formData.phone)) {
setTimeout(() => { this.showToastMsg('手机号码格式不正确');
this.showToast = false; return;
}, 2000);
} else {
this.hintText = '请完善信息';
this.showHint = true;
setTimeout(() => {
this.showHint = false;
}, 2000);
} }
if (!this.validateIdCard(this.formData.idCard)) {
this.showToastMsg('身份证号码格式不正确');
return; return;
} }
// 实际保存逻辑 this.saving = true;
uni.showToast({ try {
title: '保存成功', await athleteAPI.saveContact(this.formData);
icon: 'success' uni.showToast({ title: '保存成功', icon: 'success' });
}); setTimeout(() => { uni.navigateBack(); }, 1500);
setTimeout(() => { } catch (err) {
uni.navigateBack(); console.error('保存联系人失败:', err);
}, 1500); this.showToastMsg('保存失败,请重试');
} finally {
this.saving = false;
}
} }
} }
}; };
@@ -201,13 +157,8 @@ export default {
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
} }
.form-item:last-child { .form-item:last-child { border-bottom: none; }
border-bottom: none; .switch-item { padding: 30rpx; }
}
.switch-item {
padding: 30rpx;
}
.form-label { .form-label {
width: 180rpx; width: 180rpx;
@@ -228,38 +179,8 @@ export default {
color: #333333; color: #333333;
} }
.placeholder { .placeholder { color: #cccccc; }
color: #cccccc; .arrow { font-size: 40rpx; color: #cccccc; margin-left: 10rpx; }
}
.arrow {
font-size: 40rpx;
color: #cccccc;
margin-left: 10rpx;
}
.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 { .toast-message {
position: fixed; position: fixed;
@@ -273,26 +194,7 @@ export default {
z-index: 9999; z-index: 9999;
} }
.toast-text { .toast-text { font-size: 28rpx; }
font-size: 28rpx;
}
.info-text {
margin: 20rpx 30rpx;
text-align: center;
}
.info-hint {
font-size: 24rpx;
color: #C93639;
}
.warning-text {
margin: 20rpx 30rpx;
text-align: center;
font-size: 24rpx;
color: #C93639;
}
.btn-wrapper { .btn-wrapper {
position: fixed; position: fixed;

View File

@@ -59,8 +59,31 @@
<text class="empty-text">暂无集体信息</text> <text class="empty-text">暂无集体信息</text>
</view> </view>
<!-- 联系人Tab内容 (Tab 2) --> <!-- 联系人列表 (Tab 2) -->
<view class="empty-state" v-if="currentTab === 2"> <view class="player-list" v-if="currentTab === 2 && contactList.length > 0">
<view class="player-item" v-for="(item, index) in contactList" :key="index">
<view class="player-info">
<view class="player-name">
{{ item.name }}
<text class="default-tag" v-if="item.isDefault">默认</text>
</view>
<view class="player-id">手机{{ item.phone }}</view>
</view>
<view class="player-actions">
<view class="action-btn edit-btn" @click.stop="handleEditContact(item)">
<image class="action-icon" src="/static/images/编辑@3x.png" mode="aspectFit"></image>
<text>编辑</text>
</view>
<view class="action-btn delete-btn" @click.stop="handleDelete(item, 'contact')">
<image class="action-icon" src="/static/images/删除@3x.png" mode="aspectFit"></image>
<text>删除</text>
</view>
</view>
</view>
</view>
<!-- 联系人空状态 -->
<view class="empty-state" v-if="currentTab === 2 && contactList.length === 0">
<text class="empty-text">暂无联系人信息</text> <text class="empty-text">暂无联系人信息</text>
</view> </view>
@@ -98,6 +121,7 @@ export default {
currentTab: 0, currentTab: 0,
playerList: [], playerList: [],
teamList: [], teamList: [],
contactList: [],
showDeleteModal: false, showDeleteModal: false,
showSuccessToast: false, showSuccessToast: false,
currentItem: null, currentItem: null,
@@ -111,31 +135,31 @@ export default {
return '新增联系人' return '新增联系人'
}, },
deleteModalTitle() { deleteModalTitle() {
return this.deleteType === 'team' ? '删除集体' : '删除选手' if (this.deleteType === 'team') return '删除集体'
if (this.deleteType === 'contact') return '删除联系人'
return '删除选手'
}, },
deleteModalContent() { deleteModalContent() {
return this.deleteType === 'team' ? '确定要删除该集体吗?' : '确定要删除该选手吗?' if (this.deleteType === 'team') return '确定要删除该集体吗?'
if (this.deleteType === 'contact') return '确定要删除该联系人吗?'
return '确定要删除该选手吗?'
} }
}, },
onLoad() { onLoad() {
this.loadPlayerList() this.loadPlayerList()
this.loadTeamList() this.loadTeamList()
this.loadContactList()
}, },
onShow() { onShow() {
this.loadPlayerList() this.loadPlayerList()
this.loadTeamList() this.loadTeamList()
this.loadContactList()
}, },
methods: { methods: {
/**
* Load player list
*/
async loadPlayerList() { async loadPlayerList() {
try { try {
const userInfo = getUserInfo() const userInfo = getUserInfo()
if (!userInfo || !userInfo.userId) { if (!userInfo || !userInfo.userId) return
console.error('未获取到用户信息')
return
}
const res = await athleteAPI.getAthleteList({ const res = await athleteAPI.getAthleteList({
current: 1, current: 1,
@@ -143,13 +167,7 @@ export default {
createUser: userInfo.userId createUser: userInfo.userId
}) })
let list = [] let list = res.records || (Array.isArray(res) ? res : [])
if (res.records) {
list = res.records
} else if (Array.isArray(res)) {
list = res
}
this.playerList = list.map(item => ({ this.playerList = list.map(item => ({
id: item.id, id: item.id,
name: item.name || item.playerName, name: item.name || item.playerName,
@@ -163,17 +181,11 @@ export default {
} }
}, },
/**
* Load team list
*/
async loadTeamList() { async loadTeamList() {
try { try {
const userInfo = getUserInfo() const userInfo = getUserInfo()
if (!userInfo || !userInfo.userId) { if (!userInfo || !userInfo.userId) return
return
}
// Try to load team list from API
if (athleteAPI.getTeamList) { if (athleteAPI.getTeamList) {
const res = await athleteAPI.getTeamList({ const res = await athleteAPI.getTeamList({
current: 1, current: 1,
@@ -181,13 +193,7 @@ export default {
createUser: userInfo.userId createUser: userInfo.userId
}) })
let list = [] let list = res.records || (Array.isArray(res) ? res : [])
if (res.records) {
list = res.records
} else if (Array.isArray(res)) {
list = res
}
this.teamList = list.map(item => ({ this.teamList = list.map(item => ({
id: item.id, id: item.id,
name: item.name || item.teamName, name: item.name || item.teamName,
@@ -200,33 +206,55 @@ export default {
} }
}, },
async loadContactList() {
try {
const userInfo = getUserInfo()
if (!userInfo || !userInfo.userId) return
if (athleteAPI.getContactList) {
const res = await athleteAPI.getContactList({
current: 1,
size: 100,
createUser: userInfo.userId
})
let list = res.records || (Array.isArray(res) ? res : [])
this.contactList = list.map(item => ({
id: item.id,
name: item.name,
phone: item.phone,
idCard: item.idCard,
email: item.email,
address: item.address,
isDefault: item.isDefault
}))
}
} catch (err) {
console.error('加载联系人列表失败:', err)
this.contactList = []
}
},
handleTabChange(index) { handleTabChange(index) {
this.currentTab = index; this.currentTab = index;
}, },
handleAdd() { handleAdd() {
if (this.currentTab === 0) { if (this.currentTab === 0) {
uni.navigateTo({ uni.navigateTo({ url: '/pages/add-player/add-player' });
url: '/pages/add-player/add-player'
});
} else if (this.currentTab === 1) { } else if (this.currentTab === 1) {
uni.navigateTo({ uni.navigateTo({ url: '/pages/add-team/add-team' });
url: '/pages/add-team/add-team'
});
} else if (this.currentTab === 2) { } else if (this.currentTab === 2) {
uni.navigateTo({ uni.navigateTo({ url: '/pages/add-contact/add-contact' });
url: '/pages/add-contact/add-contact'
});
} }
}, },
handleEdit(item) { handleEdit(item) {
uni.navigateTo({ uni.navigateTo({ url: '/pages/edit-player/edit-player?id=' + item.id });
url: '/pages/edit-player/edit-player?id=' + item.id
});
}, },
handleEditTeam(item) { handleEditTeam(item) {
uni.navigateTo({ uni.navigateTo({ url: '/pages/edit-team/edit-team?id=' + item.id });
url: '/pages/edit-team/edit-team?id=' + item.id },
}); handleEditContact(item) {
uni.navigateTo({ url: '/pages/edit-contact/edit-contact?id=' + item.id });
}, },
handleDelete(item, type) { handleDelete(item, type) {
this.currentItem = item; this.currentItem = item;
@@ -242,27 +270,24 @@ export default {
await athleteAPI.removeTeam(this.currentItem.id) await athleteAPI.removeTeam(this.currentItem.id)
} }
const index = this.teamList.findIndex(item => item.id === this.currentItem.id); const index = this.teamList.findIndex(item => item.id === this.currentItem.id);
if (index > -1) { if (index > -1) this.teamList.splice(index, 1);
this.teamList.splice(index, 1); } else if (this.deleteType === 'contact') {
if (athleteAPI.removeContact) {
await athleteAPI.removeContact(this.currentItem.id)
} }
const index = this.contactList.findIndex(item => item.id === this.currentItem.id);
if (index > -1) this.contactList.splice(index, 1);
} else { } else {
await athleteAPI.removeAthlete(this.currentItem.id) await athleteAPI.removeAthlete(this.currentItem.id)
const index = this.playerList.findIndex(item => item.id === this.currentItem.id); const index = this.playerList.findIndex(item => item.id === this.currentItem.id);
if (index > -1) { if (index > -1) this.playerList.splice(index, 1);
this.playerList.splice(index, 1);
}
} }
this.showSuccessToast = true; this.showSuccessToast = true;
setTimeout(() => { setTimeout(() => { this.showSuccessToast = false; }, 2000);
this.showSuccessToast = false;
}, 2000);
} catch (err) { } catch (err) {
console.error('删除失败:', err) console.error('删除失败:', err)
uni.showToast({ uni.showToast({ title: '删除失败', icon: 'none' })
title: '删除失败',
icon: 'none'
})
} }
} }
} }
@@ -315,6 +340,18 @@ export default {
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
margin-bottom: 10rpx; margin-bottom: 10rpx;
display: flex;
align-items: center;
gap: 10rpx;
}
.default-tag {
font-size: 22rpx;
color: #fff;
background-color: #C93639;
padding: 4rpx 12rpx;
border-radius: 6rpx;
font-weight: normal;
} }
.player-id { .player-id {
@@ -348,10 +385,6 @@ export default {
border-color: #C93639; border-color: #C93639;
} }
.icon {
font-size: 28rpx;
}
.action-icon { .action-icon {
width: 28rpx; width: 28rpx;
height: 28rpx; height: 28rpx;

View File

@@ -0,0 +1,249 @@
<template>
<view class="add-contact-page">
<view class="form-container">
<view class="form-item" @click="showIdTypePicker = true">
<view class="form-label">证件类型</view>
<view class="form-value">
<text>{{ formData.idType }}</text>
<text class="arrow"></text>
</view>
</view>
<view class="form-item">
<view class="form-label">姓名</view>
<view class="form-value">
<input class="form-input" v-model="formData.name" placeholder="请输入姓名" placeholder-class="placeholder" />
</view>
</view>
<view class="form-item">
<view class="form-label">证件号码</view>
<view class="form-value">
<input class="form-input" v-model="formData.idCard" placeholder="请输入身份证号码" placeholder-class="placeholder" />
</view>
</view>
<view class="form-item">
<view class="form-label">手机号码</view>
<view class="form-value">
<input class="form-input" v-model="formData.phone" placeholder="请输入手机号码" placeholder-class="placeholder" type="number" />
</view>
</view>
<view class="form-item">
<view class="form-label">邮箱</view>
<view class="form-value">
<input class="form-input" v-model="formData.email" placeholder="请输入邮箱" placeholder-class="placeholder" />
</view>
</view>
<view class="form-item">
<view class="form-label">地址</view>
<view class="form-value">
<input class="form-input" v-model="formData.address" placeholder="请输入地址" placeholder-class="placeholder" />
</view>
</view>
<view class="form-item switch-item">
<view class="form-label">设置为默认联系人</view>
<view class="form-value">
<switch :checked="formData.isDefault" @change="handleSwitchChange" color="#C93639" />
</view>
</view>
</view>
<view class="toast-message" v-if="showToast">
<text class="toast-text">{{ toastMessage }}</text>
</view>
<view class="btn-wrapper">
<view class="btn save-btn" :class="{ disabled: !isFormValid || saving }" @click="handleSave">
{{ saving ? '保存中...' : '保存' }}
</view>
</view>
</view>
</template>
<script>
import athleteAPI from '@/api/athlete.js';
export default {
data() {
return {
contactId: null,
formData: {
idType: '身份证',
name: '',
idCard: '',
phone: '',
email: '',
address: '',
isDefault: false
},
showToast: false,
toastMessage: '',
saving: false
};
},
computed: {
isFormValid() {
return this.formData.name && this.formData.idCard && this.formData.phone &&
this.validateIdCard(this.formData.idCard) && this.validatePhone(this.formData.phone);
}
},
onLoad(options) {
if (options.id) {
this.contactId = options.id;
this.loadContactDetail();
}
},
methods: {
validateIdCard(idCard) {
return /^\d{17}[\dXx]$/.test(idCard);
},
validatePhone(phone) {
return /^1\d{10}$/.test(phone);
},
handleSwitchChange(e) {
this.formData.isDefault = e.detail.value;
},
showToastMsg(msg) {
this.toastMessage = msg;
this.showToast = true;
setTimeout(() => { this.showToast = false; }, 2000);
},
async loadContactDetail() {
try {
const res = await athleteAPI.getContactDetail(this.contactId);
if (res) {
this.formData = {
idType: res.idType || '身份证',
name: res.name || '',
idCard: res.idCard || '',
phone: res.phone || '',
email: res.email || '',
address: res.address || '',
isDefault: res.isDefault || false
};
}
} catch (err) {
console.error('加载联系人详情失败:', err);
this.showToastMsg('加载失败');
}
},
async handleSave() {
if (!this.isFormValid || this.saving) return;
if (!this.validatePhone(this.formData.phone)) {
this.showToastMsg('手机号码格式不正确');
return;
}
if (!this.validateIdCard(this.formData.idCard)) {
this.showToastMsg('身份证号码格式不正确');
return;
}
this.saving = true;
try {
const data = { ...this.formData, id: this.contactId };
await athleteAPI.saveContact(data);
uni.showToast({ title: '保存成功', icon: 'success' });
setTimeout(() => { uni.navigateBack(); }, 1500);
} catch (err) {
console.error('保存联系人失败:', err);
this.showToastMsg('保存失败,请重试');
} finally {
this.saving = false;
}
}
}
};
</script>
<style lang="scss" scoped>
.add-contact-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 200rpx;
}
.form-container {
background-color: #fff;
margin: 30rpx;
border-radius: 16rpx;
overflow: hidden;
}
.form-item {
display: flex;
align-items: center;
padding: 35rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.form-item:last-child { border-bottom: none; }
.switch-item { padding: 30rpx; }
.form-label {
width: 180rpx;
font-size: 30rpx;
color: #333333;
}
.form-value {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.form-input {
flex: 1;
font-size: 30rpx;
color: #333333;
}
.placeholder { color: #cccccc; }
.arrow { font-size: 40rpx; color: #cccccc; margin-left: 10rpx; }
.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 {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 30rpx;
background-color: #f5f5f5;
}
.btn {
width: 100%;
text-align: center;
padding: 30rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: bold;
}
.save-btn {
background-color: #C93639;
color: #fff;
}
.save-btn.disabled {
background-color: rgba(201, 54, 57, 0.5);
}
</style>

View File

@@ -98,22 +98,22 @@
<view class="event-info-card"> <view class="event-info-card">
<view class="event-title">{{ eventInfo.title }}</view> <view class="event-title">{{ eventInfo.title }}</view>
<view class="divider"></view> <view class="divider"></view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">地点</text> <text class="label">地点</text>
<text class="value">{{ eventInfo.location }}</text> <text class="value">{{ eventInfo.location }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">比赛时间</text> <text class="label">比赛时间</text>
<text class="value">{{ eventInfo.matchTime }}</text> <text class="value">{{ eventInfo.matchTime }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">报名项目</text> <text class="label">报名项目</text>
<text class="value">{{ eventInfo.projects }}</text> <text class="value">{{ eventInfo.projects }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">联系人</text> <text class="label">联系人</text>
<text class="value">{{ eventInfo.contact }}</text> <text class="value" :class="{ placeholder: !selectedContact }">{{ selectedContact ? selectedContact.name + " " + selectedContact.phone : "请选择联系人" }}</text>
<text class="edit-icon">📋</text> <text class="arrow"></text>
</view> </view>
<view class="info-hint">(注意是否用此号码接收信息)</view> <view class="info-hint">(注意是否用此号码接收信息)</view>
<view class="info-item participants-item"> <view class="info-item participants-item">
@@ -151,21 +151,21 @@
<view class="event-info-card"> <view class="event-info-card">
<view class="event-title">{{ eventInfo.title }}</view> <view class="event-title">{{ eventInfo.title }}</view>
<view class="divider"></view> <view class="divider"></view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">地点</text> <text class="label">地点</text>
<text class="value">{{ eventInfo.location }}</text> <text class="value">{{ eventInfo.location }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">比赛时间</text> <text class="label">比赛时间</text>
<text class="value">{{ eventInfo.matchTime }}</text> <text class="value">{{ eventInfo.matchTime }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">报名项目</text> <text class="label">报名项目</text>
<text class="value">{{ eventInfo.projects }}</text> <text class="value">{{ eventInfo.projects }}</text>
</view> </view>
<view class="info-item"> <view class="info-item contact-item" @click="showContactPicker = true">
<text class="label">联系人</text> <text class="label">联系人</text>
<text class="value">{{ eventInfo.contact }}</text> <text class="value" :class="{ placeholder: !selectedContact }">{{ selectedContact ? selectedContact.name + " " + selectedContact.phone : "请选择联系人" }}</text>
</view> </view>
<template v-if="!isTeamProject"> <template v-if="!isTeamProject">
@@ -211,6 +211,32 @@
</scroll-view> </scroll-view>
</view> </view>
</view> </view>
<!-- 联系人选择弹窗 -->
<view class="contact-modal" v-if="showContactPicker" @click="showContactPicker = false">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">选择联系人</text>
<text class="close-icon" @click="showContactPicker = false"></text>
</view>
<view class="add-contact-btn" @click="goToAddContact">
<text class="add-icon"></text>
<text>新增联系人</text>
</view>
<scroll-view class="modal-body contact-list" scroll-y>
<view class="contact-item" v-for="(item, index) in contactList" :key="index" @click="selectContact(item)">
<view class="contact-info">
<view class="contact-name">{{ item.name }}</view>
<view class="contact-phone">{{ item.phone }}</view>
</view>
<view class="contact-check" v-if="selectedContact && selectedContact.id === item.id"></view>
</view>
<view class="empty-state" v-if="contactList.length === 0">
<text class="empty-text">暂无联系人请先新增</text>
</view>
</scroll-view>
</view>
</view>
</view> </view>
</template> </template>
@@ -240,6 +266,9 @@ export default {
selectedPlayers: [], selectedPlayers: [],
selectedTeams: [], selectedTeams: [],
showPlayerModal: false, showPlayerModal: false,
showContactPicker: false,
contactList: [],
selectedContact: null,
totalPrice: 0, totalPrice: 0,
registrationId: '' registrationId: ''
}; };
@@ -290,6 +319,38 @@ export default {
} }
}, },
methods: { methods: {
// Contact methods
async loadContactList() {
try {
const res = await athleteAPI.getContactList({ current: 1, size: 100 })
this.contactList = (res.records || []).map(item => ({
id: item.id,
name: item.name,
phone: item.phone,
idCard: item.idCard
}))
// Auto select default contact if exists
if (!this.selectedContact && this.contactList.length > 0) {
const defaultContact = this.contactList.find(c => c.isDefault) || this.contactList[0]
this.selectedContact = defaultContact
}
} catch (err) {
console.error('加载联系人列表失败:', err)
}
},
selectContact(contact) {
this.selectedContact = contact
this.showContactPicker = false
},
goToAddContact() {
this.showContactPicker = false
uni.navigateTo({
url: '/pages/add-contact/add-contact'
})
},
async loadEventDetail(id) { async loadEventDetail(id) {
try { try {
const res = await competitionAPI.getCompetitionDetail(id) const res = await competitionAPI.getCompetitionDetail(id)
@@ -487,6 +548,7 @@ export default {
} }
this.totalPrice = this.calculateTotalPrice() this.totalPrice = this.calculateTotalPrice()
this.loadContactList()
this.$nextTick(() => { this.currentStep = 2 }) this.$nextTick(() => { this.currentStep = 2 })
}, },
@@ -518,7 +580,7 @@ export default {
projectIds: this.selectedProjects.map(p => String(p.id)), projectIds: this.selectedProjects.map(p => String(p.id)),
teamIds: selected.map(t => String(t.id)), teamIds: selected.map(t => String(t.id)),
athleteIds: [], athleteIds: [],
contactPhone: this.eventInfo.contact || '', contactPhone: this.selectedContact ? this.selectedContact.phone : '',
totalAmount: parseFloat(this.totalPrice) || 0 totalAmount: parseFloat(this.totalPrice) || 0
} }
} else { } else {
@@ -532,7 +594,7 @@ export default {
competitionId: String(this.eventId), competitionId: String(this.eventId),
projectIds: this.selectedProjects.map(p => String(p.id)), projectIds: this.selectedProjects.map(p => String(p.id)),
athleteIds: selected.map(p => String(p.id)), athleteIds: selected.map(p => String(p.id)),
contactPhone: this.eventInfo.contact || '', contactPhone: this.selectedContact ? this.selectedContact.phone : '',
totalAmount: parseFloat(this.totalPrice) || 0 totalAmount: parseFloat(this.totalPrice) || 0
} }
} }
@@ -1014,6 +1076,84 @@ export default {
color: #C93639; color: #C93639;
margin-top: 10rpx; margin-top: 10rpx;
} }
/* Contact Modal Styles */
.contact-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1000;
}
.contact-modal .modal-content {
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
width: 100%;
max-height: 70vh;
}
.contact-modal .add-contact-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
border-bottom: 1rpx solid #eee;
color: #C93639;
font-size: 28rpx;
}
.contact-modal .add-contact-btn .add-icon {
margin-right: 10rpx;
font-size: 32rpx;
}
.contact-modal .contact-list {
max-height: 50vh;
}
.contact-modal .contact-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.contact-modal .contact-info {
flex: 1;
}
.contact-modal .contact-name {
font-size: 30rpx;
color: #333;
margin-bottom: 8rpx;
}
.contact-modal .contact-phone {
font-size: 26rpx;
color: #999;
}
.contact-modal .contact-check {
color: #C93639;
font-size: 36rpx;
font-weight: bold;
}
.contact-item.clickable,
.info-item.contact-item {
cursor: pointer;
}
.info-item .placeholder {
color: #999;
}
</style> </style>
.empty-state { .empty-state {

View File

@@ -138,7 +138,7 @@ export default {
matchTime: this.formatTimeRange(startTime, endTime) || matchTime: this.formatTimeRange(startTime, endTime) ||
item.matchTime || item.competitionTime || '待定', item.matchTime || item.competitionTime || '待定',
registerCount: item.registrationCount || item.registerCount || item.signUpCount || item.totalParticipants || '0', registerCount: item.registrationCount || item.registerCount || item.signUpCount || item.totalParticipants || '0',
status: this.getStatus(item.status) status: this.getStatus(item.status, regEndTime)
} }
}) })
@@ -174,12 +174,20 @@ export default {
* @param {Number|String} status 状态码 * @param {Number|String} status 状态码
* @returns {String} * @returns {String}
*/ */
getStatus(status) { getStatus(status, regEndTime) {
// 根据后端状态码映射为前端需要的状态 // 根据后端状态码映射为前端需要的状态
// 1: 报名中, 2: 进行中, 3: 已结束 // 1: 报名中, 2: 进行中, 3: 已结束
if (status === 3 || status === '3' || status === 'finished') { if (status === 3 || status === '3' || status === 'finished') {
return 'finished' return 'finished'
} }
// 根据报名结束时间判断是否已结束
if (regEndTime) {
const endDate = new Date(regEndTime)
const now = new Date()
if (now > endDate) {
return 'finished'
}
}
return 'open' return 'open'
}, },

View File

@@ -42,8 +42,8 @@
<text class="value participants">{{ item.participants }}</text> <text class="value participants">{{ item.participants }}</text>
</view> </view>
<view class="event-footer" v-if="item.status === 'ongoing'"> <view class="event-footer">
<view class="register-note"> <view class="register-note" v-if="item.status === 'ongoing'">
<text>点击后进入赛事详情页面</text> <text>点击后进入赛事详情页面</text>
</view> </view>
<view class="view-cert-btn" @click.stop="handleViewCert(item)"> <view class="view-cert-btn" @click.stop="handleViewCert(item)">
@@ -58,6 +58,26 @@
<view class="empty-state" v-if="filteredList.length === 0"> <view class="empty-state" v-if="filteredList.length === 0">
<text class="empty-text">暂无报名记录</text> <text class="empty-text">暂无报名记录</text>
</view> </view>
<!-- 证件弹窗 -->
<view class="cert-modal" v-if="showCertModal" @click="closeCertModal">
<view class="cert-modal-content" @click.stop>
<view class="cert-modal-header">
<text class="cert-modal-title">参赛选手证件</text>
<text class="cert-close-icon" @click="closeCertModal"></text>
</view>
<scroll-view class="cert-modal-body" scroll-y>
<view class="cert-athlete-item" v-for="(athlete, index) in certAthleteList" :key="index">
<view class="cert-athlete-name">{{ athlete.playerName }}</view>
<view class="cert-athlete-info">性别{{ athlete.gender === 1 ? '男' : '女' }}</view>
<view class="cert-athlete-info">身份证{{ athlete.idCard || '暂无' }}</view>
</view>
<view class="cert-empty" v-if="certAthleteList.length === 0">
<text>暂无选手信息</text>
</view>
</scroll-view>
</view>
</view>
</view> </view>
</template> </template>
@@ -80,7 +100,9 @@ export default {
current: 1, current: 1,
size: 20 size: 20
}, },
hasMore: true hasMore: true,
showCertModal: false,
certAthleteList: []
}; };
}, },
onLoad() { onLoad() {
@@ -235,7 +257,8 @@ export default {
) || '', ) || '',
projects: item.projectNames || this.formatProjects(item.projects) || '', projects: item.projectNames || this.formatProjects(item.projects) || '',
contact: item.contactPhone || item.contactPerson || '', contact: item.contactPhone || item.contactPerson || '',
participants: item.athleteNames || `${item.totalParticipants || 0}` participants: item.athleteNames || `${item.totalParticipants || 0}`,
athleteList: item.athleteList || []
} }
}, },
@@ -322,11 +345,21 @@ export default {
}); });
}, },
handleViewCert(item) { handleViewCert(item) {
// Get athlete list from the item
if (item.athleteList && item.athleteList.length > 0) {
this.certAthleteList = item.athleteList;
this.showCertModal = true;
} else {
uni.showToast({ uni.showToast({
title: '查看证件', title: '暂无选手证件信息',
icon: 'none' icon: 'none'
}); });
} }
},
closeCertModal() {
this.showCertModal = false;
this.certAthleteList = [];
}
} }
}; };
</script> </script>
@@ -446,4 +479,77 @@ export default {
font-size: 28rpx; font-size: 28rpx;
color: #999999; color: #999999;
} }
/* Cert Modal Styles */
.cert-modal {
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;
}
.cert-modal-content {
background-color: #fff;
border-radius: 16rpx;
width: 85%;
max-height: 70vh;
overflow: hidden;
}
.cert-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.cert-modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.cert-close-icon {
font-size: 36rpx;
color: #999;
cursor: pointer;
}
.cert-modal-body {
max-height: 50vh;
padding: 20rpx 30rpx;
}
.cert-athlete-item {
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.cert-athlete-name {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.cert-athlete-info {
font-size: 26rpx;
color: #666;
margin-bottom: 6rpx;
}
.cert-empty {
text-align: center;
padding: 40rpx;
color: #999;
}
</style> </style>