- 新增按钮背景图片资源(btn-bg.png、progress-title.png、title-bg3.png、title-bg4.png) - 扩展 ActionButton 组件支持自定义按钮类型,新增 custom 类型用于背景图片驱动的按钮 - 添加 btnBg 和 fontSize 属性到 ActionButton,支持动态背景和字体大小配置 - 重构 ActionButton 样式结构,分离 standard-btn 和 custom-btn 两种按钮样式 - 优化按钮禁用状态的样式处理和交互反馈 - 更新 CompetitionLayout 组件支持新的标题背景类型(titleBg3、titleBg4) - 新增 titleTextColor 和 fontSize 属性到 CompetitionLayout,增强标题和按钮的定制化能力 - 更新所有用户页面组件(cover、draw、game、groups、rules、teams)以适配新的按钮和布局配置
271 lines
6.6 KiB
Vue
271 lines
6.6 KiB
Vue
<script lang="ts" setup>
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
import groupType1 from '@/assets/imgs/user/group-type-1.svg'
|
||
import groupType2 from '@/assets/imgs/user/group-type-2.svg'
|
||
import startIcon from '@/assets/imgs/user/star-icon.webp'
|
||
import CompetitionLayout from '@/components/custom/user/CompetitionLayout.vue'
|
||
import { useRouterPush } from '@/hooks/common/router'
|
||
import { fetchGetGroupListByActivityID } from '@/service/api/game'
|
||
|
||
const { routerPushByKey, routerBack } = useRouterPush()
|
||
const route = useRoute()
|
||
|
||
const groups = ref<Api.Competition.ActivityTeamGroup[]>([])
|
||
const loading = ref(false)
|
||
const activityId = route.query.activityId as string
|
||
const isAllEnd = ref(false)
|
||
|
||
// 是否有加时赛
|
||
const hasExtraTime = computed(() => {
|
||
const extraTimeGroups = groups.value.filter(item => item.RoundType === 1)
|
||
return extraTimeGroups.length > 0 && extraTimeGroups.every(item => item.IsEnd)
|
||
})
|
||
|
||
async function getGroups() {
|
||
if (!activityId)
|
||
return
|
||
|
||
loading.value = true
|
||
try {
|
||
const { data, error } = await fetchGetGroupListByActivityID(activityId, 0)
|
||
if (error) {
|
||
window?.$message?.error(error.message)
|
||
return
|
||
}
|
||
|
||
const list = data?.data || []
|
||
if (list && Array.isArray(list)) {
|
||
groups.value = list.map((item: any) => ({
|
||
...item,
|
||
icon: 'activity',
|
||
}))
|
||
}
|
||
isAllEnd.value = list.filter(item => item.RoundType === 0).every((item: Api.Competition.ActivityTeamGroup) => item.IsEnd)
|
||
}
|
||
finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const showContent = ref(false)
|
||
|
||
function handleBack() {
|
||
routerBack()
|
||
}
|
||
|
||
function handleNext() {
|
||
routerPushByKey('user_rank-list', { query: { activityId, RoundType: 0 } })
|
||
}
|
||
|
||
function selectGroup(item: Api.Competition.ActivityTeamGroup) {
|
||
if (item.IsEnd)
|
||
return
|
||
|
||
// 选择组别逻辑
|
||
routerPushByKey('user_teams', { query: { activityId, groupsId: item.Id, RoundType: item.RoundType } })
|
||
}
|
||
|
||
/**
|
||
* 查看加时赛结果
|
||
*/
|
||
function handleNext2() {
|
||
routerPushByKey('user_rank-list', { query: { activityId, RoundType: 1 } }, true)
|
||
}
|
||
|
||
onMounted(() => {
|
||
setTimeout(() => {
|
||
showContent.value = true
|
||
getGroups()
|
||
}, 100)
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<CompetitionLayout
|
||
:show-back="true" back-btn-text="返回" :show-next="isAllEnd" next-btn-text="查看常规赛结果" font-size="22" next-btn-text2="查看加时赛结果"
|
||
:show-next-btn2="hasExtraTime && isAllEnd" :title-bg-type="2" title="展示组别" @back="handleBack" @next="handleNext"
|
||
@next2="handleNext2"
|
||
>
|
||
<!-- Groups Grid -->
|
||
<div class="groups-container">
|
||
<TransitionGroup name="list-anim" tag="div" class="groups-grid">
|
||
<div
|
||
v-for="(item, index) in groups" v-show="showContent" :key="item.Id" class="group-item"
|
||
:class="{ 'is-disabled': item.IsEnd }" :style="{ '--delay': `${index * 0.05}s` }" @click="selectGroup(item)"
|
||
>
|
||
<div class="icon-wrapper">
|
||
<div class="flower-bg" />
|
||
<div class="inner-icon">
|
||
<NImage :src="startIcon" alt="star" class="star-img" width="180" height="187" :preview-disabled="true" object-fit="contain" />
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="group-name-tag"
|
||
:class="index % 2 === 0 ? 'type-moon' : 'type-sun'"
|
||
:style="{ backgroundImage: `url(${index % 2 === 0 ? groupType2 : groupType1})` }"
|
||
>
|
||
{{ item.Name }}
|
||
</div>
|
||
</div>
|
||
</TransitionGroup>
|
||
</div>
|
||
</CompetitionLayout>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.groups-container {
|
||
width: 100%;
|
||
padding: 0 60px;
|
||
margin-top: 60px;
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: flex-start;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.groups-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: 15px; // 减小间距
|
||
width: 100%;
|
||
max-width: 1400px; // 增加最大宽度限制,允许在一行放下更多
|
||
}
|
||
|
||
.group-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
transition: transform 0.3s ease;
|
||
// 适配不同屏幕宽度
|
||
width: calc(20% - 16px); // 默认一行5个 (100% / 5 ≈ 20%)
|
||
|
||
// 当总数大于10时,启用紧凑模式(一行6个)
|
||
.grid-dense & {
|
||
width: calc(16.66% - 16px);
|
||
}
|
||
|
||
// 大屏下如果没启用紧凑模式,依然保持一行5个,或者根据需要调整
|
||
// @media (min-width: 1440px) {
|
||
// width: calc(16.66% - 16px); // 大屏一行6个
|
||
// }
|
||
|
||
// 小屏适配
|
||
@media (max-width: 1200px) {
|
||
width: calc(25% - 16px); // 中屏一行4个
|
||
|
||
.grid-dense & {
|
||
width: calc(20% - 16px); // 紧凑模式下中屏一行5个
|
||
}
|
||
}
|
||
|
||
margin: 0;
|
||
margin-bottom: 20px;
|
||
|
||
&.is-disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
filter: grayscale(100%);
|
||
|
||
&:hover {
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
&:hover {
|
||
transform: translateY(-5px);
|
||
}
|
||
|
||
.icon-wrapper {
|
||
// 稍微缩小图标区域以适配更密集的布局
|
||
transform: scale(0.9);
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-size: contain;
|
||
margin-left: 20px;
|
||
|
||
.star-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
}
|
||
|
||
.group-name-tag {
|
||
margin-top: 5px; // 减小顶部间距
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 100% 100%;
|
||
width: 180px; // 稍微缩小标签宽度
|
||
height: 72px; // 按比例缩小高度
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 1.2rem; // 稍微缩小字号
|
||
color: #5d4037;
|
||
font-weight: bold;
|
||
position: relative;
|
||
// border: 1px solid #5d4037;
|
||
|
||
&.type-moon {
|
||
padding-left: 20px;
|
||
padding-top: 12px;
|
||
margin-top: -5px; // 调整错落位置
|
||
}
|
||
|
||
&.type-sun {
|
||
padding-left: 30px;
|
||
padding-top: -1px;
|
||
margin-top: 0px;
|
||
}
|
||
|
||
// .sun-icon {
|
||
// position: absolute;
|
||
// right: 20px;
|
||
// top: 10px;
|
||
// font-size: 1rem;
|
||
// z-index: 2;
|
||
// }
|
||
|
||
// &::before {
|
||
// content: '🌸';
|
||
// position: absolute;
|
||
// left: -12px;
|
||
// bottom: -4px;
|
||
// font-size: 0.9rem;
|
||
// }
|
||
}
|
||
}
|
||
|
||
// Transitions
|
||
.fade-slide-down-enter-active,
|
||
.fade-slide-down-leave-active {
|
||
transition: all 0.8s ease;
|
||
}
|
||
|
||
.fade-slide-down-enter-from,
|
||
.fade-slide-down-leave-to {
|
||
opacity: 0;
|
||
transform: translateY(-30px);
|
||
}
|
||
|
||
.list-anim-enter-active,
|
||
.list-anim-leave-active {
|
||
transition: all 0.5s cubic-bezier(0.5, 0, 0.25, 1);
|
||
transition-delay: var(--delay);
|
||
}
|
||
|
||
.list-anim-enter-from,
|
||
.list-anim-leave-to {
|
||
opacity: 0;
|
||
transform: scale(0.5);
|
||
}
|
||
|
||
/* Responsive styles auto-handled by flex-wrap */
|
||
</style>
|