feat: contact us
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 224 B |
BIN
src/assets/images/award/copy.png
Normal file
BIN
src/assets/images/award/copy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 269 B |
BIN
src/assets/images/award/mail.png
Normal file
BIN
src/assets/images/award/mail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 248 B |
75
src/components/Message/Message.vue
Normal file
75
src/components/Message/Message.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<!-- Message.vue -->
|
||||||
|
<template>
|
||||||
|
<transition name="fade">
|
||||||
|
<div v-if="visible" class="message" :class="`message--${type}`">
|
||||||
|
<div class="message-content">
|
||||||
|
<span>{{ message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
message: string
|
||||||
|
type: 'success' | 'error'
|
||||||
|
duration?: number // 可选,自动关闭时长,默认 3000ms
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
const visible = ref(true)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
visible.value = false
|
||||||
|
clearTimeout(timer)
|
||||||
|
setTimeout(() => emit('close'), 300) // 动画过渡后emit close
|
||||||
|
}, props.duration || 3000)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.message {
|
||||||
|
position: fixed;
|
||||||
|
top: 10%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
min-width: 300px;
|
||||||
|
padding: 1.2rem 1.6rem;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
color: #232323;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
z-index: 2000;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background-color: #f0f9eb;
|
||||||
|
border: 0.1rem solid #e1f3d8;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
border: 0.1rem solid #fde2e2;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
61
src/components/Message/message.ts
Normal file
61
src/components/Message/message.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// message.ts
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import { provide, inject } from 'vue' // 新增导入
|
||||||
|
import type { InjectionKey } from 'vue'
|
||||||
|
|
||||||
|
import Message from './Message.vue'
|
||||||
|
|
||||||
|
interface MessageOptions {
|
||||||
|
message: string
|
||||||
|
type: 'success' | 'error'
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageInstances: any[] = [] // 存储多个消息实例,防止重叠
|
||||||
|
|
||||||
|
const showMessage = (options: MessageOptions) => {
|
||||||
|
const app = createApp(Message, {
|
||||||
|
...options,
|
||||||
|
onClose: () => {
|
||||||
|
app.unmount()
|
||||||
|
const index = messageInstances.indexOf(app)
|
||||||
|
if (index > -1) messageInstances.splice(index, 1)
|
||||||
|
container.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const container = document.createElement('div')
|
||||||
|
document.body.appendChild(container)
|
||||||
|
app.mount(container)
|
||||||
|
|
||||||
|
messageInstances.push(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义 InjectionKey(TypeScript 类型安全)
|
||||||
|
const MessageKey: InjectionKey<{
|
||||||
|
success: (message: string, duration?: number) => void
|
||||||
|
error: (message: string, duration?: number) => void
|
||||||
|
}> = Symbol('message')
|
||||||
|
|
||||||
|
// 插件安装
|
||||||
|
const messagePlugin = {
|
||||||
|
install(app: App) {
|
||||||
|
const message = {
|
||||||
|
success: (message: string, duration?: number) =>
|
||||||
|
showMessage({ message, type: 'success', duration }),
|
||||||
|
error: (message: string, duration?: number) =>
|
||||||
|
showMessage({ message, type: 'error', duration })
|
||||||
|
}
|
||||||
|
app.provide(MessageKey, message) // 使用 provide 注入
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:composable 函数,用于在组件中注入
|
||||||
|
export const useMessage = () => {
|
||||||
|
const message = inject(MessageKey)
|
||||||
|
if (!message) throw new Error('useMessage must be used after messagePlugin is installed')
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
export default messagePlugin
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
export default {
|
export default {
|
||||||
AwardsPage: {
|
AwardsPage: {
|
||||||
submitApplication: 'Submit your Application',
|
submitApplication: 'Submit your Application',
|
||||||
|
contactUs: 'Contact Us',
|
||||||
|
contactHeader: 'We\'re here to help',
|
||||||
|
contactDesc:'For questions on eligibility, competition rules,\n judging criteria, or any other enquiries,\n feel free to reach out via our official email.',
|
||||||
|
sendEmail: 'Send Email',
|
||||||
|
copyMail: 'Successfully copied to clipboard!',
|
||||||
|
sendEmailFailed: 'Failed to open email client, we have copied the email to your clipboard.',
|
||||||
applicationDeadline: 'Application Deadline:15th July 2026',
|
applicationDeadline: 'Application Deadline:15th July 2026',
|
||||||
howToApply: 'How to Apply',
|
howToApply: 'How to Apply',
|
||||||
stepByStep: 'Step by step',
|
stepByStep: 'Step by step',
|
||||||
@@ -189,11 +195,12 @@ export default {
|
|||||||
video: `Video: Design process, 1080×1920 pixels (9:16 ratio), maximum 60 seconds`
|
video: `Video: Design process, 1080×1920 pixels (9:16 ratio), maximum 60 seconds`
|
||||||
},
|
},
|
||||||
// PDF 上传
|
// PDF 上传
|
||||||
uploadPdfTitle: 'How will you use AiDA in your design process?',
|
uploadPdfTitle:
|
||||||
|
'Please submit a single PDF with your design topic, mood board, and AiDA concept statement.',
|
||||||
clickToUploadPdf: 'Click to upload or drag and drop',
|
clickToUploadPdf: 'Click to upload or drag and drop',
|
||||||
pdfFileLimit: 'PDF file, max 20MB',
|
pdfFileLimit: 'PDF file, max 20MB',
|
||||||
// 视频上传
|
// 视频上传
|
||||||
uploadVideoTitle: 'How will you use AiDA in your design process?',
|
uploadVideoTitle: 'Please submit a video showcasing your creative process using AiDA.',
|
||||||
clickToUploadVideo: 'Click to upload or drag and drop',
|
clickToUploadVideo: 'Click to upload or drag and drop',
|
||||||
videoFileLimit: 'Video file (MP4), 1080p, max 100MB',
|
videoFileLimit: 'Video file (MP4), 1080p, max 100MB',
|
||||||
// 条款与条件
|
// 条款与条件
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
export default {
|
export default {
|
||||||
AwardsPage: {
|
AwardsPage: {
|
||||||
submitApplication: '提交申请',
|
submitApplication: '提交申请',
|
||||||
|
contactUs: '联系我们',
|
||||||
|
contactHeader: '有关比赛,欢迎咨询',
|
||||||
|
contactDesc:
|
||||||
|
'无论是报名资格、参赛细则、评审标准,还是其他比赛相关问题,欢迎通过以下邮箱联络我们:',
|
||||||
|
sendEmail: '发送邮件',
|
||||||
|
copyMail:'已成功复制到剪贴板',
|
||||||
|
sendEmailFailed: '无法打开邮件客户端,我们已将邮箱地址复制到您的剪贴板。',
|
||||||
applicationDeadline: '申请期限:2026年7月15日',
|
applicationDeadline: '申请期限:2026年7月15日',
|
||||||
howToApply: '申请方法',
|
howToApply: '申请方法',
|
||||||
stepByStep: '步骤指南',
|
stepByStep: '步骤指南',
|
||||||
@@ -62,7 +69,7 @@ export default {
|
|||||||
panelOfJudges: '终审评委团',
|
panelOfJudges: '终审评委团',
|
||||||
expertise: '权威阵容',
|
expertise: '权威阵容',
|
||||||
judgesHat: {
|
judgesHat: {
|
||||||
jae: 'Code‑Create 韩国分公司总监\nBesfxxk 创意总监',
|
jae: 'Code‑Create (韩国)分公司总监\nBesfxxk 创意总监',
|
||||||
diego: 'OnTheList(香港)\n联合创始人兼首席执行官',
|
diego: 'OnTheList(香港)\n联合创始人兼首席执行官',
|
||||||
gregory: 'Gabriela Hearst\n(意大利)高级设计师',
|
gregory: 'Gabriela Hearst\n(意大利)高级设计师',
|
||||||
vincenzo: '《南华早报》Style 杂志\n(香港)主编',
|
vincenzo: '《南华早报》Style 杂志\n(香港)主编',
|
||||||
@@ -178,13 +185,13 @@ export default {
|
|||||||
video: `视频:创作过程,分辨率1080×1920 像素\n(9:16 纵向比例),最长 60 秒`
|
video: `视频:创作过程,分辨率1080×1920 像素\n(9:16 纵向比例),最长 60 秒`
|
||||||
},
|
},
|
||||||
// PDF 上传
|
// PDF 上传
|
||||||
uploadPdfTitle: '您在设计过程中如何使用 AiDA?',
|
uploadPdfTitle: '请提供一份包含设计主题、灵感情绪板及 AiDA 创作概念说明的单一 PDF 文件。',
|
||||||
clickToUploadPdf: '点击选择或拖拽文件上传',
|
clickToUploadPdf: '点击选择或拖拽文件上传',
|
||||||
pdfFileLimit: 'PDF 文件,不超过 20MB',
|
pdfFileLimit: 'PDF 文件,不超过 20MB',
|
||||||
// 视频上传
|
// 视频上传
|
||||||
uploadVideoTitle: '您在设计过程中如何使用 AiDA?',
|
uploadVideoTitle: '请提供一段屏幕录制视频,展示您使用 AiDA 的创作过程。',
|
||||||
clickToUploadVideo: '点击选择或拖拽文件上传',
|
clickToUploadVideo: '点击选择或拖拽文件上传',
|
||||||
videoFileLimit: '视频文件(MP4, MOV),1080P,不超过100MB',
|
videoFileLimit: '视频文件(MP4),1080P,不超过100MB',
|
||||||
// 条款与条件
|
// 条款与条件
|
||||||
termsAndConditions: '参赛条款',
|
termsAndConditions: '参赛条款',
|
||||||
conditionFirst: '我确认所提交的作品均为原创,且由我本人独立创作。',
|
conditionFirst: '我确认所提交的作品均为原创,且由我本人独立创作。',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'normalize.css'
|
|||||||
import './assets/css/style.css'
|
import './assets/css/style.css'
|
||||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||||
import "virtual:svg-icons-register";
|
import "virtual:svg-icons-register";
|
||||||
|
import messagePlugin from './components/Message/message'
|
||||||
|
|
||||||
import i18n from "./lang/index";
|
import i18n from "./lang/index";
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ app.use(router)
|
|||||||
.use(store)
|
.use(store)
|
||||||
.component("SvgIcon", SvgIcon)
|
.component("SvgIcon", SvgIcon)
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
|
.use(messagePlugin)
|
||||||
.mount('#app')
|
.mount('#app')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ onBeforeUnmount(() => {
|
|||||||
.desc {
|
.desc {
|
||||||
font-family: 'Arial';
|
font-family: 'Arial';
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 2.4rem;
|
font-size: 2rem;
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
|
|||||||
@@ -3,21 +3,36 @@
|
|||||||
<div class="header-wrapper">
|
<div class="header-wrapper">
|
||||||
<div class="header flex align-center space-between">
|
<div class="header flex align-center space-between">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<img
|
<img src="@/assets/images/award/code_create_logo.png" class="logo" />
|
||||||
src="@/assets/images/award/code_create_logo.png"
|
|
||||||
class="logo"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="header-right flex align-center">
|
||||||
|
<div class="contact flex align-center" @click="handleContactClick">
|
||||||
|
<img src="@/assets/images/award/mail.png" class="contact-icon" />
|
||||||
|
<div class="text">{{ $t('AwardsPage.contactUs') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="gap" />
|
||||||
|
<div class="language flex align-center">
|
||||||
<div
|
<div
|
||||||
class="header-right flex align-center"
|
class="text"
|
||||||
@click="handleBtnClick"
|
:class="{ active: locale === 'CHINESE_SIMPLIFIED' }"
|
||||||
|
@click="handleChangeLanguage('CHINESE_SIMPLIFIED')"
|
||||||
>
|
>
|
||||||
|
中文
|
||||||
|
</div>
|
||||||
|
<span class="text">/</span>
|
||||||
|
<div
|
||||||
|
class="text"
|
||||||
|
:class="{ active: locale === 'ENGLISH' }"
|
||||||
|
@click="handleChangeLanguage('ENGLISH')"
|
||||||
|
>
|
||||||
|
EN
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gap" />
|
||||||
|
<div class="submit flex align-center" @click="handleBtnClick">
|
||||||
<div class="text">{{ btnText }}</div>
|
<div class="text">{{ btnText }}</div>
|
||||||
<img
|
<img src="@/assets/images/award/arrow.png" alt="" class="arrow" />
|
||||||
src="@/assets/images/award/arrow.png"
|
</div>
|
||||||
alt=""
|
|
||||||
class="arrow"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-placeholder"></div>
|
<div class="header-placeholder"></div>
|
||||||
@@ -25,86 +40,71 @@
|
|||||||
<router-view />
|
<router-view />
|
||||||
<div class="footer flex space-between align-center">
|
<div class="footer flex space-between align-center">
|
||||||
<div class="social-list flex">
|
<div class="social-list flex">
|
||||||
<a
|
<a href="https://xhslink.com/m/5Ony2FapizV" target="_blank">
|
||||||
href="https://xhslink.com/m/5Ony2FapizV"
|
<img src="@/assets/images/award/xiaohongshu.svg" alt="" />
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/xiaohongshu.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="https://www.linkedin.com/company/code-create-limited" target="_blank">
|
||||||
href="https://www.linkedin.com/company/code-create-limited"
|
<img src="@/assets/images/award/linkdin.svg" alt="" />
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/linkdin.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="https://www.facebook.com/CodeCreateAI" target="_blank">
|
||||||
href="https://www.facebook.com/CodeCreateAI"
|
<img src="@/assets/images/award/facebook.svg" alt="" />
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/facebook.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="https://www.tiktok.com/@aida_codecreate" target="_blank">
|
||||||
href="https://www.tiktok.com/@aida_codecreate"
|
<img src="@/assets/images/award/tiktok.svg" alt="" />
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/tiktok.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="javascript:void(0)" @click="showQRcode = true">
|
||||||
href="javascript:void(0)"
|
<img src="@/assets/images/award/weichat.svg" alt="" />
|
||||||
@click="showQRcode = true"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/images/award/weichat.svg"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="copyright">© Code-Create 2026</div>
|
<div class="copyright">© Code-Create 2026</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="qrcode-mask flex flex-center" v-if="showQRcode">
|
||||||
class="qrcode-mask flex flex-center"
|
|
||||||
v-if="showQRcode"
|
|
||||||
>
|
|
||||||
<div class="code-wrapper flex flex-col align-center">
|
<div class="code-wrapper flex flex-col align-center">
|
||||||
|
<img src="@/assets/images/award/close.svg" class="close-icon" @click="handleCloseQRcode" />
|
||||||
|
<div class="code-title">{{ $t('AwardsPage.wechatTitle') }}</div>
|
||||||
|
<img src="@/assets/images/award/qrcode.jpg" class="qrcode" />
|
||||||
|
<div class="tips">{{ $t('AwardsPage.wechatDesc') }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact-modal flex flex-center" v-if="showContactModal">
|
||||||
|
<div class="contact-modal-wrapper">
|
||||||
<img
|
<img
|
||||||
src="@/assets/images/award/close.svg"
|
src="@/assets/images/award/close.svg"
|
||||||
class="close-icon"
|
class="close-icon"
|
||||||
@click="handleCloseQRcode"
|
@click="showContactModal = false"
|
||||||
/>
|
/>
|
||||||
<div class="code-title">{{ $t('AwardsPage.wechatTitle') }}</div>
|
<div class="header">
|
||||||
<img
|
{{ $t('AwardsPage.contactHeader') }}
|
||||||
src="@/assets/images/award/qrcode.jpg"
|
</div>
|
||||||
class="qrcode"
|
<div class="desc">
|
||||||
/>
|
{{ $t('AwardsPage.contactDesc') }}
|
||||||
<div class="tips">{{ $t('AwardsPage.wechatDesc') }}</div>
|
</div>
|
||||||
|
<div class="mail flex align-center space-between">
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted } from 'vue'
|
import { ref, computed, watch, onMounted } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { getCookie } from '@/utils/cookie'
|
import { getCookie, setCookie } from '@/utils/cookie'
|
||||||
|
import { useMessage } from '@/components/Message/message'
|
||||||
|
|
||||||
const route = useRoute()
|
const { success, error } = useMessage()
|
||||||
const router = useRouter()
|
|
||||||
const { locale } = useI18n()
|
|
||||||
|
|
||||||
onMounted(() => {
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const { locale, t } = useI18n()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
// 初始化语言设置
|
// 初始化语言设置
|
||||||
const loginLanguage = localStorage.getItem('loginLanguage')
|
const loginLanguage = localStorage.getItem('loginLanguage')
|
||||||
if (loginLanguage) {
|
if (loginLanguage) {
|
||||||
@@ -115,27 +115,27 @@
|
|||||||
locale.value = userLanguage
|
locale.value = userLanguage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const showQRcode = ref(false)
|
const showQRcode = ref(false)
|
||||||
const handleCloseQRcode = () => {
|
const handleCloseQRcode = () => {
|
||||||
showQRcode.value = false
|
showQRcode.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
type BtnType = 'index' | 'form'
|
type BtnType = 'index' | 'form'
|
||||||
const btnType = ref<BtnType>('index')
|
const btnType = ref<BtnType>('index')
|
||||||
const btnText = computed(() => {
|
const btnText = computed(() => {
|
||||||
if (btnType.value === 'index') {
|
if (btnType.value === 'index') {
|
||||||
return locale.value === 'CHINESE_SIMPLIFIED' ? '提交申请' : 'Submit your Application'
|
return locale.value === 'CHINESE_SIMPLIFIED' ? '提交申请' : 'Submit your Application'
|
||||||
}
|
}
|
||||||
if (btnType.value === 'form') {
|
if (btnType.value === 'form') {
|
||||||
return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back to Introduction'
|
return locale.value === 'CHINESE_SIMPLIFIED' ? '赛事介绍' : 'Back to Introduction'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
val => {
|
(val) => {
|
||||||
if (val.includes('contestants')) {
|
if (val.includes('contestants')) {
|
||||||
btnType.value = 'form'
|
btnType.value = 'form'
|
||||||
} else {
|
} else {
|
||||||
@@ -145,20 +145,114 @@
|
|||||||
{
|
{
|
||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const handleBtnClick = () => {
|
|
||||||
|
|
||||||
|
const showContactModal = ref(false)
|
||||||
|
const handleContactClick = () => {
|
||||||
|
showContactModal.value = true
|
||||||
|
}
|
||||||
|
const handleContactUs = () => {
|
||||||
|
const email = 'awards2026@code-createcom.hk'
|
||||||
|
const mailtoLink = `mailto:${email}`
|
||||||
|
|
||||||
|
// 检测是否有邮件客户端
|
||||||
|
let hasEmailClient = false
|
||||||
|
let blurTimer = null
|
||||||
|
|
||||||
|
// 监听窗口失去焦点事件(如果打开了邮件客户端,窗口会失去焦点)
|
||||||
|
const handleBlur = () => {
|
||||||
|
hasEmailClient = true
|
||||||
|
if (blurTimer) {
|
||||||
|
clearTimeout(blurTimer)
|
||||||
|
}
|
||||||
|
window.removeEventListener('blur', handleBlur)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('blur', handleBlur)
|
||||||
|
|
||||||
|
// 尝试打开邮件客户端(使用临时链接元素,避免页面跳转)
|
||||||
|
try {
|
||||||
|
const tempLink = document.createElement('a')
|
||||||
|
tempLink.href = mailtoLink
|
||||||
|
tempLink.style.display = 'none'
|
||||||
|
document.body.appendChild(tempLink)
|
||||||
|
tempLink.click()
|
||||||
|
document.body.removeChild(tempLink)
|
||||||
|
} catch (e) {
|
||||||
|
// 如果打开失败,直接复制邮箱
|
||||||
|
hasEmailClient = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置超时检测,如果500ms内窗口没有失去焦点,说明没有邮件客户端
|
||||||
|
blurTimer = setTimeout(() => {
|
||||||
|
window.removeEventListener('blur', handleBlur)
|
||||||
|
if (!hasEmailClient) {
|
||||||
|
// 没有邮件客户端,复制邮箱到剪贴板
|
||||||
|
console.log('没有客户端')
|
||||||
|
|
||||||
|
copyToClipboard(email)
|
||||||
|
error(t('AwardsPage.sendEmailFailed'))
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyToClipboard = (text) => {
|
||||||
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
|
// 使用现代 Clipboard API
|
||||||
|
navigator.clipboard.writeText(text).catch(() => {
|
||||||
|
// 如果失败,使用传统方法
|
||||||
|
fallbackCopyToClipboard(text)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 使用传统方法
|
||||||
|
fallbackCopyToClipboard(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackCopyToClipboard = (text) => {
|
||||||
|
const textArea = document.createElement('textarea')
|
||||||
|
textArea.value = text
|
||||||
|
textArea.style.position = 'fixed'
|
||||||
|
textArea.style.left = '-999999px'
|
||||||
|
textArea.style.top = '-999999px'
|
||||||
|
document.body.appendChild(textArea)
|
||||||
|
textArea.focus()
|
||||||
|
textArea.select()
|
||||||
|
try {
|
||||||
|
document.execCommand('copy')
|
||||||
|
} catch (err) {
|
||||||
|
console.error('复制失败:', err)
|
||||||
|
}
|
||||||
|
document.body.removeChild(textArea)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickCopyEmail = () => {
|
||||||
|
copyToClipboard('awards2026@code-createcom.hk')
|
||||||
|
success(t('AwardsPage.copyMail'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChangeLanguage = (language) => {
|
||||||
|
if (language === 'CHINESE_SIMPLIFIED') {
|
||||||
|
locale.value = 'CHINESE_SIMPLIFIED'
|
||||||
|
} else {
|
||||||
|
locale.value = 'ENGLISH'
|
||||||
|
}
|
||||||
|
localStorage.setItem('loginLanguage', language)
|
||||||
|
setCookie('language', language)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBtnClick = () => {
|
||||||
const lang = route.params.lang ? `/${route.params.lang}` : ''
|
const lang = route.params.lang ? `/${route.params.lang}` : ''
|
||||||
if (btnType.value === 'index') {
|
if (btnType.value === 'index') {
|
||||||
router.push(`${lang}/contestants`)
|
router.push(`${lang}/contestants`)
|
||||||
} else {
|
} else {
|
||||||
router.push(`${lang}/`)
|
router.push(`${lang}/`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.award-container {
|
.award-container {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
// 隐藏滚动条箭头,只显示滚动条本体
|
// 隐藏滚动条箭头,只显示滚动条本体
|
||||||
@@ -171,8 +265,8 @@
|
|||||||
}
|
}
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
}
|
}
|
||||||
.header-wrapper {
|
.header-wrapper {
|
||||||
.header-placeholder {
|
.header-placeholder {
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
}
|
}
|
||||||
@@ -180,7 +274,7 @@
|
|||||||
height: 8rem;
|
height: 8rem;
|
||||||
background-color: #232323;
|
background-color: #232323;
|
||||||
padding-left: 21.5rem;
|
padding-left: 21.5rem;
|
||||||
padding-right: 8.6rem;
|
padding-right: 21.4rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -193,12 +287,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.header-right {
|
.header-right {
|
||||||
column-gap: 1rem;
|
column-gap: 1.2rem;
|
||||||
cursor: pointer;
|
color: #fff;
|
||||||
|
.gap {
|
||||||
|
width: 0.05rem;
|
||||||
|
height: 2.4rem;
|
||||||
|
background-color: #979797;
|
||||||
|
// border: 0.05rem solid #979797
|
||||||
|
}
|
||||||
.text {
|
.text {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
.contact {
|
||||||
|
column-gap: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.language {
|
||||||
|
column-gap: 0.8rem;
|
||||||
|
padding: 0 1.25rem;
|
||||||
|
span.text {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
div.text {
|
||||||
|
cursor: pointer;
|
||||||
|
&.active {
|
||||||
|
color: #979797;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.submit {
|
||||||
|
cursor: pointer;
|
||||||
|
column-gap: 1rem;
|
||||||
.arrow {
|
.arrow {
|
||||||
width: 2.4rem;
|
width: 2.4rem;
|
||||||
height: 2.4rem;
|
height: 2.4rem;
|
||||||
@@ -206,7 +325,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.footer {
|
}
|
||||||
|
.footer {
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
padding-left: 21.5rem;
|
padding-left: 21.5rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -225,8 +345,8 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.qrcode-mask {
|
.qrcode-mask {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -266,5 +386,74 @@
|
|||||||
color: #585858;
|
color: #585858;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.contact-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.45);
|
||||||
|
z-index: 9;
|
||||||
|
.contact-modal-wrapper {
|
||||||
|
width: 60rem;
|
||||||
|
height: 49.4rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
padding: 8.2rem 8.6rem;
|
||||||
|
position: relative;
|
||||||
|
.close-icon {
|
||||||
|
width: 2.4rem;
|
||||||
|
height: 2.4rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'PoppinsBold';
|
||||||
|
font-size: 3rem;
|
||||||
|
color: #232323;
|
||||||
|
margin-bottom: 2.2rem;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Arial';
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
color: #585858;
|
||||||
|
white-space: pre-line;
|
||||||
|
line-height: 2rem;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
}
|
||||||
|
.mail {
|
||||||
|
height: 6rem;
|
||||||
|
line-height: 6rem;
|
||||||
|
background-color: #f6f6f4;
|
||||||
|
border: 0.2rem solid #e6e6e6;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
padding: 0 2rem 0 3rem;
|
||||||
|
font-family: 'ArialBold';
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 5.3rem;
|
||||||
|
.copy-icon {
|
||||||
|
width: 2.4rem;
|
||||||
|
height: 2.4rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.send-btn {
|
||||||
|
height: 4.2rem;
|
||||||
|
line-height: 4.2rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #232323;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
font-family: 'ArialBold';
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed,onMounted } 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'
|
||||||
@@ -56,6 +56,10 @@
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
router.replace('/')
|
||||||
|
})
|
||||||
|
|
||||||
const isZh = computed(() => {
|
const isZh = computed(() => {
|
||||||
return locale.value === 'CHINESE_SIMPLIFIED'
|
return locale.value === 'CHINESE_SIMPLIFIED'
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user