|
|
@@ -1,6 +1,49 @@
|
|
|
<template>
|
|
|
<footer class="border-t border-okx-border/70 bg-okx-black">
|
|
|
<div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
|
|
|
+ <div class="mb-8 flex justify-start">
|
|
|
+ <div ref="footerLanguageMenuRef" class="relative flex flex-col items-start"
|
|
|
+ @mouseenter="footerLanguageMenuOpen = true" @mouseleave="footerLanguageMenuOpen = false">
|
|
|
+ <button type="button"
|
|
|
+ class="flex items-center gap-3 rounded-none border border-white/30 bg-white/10 px-5 py-3 text-sm font-semibold text-okx-text transition hover:border-white/60 hover:bg-white/15"
|
|
|
+ aria-haspopup="true" :aria-expanded="footerLanguageMenuOpen" aria-controls="footer-language-menu"
|
|
|
+ @click.stop="toggleFooterLanguageMenu" @keydown.escape.prevent="footerLanguageMenuOpen = false">
|
|
|
+ <svg class="h-5 w-5 text-okx-text" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <path
|
|
|
+ d="M12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9Zm0 0c3 0 5-4.029 5-9s-2-9-5-9-5 4.029-5 9 2 9 5 9Zm-8-9h16"
|
|
|
+ stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
|
|
+ </svg>
|
|
|
+ <span>{{ footerCurrentLanguage.label }}</span>
|
|
|
+ <svg class="h-3.5 w-3.5 text-okx-text" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
|
|
+ stroke-linejoin="round" />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <Transition enter-active-class="transition duration-150 ease-out" enter-from-class="opacity-0 -translate-y-1"
|
|
|
+ enter-to-class="opacity-100 translate-y-0" leave-active-class="transition duration-100 ease-in"
|
|
|
+ leave-from-class="opacity-100 translate-y-0" leave-to-class="opacity-0 -translate-y-1">
|
|
|
+ <div v-if="footerLanguageMenuOpen" id="footer-language-menu"
|
|
|
+ class="absolute bottom-full w-64 border border-white/30 bg-okx-black/95 p-5 text-sm text-okx-text shadow-2xl backdrop-blur-lg">
|
|
|
+ <div class="pb-2 text-xs font-semibold uppercase tracking-wide text-white/60">{{ t('header.language') }}</div>
|
|
|
+ <div class="max-h-60 space-y-1 overflow-y-auto pr-1 footer-scrollbar">
|
|
|
+ <button v-for="lang in languages" :key="lang.id" type="button"
|
|
|
+ class="flex w-full items-center justify-between rounded-xl px-3 py-1.5 text-left transition hover:bg-white/10"
|
|
|
+ :class="{ 'bg-white/10': lang.id === footerCurrentLanguage.id }"
|
|
|
+ @click.stop="selectFooterLanguage(lang)">
|
|
|
+ <span :class="lang.id === footerCurrentLanguage.id ? 'text-white' : 'text-white/60'">{{ lang.label}}</span>
|
|
|
+ <svg v-if="lang.id === footerCurrentLanguage.id" class="h-3.5 w-3.5 text-white" viewBox="0 0 16 16"
|
|
|
+ fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <path d="M3.5 8l2.5 2.5L12.5 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
|
|
+ stroke-linejoin="round" />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Transition>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div class="grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-3">
|
|
|
<div v-for="group in groups" :key="group.title">
|
|
|
<h3 class="mb-4 font-display text-base font-semibold text-white">{{ group.title }}</h3>
|
|
|
@@ -11,11 +54,13 @@
|
|
|
</ul>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="mt-10 flex flex-col items-center justify-between gap-4 border-t border-okx-border/70 pt-6 sm:flex-row">
|
|
|
- <p class="text-xs text-okx-text/60">© {{ new Date().getFullYear() }} OKX Web3</p>
|
|
|
+ <div
|
|
|
+ class="mt-10 flex flex-col items-center justify-between gap-4 border-t border-okx-border/70 pt-6 sm:flex-row">
|
|
|
+ <p class="text-xs text-okx-text/60">{{ t('footer.copyright', { year: new Date().getFullYear() }) }}</p>
|
|
|
<div class="flex items-center gap-4">
|
|
|
- <a :href="url('/help/okx-web3-ecosystem-privacy-policy')" class="text-xs text-okx-text/60 hover:text-white">隐私政策</a>
|
|
|
- <a :href="url('/help/category/terms-of-agreement')" class="text-xs text-okx-text/60 hover:text-white">服务条款</a>
|
|
|
+ <a :href="url('/help/okx-web3-ecosystem-privacy-policy')"
|
|
|
+ class="text-xs text-okx-text/60 hover:text-white">{{ t('footer.privacyPolicy') }}</a>
|
|
|
+ <a :href="url('/help/category/terms-of-agreement')" class="text-xs text-okx-text/60 hover:text-white">{{ t('footer.termsOfService') }}</a>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -23,50 +68,115 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
+import { onBeforeUnmount, onMounted, ref, computed } from 'vue'
|
|
|
+import { useI18n } from 'vue-i18n'
|
|
|
+import { languageOptions, type LanguageOption } from '@/constants/languages'
|
|
|
+import { useLocale } from '@/composables/useLocale'
|
|
|
+import type { LocaleKey } from '@/i18n'
|
|
|
+
|
|
|
+const { t } = useI18n()
|
|
|
+const { currentLocale, setLocale } = useLocale()
|
|
|
+
|
|
|
const base = 'https://web3.okx.com/zh-hans'
|
|
|
const url = (path: string) => `${base}${path}`
|
|
|
|
|
|
interface GroupItem { text: string; href: string }
|
|
|
interface Group { title: string; items: GroupItem[] }
|
|
|
|
|
|
-const groups: Group[] = [
|
|
|
+const groups = computed<Group[]>(() => [
|
|
|
{
|
|
|
- title: '关于 OKX Wallet',
|
|
|
+ title: t('footer.about.title'),
|
|
|
items: [
|
|
|
- { text: '下载', href: url('/download') },
|
|
|
- { text: '学院', href: url('/learn') },
|
|
|
- { text: '关于我们', href: url('/about.html') },
|
|
|
- { text: '就业机会', href: url('/join-us') },
|
|
|
- { text: '联系我们', href: url('/contact-us.html') },
|
|
|
- { text: '服务条款', href: url('/help/category/terms-of-agreement') },
|
|
|
- { text: '隐私政策', href: url('/help/okx-web3-ecosystem-privacy-policy') }
|
|
|
+ { text: t('footer.about.download'), href: url('/download') },
|
|
|
+ { text: t('footer.about.learn'), href: url('/learn') },
|
|
|
+ { text: t('footer.about.aboutUs'), href: url('/about.html') },
|
|
|
+ { text: t('footer.about.careers'), href: url('/join-us') },
|
|
|
+ { text: t('footer.about.contact'), href: url('/contact-us.html') },
|
|
|
+ { text: t('footer.about.terms'), href: url('/help/category/terms-of-agreement') },
|
|
|
+ { text: t('footer.about.privacy'), href: url('/help/okx-web3-ecosystem-privacy-policy') }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
- title: '产品',
|
|
|
+ title: t('footer.products.title'),
|
|
|
items: [
|
|
|
- { text: '行情', href: url('/token') },
|
|
|
- { text: '币币兑换', href: url('/dex-swap') },
|
|
|
- { text: '市场', href: url('/marketplace/nft') },
|
|
|
- { text: '赚币', href: url('/earn') },
|
|
|
- { text: '发现', href: url('/discover') },
|
|
|
- { text: '开发者中心', href: url('/build') },
|
|
|
- { text: '浏览器', href: url('/explorer') },
|
|
|
- { text: '安全', href: url('/security') }
|
|
|
+ { text: t('footer.products.market'), href: url('/token') },
|
|
|
+ { text: t('footer.products.swap'), href: url('/dex-swap') },
|
|
|
+ { text: t('footer.products.marketplace'), href: url('/marketplace/nft') },
|
|
|
+ { text: t('footer.products.earn'), href: url('/earn') },
|
|
|
+ { text: t('footer.products.discover'), href: url('/discover') },
|
|
|
+ { text: t('footer.products.build'), href: url('/build') },
|
|
|
+ { text: t('footer.products.explorer'), href: url('/explorer') },
|
|
|
+ { text: t('footer.products.security'), href: url('/security') }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
- title: '用户支持',
|
|
|
+ title: t('footer.support.title'),
|
|
|
items: [
|
|
|
- { text: '帮助中心', href: url('/help') },
|
|
|
- { text: '官方渠道验证', href: url('/support-center/channel-verification') },
|
|
|
- { text: '公告', href: url('/help/category/announcements') },
|
|
|
- { text: 'DEX 费率标准', href: url('/dex-fees') },
|
|
|
- { text: '加入 OKX 社群', href: url('/community') },
|
|
|
- { text: '比特币钱包', href: url('/wallet/bitcoin') },
|
|
|
- { text: '以太坊钱包', href: url('/wallet/ethereum') },
|
|
|
- { text: 'Solana 钱包', href: url('/wallet/solana') }
|
|
|
+ { text: t('footer.support.help'), href: url('/help') },
|
|
|
+ { text: t('footer.support.verification'), href: url('/support-center/channel-verification') },
|
|
|
+ { text: t('footer.support.announcements'), href: url('/help/category/announcements') },
|
|
|
+ { text: t('footer.support.fees'), href: url('/dex-fees') },
|
|
|
+ { text: t('footer.support.community'), href: url('/community') },
|
|
|
+ { text: t('footer.support.bitcoinWallet'), href: url('/wallet/bitcoin') },
|
|
|
+ { text: t('footer.support.ethereumWallet'), href: url('/wallet/ethereum') },
|
|
|
+ { text: t('footer.support.solanaWallet'), href: url('/wallet/solana') }
|
|
|
]
|
|
|
}
|
|
|
-]
|
|
|
+])
|
|
|
+
|
|
|
+const languages = languageOptions
|
|
|
+const footerLanguageMenuOpen = ref(false)
|
|
|
+const footerLanguageMenuRef = ref<HTMLElement | null>(null)
|
|
|
+
|
|
|
+// 根据当前语言代码找到对应的语言选项
|
|
|
+const footerCurrentLanguage = computed(() => {
|
|
|
+ return languages.find((lang) => lang.id === currentLocale.value) || languages[2] // 默认英语
|
|
|
+})
|
|
|
+
|
|
|
+const toggleFooterLanguageMenu = () => {
|
|
|
+ footerLanguageMenuOpen.value = !footerLanguageMenuOpen.value
|
|
|
+}
|
|
|
+
|
|
|
+const selectFooterLanguage = (lang: LanguageOption) => {
|
|
|
+ setLocale(lang.id as LocaleKey)
|
|
|
+ footerLanguageMenuOpen.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const handleFooterClickOutside = (event: MouseEvent) => {
|
|
|
+ if (footerLanguageMenuRef.value && !footerLanguageMenuRef.value.contains(event.target as Node)) {
|
|
|
+ footerLanguageMenuOpen.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ document.addEventListener('click', handleFooterClickOutside)
|
|
|
+})
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ document.removeEventListener('click', handleFooterClickOutside)
|
|
|
+})
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.footer-scrollbar {
|
|
|
+ scrollbar-width: thin;
|
|
|
+ scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-scrollbar::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-scrollbar::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-scrollbar::-webkit-scrollbar-thumb {
|
|
|
+ background: rgba(255, 255, 255, 0.25);
|
|
|
+ border-radius: 9999px;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.4);
|
|
|
+}
|
|
|
+</style>
|