refactor: extract EncyclopediaProgressCard component and limit map pitch

- Create EncyclopediaProgressCard with dynamic progress color, reuse in
  encyclopedia and profile pages to unify styling
- Add skyColor and maxPitch to AMap 3D view to reduce empty tile area
  when tilting
- Extend AMap type declarations for skyColor, maxPitch, getPitch, getRotation
This commit is contained in:
2026-05-24 17:43:25 +08:00
parent 1e0da1fe36
commit 2a9f8d6a9c
5 changed files with 93 additions and 82 deletions
+9 -55
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { NAlert, NCard, NEmpty, NIcon, NProgress, NSkeleton } from 'naive-ui'
import { onMounted, ref } from 'vue'
import { NAlert, NCard, NEmpty, NIcon, NSkeleton } from 'naive-ui'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useEncyclopediaStore } from '@/stores/encyclopedia'
import EncyclopediaProgressCard from '@/components/cloud/EncyclopediaProgressCard.vue'
import { Lock } from '@vicons/tabler'
import type { CloudType } from '@/types/database'
@@ -20,26 +21,6 @@ const rarityMeta = {
rare: { label: '罕见', chip: 'bg-rose-100 text-rose-700 border-rose-200', glow: 'from-rose-100 to-white' },
} satisfies Record<CloudType['rarity'], { label: string; chip: string; glow: string }>
function lerpHex(a: string, b: string, t: number) {
const ah = parseInt(a.slice(1), 16)
const bh = parseInt(b.slice(1), 16)
const r = Math.round(((ah >> 16) & 0xff) + (((bh >> 16) & 0xff) - ((ah >> 16) & 0xff)) * t)
const g = Math.round(((ah >> 8) & 0xff) + (((bh >> 8) & 0xff) - ((ah >> 8) & 0xff)) * t)
const bv = Math.round((ah & 0xff) + ((bh & 0xff) - (ah & 0xff)) * t)
return `#${((r << 16) | (g << 8) | bv).toString(16).padStart(6, '0')}`
}
function progressColor(percent: number) {
const t = Math.min(1, Math.max(0, percent / 100))
if (t <= 0.5) return lerpHex('#0ea5e9', '#f59e0b', t * 2)
return lerpHex('#f59e0b', '#ef4444', (t - 0.5) * 2)
}
const totalTypes = computed(() => encyclopediaStore.cloudTypes.length || 10)
const unlockedCount = computed(() => (authStore.isLoggedIn ? encyclopediaStore.unlockedCount : 0))
const progressText = computed(() => `${unlockedCount.value}/${totalTypes.value}`)
const currentProgressColor = computed(() => progressColor(authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0))
function isUnlocked(cloudTypeId: number) {
return authStore.isLoggedIn && encyclopediaStore.isUnlocked(cloudTypeId)
}
@@ -92,39 +73,12 @@ onMounted(async () => {
</p>
</div>
<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" :style="{ color: currentProgressColor }">{{ progressText }}</p>
</div>
<div
class="flex h-16 w-16 items-center justify-center border text-xl font-semibold text-white transition-colors duration-500"
:style="{ backgroundColor: currentProgressColor, borderColor: currentProgressColor }"
>
{{ authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0 }}%
</div>
</div>
<NProgress
class="mt-5"
type="line"
:show-indicator="false"
:percentage="authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0"
:color="{ stops: ['#0ea5e9', currentProgressColor] }"
:height="12"
rail-color="#e2e8f0"
/>
<p class="mt-4 text-sm text-slate-500">
<template v-if="authStore.isLoggedIn">
已解锁 {{ unlockedCount }} 枚徽章,还差 {{ Math.max(totalTypes - unlockedCount, 0) }} 枚。
</template>
<template v-else>
登录后可同步你的个人图鉴进度。
</template>
</p>
</NCard>
<EncyclopediaProgressCard
:unlocked-count="authStore.isLoggedIn ? encyclopediaStore.unlockedCount : 0"
:total-count="encyclopediaStore.cloudTypes.length || 10"
:percent="encyclopediaStore.unlockPercent"
:is-logged-in="authStore.isLoggedIn"
/>
</div>
</div>
</div>