feat: add admin console and account settings

Implement the admin dashboard with review, user, and image management workflows. Add profile settings, password reset, pending upload defaults, community placeholder routing, Vercel SPA rewrites, refreshed header styling, and the OpenCloud color-system skill.
This commit is contained in:
2026-05-22 21:04:49 +08:00
parent 599c8107d1
commit 9150a17097
15 changed files with 1410 additions and 50 deletions
+39 -17
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { NButton, NSpace, NTag } from 'naive-ui'
import { NButton, NSpace } from 'naive-ui'
import { useAuthStore } from '@/stores/auth'
import { RouterLink, useRoute } from 'vue-router'
@@ -10,6 +10,8 @@ const isMapRoute = computed(() => route.name === 'map')
const headerHidden = ref(false)
const headerPinnedOpen = ref(false)
const activeNavClass = 'bg-teal-100 text-teal-800 ring-1 ring-teal-200'
const inactiveNavClass = 'text-slate-600 hover:bg-teal-50 hover:text-teal-800'
let lastScrollY = 0
const HIDE_HEADER_EVENT = 'opencloud:hide-header'
@@ -111,24 +113,31 @@ onMounted(() => {
<RouterLink
to="/"
class="px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'map' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'map' ? activeNavClass : inactiveNavClass"
>
地图
</RouterLink>
<RouterLink
to="/encyclopedia"
to="/community"
class="px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'community' ? activeNavClass : inactiveNavClass"
>
图鉴
社区
</RouterLink>
<RouterLink
to="/gallery"
class="px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'gallery' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'gallery' ? activeNavClass : inactiveNavClass"
>
画廊
</RouterLink>
<RouterLink
to="/encyclopedia"
class="px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? activeNavClass : inactiveNavClass"
>
图鉴
</RouterLink>
</nav>
<NSpace align="center" :size="8" class="shrink-0 md:[&_.n-button]:px-4">
@@ -137,9 +146,12 @@ onMounted(() => {
to="/upload"
class="no-underline"
>
<NButton type="primary" strong size="small" class="md:!h-10 md:!px-4 md:!text-sm">
<span
class="inline-flex h-8 items-center border border-sky-200 bg-sky-100 px-3 text-sm font-medium uppercase tracking-[0.12em] text-sky-800 shadow-[4px_4px_0_0_rgba(14,165,233,0.14)] transition-colors hover:bg-sky-50 hover:text-sky-900 md:h-10 md:px-4"
:class="route.name === 'upload' ? 'ring-1 ring-sky-300' : ''"
>
上传
</NButton>
</span>
</RouterLink>
<template v-if="authStore.isLoggedIn">
@@ -147,9 +159,12 @@ onMounted(() => {
to="/profile"
class="no-underline"
>
<NTag type="success" bordered size="small" class="max-w-[7rem] truncate md:max-w-none">
{{ authStore.profile?.username }}
</NTag>
<span
class="inline-flex h-8 max-w-[7.5rem] items-center border border-teal-100 bg-white/80 px-3 text-sm font-medium text-teal-800 shadow-[3px_3px_0_0_rgba(20,184,166,0.08)] transition-colors hover:border-teal-200 hover:bg-teal-50 md:h-10 md:max-w-none"
:class="route.name === 'profile' || route.name === 'profile-settings' ? 'bg-teal-50 ring-1 ring-teal-200' : ''"
>
<span class="truncate">@{{ authStore.profile?.username }}</span>
</span>
</RouterLink>
<NButton
quaternary
@@ -182,29 +197,36 @@ onMounted(() => {
<RouterLink
to="/"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'map' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'map' ? activeNavClass : inactiveNavClass"
>
地图
</RouterLink>
<RouterLink
to="/encyclopedia"
to="/community"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'community' ? activeNavClass : inactiveNavClass"
>
图鉴
社区
</RouterLink>
<RouterLink
to="/gallery"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'gallery' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'gallery' ? activeNavClass : inactiveNavClass"
>
画廊
</RouterLink>
<RouterLink
to="/encyclopedia"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? activeNavClass : inactiveNavClass"
>
图鉴
</RouterLink>
<RouterLink
v-if="authStore.isLoggedIn"
to="/profile"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'profile' ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'"
:class="route.name === 'profile' ? activeNavClass : inactiveNavClass"
>
主页
</RouterLink>