feat: 移动端适配

This commit is contained in:
2026-03-27 10:54:14 +08:00
parent 60ebdb3d77
commit 14b951d3fb
21 changed files with 315 additions and 141 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 816 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Binary file not shown.

View File

@@ -80,12 +80,12 @@ export default {
panelOfJudges: 'Panel of Judges',
expertise: 'Expertise',
judgesHat: {
jae: 'Code-Create\nKorea Branch Director\nBesfxxk creative director',
jae: 'Code-Create Branch Director\nBesfxxk creative director\n(Korea)',
diego: 'Co-founder & Chief Father\nOfficer of OnTheList\n(Hong Kong)',
gregory: 'Senior Designer at\nGabriela Heasrst (Italy)',
gregory: 'Senior Designer at\nGabriela Hearst (Italy)',
vincenzo: 'Cheif Editor of SCMP Style\n(Hong Kong)',
tim: 'Group Fashion Direction of\n Modern Media Group\n(Shanghai)',
desmond: 'Cheif Editor of Vogue\n(Singapore)'
desmond: 'Chief Editor of Vogue\n(Singapore)'
},
awardPrizes: 'Award & Prizes',
recognition: 'Recognition',
@@ -136,7 +136,7 @@ export default {
receivingOutfits: 'Receiving Outfits',
fromFinalistsLabel: 'from Finallists',
timeOctober: 'October',
receivingOutfitsDesc: 'AiDA receives physical\noutfits from all 20\nfinalists.',
receivingOutfitsDesc: 'Finalists send one physical outfit to Code-Create (address to advise)',
awardCeremony: 'Award',
ceremonyLabel: 'Ceremony',
timeNov12: 'Nov 12',

View File

@@ -131,7 +131,7 @@ export default {
receivingOutfits: '入围者',
fromFinalistsLabel: '提交成衣',
timeOctober: '10月',
receivingOutfitsDesc: 'AiDA 接收每位入围\n的1套实物服装',
receivingOutfitsDesc: '入围者需向Code-Create寄送一套实物服装地址另行通知',
awardCeremony: '奖项颁发仪式',
ceremonyLabel: '',
timeNov12: '11月12日',

View File

@@ -402,7 +402,7 @@ ul {
// width: 46.8rem;
text-align: center;
color: #585858;
font-family: 'Arial';
font-family: 'Instrument';
font-weight: 400;
line-height: 3rem;
font-size: 2.4rem;
@@ -581,6 +581,7 @@ ul {
padding: 0 6rem;
font-size: 2rem;
text-align: left;
width: 100%;
}
}
}

View File

@@ -211,6 +211,7 @@ p {
padding: 0 8.5vw;
font-size: 2rem;
line-height: 3rem;
white-space: normal;
}
}
&.pad {

View File

@@ -229,6 +229,7 @@ onBeforeUnmount(() => {
line-height: 7.6rem;
&.smaller {
font-size: 3.6rem;
line-height: 3.8rem;
}
}
.prize-name {
@@ -265,7 +266,7 @@ onBeforeUnmount(() => {
.right {
.prize-item {
width: 29.2rem;
height: 27rem;
height: auto;
padding: 5rem 3rem;
row-gap: 1.8rem;
&,

View File

@@ -167,6 +167,8 @@ onBeforeUnmount(() => {
color: #e0e0e0;
text-align: center;
white-space: pre-line;
line-height: 3rem;
width: 28.2rem;
}
}
}
@@ -174,7 +176,7 @@ onBeforeUnmount(() => {
height: auto;
padding: 6rem 0 13rem;
background: url('@/assets/images/mobile_version_background/section_bg.png') no-repeat;
background-size: 100% 100%;
background-size: cover;
.title {
font-size: 3.2rem;
margin-bottom: 0.8rem;
@@ -193,9 +195,10 @@ onBeforeUnmount(() => {
.item {
width: initial;
height: initial;
&:nth-of-type(3) {
width: 30rem;
height: 36.8rem;
height: auto;
padding: 3.2rem 0;
}
.icon {
@@ -206,6 +209,10 @@ onBeforeUnmount(() => {
font-size: 2.4rem;
margin: 0.8rem 0 1.2rem;
}
.desc {
width: 23rem;
line-height: 2.4rem;
}
}
}
}

View File

@@ -1,5 +1,9 @@
<template>
<nav class="step-bar" role="tablist" :aria-label="t('AwardApply.applicationForm')">
<nav
class="step-bar flex align-center space-between"
role="tablist"
:aria-label="t('AwardApply.applicationForm')"
>
<button
v-for="(key, index) in tabKeys"
:key="key"
@@ -11,7 +15,7 @@
:tabindex="modelValue === index ? 0 : -1"
@click="onSelect(index)"
>
<div class="step-cluster">
<div class="step-cluster flex flex-center">
<div class="step-head">
<span class="step-badge">{{ index + 1 }}</span>
<span class="step-label">{{ t(`AwardApply.${key}`) }}</span>
@@ -56,17 +60,14 @@ function onSelect(index: number) {
@label-inactive: #9e9e9e;
@badge-inactive-bg: #dcdcdc;
@badge-inactive-text: #9f9f9f;
@bar-height: 0.4rem;
@bar-height: 0.6rem;
.step-bar {
height: 100%;
display: flex;
align-items: stretch;
justify-content: space-between;
width: 100%;
background: #fff;
border-bottom: 1px solid #e5e5e5;
padding: 0 3rem;
border-bottom: 0.1rem solid #dcdcdc;
padding: 0 3.8rem;
box-sizing: border-box;
}
@@ -75,88 +76,65 @@ function onSelect(index: number) {
display: flex;
flex-direction: column;
align-items: center;
min-width: 0;
margin: 0;
padding: 1.6rem 0.4rem 0;
border: none;
background: transparent;
cursor: pointer;
font: inherit;
color: inherit;
-webkit-tap-highlight-color: transparent;
position: relative;
height: 100%;
&:focus-visible {
outline: 2px solid @active-red;
outline-offset: 2px;
}
}
.step-cluster {
display: inline-flex;
flex-direction: column;
align-items: center;
max-width: 100%;
}
height: 100%;
.step-head {
display: inline-flex;
align-items: center;
gap: 1rem;
gap: 1.2rem;
max-width: 100%;
}
.step-badge {
flex-shrink: 0;
width: 3.2rem;
height: 3.2rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-family: 'ArialBold';
font-weight: 700;
font-size: 1.8rem;
line-height: 1;
background: @badge-inactive-bg;
color: @badge-inactive-text;
transition: background 0.2s ease, color 0.2s ease;
font-family: 'Poppins';
font-weight: 400;
font-size: 2.4rem;
color: #9f9f9f;
}
.step-label {
font-family: 'Poppins';
font-weight: 400;
font-size: 1.6rem;
font-size: 2.4rem;
color: @label-inactive;
white-space: nowrap;
color: #9f9f9f;
}
.step-item.active {
.step-badge {
background: @active-red;
color: #fff;
}
.step-label {
color: @label-active;
}
}
.step-indicator {
width: 120%;
min-height: @bar-height;
margin-top: 1.2rem;
display: flex;
justify-content: center;
align-items: flex-end;
}
/* 略宽于上方徽章+文案(与 step-head 同宽基准 + 左右扩展) */
width: 100%;
height: @bar-height;
position: absolute;
bottom: 0;
left: 0;
.step-indicator-bar {
display: block;
height: @bar-height;
width: calc(100% + 1.2rem);
min-width: 0;
width: 100%;
// min-width: 0;
background: @active-red;
border-radius: 0.1rem 0.1rem 0 0;
border-radius: 1rem;
}
}
}
&.active {
.step-badge,
.step-label {
color: #c7342c;
font-family: 'PoppinsBold';
font-weight: 700;
// background: @active-red;
}
}
}
</style>

View File

@@ -8,7 +8,11 @@
<div class="desc">{{ $t('AwardsPage.shapingTheFuture') }}</div>
<!-- 纵向时间线移动端排除平板 -->
<div v-if="isMobile && !isPad" class="timeline-point timeline-vertical flex flex-col" ref="timelineRef">
<div
v-if="isMobile && !isPad"
class="timeline-point timeline-vertical flex flex-col"
ref="timelineRef"
>
<div class="vertical-line"></div>
<div v-for="(item, index) in points" :key="'vertical-' + item.time" class="vertical-item">
<div class="vertical-node">
@@ -16,7 +20,12 @@
</div>
<div class="vertical-content">
<div class="vertical-time">{{ $t(item.time) }}</div>
<div class="vertical-label">{{ $t(item.label) }}</div>
<div class="vertical-label">
{{ $t(item.label)
}}<template v-if="item.subLabel">
{{ $t(item.subLabel) }}
</template>
</div>
<div class="vertical-desc">{{ $t(item.desc) }}</div>
</div>
</div>
@@ -64,7 +73,7 @@ import { nextTick, onBeforeUnmount, onMounted, ref, computed, inject } from 'vue
import { useI18n } from 'vue-i18n'
import { gsap } from 'gsap'
const { t } = useI18n()
// const { t } = useI18n()
const isMobile = inject<boolean>('isMobile')
const isPad = inject<boolean>('isPad')
@@ -188,7 +197,8 @@ const playAnimation = () => {
// 行内内容:桌面端用 .grid-cell纵向用 .vertical-item
// 纵向时,每个 item 从上方落下 + 渐显
const textItems = isMobile.value && !isPad.value
const textItems =
isMobile.value && !isPad.value
? containerRef.value.querySelectorAll('.vertical-item')
: containerRef.value.querySelectorAll('.grid-cell')
if (textItems && textItems.length) {

View File

@@ -5,7 +5,7 @@
<div class="header-left">
<img src="@/assets/images/award/code_create_logo.png" class="logo" />
</div>
<div class="header-right flex align-center">
<div class="header-right flex align-center" v-if="!isMobile">
<div class="contact flex align-center" @click="handleContactClick">
<img src="@/assets/images/award/mail.png" class="contact-icon" />
<div class="text" v-if="!isMobile">{{ $t('AwardsPage.contactUs') }}</div>
@@ -34,11 +34,25 @@
<img src="@/assets/images/award/arrow.png" alt="" class="arrow" />
</div>
</div>
<div v-else class="header-right mobile">
<img
v-show="!showDrawer"
src="@/assets/images/award/menu.png"
class="menu-icon"
@click="handleChangeDrawer"
/>
<img
v-show="showDrawer"
src="@/assets/images/award/close-menu.png"
class="menu-icon close"
@click="handleChangeDrawer"
/>
</div>
</div>
<div class="header-placeholder"></div>
</div>
<router-view />
<div class="footer flex space-between align-center">
<div class="footer flex space-between align-center" :class="{ mobile: isMobile || isTablet }">
<div class="social-list flex">
<a href="https://xhslink.com/m/5Ony2FapizV" target="_blank">
<img src="@/assets/images/award/xiaohongshu.svg" alt="" />
@@ -72,6 +86,7 @@
<div class="code-title" :class="{ 'flex space-between': isMobile }">
<span>{{ $t('AwardsPage.wechatTitle') }}</span>
<img
v-if="isMobile"
src="@/assets/images/award/close.svg"
class="close mobile"
@click="handleCloseQRcode"
@@ -113,8 +128,9 @@
<div class="mail-address">awards2026@code-createcom.hk</div>
<img src="@/assets/images/award/copy.png" class="copy-icon" @click="handleClickCopyEmail" />
</div>
<div class="send-btn" @click="handleContactUs">
{{ $t('AwardsPage.sendEmail') }}
<div class="send-btn flex flex-center" @click="handleContactUs">
<img v-if="isMobile" src="@/assets/images/award/mail.png" class="contact-icon" />
<span>{{ $t('AwardsPage.sendEmail') }}</span>
</div>
<div v-if="isMobile" class="send-btn copy" @click="handleClickCopyEmail">
{{ $t('AwardsPage.copyMailAddress') }}
@@ -122,6 +138,41 @@
</div>
<!-- <div class="contact-modal-mobile-wrapper"></div> -->
</div>
<div
class="drawer-mask"
:class="{ 'drawer-closing': drawerClosing }"
v-if="showDrawer || drawerClosing"
>
<div class="drawer-wrapper" @animationend="handleDrawerAnimationEnd">
<div class="drawer-item flex align-center space-between" @click="handleBtnClick">
<span class="text">{{ t('AwardsPage.submitApplication') }}</span>
<img src="@/assets/images/award/drawer-arrow.png" class="drawer-icon arrow" />
</div>
<div class="gap" />
<div class="drawer-item flex align-center space-between" @click="handleContactClick">
<span class="text">{{ $t('AwardsPage.contactUs') }}</span>
<img src="@/assets/images/award/drawer-mail.png" class="drawer-icon mail" />
</div>
<div class="gap" />
<div class="drawer-item language flex align-center">
<div
class="text"
:class="{ active: locale === 'CHINESE_SIMPLIFIED' }"
@click="handleChangeLanguage('CHINESE_SIMPLIFIED')"
>
中文
</div>
<span class="line">/</span>
<div
class="text"
:class="{ active: locale === 'ENGLISH' }"
@click="handleChangeLanguage('ENGLISH')"
>
EN
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
@@ -161,20 +212,11 @@ const handleCloseQRcode = () => {
type BtnType = 'index' | 'form'
const btnType = ref<BtnType>('index')
const btnText = computed(() => {
if (isMobile.value) {
if (btnType.value === 'index') {
return t('AwardsPage.submitMobile')
}
if (btnType.value === 'form') {
return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back'
}
} else {
if (btnType.value === 'index') {
return t('AwardsPage.submitApplication')
}
if (btnType.value === 'form') {
return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back to Introduction'
}
return t('AwardApply.backToIntroduction')
}
})
@@ -192,8 +234,28 @@ watch(
}
)
const showDrawer = ref(false)
const drawerClosing = ref(false)
const handleChangeDrawer = () => {
if (showDrawer.value) {
drawerClosing.value = true
} else {
drawerClosing.value = false
showDrawer.value = true
}
}
const handleDrawerAnimationEnd = () => {
if (drawerClosing.value) {
showDrawer.value = false
drawerClosing.value = false
}
}
const showContactModal = ref(false)
const handleContactClick = () => {
if (showDrawer.value) {
handleChangeDrawer()
}
showContactModal.value = true
}
const handleContactUs = () => {
@@ -357,6 +419,12 @@ const handleBtnClick = () => {
height: 2.4rem;
}
}
&.mobile {
.menu-icon {
width: 5rem;
height: 5rem;
}
}
}
}
}
@@ -379,6 +447,77 @@ const handleBtnClick = () => {
font-weight: 400;
font-size: 1.2rem;
}
&.mobile {
padding: 0 6.6rem;
img {
width: 4rem;
height: 4rem;
}
.copyright {
font-size: 2rem;
}
}
}
.drawer-mask {
position: fixed;
top: 8rem;
left: 0;
right: 0;
bottom: 0;
background-color: #00000033;
z-index: 8;
.drawer-wrapper {
padding: 7.8rem 6.6rem 8.2rem;
background-color: #fff;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.4rem;
border-bottom-left-radius: 2.4rem;
border-bottom-right-radius: 2.4rem;
animation: drawer-in 0.3s ease-in-out;
.drawer-item {
&.language {
column-gap: 0.8rem;
// padding: 0 1.25rem;
color: #c0c0c0;
.line {
user-select: none;
}
.text {
&.active {
color: #232323;
}
}
}
}
.gap {
border: 1px solid #d5d5d5;
margin: 3.2rem 0;
}
}
}
@keyframes drawer-in {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
@keyframes drawer-out {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
.drawer-closing {
background-color: transparent;
.drawer-wrapper {
animation: drawer-out 0.3s ease-in-out forwards;
}
}
.qrcode-mask {
position: fixed;
@@ -432,7 +571,7 @@ const handleBtnClick = () => {
}
.code-title {
width: 100%;
border-bottom: 1px solid #E0E0E0;
border-bottom: 1px solid #e0e0e0;
padding-bottom: 1.6rem;
}
.qrcode {
@@ -521,7 +660,11 @@ const handleBtnClick = () => {
border-radius: 0;
border-top-left-radius: 2.4rem;
border-top-right-radius: 2.4rem;
display: flex;
flex-direction: column;
align-items: center;
.header {
width: 100%;
border-bottom: 1px solid #e0e0e0;
// height: 9.6rem;
padding-top: 4.8rem;
@@ -543,10 +686,22 @@ const handleBtnClick = () => {
margin-bottom: 2rem;
}
.desc {
width: 46rem;
font-size: 2.2rem;
line-height: 3rem;
margin-bottom: 11rem;
}
.send-btn {
width: 100%;
height: 8rem;
line-height: 8rem;
font-size: 2.4rem;
column-gap: 0.8rem;
.contact-icon{
width: 2.4rem;
// height: 1.8rem;
}
}
.send-btn.copy {
background-color: #f1f1f1;
color: #969696;
@@ -574,10 +729,10 @@ const handleBtnClick = () => {
}
}
}
.footer {
row-gap: 0.8rem;
flex-direction: column;
justify-content: center;
}
// .footer {
// row-gap: 0.8rem;
// flex-direction: column;
// justify-content: center;
// }
}
</style>

View File

@@ -40,7 +40,7 @@
v-if="!isCompleted && !isExpired"
:class="{ mobile: isMobile, tablet: isTablet }"
>
<div class="form-header" v-if="showStep(0) && isMobile || isTablet">
<div class="form-header" v-if="showStep(0) && (isMobile || isTablet)">
<div class="form-title poppins-bold">
{{ t('AwardApply.emailVerification') }}
</div>
@@ -340,7 +340,7 @@
{{ t('AwardApply.submitYourDesign') }}
</div>
<div class="desc">
{{ t('AwardApply.unfinishedFormTip') }}
{{ t('AwardApply.stepTips') }}
</div>
</div>
</div>
@@ -1264,7 +1264,7 @@ onUnmounted(() => {
position: absolute;
}
.steps-container {
height: 6.8rem;
height: 10rem;
background-color: #fff;
position: absolute;
bottom: 0;

View File

@@ -1,15 +1,16 @@
<template>
<div class="award-page" :class="{ 'is-zh': isZh, mobile: isMobile }">
<div class="banner" :class="{ mobile: isMobile }">
<div class="banner" :class="{ mobile: isMobile, tablet: isTablet }">
<video
:src="bannerUrl"
class="banner-video"
autoplay
muted
loop
class="banner-video"
playsinline
webkit-playsinline
x5-playsinline
:controls="false"
></video>
<div class="submit-btn flex flex-center" @click="handleSubmitApplication">
<div>{{ $t('AwardsPage.submitApplication') }}</div>
@@ -43,6 +44,8 @@ import banner from '@/assets/images/award/banner.mp4'
import bannerMobile from '@/assets/images/award/banner_mobile.mp4'
import bannerZh from '@/assets/images/award/banner_chinese.mp4'
import bannerZhMobile from '@/assets/images/award/banner_mobile_chinese.mp4'
import bannerPad from '@/assets/images/pad_version/banner_pad.mp4'
import bannerPadZh from '@/assets/images/pad_version/banner_pad_chinese.mp4'
import { useIsMobile, useIsTablet } from '@/utils/isMobile'
const { isMobile } = useIsMobile()
const { isTablet } = useIsTablet()
@@ -53,17 +56,14 @@ provide('isMobile', isMobile)
provide('isPad', isTablet)
onMounted(() => {
router.replace('/')
setTimeout(() => {
console.log('是否平板-------', isTablet.value)
console.log('是否移动端-------', isMobile.value)
}, 1000)
// setTimeout(() => {
// console.log('是否平板-------', isTablet.value)
// console.log('是否移动端-------', isMobile.value)
// }, 1000)
})
const isZh = computed(() => {
return locale.value === 'CHINESE_SIMPLIFIED'
})
@@ -72,10 +72,11 @@ const bannerUrl = computed(() => {
let url = null
if (isMobile.value) {
url = isZh.value ? bannerZhMobile : bannerMobile
} else if (isTablet.value) {
url = isZh.value ? bannerPadZh : bannerPad
} else {
url = isZh.value ? bannerZh : banner
}
return url
})
@@ -153,13 +154,33 @@ const handleSubmitApplication = () => {
}
.banner.mobile {
height: auto;
height: 123.4rem;
.submit-btn {
top: 52%;
left: 8%;
width: 41rem;
font-size: 2.4rem;
width: 46.1rem;
font-size: 2.6rem;
column-gap: 1rem;
letter-spacing: 0%;
.arrow{
width: 4rem;
height: 4rem;
}
.ddl {
white-space: pre-line;
text-align: left;
padding-left: 4rem;
bottom: -6rem;
}
}
}
.banner.tablet {
height: 136vw;
.submit-btn{
top: 74vw;
left: 11vw;
width: 46.1rem;
.ddl {
white-space: pre-line;
text-align: left;