style: 表单页面移动端
This commit is contained in:
BIN
src/assets/images/mobile_version_background/banner_bg.png
Normal file
BIN
src/assets/images/mobile_version_background/banner_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 930 KiB |
@@ -220,6 +220,12 @@ export default {
|
|||||||
checkYourEmail: 'Check your email',
|
checkYourEmail: 'Check your email',
|
||||||
enterSixDigitCode: 'Enter the 6-digit code sent to',
|
enterSixDigitCode: 'Enter the 6-digit code sent to',
|
||||||
verify: 'Verify',
|
verify: 'Verify',
|
||||||
|
verifyCode: 'Verify Code',
|
||||||
|
verifyPlaceholder: 'enter 6-digital code',
|
||||||
|
stepTabVerify: 'Verify',
|
||||||
|
stepTabProfile: 'Profile',
|
||||||
|
stepTabDesign: 'Design Material',
|
||||||
|
stepTabTerms: 'Terms',
|
||||||
resendCode: 'Resend',
|
resendCode: 'Resend',
|
||||||
resendCodeIn: 'Resend Code in',
|
resendCodeIn: 'Resend Code in',
|
||||||
// 验证消息
|
// 验证消息
|
||||||
@@ -251,6 +257,9 @@ export default {
|
|||||||
uploadVideoOnly: 'Please upload a MP4 file only.',
|
uploadVideoOnly: 'Please upload a MP4 file only.',
|
||||||
fileSizeExceeds: 'File size exceeds {sizeLimit} limit. Please upload a smaller file.',
|
fileSizeExceeds: 'File size exceeds {sizeLimit} limit. Please upload a smaller file.',
|
||||||
videoDurationExceeds: 'Video duration exceeds 60 seconds limit. Please upload a shorter video.',
|
videoDurationExceeds: 'Video duration exceeds 60 seconds limit. Please upload a shorter video.',
|
||||||
uploadFailed: 'Upload failed'
|
uploadFailed: 'Upload failed',
|
||||||
|
nextStep: 'Next Step',
|
||||||
|
stepTips: 'Please complete this form in one sitting.',
|
||||||
|
backToIntroduction: 'Back to Introduction'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default {
|
|||||||
contactDesc:
|
contactDesc:
|
||||||
'无论是报名资格、参赛细则、评审标准,还是其他比赛相关问题,欢迎通过以下邮箱联络我们:',
|
'无论是报名资格、参赛细则、评审标准,还是其他比赛相关问题,欢迎通过以下邮箱联络我们:',
|
||||||
sendEmail: '发送邮件',
|
sendEmail: '发送邮件',
|
||||||
copyMail:'已成功复制到剪贴板',
|
copyMail: '已成功复制到剪贴板',
|
||||||
sendEmailFailed: '无法打开邮件客户端,我们已将邮箱地址复制到您的剪贴板。',
|
sendEmailFailed: '无法打开邮件客户端,我们已将邮箱地址复制到您的剪贴板。',
|
||||||
applicationDeadline: '申请期限:\n2026年7月15日',
|
applicationDeadline: '申请期限:\n2026年7月15日',
|
||||||
howToApply: '申请方法',
|
howToApply: '申请方法',
|
||||||
@@ -209,6 +209,12 @@ export default {
|
|||||||
checkYourEmail: '请查看您的邮箱',
|
checkYourEmail: '请查看您的邮箱',
|
||||||
enterSixDigitCode: '请输入发送到邮箱的 6 位验证码',
|
enterSixDigitCode: '请输入发送到邮箱的 6 位验证码',
|
||||||
verify: '验证',
|
verify: '验证',
|
||||||
|
verifyCode: '验证码',
|
||||||
|
verifyPlaceholder: '输入6位数字的验证码',
|
||||||
|
stepTabVerify: '验证',
|
||||||
|
stepTabProfile: '资料',
|
||||||
|
stepTabDesign: '设计材料',
|
||||||
|
stepTabTerms: '条款',
|
||||||
resendCode: '重新发送',
|
resendCode: '重新发送',
|
||||||
resendCodeIn: '重新发送',
|
resendCodeIn: '重新发送',
|
||||||
// 验证消息
|
// 验证消息
|
||||||
@@ -240,6 +246,9 @@ export default {
|
|||||||
uploadVideoOnly: '请仅上传 MP4 文件。',
|
uploadVideoOnly: '请仅上传 MP4 文件。',
|
||||||
fileSizeExceeds: '文件大小超过 {sizeLimit} 限制。请上传较小的文件。',
|
fileSizeExceeds: '文件大小超过 {sizeLimit} 限制。请上传较小的文件。',
|
||||||
videoDurationExceeds: '视频时长不可超过60秒',
|
videoDurationExceeds: '视频时长不可超过60秒',
|
||||||
uploadFailed: '上传失败'
|
uploadFailed: '上传失败',
|
||||||
|
nextStep: '下一步',
|
||||||
|
stepTips: '请一次性完成这个表单。',
|
||||||
|
backToIntroduction: '返回赛事介绍'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,25 +6,22 @@ export const useIsMobile = () => {
|
|||||||
let resizeTimer: ReturnType<typeof setTimeout> | null = null
|
let resizeTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
const checkDevice = () => {
|
const checkDevice = () => {
|
||||||
// 使用防抖避免频繁触发
|
|
||||||
if (resizeTimer) {
|
if (resizeTimer) {
|
||||||
clearTimeout(resizeTimer)
|
clearTimeout(resizeTimer)
|
||||||
}
|
}
|
||||||
resizeTimer = setTimeout(() => {
|
resizeTimer = setTimeout(() => {
|
||||||
// 1. 现代 Client Hints API(Chrome/Edge 最准)
|
|
||||||
// if (navigator.userAgentData?.mobile !== undefined) {
|
|
||||||
// isMobile.value = navigator.userAgentData.mobile
|
|
||||||
// console.log('使用 userAgentData:', isMobile.value)
|
|
||||||
// }
|
|
||||||
// 2. 综合判断(兼容所有浏览器)
|
|
||||||
const ua = navigator.userAgent.toLowerCase()
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
const mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i
|
const mobileRegex = /android|webos|iphone|ipod|blackberry|iemobile|opera mini/i
|
||||||
|
const tabletRegex = /ipad|tablet|playbook|silk/i
|
||||||
|
|
||||||
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 1
|
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 1
|
||||||
const smallScreen = window.innerWidth <= 1200 // 你可以改成 1024 等
|
const smallScreen = window.innerWidth <= 768
|
||||||
|
const tabletScreen = window.innerWidth > 768 && window.innerWidth <= 1200
|
||||||
|
|
||||||
const uaCheck = mobileRegex.test(ua)
|
const uaMobile = mobileRegex.test(ua)
|
||||||
isMobile.value = uaCheck || smallScreen
|
const uaTablet = tabletRegex.test(ua)
|
||||||
|
|
||||||
|
isMobile.value = (uaMobile && !uaTablet) || smallScreen
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +32,7 @@ export const useIsMobile = () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkDevice()
|
checkDevice()
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
window.addEventListener('orientationchange', checkDevice) // 手机旋转必备
|
window.addEventListener('orientationchange', checkDevice)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -46,10 +43,55 @@ export const useIsMobile = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 处理 keep-alive 缓存的组件重新激活场景
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
checkDevice()
|
checkDevice()
|
||||||
})
|
})
|
||||||
|
|
||||||
return { isMobile }
|
return { isMobile }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useIsTablet = () => {
|
||||||
|
const isTablet = ref(false)
|
||||||
|
|
||||||
|
let resizeTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
const checkDevice = () => {
|
||||||
|
if (resizeTimer) {
|
||||||
|
clearTimeout(resizeTimer)
|
||||||
|
}
|
||||||
|
resizeTimer = setTimeout(() => {
|
||||||
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
|
const tabletRegex = /ipad|tablet|playbook|silk/i
|
||||||
|
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0
|
||||||
|
const tabletScreen = window.innerWidth >= 769 && window.innerWidth <= 1200
|
||||||
|
|
||||||
|
const uaTablet = tabletRegex.test(ua)
|
||||||
|
|
||||||
|
isTablet.value = (uaTablet && tabletScreen) || (hasTouch && tabletScreen)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
checkDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkDevice()
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
window.addEventListener('orientationchange', checkDevice)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', handleResize)
|
||||||
|
window.removeEventListener('orientationchange', checkDevice)
|
||||||
|
if (resizeTimer) {
|
||||||
|
clearTimeout(resizeTimer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
checkDevice()
|
||||||
|
})
|
||||||
|
|
||||||
|
return { isTablet }
|
||||||
|
}
|
||||||
|
|||||||
162
src/views/AwardPage/components/Step.vue
Normal file
162
src/views/AwardPage/components/Step.vue
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="step-bar" role="tablist" :aria-label="t('AwardApply.applicationForm')">
|
||||||
|
<button
|
||||||
|
v-for="(key, index) in tabKeys"
|
||||||
|
:key="key"
|
||||||
|
type="button"
|
||||||
|
class="step-item"
|
||||||
|
:class="{ active: modelValue === index }"
|
||||||
|
role="tab"
|
||||||
|
:aria-selected="modelValue === index"
|
||||||
|
:tabindex="modelValue === index ? 0 : -1"
|
||||||
|
@click="onSelect(index)"
|
||||||
|
>
|
||||||
|
<div class="step-cluster">
|
||||||
|
<div class="step-head">
|
||||||
|
<span class="step-badge">{{ index + 1 }}</span>
|
||||||
|
<span class="step-label">{{ t(`AwardApply.${key}`) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="step-indicator" aria-hidden="true">
|
||||||
|
<span v-if="modelValue === index" class="step-indicator-bar" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
const tabKeys = ['stepTabVerify', 'stepTabProfile', 'stepTabDesign', 'stepTabTerms'] as const
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
modelValue?: number
|
||||||
|
}>(),
|
||||||
|
{ modelValue: 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [index: number]
|
||||||
|
change: [index: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
function onSelect(index: number) {
|
||||||
|
if (props.modelValue === index) return
|
||||||
|
emit('update:modelValue', index)
|
||||||
|
emit('change', index)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
@active-red: #c7342c;
|
||||||
|
@label-active: #585858;
|
||||||
|
@label-inactive: #9e9e9e;
|
||||||
|
@badge-inactive-bg: #dcdcdc;
|
||||||
|
@badge-inactive-text: #9f9f9f;
|
||||||
|
@bar-height: 0.4rem;
|
||||||
|
|
||||||
|
.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;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item {
|
||||||
|
flex: 1;
|
||||||
|
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;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid @active-red;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-cluster {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-head {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-label {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
color: @label-inactive;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 同宽基准 + 左右扩展) */
|
||||||
|
.step-indicator-bar {
|
||||||
|
display: block;
|
||||||
|
height: @bar-height;
|
||||||
|
width: calc(100% + 1.2rem);
|
||||||
|
min-width: 0;
|
||||||
|
background: @active-red;
|
||||||
|
border-radius: 0.1rem 0.1rem 0 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -132,7 +132,12 @@ type BtnType = 'index' | 'form'
|
|||||||
const btnType = ref<BtnType>('index')
|
const btnType = ref<BtnType>('index')
|
||||||
const btnText = computed(() => {
|
const btnText = computed(() => {
|
||||||
if (isMobile.value) {
|
if (isMobile.value) {
|
||||||
return t('AwardsPage.submitMobile')
|
if (btnType.value === 'index') {
|
||||||
|
return t('AwardsPage.submitMobile')
|
||||||
|
}
|
||||||
|
if (btnType.value === 'form') {
|
||||||
|
return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (btnType.value === 'index') {
|
if (btnType.value === 'index') {
|
||||||
return t('AwardsPage.submitApplication')
|
return t('AwardsPage.submitApplication')
|
||||||
@@ -262,6 +267,9 @@ const handleBtnClick = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.award-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.header-wrapper {
|
.header-wrapper {
|
||||||
.header-placeholder {
|
.header-placeholder {
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
|
|||||||
@@ -1,19 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="apply-container">
|
<div class="apply-container" :class="{ mobile: isMobile }">
|
||||||
<div class="banner">
|
<div class="banner" :class="{ tablet: isTablet, mobile: isMobile && !isTablet }">
|
||||||
<div class="slogan">
|
<div class="slogan" @click="handleTestDate">
|
||||||
{{ t('AwardApply.slogan') }}
|
{{ t('AwardApply.slogan') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="title poppins-medium">{{ t('AwardApply.applicationForm') }}</div>
|
<div class="title poppins-medium" @click="handleTestComplete">
|
||||||
<div class="form-header" v-if="!isCompleted && !isExpired">
|
{{ t('AwardApply.applicationForm') }}
|
||||||
|
</div>
|
||||||
|
<div class="form-header" v-if="!isCompleted && !isExpired && !isMobile && !isTablet">
|
||||||
<div class="form-title poppins-bold">
|
<div class="form-title poppins-bold">
|
||||||
{{ t('AwardApply.emailVerification') }}
|
{{ t('AwardApply.emailVerification') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="desc">{{ t('AwardApply.aidaUsersOnly') }}</div>
|
<div class="desc">{{ t('AwardApply.aidaUsersOnly') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="steps-container" v-if="(isMobile || isTablet) && !isCompleted && !isExpired">
|
||||||
|
<Step v-model="step" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Success :isExpired="isExpired" v-if="isCompleted || isExpired" />
|
<div
|
||||||
<div class="form-container" v-if="!isCompleted && !isExpired">
|
class="success-wrapper"
|
||||||
|
v-if="isCompleted || isExpired"
|
||||||
|
:class="{ mobile: isMobile, tablet: isTablet }"
|
||||||
|
>
|
||||||
|
<Success
|
||||||
|
class="success-container"
|
||||||
|
:class="{ mobile: isMobile, tablet: isTablet }"
|
||||||
|
:isExpired="isExpired"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="step-btn back"
|
||||||
|
v-show="(isCompleted || isExpired) && (isMobile || isTablet)"
|
||||||
|
@click="handleBackToIntroduction"
|
||||||
|
>
|
||||||
|
{{ t('AwardApply.backToIntroduction') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="form-container"
|
||||||
|
v-if="!isCompleted && !isExpired"
|
||||||
|
:class="{ mobile: isMobile, tablet: isTablet }"
|
||||||
|
>
|
||||||
|
<div class="form-header" v-if="showStep(0)">
|
||||||
|
<div class="form-title poppins-bold">
|
||||||
|
{{ t('AwardApply.emailVerification') }}
|
||||||
|
</div>
|
||||||
|
<div class="desc">{{ t('AwardApply.aidaUsersOnly') }}</div>
|
||||||
|
</div>
|
||||||
<div class="form-content">
|
<div class="form-content">
|
||||||
<a-form
|
<a-form
|
||||||
name="form"
|
name="form"
|
||||||
@@ -23,7 +55,7 @@
|
|||||||
:rules="rulesRef"
|
:rules="rulesRef"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
>
|
>
|
||||||
<div class="email-box full-row">
|
<div class="email-box full-row" v-show="showStep(0)">
|
||||||
<a-form-item name="email" required :label="t('AwardApply.emailAddress')">
|
<a-form-item name="email" required :label="t('AwardApply.emailAddress')">
|
||||||
<div class="email-wrapper flex align-center">
|
<div class="email-wrapper flex align-center">
|
||||||
<a-input v-model:value="form.email" />
|
<a-input v-model:value="form.email" />
|
||||||
@@ -47,13 +79,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row full-row">
|
<div
|
||||||
|
v-if="isMobile || isTablet"
|
||||||
|
class="email-box verify-box full-row"
|
||||||
|
v-show="showStep(0)"
|
||||||
|
>
|
||||||
|
<a-form-item name="verify" required :label="t('AwardApply.verifyCode')">
|
||||||
|
<div class="email-wrapper flex align-center">
|
||||||
|
<a-input
|
||||||
|
v-model:value="mobileVerifyCode"
|
||||||
|
:placeholder="t('AwardApply.verifyPlaceholder')"
|
||||||
|
/>
|
||||||
|
<div class="code-btn" @click="handleVerifyCode">
|
||||||
|
{{ t('AwardApply.verify') }}
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
v-if="hasValidEmail"
|
||||||
|
src="@/assets/images/award/checked.png"
|
||||||
|
alt=""
|
||||||
|
class="checked-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div v-show="showStep(1)" class="form-row full-row" :class="{ mobile: isMobile }">
|
||||||
<div class="form-title poppins-bold">
|
<div class="form-title poppins-bold">
|
||||||
{{ t('AwardApply.personalInformation') }}
|
{{ t('AwardApply.personalInformation') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="desc">{{ t('AwardApply.tellUsAboutYourself') }}</div>
|
<div class="desc">{{ t('AwardApply.tellUsAboutYourself') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-info flex">
|
<div v-show="showStep(1)" class="user-info flex">
|
||||||
<template v-for="item in formKeys" :key="item.key">
|
<template v-for="item in formKeys" :key="item.key">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="item.key !== 'email'"
|
v-if="item.key !== 'email'"
|
||||||
@@ -74,9 +129,9 @@
|
|||||||
>
|
>
|
||||||
<template #suffixIcon>
|
<template #suffixIcon>
|
||||||
<!-- <img
|
<!-- <img
|
||||||
class="arrow-down-icon"
|
class="arrow-down-icon"
|
||||||
src="@/assets/images/award/arrow_down.svg"
|
src="@/assets/images/award/arrow_down.svg"
|
||||||
/> -->
|
/> -->
|
||||||
</template>
|
</template>
|
||||||
</a-select>
|
</a-select>
|
||||||
<div class="arrow-wrapper flex flex-center">
|
<div class="arrow-wrapper flex flex-center">
|
||||||
@@ -86,176 +141,182 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row full-row">
|
<template v-if="showStep(2)">
|
||||||
<div class="form-title poppins-bold">
|
<div class="form-row full-row">
|
||||||
{{ t('AwardApply.designInformation') }}
|
<div class="form-title poppins-bold">
|
||||||
</div>
|
{{ t('AwardApply.designInformation') }}
|
||||||
<div class="desc">
|
</div>
|
||||||
{{ t('AwardApply.shareYourCreativeVision') }}
|
<div class="desc">
|
||||||
</div>
|
{{ t('AwardApply.shareYourCreativeVision') }}
|
||||||
</div>
|
|
||||||
<a-form-item
|
|
||||||
class="full-row design-title"
|
|
||||||
name="designTitle"
|
|
||||||
:label="t('AwardApply.designTitle')"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<a-input v-model:value="form.designTitle" :disabled="readOnly" />
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item
|
|
||||||
class="full-row design-desc"
|
|
||||||
name="designDescription"
|
|
||||||
:label="t('AwardApply.designDescription')"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<a-textarea
|
|
||||||
class="textarea"
|
|
||||||
:disabled="readOnly"
|
|
||||||
v-model:value="form.designDescription"
|
|
||||||
:placeholder="t('AwardApply.designDescriptionPlaceholder')"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
<div class="form-row full-row">
|
|
||||||
<div class="form-title poppins-bold">
|
|
||||||
{{ t('AwardApply.submissionFiles') }}
|
|
||||||
</div>
|
|
||||||
<div class="desc">
|
|
||||||
{{ t('AwardApply.uploadYourDesignMaterials') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="information full-row">
|
|
||||||
<div class="information-title flex align-center">
|
|
||||||
<div class="point"></div>
|
|
||||||
<div class="text poppins-bold">
|
|
||||||
{{ t('AwardApply.submissionRequirements') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="information-list flex space-between">
|
|
||||||
<li class="information-item">
|
|
||||||
{{ t('AwardApply.pdfRequirement') }}
|
|
||||||
</li>
|
|
||||||
<div class="right">
|
|
||||||
<li class="information-item">
|
|
||||||
{{ t('AwardApply.rightContent.format') }}
|
|
||||||
</li>
|
|
||||||
<li class="information-item">
|
|
||||||
{{ t('AwardApply.rightContent.video') }}
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="upload-container full-row">
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
class="full-row"
|
class="full-row design-title"
|
||||||
name="pdfPath"
|
name="designTitle"
|
||||||
|
:label="t('AwardApply.designTitle')"
|
||||||
required
|
required
|
||||||
:validate-trigger="[]"
|
|
||||||
:label="t('AwardApply.uploadPdfTitle')"
|
|
||||||
>
|
>
|
||||||
<div>
|
<a-input v-model:value="form.designTitle" :disabled="readOnly" />
|
||||||
<a-upload-dragger
|
|
||||||
v-model:fileList="pdfList"
|
|
||||||
:disabled="readOnly"
|
|
||||||
:maxCount="1"
|
|
||||||
:showUploadList="false"
|
|
||||||
@change="(info) => handleFileChange(info, 'pdf')"
|
|
||||||
:customRequest="handleUploadPdf"
|
|
||||||
:beforeUpload="beforeUploadPdf"
|
|
||||||
@remove="handleRemoveFile('pdf')"
|
|
||||||
accept=".pdf"
|
|
||||||
>
|
|
||||||
<img src="@/assets/images/award/upload.png" alt="" class="upload-icon" />
|
|
||||||
<p class="desc">
|
|
||||||
{{ t('AwardApply.clickToUploadPdf') }}
|
|
||||||
</p>
|
|
||||||
<p class="limit">
|
|
||||||
{{ t('AwardApply.pdfFileLimit') }}
|
|
||||||
</p>
|
|
||||||
</a-upload-dragger>
|
|
||||||
<!-- 自定义文件列表显示 -->
|
|
||||||
<div
|
|
||||||
v-if="pdfUploadStatus === 'success' && pdfList.length > 0"
|
|
||||||
class="custom-upload-list flex align-center space-between"
|
|
||||||
>
|
|
||||||
<div class="flex align-center">
|
|
||||||
<SvgIcon name="CFile" />
|
|
||||||
{{ pdfList[0]?.name }}
|
|
||||||
</div>
|
|
||||||
<div @click="handleRemoveFile('pdf')" class="delete-file" title="delete file">
|
|
||||||
<SvgIcon name="CDelete" color="red" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="pdfUploadStatus === 'uploading' || pdfUploadStatus === 'success'"
|
|
||||||
class="uploading-container"
|
|
||||||
>
|
|
||||||
<UploadStatus :status="pdfUploadStatus" type="pdf" />
|
|
||||||
<div class="progress-bar-container" v-if="pdfUploadStatus === 'uploading'">
|
|
||||||
<div class="progress-bar" :style="{ width: `${uploadProgressPdf}%` }"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
|
||||||
<div class="upload-container full-row">
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
class="full-row"
|
class="full-row design-desc"
|
||||||
name="videoPath"
|
name="designDescription"
|
||||||
|
:label="t('AwardApply.designDescription')"
|
||||||
required
|
required
|
||||||
:validate-trigger="[]"
|
|
||||||
:label="t('AwardApply.uploadVideoTitle')"
|
|
||||||
>
|
>
|
||||||
<div>
|
<a-textarea
|
||||||
<a-upload-dragger
|
class="textarea"
|
||||||
v-model:fileList="videoList"
|
:disabled="readOnly"
|
||||||
:disabled="readOnly"
|
v-model:value="form.designDescription"
|
||||||
:maxCount="1"
|
:placeholder="t('AwardApply.designDescriptionPlaceholder')"
|
||||||
:showUploadList="false"
|
/>
|
||||||
@change="(info) => handleFileChange(info, 'video')"
|
|
||||||
:customRequest="handleUploadVideo"
|
|
||||||
:beforeUpload="beforeUploadVideo"
|
|
||||||
@remove="handleRemoveFile('video')"
|
|
||||||
accept=".mp4"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/upload_video_icon.png"
|
|
||||||
alt=""
|
|
||||||
class="upload-icon"
|
|
||||||
/>
|
|
||||||
<p class="desc">
|
|
||||||
{{ t('AwardApply.clickToUploadVideo') }}
|
|
||||||
</p>
|
|
||||||
<p class="limit">
|
|
||||||
{{ t('AwardApply.videoFileLimit') }}
|
|
||||||
</p>
|
|
||||||
</a-upload-dragger>
|
|
||||||
<!-- 自定义文件列表显示 -->
|
|
||||||
<div
|
|
||||||
v-if="videoUploadStatus === 'success' && videoList.length > 0"
|
|
||||||
class="custom-upload-list flex align-center space-between"
|
|
||||||
>
|
|
||||||
<div class="flex align-center">
|
|
||||||
<SvgIcon name="CFile" />
|
|
||||||
{{ videoList[0]?.name }}
|
|
||||||
</div>
|
|
||||||
<div @click="handleRemoveFile('video')" class="delete-file" title="delete file">
|
|
||||||
<SvgIcon name="CDelete" color="red" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="videoUploadStatus === 'success' || videoUploadStatus === 'uploading'"
|
|
||||||
class="uploading-container"
|
|
||||||
>
|
|
||||||
<UploadStatus :status="videoUploadStatus" type="video" />
|
|
||||||
<div class="progress-bar-container" v-if="videoUploadStatus === 'uploading'">
|
|
||||||
<div class="progress-bar" :style="{ width: `${uploadProgressVideo}%` }"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
<div class="form-row full-row">
|
||||||
|
<div class="form-title poppins-bold">
|
||||||
|
{{ t('AwardApply.submissionFiles') }}
|
||||||
|
</div>
|
||||||
|
<div class="desc">
|
||||||
|
{{ t('AwardApply.uploadYourDesignMaterials') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="information full-row">
|
||||||
|
<div class="information-title flex align-center">
|
||||||
|
<div class="point"></div>
|
||||||
|
<div class="text poppins-bold">
|
||||||
|
{{ t('AwardApply.submissionRequirements') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="information-list flex space-between">
|
||||||
|
<li class="information-item">
|
||||||
|
{{ t('AwardApply.pdfRequirement') }}
|
||||||
|
</li>
|
||||||
|
<div class="right">
|
||||||
|
<li class="information-item">
|
||||||
|
{{ t('AwardApply.rightContent.format') }}
|
||||||
|
</li>
|
||||||
|
<li class="information-item">
|
||||||
|
{{ t('AwardApply.rightContent.video') }}
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="upload-container full-row">
|
||||||
|
<a-form-item
|
||||||
|
class="full-row"
|
||||||
|
name="pdfPath"
|
||||||
|
required
|
||||||
|
:validate-trigger="[]"
|
||||||
|
:label="t('AwardApply.uploadPdfTitle')"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<a-upload-dragger
|
||||||
|
v-model:fileList="pdfList"
|
||||||
|
:disabled="readOnly"
|
||||||
|
:maxCount="1"
|
||||||
|
:showUploadList="false"
|
||||||
|
@change="(info) => handleFileChange(info, 'pdf')"
|
||||||
|
:customRequest="handleUploadPdf"
|
||||||
|
:beforeUpload="beforeUploadPdf"
|
||||||
|
@remove="handleRemoveFile('pdf')"
|
||||||
|
accept=".pdf"
|
||||||
|
>
|
||||||
|
<img src="@/assets/images/award/upload.png" alt="" class="upload-icon" />
|
||||||
|
<p class="desc">
|
||||||
|
{{ t('AwardApply.clickToUploadPdf') }}
|
||||||
|
</p>
|
||||||
|
<p class="limit">
|
||||||
|
{{ t('AwardApply.pdfFileLimit') }}
|
||||||
|
</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
<!-- 自定义文件列表显示 -->
|
||||||
|
<div
|
||||||
|
v-if="pdfUploadStatus === 'success' && pdfList.length > 0"
|
||||||
|
class="custom-upload-list flex align-center space-between"
|
||||||
|
>
|
||||||
|
<div class="flex align-center">
|
||||||
|
<SvgIcon name="CFile" />
|
||||||
|
{{ pdfList[0]?.name }}
|
||||||
|
</div>
|
||||||
|
<div @click="handleRemoveFile('pdf')" class="delete-file" title="delete file">
|
||||||
|
<SvgIcon name="CDelete" color="red" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="pdfUploadStatus === 'uploading' || pdfUploadStatus === 'success'"
|
||||||
|
class="uploading-container"
|
||||||
|
>
|
||||||
|
<UploadStatus :status="pdfUploadStatus" type="pdf" />
|
||||||
|
<div class="progress-bar-container" v-if="pdfUploadStatus === 'uploading'">
|
||||||
|
<div class="progress-bar" :style="{ width: `${uploadProgressPdf}%` }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="upload-container full-row">
|
||||||
|
<a-form-item
|
||||||
|
class="full-row"
|
||||||
|
name="videoPath"
|
||||||
|
required
|
||||||
|
:validate-trigger="[]"
|
||||||
|
:label="t('AwardApply.uploadVideoTitle')"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<a-upload-dragger
|
||||||
|
v-model:fileList="videoList"
|
||||||
|
:disabled="readOnly"
|
||||||
|
:maxCount="1"
|
||||||
|
:showUploadList="false"
|
||||||
|
@change="(info) => handleFileChange(info, 'video')"
|
||||||
|
:customRequest="handleUploadVideo"
|
||||||
|
:beforeUpload="beforeUploadVideo"
|
||||||
|
@remove="handleRemoveFile('video')"
|
||||||
|
accept=".mp4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="@/assets/images/award/upload_video_icon.png"
|
||||||
|
alt=""
|
||||||
|
class="upload-icon"
|
||||||
|
/>
|
||||||
|
<p class="desc">
|
||||||
|
{{ t('AwardApply.clickToUploadVideo') }}
|
||||||
|
</p>
|
||||||
|
<p class="limit">
|
||||||
|
{{ t('AwardApply.videoFileLimit') }}
|
||||||
|
</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
<!-- 自定义文件列表显示 -->
|
||||||
|
<div
|
||||||
|
v-if="videoUploadStatus === 'success' && videoList.length > 0"
|
||||||
|
class="custom-upload-list flex align-center space-between"
|
||||||
|
>
|
||||||
|
<div class="flex align-center">
|
||||||
|
<SvgIcon name="CFile" />
|
||||||
|
{{ videoList[0]?.name }}
|
||||||
|
</div>
|
||||||
|
<div @click="handleRemoveFile('video')" class="delete-file" title="delete file">
|
||||||
|
<SvgIcon name="CDelete" color="red" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="videoUploadStatus === 'success' || videoUploadStatus === 'uploading'"
|
||||||
|
class="uploading-container"
|
||||||
|
>
|
||||||
|
<UploadStatus :status="videoUploadStatus" type="video" />
|
||||||
|
<div class="progress-bar-container" v-if="videoUploadStatus === 'uploading'">
|
||||||
|
<div class="progress-bar" :style="{ width: `${uploadProgressVideo}%` }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div class="conditions">
|
<div
|
||||||
|
class="conditions"
|
||||||
|
:class="{ mobile: isMobile, tablet: isTablet }"
|
||||||
|
v-show="showStep(3)"
|
||||||
|
>
|
||||||
<div class="confitions-title poppins-bold">
|
<div class="confitions-title poppins-bold">
|
||||||
{{ t('AwardApply.termsAndConditions') }}
|
{{ t('AwardApply.termsAndConditions') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -273,7 +334,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="submit-container">
|
|
||||||
|
<div class="submit-container" v-if="showStep(3)">
|
||||||
<div class="submit-btn poppins-bold" @click="handleSubmitForm">
|
<div class="submit-btn poppins-bold" @click="handleSubmitForm">
|
||||||
{{ t('AwardApply.submitYourDesign') }}
|
{{ t('AwardApply.submitYourDesign') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -282,6 +344,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="(isMobile || isTablet) && step < 3" class="step-container">
|
||||||
|
<div class="step-btn" @click="handleNextStep">
|
||||||
|
{{ t('AwardApply.nextStep') }}
|
||||||
|
</div>
|
||||||
|
<div class="step-tips">{{ t('AwardApply.stepTips') }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="showModal"
|
v-model:visible="showModal"
|
||||||
@@ -321,6 +389,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onUnmounted, onMounted, computed, nextTick } from 'vue'
|
import { ref, onUnmounted, onMounted, computed, nextTick } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import type { Rule } from 'ant-design-vue/es/form'
|
import type { Rule } from 'ant-design-vue/es/form'
|
||||||
@@ -328,22 +397,52 @@ import { message, Upload } from 'ant-design-vue'
|
|||||||
import { Https } from '@/utils/request'
|
import { Https } from '@/utils/request'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { UploadChangeParam } from 'ant-design-vue'
|
import type { UploadChangeParam } from 'ant-design-vue'
|
||||||
|
import { useIsMobile, useIsTablet } from '@/utils/isMobile'
|
||||||
import VerifycationCodeInput from './components/VerificationCodeInput.vue'
|
import VerifycationCodeInput from './components/VerificationCodeInput.vue'
|
||||||
import UploadStatus from './components/UploadStatus.vue'
|
import UploadStatus from './components/UploadStatus.vue'
|
||||||
import Success from './components/Success.vue'
|
import Success from './components/Success.vue'
|
||||||
|
import Step from './components/Step.vue'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { isMobile } = useIsMobile()
|
||||||
|
const { isTablet } = useIsTablet()
|
||||||
|
|
||||||
|
const step = ref(0)
|
||||||
|
const showStep = (val: number) => {
|
||||||
|
if (!isMobile.value && !isTablet.value) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return step.value === val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 是否晚于2026年7月15日
|
// 是否晚于2026年7月15日
|
||||||
|
const date = ref('2026-07-15')
|
||||||
const isExpired = computed(() => {
|
const isExpired = computed(() => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const targetDate = new Date('2026-07-15')
|
const targetDate = new Date(date.value)
|
||||||
return now > targetDate
|
return now > targetDate
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isCompleted = ref(false)
|
const isCompleted = ref(false)
|
||||||
|
|
||||||
|
let dateClick = 0
|
||||||
|
const handleTestDate = () => {
|
||||||
|
dateClick++
|
||||||
|
if (dateClick % 2 === 0) {
|
||||||
|
date.value = date.value === '2026-07-15' ? '2026-01-15' : '2026-07-15'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let completeClick = 0
|
||||||
|
const handleTestComplete = () => {
|
||||||
|
completeClick++
|
||||||
|
if (completeClick % 4 === 0) {
|
||||||
|
isCompleted.value = !isCompleted.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const readOnly = computed(() => {
|
const readOnly = computed(() => {
|
||||||
if (route.query.id && !hasValidEmail.value) {
|
if (route.query.id && !hasValidEmail.value) {
|
||||||
return true
|
return true
|
||||||
@@ -633,11 +732,14 @@ const handleSendCode = debounce(async () => {
|
|||||||
|
|
||||||
startCountdown()
|
startCountdown()
|
||||||
emailCode.value = ['', '', '', '', '', ''] // 重置验证码输入
|
emailCode.value = ['', '', '', '', '', ''] // 重置验证码输入
|
||||||
showModal.value = true
|
if (!isMobile.value && !isTablet.value) {
|
||||||
|
showModal.value = true
|
||||||
|
}
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
const verifyCode = ref(null)
|
const verifyCode = ref(null)
|
||||||
|
const mobileVerifyCode = ref('')
|
||||||
|
|
||||||
const setVerifyCode = (value) => {
|
const setVerifyCode = (value) => {
|
||||||
verifyCode.value = value
|
verifyCode.value = value
|
||||||
@@ -646,9 +748,14 @@ const setVerifyCode = (value) => {
|
|||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
showModal.value = false
|
showModal.value = false
|
||||||
}
|
}
|
||||||
const handleVerifyCode = () => {
|
const handleVerifyCode = async () => {
|
||||||
// console.log(verifyCode.value)
|
// console.log(verifyCode.value)
|
||||||
|
if (isMobile.value || isTablet.value) {
|
||||||
|
await formRef.value.validateFields(['email'])
|
||||||
|
}
|
||||||
|
if (isMobile.value || isTablet.value) {
|
||||||
|
verifyCode.value = mobileVerifyCode.value
|
||||||
|
}
|
||||||
if (verifyCode.value.length !== 6) {
|
if (verifyCode.value.length !== 6) {
|
||||||
message.error(t('AwardApply.pleaseEnterCompleteCode'))
|
message.error(t('AwardApply.pleaseEnterCompleteCode'))
|
||||||
return
|
return
|
||||||
@@ -1071,6 +1178,16 @@ const handleEchoForm = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleNextStep = () => {
|
||||||
|
if (step.value < 3) {
|
||||||
|
step.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBackToIntroduction = () => {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (route.query.id) {
|
if (route.query.id) {
|
||||||
handleEchoForm()
|
handleEchoForm()
|
||||||
@@ -1104,7 +1221,22 @@ onUnmounted(() => {
|
|||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
.success-wrapper {
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
.success-container {
|
||||||
|
&.mobile {
|
||||||
|
height: auto;
|
||||||
|
top: initial;
|
||||||
|
margin: 4rem 3.8rem 0;
|
||||||
|
padding: 13.5rem 13.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tablet {
|
||||||
|
.step-btn {
|
||||||
|
margin: 6rem 23.3rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.banner {
|
.banner {
|
||||||
height: 54.8rem;
|
height: 54.8rem;
|
||||||
background: url('@/assets/images/award/form_bg.png') no-repeat;
|
background: url('@/assets/images/award/form_bg.png') no-repeat;
|
||||||
@@ -1125,37 +1257,62 @@ onUnmounted(() => {
|
|||||||
color: #c7342c;
|
color: #c7342c;
|
||||||
font-size: 8rem;
|
font-size: 8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-header {
|
.form-header {
|
||||||
height: 16.8rem;
|
|
||||||
width: calc(100% - 42.8rem);
|
width: calc(100% - 42.8rem);
|
||||||
left: 21.4rem;
|
left: 21.4rem;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
}
|
||||||
|
.steps-container {
|
||||||
|
height: 6.8rem;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top-left-radius: 0.8rem;
|
position: absolute;
|
||||||
border-top-right-radius: 0.8rem;
|
bottom: 0;
|
||||||
text-align: left;
|
left: 0;
|
||||||
padding: 6rem 6rem 0;
|
width: 100%;
|
||||||
|
}
|
||||||
.form-title {
|
&.mobile {
|
||||||
color: #232323;
|
height: 24.8rem;
|
||||||
font-size: 3rem;
|
background: url('@/assets/images/mobile_version_background/banner_bg.png') no-repeat;
|
||||||
margin-bottom: 1rem;
|
background-size: 100% 100%;
|
||||||
|
padding: 4.1rem 6rem 0;
|
||||||
|
.slogan {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
}
|
}
|
||||||
|
.title {
|
||||||
.desc {
|
font-size: 4rem;
|
||||||
color: #b10000;
|
|
||||||
font-family: 'Instrument';
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 2.4rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.form-header {
|
||||||
|
height: 16.8rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border-top-left-radius: 0.8rem;
|
||||||
|
border-top-right-radius: 0.8rem;
|
||||||
|
text-align: left;
|
||||||
|
padding: 6rem 6rem 0;
|
||||||
|
|
||||||
|
.form-title {
|
||||||
|
color: #232323;
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
color: #b10000;
|
||||||
|
font-family: 'Instrument';
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
margin-top: 12rem;
|
margin-top: 12rem;
|
||||||
margin-bottom: 6rem;
|
margin-bottom: 6rem;
|
||||||
|
&.mobile {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.form-title {
|
.form-title {
|
||||||
color: #232323;
|
color: #232323;
|
||||||
@@ -1307,6 +1464,58 @@ onUnmounted(() => {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.mobile,
|
||||||
|
&.tablet {
|
||||||
|
margin-top: 4rem;
|
||||||
|
.form-row {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.form-title {
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.mobile:not(.tablet) {
|
||||||
|
padding: 0 3.8rem 4rem;
|
||||||
|
.form-header {
|
||||||
|
padding: 3rem 3rem 0;
|
||||||
|
height: auto;
|
||||||
|
padding-bottom: 2.4rem;
|
||||||
|
}
|
||||||
|
.form-content {
|
||||||
|
padding: 0 3rem 20rem;
|
||||||
|
.verify-box {
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
.ant-form .ant-form-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-btn {
|
||||||
|
text-align: center;
|
||||||
|
margin: 6rem 3rem 0;
|
||||||
|
background-color: #454545;
|
||||||
|
height: 5rem;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 5rem;
|
||||||
|
font-family: 'PoppinsBold';
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
cursor: pointer;
|
||||||
|
&.back {
|
||||||
|
margin: 6rem 6.6rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.step-tips {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 2rem;
|
||||||
|
font-family: 'Instrument';
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
color: #6d6d6d;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-form-item-label) {
|
:deep(.ant-form-item-label) {
|
||||||
@@ -1438,7 +1647,9 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
.conditions {
|
.conditions {
|
||||||
margin-top: 12rem;
|
margin-top: 12rem;
|
||||||
|
&.mobile {
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
&-title {
|
&-title {
|
||||||
color: #232323;
|
color: #232323;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
@@ -1486,6 +1697,17 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.mobile:not(.tablet) {
|
||||||
|
.condition-list {
|
||||||
|
.condition-item {
|
||||||
|
align-items: flex-start;
|
||||||
|
height: auto;
|
||||||
|
min-height: 6rem;
|
||||||
|
line-height: 2.4rem;
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-container {
|
.submit-container {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, provide,onUnmounted } from 'vue'
|
import { ref, computed, onMounted, provide, onUnmounted } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import JudgesSection from './components/JudgesSection.vue'
|
import JudgesSection from './components/JudgesSection.vue'
|
||||||
@@ -43,33 +43,27 @@ import banner from '@/assets/images/award/banner.mp4'
|
|||||||
import bannerMobile from '@/assets/images/award/banner_mobile.mp4'
|
import bannerMobile from '@/assets/images/award/banner_mobile.mp4'
|
||||||
import bannerZh from '@/assets/images/award/banner_chinese.mp4'
|
import bannerZh from '@/assets/images/award/banner_chinese.mp4'
|
||||||
import bannerZhMobile from '@/assets/images/award/banner_mobile_chinese.mp4'
|
import bannerZhMobile from '@/assets/images/award/banner_mobile_chinese.mp4'
|
||||||
import { useIsMobile } from '@/utils/isMobile'
|
import { useIsMobile, useIsTablet } from '@/utils/isMobile'
|
||||||
const { isMobile } = useIsMobile()
|
const { isMobile } = useIsMobile()
|
||||||
|
const { isTablet } = useIsTablet()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
provide('isMobile', isMobile)
|
provide('isMobile', isMobile)
|
||||||
|
|
||||||
const isPad = ref(false)
|
provide('isPad', isTablet)
|
||||||
provide('isPad', isPad)
|
|
||||||
|
|
||||||
const checkIsPad = () => {
|
|
||||||
const ua = navigator.userAgent.toLowerCase()
|
|
||||||
const padRegex = /ipad|tablet|playbook|silk/i
|
|
||||||
const isPadUA = padRegex.test(ua)
|
|
||||||
const isPadWidth = window.innerWidth > 768 && window.innerWidth <= 1200
|
|
||||||
isPad.value = isPadUA || isPadWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkIsPad()
|
|
||||||
window.addEventListener('resize', checkIsPad)
|
|
||||||
router.replace('/')
|
router.replace('/')
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('是否平板-------', isTablet.value)
|
||||||
|
console.log('是否移动端-------', isMobile.value)
|
||||||
|
}, 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.removeEventListener('resize', checkIsPad)
|
|
||||||
})
|
|
||||||
const isZh = computed(() => {
|
const isZh = computed(() => {
|
||||||
return locale.value === 'CHINESE_SIMPLIFIED'
|
return locale.value === 'CHINESE_SIMPLIFIED'
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user