UI优化和bug修复: 1.调整小程序消息列表图标样式 2.优化web端比赛晋级页面布局 3.修复小程序消息列表日期显示问题
| | |
| | | public class PromotionCompetitionResponse { |
| | | |
| | | private Long id; |
| | | private Long competitionId; |
| | | private String competitionName; |
| | | private String stageName; |
| | | private Integer maxParticipants; |
| | |
| | | |
| | | public PromotionCompetitionResponse(Activity competition, Activity stage, Integer currentCount) { |
| | | this.id = stage.getId(); |
| | | this.competitionId = competition.getId(); |
| | | this.competitionName = competition.getName(); |
| | | this.stageName = stage.getName(); |
| | | this.maxParticipants = stage.getPlayerMax(); |
| | |
| | | this.id = id; |
| | | } |
| | | |
| | | public Long getCompetitionId() { |
| | | return competitionId; |
| | | } |
| | | |
| | | public void setCompetitionId(Long competitionId) { |
| | | this.competitionId = competitionId; |
| | | } |
| | | |
| | | public String getCompetitionName() { |
| | | return competitionName; |
| | | } |
| | |
| | | return PromotionResult.failure("请选择要晋级的参赛者"); |
| | | } |
| | | |
| | | // 查询目标晋级阶段信息(用户点击的阶段就是要晋级到的阶段) |
| | | Activity targetStage = activityRepository.findById(input.getCompetitionId()).orElse(null); |
| | | // 查询目标晋级阶段信息 |
| | | Activity targetStage = activityRepository.findById(input.getTargetStageId()).orElse(null); |
| | | if (targetStage == null) { |
| | | return PromotionResult.failure("目标晋级阶段不存在"); |
| | | } |
| | |
| | | activityPlayerDetail(id: ID!): ActivityPlayerDetailResponse |
| | | # 微信端获取选手报名状态 |
| | | getPlayerRegistrationState(activityId: ID!): PlayerRegistrationResponse |
| | | # 获取比赛晋级列表 |
| | | promotionCompetitions(name: String, page: Int, size: Int): [PromotionCompetitionResponse!]! |
| | | # 获取可晋级参赛者列表 |
| | | promotableParticipants(currentStageId: ID!): PromotableParticipantsResponse |
| | | } |
| | | |
| | | extend type Mutation { |
| | |
| | | submitActivityRegistration(input: ActivityRegistrationInput!): ActivityRegistrationResponse |
| | | # 保存评委评分 |
| | | saveActivityPlayerRating(input: ActivityPlayerRatingInput!): Boolean |
| | | # 执行学员晋级操作 |
| | | promoteParticipants(input: PromotionInput!): PromotionResult |
| | | } |
| | | |
| | | type ActivityPlayer { |
| | |
| | | input ActivityPlayerRatingItemInput { |
| | | itemId: ID! |
| | | score: Float! |
| | | } |
| | | |
| | | # 比赛晋级列表响应类型 |
| | | type PromotionCompetitionResponse { |
| | | id: ID! |
| | | competitionId: ID! |
| | | competitionName: String! |
| | | stageName: String! |
| | | maxParticipants: Int |
| | | currentCount: Int! |
| | | status: Int |
| | | startTime: String |
| | | endTime: String |
| | | sortOrder: Int |
| | | state: Int |
| | | } |
| | | |
| | | # 可晋级参赛者列表响应类型 |
| | | type PromotableParticipantsResponse { |
| | | participants: [PromotableParticipantResponse!]! |
| | | selectableCount: Int |
| | | totalCount: Int |
| | | previousStageName: String |
| | | currentStageName: String |
| | | } |
| | | |
| | | # 可晋级参赛者响应类型 |
| | | type PromotableParticipantResponse { |
| | | id: ID! |
| | | playerName: String |
| | | projectName: String |
| | | phone: String |
| | | averageScore: Float |
| | | ratingCount: Int |
| | | applyTime: String |
| | | state: Int |
| | | playerId: ID |
| | | } |
| | | |
| | | # 晋级操作输入类型 |
| | | input PromotionInput { |
| | | competitionId: ID! |
| | | participantIds: [ID!]! |
| | | targetStageId: ID! |
| | | } |
| | | |
| | | # 晋级操作结果类型 |
| | | type PromotionResult { |
| | | success: Boolean! |
| | | message: String |
| | | promotedCount: Int |
| | | } |
| | |
| | | query GetPromotionCompetitions($name: String, $page: Int, $size: Int) { |
| | | promotionCompetitions(name: $name, page: $page, size: $size) { |
| | | id |
| | | competitionId |
| | | competitionName |
| | | stageName |
| | | maxParticipants |
| | |
| | | <el-table-column prop="createTime" label="创建时间" width="180" /> |
| | | <el-table-column label="操作" width="120" fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" size="small" @click="handleEdit(row)" :icon="Edit" circle title="编辑"></el-button> |
| | | <el-button type="danger" size="small" @click="handleDelete(row)" :icon="Delete" circle title="删除"></el-button> |
| | | <el-button text size="small" @click="handleEdit(row)" :icon="Edit" class="action-btn edit-btn" title="编辑"></el-button> |
| | | <el-button text size="small" @click="handleDelete(row)" :icon="Delete" class="action-btn delete-btn" title="删除"></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | } |
| | | } |
| | | |
| | | /* 操作按钮样式 */ |
| | | .action-btn { |
| | | padding: 8px !important; |
| | | margin: 0 6px; |
| | | border-radius: 6px; |
| | | transition: all 0.2s ease; |
| | | background: transparent !important; |
| | | border: none !important; |
| | | } |
| | | |
| | | .edit-btn { |
| | | color: #409eff !important; |
| | | } |
| | | |
| | | .edit-btn:hover { |
| | | color: #337ecc !important; |
| | | background: rgba(64, 158, 255, 0.1) !important; |
| | | transform: scale(1.2); |
| | | } |
| | | |
| | | .delete-btn { |
| | | color: #f56c6c !important; |
| | | } |
| | | |
| | | .delete-btn:hover { |
| | | color: #dd6161 !important; |
| | | background: rgba(245, 108, 108, 0.1) !important; |
| | | transform: scale(1.2); |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | |
| | | <el-icon><Search /></el-icon> |
| | | 搜索 |
| | | </el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Search, TrophyBase, InfoFilled } from '@element-plus/icons-vue' |
| | | import PromotionApi from '@/api/promotion' |
| | | import { getActivity } from '@/api/activity' |
| | | |
| | | const router = useRouter() |
| | | |
| | |
| | | selectedParticipants.value = selection |
| | | } |
| | | |
| | | |
| | | |
| | | // 确认晋级 |
| | | const confirmPromotion = async () => { |
| | | if (selectedParticipants.value.length === 0) { |
| | |
| | | const participantIds = selectedParticipants.value.map(p => p.id) |
| | | |
| | | const result = await PromotionApi.promoteParticipants( |
| | | selectedCompetition.value.id, |
| | | participantIds, |
| | | null // 目标阶段ID,这里可以根据需要设置 |
| | | ) |
| | | selectedCompetition.value.competitionId, // 主比赛ID |
| | | participantIds, |
| | | selectedCompetition.value.id // 当前阶段ID作为目标阶段ID |
| | | ) |
| | | |
| | | if (result && result.success) { |
| | | ElMessage.success(result.message || `成功晋级 ${result.promotedCount} 名人员`) |
| | |
| | | .search-toolbar { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | justify-content: flex-end; |
| | | margin-bottom: 20px; |
| | | padding: 16px; |
| | | background-color: #f9fafb; |
| | |
| | | width: 140rpx; |
| | | text-align: center; |
| | | /* align-self 已移除,由父级 .btn-row 控制对齐 */ |
| | | /* 移除button默认样式 */ |
| | | margin: 0; |
| | | position: static; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .ghost-btn::after { |
| | | border: none; |
| | | } |
| | | |
| | | .ghost-btn:active { |
| | |
| | | <view wx:else class="message-list"> |
| | | <view wx:for="{{messages}}" wx:key="id" class="message-card"> |
| | | <view class="icon-wrapper"> |
| | | <!-- 按要求使用统一图标 --> |
| | | <icon type="info" size="22" color="#FFFFFF"></icon> |
| | | <!-- 使用更合适的消息图标 --> |
| | | <text class="icon ic-comment"></text> |
| | | </view> |
| | | <view class="text-wrapper"> |
| | | <text class="title">{{item.content}}</text> |
| | |
| | | } |
| | | |
| | | .icon-wrapper { |
| | | width: 80rpx; |
| | | height: 80rpx; |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | background-color: #007aff; /* 统一使用蓝色 */ |
| | | background-color: #e6f3ff; /* 浅蓝色背景 */ |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .icon-wrapper .icon { |
| | | font-size: 28rpx; |
| | | color: #007aff; /* 深蓝色图标 */ |
| | | } |
| | | |
| | | .text-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |
| | |
| | | var formatTime = function (dateStr) { |
| | | if (!dateStr) return ''; |
| | | // 兼容 iOS |
| | | var date = getDate(dateStr.split('-').join('/')); |
| | | var year = date.getFullYear(); |
| | | var month = date.getMonth() + 1; |
| | | var day = date.getDate(); |
| | | var hour = date.getHours(); |
| | | var minute = date.getMinutes(); |
| | | |
| | | var formatNumber = function(n) { |
| | | n = n.toString(); |
| | | return n[1] ? n : '0' + n; |
| | | |
| | | var date; |
| | | |
| | | // 处理时间戳(数字) |
| | | if (typeof dateStr === 'number') { |
| | | date = getDate(dateStr); |
| | | } |
| | | // 处理字符串格式的日期 |
| | | else if (typeof dateStr === 'string') { |
| | | // 尝试直接解析 |
| | | date = getDate(dateStr); |
| | | } |
| | | else { |
| | | return ''; |
| | | } |
| | | |
| | | // 检查日期对象是否创建成功 |
| | | try { |
| | | var year = date.getFullYear(); |
| | | var month = date.getMonth() + 1; |
| | | var day = date.getDate(); |
| | | var hour = date.getHours(); |
| | | var minute = date.getMinutes(); |
| | | |
| | | // 如果获取到的值是NaN,说明日期无效 |
| | | if (year !== year || month !== month || day !== day) { |
| | | return ''; |
| | | } |
| | | |
| | | return year + '-' + formatNumber(month) + '-' + formatNumber(day) + ' ' + formatNumber(hour) + ':' + formatNumber(minute); |
| | | } catch (e) { |
| | | return ''; |
| | | } |
| | | } |
| | | |
| | | return year + '-' + formatNumber(month) + '-' + formatNumber(day) + ' ' + formatNumber(hour) + ':' + formatNumber(minute); |
| | | var formatNumber = function(n) { |
| | | n = n.toString(); |
| | | return n[1] ? n : '0' + n; |
| | | } |
| | | |
| | | module.exports = { |