269 lines
13 KiB
Vue
269 lines
13 KiB
Vue
<script setup lang="ts">
|
|
import type { AppInfo } from "~/types";
|
|
import { apps } from "~/data/apps";
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const { t, locale } = useI18n();
|
|
|
|
// 直接从数据文件获取应用详情
|
|
const app = computed(() => apps.find(a => a.id === route.params.id));
|
|
|
|
// 如果应用不存在,跳转回首页
|
|
if (!app.value) {
|
|
navigateTo("/");
|
|
}
|
|
|
|
// 下载处理
|
|
async function handleDownload(type: "ios" | "android" | "h5") {
|
|
if (!app.value) {
|
|
return;
|
|
}
|
|
|
|
const url = app.value.downloads[type];
|
|
|
|
if (!url) {
|
|
return;
|
|
}
|
|
|
|
if (type === "h5") {
|
|
navigateTo(url, { external: true, open: { target: "_blank" } });
|
|
}
|
|
else {
|
|
navigateTo(url, { external: true });
|
|
}
|
|
|
|
await $fetch(`/api/track/${type}`, {
|
|
method: "POST",
|
|
body: { appId: app.value.id },
|
|
});
|
|
}
|
|
|
|
// 返回首页
|
|
function goBack() {
|
|
router.back();
|
|
}
|
|
|
|
// SEO
|
|
useHead({
|
|
title: app.value ? `${app.value.name} - Riwa App Store` : "Riwa App Store",
|
|
meta: [
|
|
{
|
|
name: "description",
|
|
content: app.value?.shortDescription[locale.value as "zh-CN" | "en-US"] || "",
|
|
},
|
|
],
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="app" class="min-h-screen relative overflow-hidden bg-gradient-to-br from-gray-50 via-blue-50/30 to-purple-50/30 dark:from-gray-950 dark:via-blue-950/20 dark:to-purple-950/20">
|
|
<!-- 科技感网格背景 -->
|
|
<div class="fixed inset-0 opacity-30 dark:opacity-20 pointer-events-none">
|
|
<div class="absolute inset-0" style="background-image: linear-gradient(rgba(99, 102, 241, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(99, 102, 241, 0.1) 1px, transparent 1px); background-size: 50px 50px;" />
|
|
</div>
|
|
|
|
<!-- 动态发光球体背景 -->
|
|
<div class="fixed inset-0 pointer-events-none overflow-hidden">
|
|
<div class="absolute top-1/4 -left-48 w-96 h-96 bg-primary-500/20 rounded-full blur-3xl animate-pulse" />
|
|
<div class="absolute bottom-1/4 -right-48 w-96 h-96 bg-purple-500/20 rounded-full blur-3xl animate-pulse" style="animation-delay: 1s;" />
|
|
</div>
|
|
|
|
<UContainer class="relative z-10">
|
|
<!-- Header -->
|
|
<header class="sticky top-0 z-50">
|
|
<div class="flex items-center gap-4 py-4">
|
|
<UButton
|
|
icon="i-heroicons-arrow-left"
|
|
color="neutral"
|
|
variant="ghost"
|
|
class="hover:scale-110 transition-transform duration-300"
|
|
@click="goBack"
|
|
/>
|
|
<h1 class="text-xl font-bold bg-gradient-to-r from-gray-900 via-primary-600 to-purple-600 dark:from-white dark:via-primary-400 dark:to-purple-400 bg-clip-text text-transparent">
|
|
{{ app.name }}
|
|
</h1>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Main Content -->
|
|
<div class="py-8 animate-fade-in">
|
|
<!-- App Header -->
|
|
<div class="flex items-start gap-6 mb-8 group">
|
|
<div class="relative">
|
|
<div class="absolute -inset-2 bg-gradient-to-r from-primary-500 via-purple-500 to-blue-500 rounded-3xl opacity-50 blur-xl group-hover:opacity-100 transition-all duration-500 animate-pulse" />
|
|
<div class="size-24 rounded-3xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-4xl shrink-0 shadow-2xl shadow-blue-500/50 relative overflow-hidden group-hover:scale-110 transition-all duration-500 p-3">
|
|
<div class="absolute inset-0 bg-gradient-to-tr from-white/0 via-white/30 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-700" />
|
|
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-2xl">
|
|
</div>
|
|
</div>
|
|
<div class="flex-1">
|
|
<h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
|
{{ app.name }}
|
|
</h2>
|
|
<p class="text-lg text-gray-600 dark:text-gray-400 mb-4">
|
|
{{ app.shortDescription[locale as 'zh-CN' | 'en-US'] }}
|
|
</p>
|
|
<div class="flex items-center gap-4 text-sm text-gray-500 dark:text-gray-500">
|
|
<span>{{ locale === 'zh-CN' ? '版本' : 'Version' }} {{ app.version }}</span>
|
|
<span>•</span>
|
|
<span>{{ app.releaseDate }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Download Buttons -->
|
|
<div class="grid md:grid-cols-3 gap-4 mb-8">
|
|
<div v-if="app.downloads.ios" class="relative group">
|
|
<div class="absolute -inset-1 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UButton
|
|
icon="i-heroicons-device-phone-mobile"
|
|
size="xl"
|
|
block
|
|
class="relative transition-all duration-300 hover:shadow-2xl hover:shadow-blue-500/50 hover:-translate-y-2"
|
|
@click="handleDownload('ios')"
|
|
>
|
|
<div class="text-left w-full">
|
|
<div class="font-semibold text-base">
|
|
iOS
|
|
</div>
|
|
<div v-if="app.size?.ios" class="text-xs opacity-80">
|
|
{{ app.size.ios }}
|
|
</div>
|
|
</div>
|
|
</UButton>
|
|
</div>
|
|
<div v-if="app.downloads.android" class="relative group">
|
|
<div class="absolute -inset-1 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UButton
|
|
icon="i-heroicons-device-tablet"
|
|
size="xl"
|
|
block
|
|
class="relative transition-all duration-300 hover:shadow-2xl hover:shadow-blue-500/50 hover:-translate-y-2"
|
|
@click="handleDownload('android')"
|
|
>
|
|
<div class="text-left w-full">
|
|
<div class="font-semibold text-base">
|
|
Android
|
|
</div>
|
|
<div v-if="app.size?.android" class="text-xs opacity-80">
|
|
{{ app.size.android }}
|
|
</div>
|
|
</div>
|
|
</UButton>
|
|
</div>
|
|
<div v-if="app.downloads.h5" class="relative group">
|
|
<div class="absolute -inset-1 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UButton
|
|
icon="i-heroicons-globe-alt"
|
|
size="xl"
|
|
block
|
|
class="relative transition-all duration-300 hover:shadow-2xl hover:shadow-blue-500/50 hover:-translate-y-2"
|
|
@click="handleDownload('h5')"
|
|
>
|
|
<div class="text-left w-full">
|
|
<div class="font-semibold text-base">
|
|
Web
|
|
</div>
|
|
<div class="text-xs opacity-80">
|
|
PWA
|
|
</div>
|
|
</div>
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
|
<div class="relative group">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-primary-500 to-purple-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-primary-500/20 transition-all duration-500 hover:-translate-y-1">
|
|
<div class="text-center">
|
|
<div class="text-3xl font-bold bg-gradient-to-r from-primary-500 to-purple-500 bg-clip-text text-transparent">
|
|
{{ app.stats.total.toLocaleString() }}
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
|
{{ locale === 'zh-CN' ? '总下载' : 'Total Downloads' }}
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
<div class="relative group">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-purple-500/20 transition-all duration-500 hover:-translate-y-1">
|
|
<div class="text-center">
|
|
<div class="text-3xl font-bold bg-gradient-to-r from-purple-500 to-pink-500 bg-clip-text text-transparent">
|
|
{{ app.stats.today.toLocaleString() }}
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
|
{{ locale === 'zh-CN' ? '今日下载' : 'Today' }}
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
<div class="relative group">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-blue-500/20 transition-all duration-500 hover:-translate-y-1">
|
|
<div class="text-center">
|
|
<div class="text-3xl font-bold bg-gradient-to-r from-blue-500 to-cyan-500 bg-clip-text text-transparent">
|
|
{{ app.stats.ios.toLocaleString() }}
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
|
iOS
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
<div class="relative group">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-indigo-500 to-purple-500 rounded-xl opacity-0 group-hover:opacity-100 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-indigo-500/20 transition-all duration-500 hover:-translate-y-1">
|
|
<div class="text-center">
|
|
<div class="text-3xl font-bold bg-gradient-to-r from-indigo-500 to-purple-500 bg-clip-text text-transparent">
|
|
{{ app.stats.android.toLocaleString() }}
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
|
Android
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<div class="relative group mb-8">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-primary-500 via-purple-500 to-blue-500 rounded-xl opacity-0 group-hover:opacity-30 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-primary-500/10 transition-all duration-500">
|
|
<h3 class="text-xl font-semibold bg-gradient-to-r from-gray-900 via-primary-600 to-purple-600 dark:from-white dark:via-primary-400 dark:to-purple-400 bg-clip-text text-transparent mb-4">
|
|
{{ locale === 'zh-CN' ? '应用介绍' : 'Description' }}
|
|
</h3>
|
|
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
|
{{ app.description[locale as 'zh-CN' | 'en-US'] }}
|
|
</p>
|
|
</UCard>
|
|
</div>
|
|
|
|
<!-- What's New -->
|
|
<div class="relative group">
|
|
<div class="absolute -inset-0.5 bg-gradient-to-r from-purple-500 via-pink-500 to-rose-500 rounded-xl opacity-0 group-hover:opacity-30 blur transition-all duration-500" />
|
|
<UCard class="relative backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 hover:shadow-xl hover:shadow-purple-500/10 transition-all duration-500">
|
|
<h3 class="text-xl font-semibold bg-gradient-to-r from-gray-900 via-purple-600 to-pink-600 dark:from-white dark:via-purple-400 dark:to-pink-400 bg-clip-text text-transparent mb-4">
|
|
{{ locale === 'zh-CN' ? '更新内容' : "What's New" }}
|
|
</h3>
|
|
<ul class="space-y-3">
|
|
<li
|
|
v-for="(note, index) in app.releaseNotes[locale as 'zh-CN' | 'en-US']"
|
|
:key="index"
|
|
class="flex items-start gap-3 text-gray-700 dark:text-gray-300 animate-fade-in-up"
|
|
:style="`animation-delay: ${index * 0.1}s`"
|
|
>
|
|
<span class="text-primary-500 mt-1 text-lg animate-pulse">•</span>
|
|
<span>{{ note }}</span>
|
|
</li>
|
|
</ul>
|
|
</UCard>
|
|
</div>
|
|
</div>
|
|
</UContainer>
|
|
</div>
|
|
</template>
|