Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -1,72 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import type { AppInfo } from '~/types'
|
||||
import { apps } from '~/data/apps'
|
||||
import type { AppInfo } from "~/types";
|
||||
import { apps } from "~/data/apps";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t, locale } = useI18n()
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
// 直接从数据文件获取应用详情
|
||||
const app = computed(() => apps.find(a => a.id === route.params.id))
|
||||
const app = computed(() => apps.find(a => a.id === route.params.id));
|
||||
|
||||
// 如果应用不存在,跳转回首页
|
||||
if (!app.value) {
|
||||
navigateTo('/')
|
||||
navigateTo("/");
|
||||
}
|
||||
|
||||
// 下载处理
|
||||
async function handleDownload(type: 'ios' | 'android' | 'h5') {
|
||||
async function handleDownload(type: "ios" | "android" | "h5") {
|
||||
if (!app.value) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const url = app.value.downloads[type]
|
||||
const url = app.value.downloads[type];
|
||||
|
||||
if (!url) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'h5') {
|
||||
navigateTo(url, { external: true, open: { target: '_blank' } })
|
||||
if (type === "h5") {
|
||||
navigateTo(url, { external: true, open: { target: "_blank" } });
|
||||
}
|
||||
else {
|
||||
navigateTo(url, { external: true })
|
||||
navigateTo(url, { external: true });
|
||||
}
|
||||
|
||||
await $fetch(`/api/track/${type}`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: { appId: app.value.id },
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 返回首页
|
||||
function goBack() {
|
||||
router.back()
|
||||
router.back();
|
||||
}
|
||||
|
||||
// SEO
|
||||
useHead({
|
||||
title: app.value ? `${app.value.name} - Riwa App Store` : 'Riwa App Store',
|
||||
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'] || '',
|
||||
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="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>
|
||||
<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>
|
||||
<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">
|
||||
@@ -91,10 +91,10 @@ useHead({
|
||||
<!-- 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>
|
||||
<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"></div>
|
||||
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-2xl" />
|
||||
<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">
|
||||
@@ -115,7 +115,7 @@ useHead({
|
||||
<!-- 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"></div>
|
||||
<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"
|
||||
@@ -123,16 +123,18 @@ useHead({
|
||||
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 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>
|
||||
</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"></div>
|
||||
<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"
|
||||
@@ -140,16 +142,18 @@ useHead({
|
||||
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 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>
|
||||
</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"></div>
|
||||
<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"
|
||||
@@ -158,7 +162,9 @@ useHead({
|
||||
@click="handleDownload('h5')"
|
||||
>
|
||||
<div class="text-left w-full">
|
||||
<div class="font-semibold text-base">Web</div>
|
||||
<div class="font-semibold text-base">
|
||||
Web
|
||||
</div>
|
||||
<div class="text-xs opacity-80">
|
||||
PWA
|
||||
</div>
|
||||
@@ -170,12 +176,12 @@ useHead({
|
||||
<!-- 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"></div>
|
||||
<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>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
||||
{{ locale === 'zh-CN' ? '总下载' : 'Total Downloads' }}
|
||||
</div>
|
||||
@@ -183,7 +189,7 @@ useHead({
|
||||
</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"></div>
|
||||
<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">
|
||||
@@ -196,7 +202,7 @@ useHead({
|
||||
</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"></div>
|
||||
<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">
|
||||
@@ -209,7 +215,7 @@ useHead({
|
||||
</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"></div>
|
||||
<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">
|
||||
@@ -225,7 +231,7 @@ useHead({
|
||||
|
||||
<!-- 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"></div>
|
||||
<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' }}
|
||||
@@ -238,11 +244,11 @@ useHead({
|
||||
|
||||
<!-- 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"></div>
|
||||
<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>
|
||||
</h3>
|
||||
<ul class="space-y-3">
|
||||
<li
|
||||
v-for="(note, index) in app.releaseNotes[locale as 'zh-CN' | 'en-US']"
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import type { AppInfo } from '~/types'
|
||||
import type { AppInfo } from "~/types";
|
||||
|
||||
import { apps as appsData, categories as categoriesData } from '~/data/apps'
|
||||
import { apps as appsData, categories as categoriesData } from "~/data/apps";
|
||||
|
||||
const { t, locale, setLocale } = useI18n()
|
||||
const colorMode = useColorMode()
|
||||
const { t, locale, setLocale } = useI18n();
|
||||
const colorMode = useColorMode();
|
||||
|
||||
// 直接使用数据文件
|
||||
const apps = computed(() => appsData)
|
||||
const categories = computed(() => categoriesData)
|
||||
const apps = computed(() => appsData);
|
||||
const categories = computed(() => categoriesData);
|
||||
|
||||
// 当前选中的分类
|
||||
const selectedCategory = ref('all')
|
||||
const selectedCategory = ref("all");
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('')
|
||||
const searchKeyword = ref("");
|
||||
|
||||
// 过滤后的应用列表
|
||||
const filteredApps = computed(() => {
|
||||
let result = apps.value
|
||||
let result = apps.value;
|
||||
|
||||
// 按分类过滤
|
||||
if (selectedCategory.value !== 'all') {
|
||||
result = result.filter(app => app.category === selectedCategory.value)
|
||||
if (selectedCategory.value !== "all") {
|
||||
result = result.filter(app => app.category === selectedCategory.value);
|
||||
}
|
||||
|
||||
// 按搜索关键词过滤
|
||||
if (searchKeyword.value.trim()) {
|
||||
const keyword = searchKeyword.value.toLowerCase()
|
||||
const keyword = searchKeyword.value.toLowerCase();
|
||||
result = result.filter(app =>
|
||||
app.name.toLowerCase().includes(keyword)
|
||||
|| app.shortDescription[locale.value as 'zh-CN' | 'en-US'].toLowerCase().includes(keyword),
|
||||
)
|
||||
|| app.shortDescription[locale.value as "zh-CN" | "en-US"].toLowerCase().includes(keyword),
|
||||
);
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
return result;
|
||||
});
|
||||
|
||||
// 切换语言
|
||||
function toggleLanguage() {
|
||||
setLocale(locale.value === 'zh-CN' ? 'en-US' : 'zh-CN')
|
||||
setLocale(locale.value === "zh-CN" ? "en-US" : "zh-CN");
|
||||
}
|
||||
|
||||
// 打开应用详情
|
||||
function openAppDetail(app: AppInfo) {
|
||||
navigateTo(`/apps/${app.id}`)
|
||||
navigateTo(`/apps/${app.id}`);
|
||||
}
|
||||
|
||||
// 下载处理
|
||||
async function handleDownload(app: AppInfo, type: 'ios' | 'android' | 'h5', event?: MouseEvent | TouchEvent) {
|
||||
const url = app.downloads[type]
|
||||
async function handleDownload(app: AppInfo, type: "ios" | "android" | "h5", event?: MouseEvent | TouchEvent) {
|
||||
const url = app.downloads[type];
|
||||
|
||||
if (!url) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'h5') {
|
||||
navigateTo(url, { external: true, open: { target: '_blank' } })
|
||||
if (type === "h5") {
|
||||
navigateTo(url, { external: true, open: { target: "_blank" } });
|
||||
}
|
||||
else {
|
||||
navigateTo(url, { external: true })
|
||||
navigateTo(url, { external: true });
|
||||
}
|
||||
|
||||
await $fetch(`/api/track/${type}`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: { appId: app.id },
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const isDark = computed(() => colorMode.value === 'dark')
|
||||
const isDark = computed(() => colorMode.value === "dark");
|
||||
|
||||
// SEO
|
||||
useHead({
|
||||
title: locale.value === 'zh-CN' ? 'Riwa 应用商店' : 'Riwa App Store',
|
||||
title: locale.value === "zh-CN" ? "Riwa 应用商店" : "Riwa App Store",
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: locale.value === 'zh-CN'
|
||||
? '下载 Riwa 系列应用,包括 Riwa 主应用、Riwa 钱包和 Riwa 聊天等'
|
||||
: 'Download Riwa apps including Riwa main app, Riwa Wallet, and Riwa Chat',
|
||||
name: "description",
|
||||
content: locale.value === "zh-CN"
|
||||
? "下载 Riwa 系列应用,包括 Riwa 主应用、Riwa 钱包和 Riwa 聊天等"
|
||||
: "Download Riwa apps including Riwa main app, Riwa Wallet, and Riwa Chat",
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen relative overflow-hidden bg-linear-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 animate-pulse-subtle" 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="absolute inset-0 animate-pulse-subtle" 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-breathe"></div>
|
||||
<div class="absolute bottom-1/4 -right-48 w-96 h-96 bg-purple-500/20 rounded-full blur-3xl animate-breathe" style="animation-delay: 1s;"></div>
|
||||
<div class="absolute top-1/2 left-1/2 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl animate-breathe" style="animation-delay: 2s;"></div>
|
||||
<div class="absolute top-1/4 -left-48 w-96 h-96 bg-primary-500/20 rounded-full blur-3xl animate-breathe" />
|
||||
<div class="absolute bottom-1/4 -right-48 w-96 h-96 bg-purple-500/20 rounded-full blur-3xl animate-breathe" style="animation-delay: 1s;" />
|
||||
<div class="absolute top-1/2 left-1/2 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl animate-breathe" style="animation-delay: 2s;" />
|
||||
<!-- 额外的动态光球 -->
|
||||
<div class="absolute top-1/3 right-1/4 w-64 h-64 bg-cyan-500/15 rounded-full blur-2xl animate-float" style="animation-delay: 0.5s;"></div>
|
||||
<div class="absolute bottom-1/3 left-1/3 w-72 h-72 bg-indigo-500/15 rounded-full blur-2xl animate-float" style="animation-delay: 1.5s;"></div>
|
||||
<div class="absolute top-1/3 right-1/4 w-64 h-64 bg-cyan-500/15 rounded-full blur-2xl animate-float" style="animation-delay: 0.5s;" />
|
||||
<div class="absolute bottom-1/3 left-1/3 w-72 h-72 bg-indigo-500/15 rounded-full blur-2xl animate-float" style="animation-delay: 1.5s;" />
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
@@ -107,8 +107,8 @@ useHead({
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<div class="flex items-center gap-3 group">
|
||||
<div class="size-10 rounded-xl bg-linear-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-xl relative overflow-hidden shadow-lg shadow-blue-500/50">
|
||||
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/20 to-white/0 -translate-x-full group-hover:translate-x-full transition-transform duration-700"></div>
|
||||
<div class="absolute -inset-1 bg-blue-400/30 rounded-xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring"></div>
|
||||
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/20 to-white/0 -translate-x-full group-hover:translate-x-full transition-transform duration-700" />
|
||||
<div class="absolute -inset-1 bg-blue-400/30 rounded-xl blur-md opacity-0 group-hover:opacity-100 animate-pulse-ring" />
|
||||
<span class="relative z-10">R</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold bg-linear-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 animate-gradient">
|
||||
@@ -180,30 +180,30 @@ useHead({
|
||||
class="cursor-pointer backdrop-blur-sm bg-white/90 dark:bg-gray-900/90 border border-gray-200/50 dark:border-gray-800/50 duration-500 hover:shadow-2xl hover:shadow-primary-500/20 hover:-translate-y-2 hover:rotate-y-2 active:scale-95 active:shadow-lg active:shadow-primary-500/40 relative overflow-hidden"
|
||||
@click="openAppDetail(app)"
|
||||
>
|
||||
<!-- 内部发光效果 -->
|
||||
<div class="absolute inset-0 bg-linear-to-br from-primary-500/5 to-purple-500/5"></div>
|
||||
|
||||
<div class="flex items-start gap-4 relative z-10">
|
||||
<!-- 内部发光效果 -->
|
||||
<div class="absolute inset-0 bg-linear-to-br from-primary-500/5 to-purple-500/5" />
|
||||
|
||||
<div class="flex items-start gap-4 relative z-10">
|
||||
<!-- App Icon -->
|
||||
<div class="size-16 rounded-2xl bg-linear-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-2xl shrink-0 shadow-lg shadow-blue-500/50 relative overflow-hidden group-hover:shadow-2xl group-hover:shadow-blue-500/60 transition-all duration-500 group-hover:scale-110 group-hover:rotate-3 group-active:scale-105 group-active:rotate-1 p-2">
|
||||
<!-- 动态发光效果 -->
|
||||
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/30 to-white/0 -translate-x-full group-hover:translate-x-full group-active:translate-x-[50%] transition-transform duration-700"></div>
|
||||
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-lg transition-all duration-300 group-active:scale-95 group-hover:-rotate-3" />
|
||||
<div class="absolute inset-0 bg-linear-to-tr from-white/0 via-white/30 to-white/0 -translate-x-full group-hover:translate-x-full group-active:translate-x-[50%] transition-transform duration-700" />
|
||||
<img :src="app.icon" :alt="app.name" class="size-full object-contain relative z-10 rounded-lg transition-all duration-300 group-active:scale-95 group-hover:-rotate-3">
|
||||
</div>
|
||||
|
||||
<!-- App Info -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white truncate group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-300">
|
||||
{{ app.name }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2 mt-1 transition-colors duration-300 group-hover:text-gray-700 dark:group-hover:text-gray-300">
|
||||
{{ app.shortDescription[locale as 'zh-CN' | 'en-US'] }}
|
||||
</p>
|
||||
<div class="flex items-center gap-3 mt-3 text-xs text-gray-500 dark:text-gray-500 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-300">
|
||||
<span class="animate-pulse-subtle">v{{ app.version }}</span>
|
||||
<span>•</span>
|
||||
<span class="font-medium">{{ app.stats.total.toLocaleString() }} {{ locale === 'zh-CN' ? '次下载' : 'downloads' }}</span>
|
||||
</div>
|
||||
{{ app.name }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2 mt-1 transition-colors duration-300 group-hover:text-gray-700 dark:group-hover:text-gray-300">
|
||||
{{ app.shortDescription[locale as 'zh-CN' | 'en-US'] }}
|
||||
</p>
|
||||
<div class="flex items-center gap-3 mt-3 text-xs text-gray-500 dark:text-gray-500 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-300">
|
||||
<span class="animate-pulse-subtle">v{{ app.version }}</span>
|
||||
<span>•</span>
|
||||
<span class="font-medium">{{ app.stats.total.toLocaleString() }} {{ locale === 'zh-CN' ? '次下载' : 'downloads' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -217,8 +217,7 @@ useHead({
|
||||
block
|
||||
class="transition-all duration-300 hover:shadow-lg hover:shadow-blue-500/50 hover:-translate-y-1 active:scale-95 active:shadow-xl active:shadow-blue-500/60 relative overflow-hidden group/btn"
|
||||
@click.stop="(e) => handleDownload(app, 'ios', e)"
|
||||
>
|
||||
</UButton>
|
||||
/>
|
||||
<UButton
|
||||
v-if="app.downloads.android"
|
||||
icon="i-heroicons-device-tablet"
|
||||
@@ -227,8 +226,7 @@ useHead({
|
||||
block
|
||||
class="transition-all duration-300 hover:shadow-lg hover:shadow-blue-500/50 hover:-translate-y-1 active:scale-95 active:shadow-xl active:shadow-blue-500/60 relative overflow-hidden group/btn"
|
||||
@click.stop="(e) => handleDownload(app, 'android', e)"
|
||||
>
|
||||
</UButton>
|
||||
/>
|
||||
<UButton
|
||||
v-if="app.downloads.h5"
|
||||
icon="i-heroicons-globe-alt"
|
||||
@@ -237,8 +235,7 @@ useHead({
|
||||
block
|
||||
class="transition-all duration-300 hover:shadow-lg hover:shadow-blue-500/50 hover:-translate-y-1 active:scale-95 active:shadow-xl active:shadow-blue-500/60 relative overflow-hidden group/btn"
|
||||
@click.stop="(e) => handleDownload(app, 'h5', e)"
|
||||
>
|
||||
</UButton>
|
||||
/>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
@@ -262,4 +259,4 @@ useHead({
|
||||
</UContainer>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user