| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- <script setup lang="ts">
- // P16 下载页
- import { onMounted, computed, watch } from 'vue'
- import { storeToRefs } from 'pinia'
- import { useAppVersionStore } from '@/stores/appVersion'
- import { useI18nStore } from '@/stores/i18n'
- import { useI18n } from 'vue-i18n'
- import heroVisual from '@/assets/download/hero-visual.png'
- import iconApple from '@/assets/download/icon-apple.png'
- import iconAndroid from '@/assets/download/icon-android.png'
- const { t } = useI18n()
- const i18nStore = useI18nStore()
- const { current: locale } = storeToRefs(i18nStore)
- const versionStore = useAppVersionStore()
- const { android, ios, loading } = storeToRefs(versionStore)
- onMounted(() => {
- versionStore.fetchAll()
- })
- watch(locale, () => {
- versionStore.fetchAll()
- })
- const iosUrl = computed(() => ios.value?.downloadUrl || '')
- const androidUrl = computed(() => android.value?.downloadUrl || '')
- const latestInfo = computed(() => android.value ?? ios.value)
- function handleDownload(e: MouseEvent, url: string) {
- if (!url) e.preventDefault()
- }
- </script>
- <template>
- <main class="page-main">
- <section class="download-hero">
- <div class="page-container hero-inner">
- <div class="hero-content">
- <h1 class="hero-title" v-html="t('download.heroTitle').replace('\n', '<br>')"></h1>
- <p class="hero-desc">{{ t('download.appDesc') }}</p>
- <p v-if="latestInfo?.newVersion" class="version-tag">
- {{ t('download.latestVersion') }} v{{ latestInfo.newVersion }}
- <span v-if="latestInfo.publishTime" class="version-time">· {{ latestInfo.publishTime }}</span>
- </p>
- <div class="download-buttons">
- <a
- :href="iosUrl || '#'"
- :class="['dl-btn', 'dark-card', { 'is-disabled': !iosUrl }]"
- :target="iosUrl ? '_blank' : undefined"
- rel="noopener"
- @click="handleDownload($event, iosUrl)"
- >
- <img :src="iconApple" alt="iOS" class="dl-icon" />
- <span class="dl-text">{{ loading ? t('common.loading') : (iosUrl ? t('download.ios') : t('download.iosNotReleased')) }}</span>
- </a>
- <a
- :href="androidUrl || '#'"
- :class="['dl-btn', 'dark-card', { 'is-disabled': !androidUrl }]"
- :target="androidUrl ? '_blank' : undefined"
- rel="noopener"
- @click="handleDownload($event, androidUrl)"
- >
- <img :src="iconAndroid" alt="Android" class="dl-icon" />
- <span class="dl-text">{{ loading ? t('common.loading') : (androidUrl ? t('download.android') : t('download.androidNotReleased')) }}</span>
- </a>
- </div>
- <div v-if="latestInfo?.updateDescription" class="update-notes">
- <h3 class="update-title">{{ t('download.updateNotes') }}</h3>
- <p class="update-text">{{ latestInfo.updateDescription }}</p>
- </div>
- </div>
- <div class="hero-visual">
- <img :src="heroVisual" alt="iBit APP" class="hero-img" />
- </div>
- </div>
- </section>
- </main>
- </template>
- <style scoped>
- .download-hero {
- min-height: calc(100vh - var(--header-height));
- display: flex;
- align-items: center;
- background: var(--color-bg-primary);
- position: relative;
- overflow: hidden;
- }
- .download-hero::before {
- content: '';
- position: absolute;
- right: -10%;
- top: 50%;
- transform: translateY(-50%);
- width: 720px;
- height: 720px;
- border-radius: 50%;
- background: radial-gradient(circle, rgba(240,185,11,0.12) 0%, rgba(240,185,11,0.02) 45%, transparent 70%);
- pointer-events: none;
- }
- .hero-inner {
- display: grid;
- grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
- gap: var(--space-12);
- align-items: center;
- padding-top: var(--space-12);
- padding-bottom: var(--space-12);
- position: relative;
- z-index: 1;
- }
- .hero-title {
- font-size: 48px;
- font-weight: var(--fw-bold);
- color: var(--color-text-primary);
- line-height: 1.25;
- margin-bottom: var(--space-3);
- }
- .hero-desc {
- font-size: var(--text-body);
- color: var(--color-text-secondary);
- margin-bottom: var(--space-10);
- }
- .download-buttons {
- display: flex;
- gap: var(--space-4);
- flex-wrap: wrap;
- }
- .dl-btn {
- display: inline-flex;
- align-items: center;
- gap: var(--space-3);
- padding: var(--space-4) var(--space-8);
- border-radius: var(--radius-md);
- min-width: 180px;
- cursor: pointer;
- transition: border-color var(--transition), transform var(--transition);
- }
- .dl-btn:hover {
- border-color: var(--color-primary);
- transform: translateY(-2px);
- }
- .dl-icon {
- width: 24px;
- height: 24px;
- object-fit: contain;
- }
- .dl-text {
- font-size: var(--text-body);
- font-weight: var(--fw-semibold);
- color: var(--color-text-primary);
- }
- .dl-btn.is-disabled {
- opacity: 0.5;
- cursor: not-allowed;
- pointer-events: auto;
- }
- .dl-btn.is-disabled:hover {
- border-color: inherit;
- transform: none;
- }
- .version-tag {
- display: inline-flex;
- align-items: center;
- gap: var(--space-2);
- padding: var(--space-1) var(--space-3);
- margin-bottom: var(--space-4);
- border-radius: var(--radius-sm);
- background: rgba(240, 185, 11, 0.12);
- color: var(--color-primary);
- font-size: var(--text-caption);
- font-weight: var(--fw-semibold);
- }
- .version-time {
- color: var(--color-text-secondary);
- font-weight: var(--fw-regular);
- }
- .update-notes {
- margin-top: var(--space-8);
- padding: var(--space-5) var(--space-6);
- border-radius: var(--radius-md);
- background: var(--color-bg-secondary);
- max-width: 520px;
- }
- .update-title {
- font-size: var(--text-body);
- font-weight: var(--fw-semibold);
- color: var(--color-text-primary);
- margin-bottom: var(--space-2);
- }
- .update-text {
- font-size: var(--text-caption);
- color: var(--color-text-secondary);
- line-height: 1.6;
- white-space: pre-line;
- }
- /* Hero visual */
- .hero-visual {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .hero-img {
- width: 100%;
- max-width: 640px;
- object-fit: contain;
- filter: drop-shadow(0 20px 80px rgba(240,185,11,0.2));
- }
- /* ── 移动端 ── */
- @media (max-width: 960px) {
- .hero-title { font-size: 36px; }
- }
- @media (max-width: 768px) {
- .download-hero { min-height: unset; padding: var(--space-10) 0; }
- .hero-inner {
- grid-template-columns: 1fr;
- padding-top: var(--space-6);
- padding-bottom: var(--space-6);
- gap: var(--space-6);
- }
- .hero-title { font-size: 28px; }
- .download-buttons { flex-wrap: wrap; }
- .dl-btn { flex: 1; min-width: 140px; }
- .hero-img { max-width: 380px; }
- }
- </style>
|