feat: adopt naive ui and refine shell interactions
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { NAlert, NButton, NCard, NEmpty, NSkeleton, NTag } from 'naive-ui'
|
||||
import { RouterLink, useRoute } from 'vue-router'
|
||||
import { supabase } from '@/lib/supabase'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
@@ -136,24 +137,33 @@ watch(() => route.params.id, loadPage)
|
||||
<template>
|
||||
<div class="max-w-6xl mx-auto px-4 py-8">
|
||||
<div class="mb-6">
|
||||
<RouterLink to="/encyclopedia" class="text-sm font-medium text-sky-700 hover:text-sky-800">
|
||||
← 返回图鉴总览
|
||||
<RouterLink to="/encyclopedia">
|
||||
<NButton text type="primary">← 返回图鉴总览</NButton>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="space-y-6">
|
||||
<div class="h-80 animate-pulse rounded-[32px] bg-slate-200"></div>
|
||||
<NSkeleton class="h-80 w-full" />
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div v-for="n in 3" :key="n" class="h-28 animate-pulse rounded-3xl bg-slate-200"></div>
|
||||
<NCard v-for="n in 3" :key="n">
|
||||
<NSkeleton class="h-4 w-1/3" />
|
||||
<NSkeleton class="mt-4 h-7 w-1/2" />
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="loadError" class="rounded-[28px] border border-red-200 bg-red-50 p-6 text-red-700">
|
||||
<NAlert
|
||||
v-else-if="loadError"
|
||||
type="error"
|
||||
:show-icon="false"
|
||||
:bordered="false"
|
||||
title="详情加载失败"
|
||||
>
|
||||
{{ loadError }}
|
||||
</div>
|
||||
</NAlert>
|
||||
|
||||
<template v-else-if="cloudType">
|
||||
<section class="overflow-hidden rounded-[32px] border border-slate-200 bg-white shadow-sm">
|
||||
<section class="overflow-hidden border border-slate-200 bg-white shadow-sm">
|
||||
<div class="grid lg:grid-cols-[1.15fr_0.85fr]">
|
||||
<div class="relative min-h-[360px] overflow-hidden">
|
||||
<img
|
||||
@@ -169,9 +179,9 @@ watch(() => route.params.id, loadPage)
|
||||
></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-slate-950/78 via-slate-950/18 to-transparent"></div>
|
||||
<div class="absolute bottom-8 left-8 right-8 text-white">
|
||||
<span class="inline-flex rounded-full border px-3 py-1 text-xs font-medium backdrop-blur" :class="rarityMeta[cloudType.rarity].chip">
|
||||
<NTag :bordered="false" class="border border-white/20 bg-white/12 text-white backdrop-blur">
|
||||
{{ rarityMeta[cloudType.rarity].label }}
|
||||
</span>
|
||||
</NTag>
|
||||
<h1 class="mt-4 text-4xl font-bold">{{ cloudType.name }}</h1>
|
||||
<p class="mt-2 text-lg text-white/82">{{ cloudType.name_en }}</p>
|
||||
</div>
|
||||
@@ -185,7 +195,7 @@ watch(() => route.params.id, loadPage)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 rounded-[28px] border border-slate-200 bg-white p-5">
|
||||
<NCard class="mt-8 border border-slate-200">
|
||||
<p class="text-sm text-slate-500">你的收集状态</p>
|
||||
<template v-if="isUnlocked && collectionEntry">
|
||||
<p class="mt-2 text-2xl font-bold text-slate-900">已解锁</p>
|
||||
@@ -195,26 +205,26 @@ watch(() => route.params.id, loadPage)
|
||||
<p class="mt-2 text-2xl font-bold text-slate-900">尚未解锁</p>
|
||||
<p class="mt-2 text-sm text-slate-600">拍到并上传这种云朵后,这枚徽章就会被点亮。</p>
|
||||
</template>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mt-6 grid gap-4 md:grid-cols-3">
|
||||
<div class="rounded-[28px] border border-slate-200 bg-white p-5 shadow-sm">
|
||||
<NCard class="border border-slate-200 shadow-sm">
|
||||
<p class="text-sm text-slate-500">稀有度</p>
|
||||
<p class="mt-2 text-2xl font-bold text-slate-900">{{ rarityMeta[cloudType.rarity].label }}</p>
|
||||
</div>
|
||||
<div class="rounded-[28px] border border-slate-200 bg-white p-5 shadow-sm">
|
||||
</NCard>
|
||||
<NCard class="border border-slate-200 shadow-sm">
|
||||
<p class="text-sm text-slate-500">公开云图</p>
|
||||
<p class="mt-2 text-2xl font-bold text-slate-900">{{ publicCount }}</p>
|
||||
</div>
|
||||
<div class="rounded-[28px] border border-slate-200 bg-white p-5 shadow-sm">
|
||||
</NCard>
|
||||
<NCard class="border border-slate-200 shadow-sm">
|
||||
<p class="text-sm text-slate-500">首次解锁</p>
|
||||
<p class="mt-2 text-xl font-bold text-slate-900">
|
||||
{{ collectionEntry ? formatDate(collectionEntry.unlocked_at) : '等待记录' }}
|
||||
</p>
|
||||
</div>
|
||||
</NCard>
|
||||
</section>
|
||||
|
||||
<section class="mt-8">
|
||||
@@ -229,7 +239,7 @@ watch(() => route.params.id, loadPage)
|
||||
<article
|
||||
v-for="item in gallery"
|
||||
:key="item.id"
|
||||
class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-sm"
|
||||
class="overflow-hidden border border-slate-200 bg-white shadow-sm"
|
||||
>
|
||||
<img :src="item.thumbnail_url || item.image_url" :alt="cloudType.name" class="h-56 w-full object-cover" />
|
||||
<div class="p-5">
|
||||
@@ -245,9 +255,12 @@ watch(() => route.params.id, loadPage)
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-else class="rounded-[28px] border border-dashed border-slate-300 bg-white p-8 text-center">
|
||||
<p class="text-lg font-semibold text-slate-900">还没有公开作品</p>
|
||||
<p class="mt-2 text-sm text-slate-500">等第一位观测者上传这类云图后,这里就会展示出来。</p>
|
||||
<div v-else class="border border-dashed border-slate-300 bg-white p-8">
|
||||
<NEmpty description="还没有公开作品">
|
||||
<template #extra>
|
||||
<p class="text-sm text-slate-500">等第一位观测者上传这类云图后,这里就会展示出来。</p>
|
||||
</template>
|
||||
</NEmpty>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { NAlert, NCard, NEmpty, NProgress, NSkeleton } from 'naive-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useEncyclopediaStore } from '@/stores/encyclopedia'
|
||||
@@ -74,23 +75,26 @@ onMounted(async () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-[28px] border border-white/80 bg-white/80 p-6 shadow-sm backdrop-blur">
|
||||
<NCard class="border border-white/80 bg-white/85 shadow-sm backdrop-blur" :bordered="false">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-slate-500">当前进度</p>
|
||||
<p class="mt-1 text-3xl font-bold text-slate-900">{{ progressText }}</p>
|
||||
</div>
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-full bg-slate-900 text-xl font-semibold text-white">
|
||||
<div class="flex h-16 w-16 items-center justify-center border border-slate-900 bg-slate-900 text-xl font-semibold text-white">
|
||||
{{ authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0 }}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 h-3 overflow-hidden rounded-full bg-slate-200">
|
||||
<div
|
||||
class="h-full rounded-full bg-[linear-gradient(90deg,#0ea5e9_0%,#f59e0b_100%)] transition-all duration-500"
|
||||
:style="{ width: `${authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0}%` }"
|
||||
></div>
|
||||
</div>
|
||||
<NProgress
|
||||
class="mt-5"
|
||||
type="line"
|
||||
:show-indicator="false"
|
||||
:percentage="authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0"
|
||||
color="linear-gradient(90deg,#0ea5e9 0%,#f59e0b 100%)"
|
||||
rail-color="#dbe4ee"
|
||||
:height="12"
|
||||
/>
|
||||
|
||||
<p class="mt-4 text-sm text-slate-500">
|
||||
<template v-if="authStore.isLoggedIn">
|
||||
@@ -100,27 +104,39 @@ onMounted(async () => {
|
||||
登录后可同步你的个人图鉴进度。
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-6xl mx-auto px-4 py-8">
|
||||
<div v-if="encyclopediaStore.collectionError && authStore.isLoggedIn" class="mb-6 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700">
|
||||
<NAlert
|
||||
v-if="encyclopediaStore.collectionError && authStore.isLoggedIn"
|
||||
class="mb-6"
|
||||
type="warning"
|
||||
:show-icon="false"
|
||||
:bordered="false"
|
||||
title="图鉴收藏数据暂时不可用"
|
||||
>
|
||||
图鉴收藏数据暂时不可用:{{ encyclopediaStore.collectionError }}
|
||||
</div>
|
||||
</NAlert>
|
||||
|
||||
<div v-if="encyclopediaStore.loadingCloudTypes" class="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-for="n in 6" :key="n" class="h-72 animate-pulse rounded-[28px] bg-slate-200"></div>
|
||||
<NCard v-for="n in 6" :key="n">
|
||||
<NSkeleton class="h-44 w-full" />
|
||||
<NSkeleton class="mt-5 h-5 w-2/5" />
|
||||
<NSkeleton class="mt-3 h-4 w-1/3" />
|
||||
<NSkeleton class="mt-6 h-4 w-full" :repeat="2" />
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-else-if="encyclopediaStore.cloudTypes.length" class="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||
<button
|
||||
v-for="cloudType in encyclopediaStore.cloudTypes"
|
||||
:key="cloudType.id"
|
||||
type="button"
|
||||
@click="openCard(cloudType)"
|
||||
class="group overflow-hidden rounded-[28px] border bg-white text-left shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg"
|
||||
class="group overflow-hidden border bg-white text-left shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg"
|
||||
:class="isUnlocked(cloudType.id) ? 'border-amber-300 shadow-amber-100/60' : 'border-slate-200 hover:border-slate-300'"
|
||||
>
|
||||
<div
|
||||
@@ -145,11 +161,11 @@ onMounted(async () => {
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent"></div>
|
||||
|
||||
<div v-if="!isUnlocked(cloudType.id)" class="absolute inset-0 flex items-center justify-center bg-slate-950/22 backdrop-blur-[2px]">
|
||||
<div class="rounded-full border border-white/45 bg-white/15 px-4 py-2 text-sm font-medium text-white">🔒 尚未解锁</div>
|
||||
<div class="border border-white/45 bg-white/15 px-4 py-2 text-sm font-medium text-white">🔒 尚未解锁</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute left-4 top-4">
|
||||
<span class="inline-flex rounded-full border px-3 py-1 text-xs font-medium" :class="rarityMeta[cloudType.rarity].chip">
|
||||
<span class="inline-flex border px-3 py-1 text-xs font-medium" :class="rarityMeta[cloudType.rarity].chip">
|
||||
{{ rarityMeta[cloudType.rarity].label }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -178,11 +194,15 @@ onMounted(async () => {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="border border-dashed border-slate-300 bg-white px-6 py-12">
|
||||
<NEmpty description="暂时还没有图鉴数据" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="lockHint"
|
||||
class="fixed bottom-6 left-1/2 z-50 -translate-x-1/2 rounded-full bg-slate-900 px-4 py-2 text-sm text-white shadow-lg"
|
||||
class="fixed bottom-6 left-1/2 z-50 -translate-x-1/2 border border-slate-900 bg-slate-900 px-4 py-2 text-sm text-white shadow-lg"
|
||||
>
|
||||
{{ lockHint }}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user