feat: add cloud encyclopedia system
This commit is contained in:
@@ -2,15 +2,19 @@
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useCloudsStore } from '@/stores/clouds'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { downloadCloudBadgeCard } from '@/lib/cloudBadges'
|
||||
import { useUpload } from '@/composables/useUpload'
|
||||
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const cloudsStore = useCloudsStore()
|
||||
const { items, uploading, overallProgress, currentItemIndex, totalItems, addFiles, removeItem, clearAll, validateAll, uploadAll } = useUpload()
|
||||
|
||||
const activeId = ref<string | null>(null)
|
||||
const dragOver = ref(false)
|
||||
const successMsg = ref(false)
|
||||
const unlockedBadges = ref<Awaited<ReturnType<typeof uploadAll>>['unlockedBadges']>([])
|
||||
const errorMsg = ref('')
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
@@ -68,15 +72,45 @@ async function handleSubmit() {
|
||||
return
|
||||
}
|
||||
|
||||
const ok = await uploadAll()
|
||||
if (ok) {
|
||||
const result = await uploadAll()
|
||||
if (result.ok) {
|
||||
unlockedBadges.value = result.unlockedBadges
|
||||
successMsg.value = true
|
||||
setTimeout(() => router.push('/profile'), 2000)
|
||||
} else {
|
||||
errorMsg.value = '上传失败,请稍后重试'
|
||||
}
|
||||
}
|
||||
|
||||
function resetAfterSuccess() {
|
||||
successMsg.value = false
|
||||
unlockedBadges.value = []
|
||||
errorMsg.value = ''
|
||||
}
|
||||
|
||||
async function saveBadge(badge: NonNullable<typeof unlockedBadges.value[number]>) {
|
||||
await downloadCloudBadgeCard({
|
||||
cloudName: badge.cloudName,
|
||||
cloudNameEn: badge.cloudNameEn,
|
||||
unlockedAt: badge.unlockedAt,
|
||||
username: authStore.profile?.username || authStore.user?.email || 'OpenCloud 用户',
|
||||
rarity: badge.rarity,
|
||||
})
|
||||
}
|
||||
|
||||
function rarityLabel(rarity: NonNullable<typeof unlockedBadges.value[number]>['rarity']) {
|
||||
if (rarity === 'common') return '常见'
|
||||
if (rarity === 'uncommon') return '少见'
|
||||
return '罕见'
|
||||
}
|
||||
|
||||
function formatUnlockedAt(iso: string) {
|
||||
return new Date(iso).toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
function onLatInput(e: Event) {
|
||||
const val = parseFloat((e.target as HTMLInputElement).value)
|
||||
if (activeItem.value) {
|
||||
@@ -133,10 +167,77 @@ onUnmounted(() => { for (const item of items.value) URL.revokeObjectURL(item.pre
|
||||
<input ref="fileInput" type="file" accept="image/*" multiple class="hidden" @change="handleFileSelect" />
|
||||
|
||||
<div v-if="successMsg" class="flex items-center justify-center min-h-[60vh]">
|
||||
<div class="text-center">
|
||||
<span class="text-5xl block mb-4">✅</span>
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-2">上传成功!</h2>
|
||||
<p class="text-gray-500">正在跳转到个人主页...</p>
|
||||
<div class="w-full max-w-5xl">
|
||||
<div class="text-center">
|
||||
<span class="text-5xl block mb-4">✅</span>
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-2">上传成功</h2>
|
||||
<p class="text-gray-500">
|
||||
<template v-if="unlockedBadges.length">
|
||||
新点亮了 {{ unlockedBadges.length }} 枚图鉴徽章。
|
||||
</template>
|
||||
<template v-else>
|
||||
这批云图已经进入你的收藏记录。
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="unlockedBadges.length" class="mt-8 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||
<article
|
||||
v-for="badge in unlockedBadges"
|
||||
:key="badge.cloudTypeId"
|
||||
class="rounded-[28px] border border-amber-200 bg-[linear-gradient(180deg,#fffbeb_0%,#ffffff_100%)] p-6 shadow-sm"
|
||||
>
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-amber-400 text-3xl text-white shadow-sm">
|
||||
{{ badge.cloudName.slice(0, 1) }}
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="flex items-center gap-3">
|
||||
<h3 class="text-2xl font-bold text-gray-900">{{ badge.cloudName }}</h3>
|
||||
<span class="rounded-full border border-amber-200 bg-white px-3 py-1 text-xs font-medium text-amber-700">
|
||||
{{ rarityLabel(badge.rarity) }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">{{ badge.cloudNameEn }}</p>
|
||||
<p class="mt-4 text-sm text-gray-600">解锁时间:{{ formatUnlockedAt(badge.unlockedAt) }}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex gap-3">
|
||||
<button
|
||||
@click="saveBadge(badge)"
|
||||
class="flex-1 rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-medium text-white hover:bg-slate-800"
|
||||
>
|
||||
保存分享卡片
|
||||
</button>
|
||||
<button
|
||||
@click="router.push(`/encyclopedia/${badge.cloudTypeId}`)"
|
||||
class="flex-1 rounded-xl border border-gray-300 px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
查看详情
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||
<button
|
||||
@click="resetAfterSuccess"
|
||||
class="rounded-xl border border-gray-300 px-5 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
继续上传
|
||||
</button>
|
||||
<button
|
||||
@click="router.push('/encyclopedia')"
|
||||
class="rounded-xl bg-sky-500 px-5 py-2.5 text-sm font-medium text-white hover:bg-sky-600"
|
||||
>
|
||||
前往图鉴
|
||||
</button>
|
||||
<button
|
||||
@click="router.push('/profile')"
|
||||
class="rounded-xl border border-gray-300 px-5 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
返回个人主页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user