优化密码找回流程、管理后台批量操作和上传体验 #2

Merged
Mplan merged 9 commits from fix into main 2026-05-31 17:00:16 +08:00
3 changed files with 69 additions and 4 deletions
Showing only changes of commit eaa475a5e2 - Show all commits
+12 -1
View File
@@ -120,6 +120,12 @@ const router = createRouter({
component: () => import('@/views/system/ForbiddenView.vue'),
meta: { title: '无权访问', noindex: true },
},
{
path: '/401',
name: 'auth-required',
component: () => import('@/views/system/AuthRequiredView.vue'),
meta: { title: '需要登录', noindex: true },
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
@@ -136,7 +142,12 @@ router.beforeEach((to, _from, next) => {
const authStore = useAuthStore()
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
next({ name: 'login', query: { redirect: to.fullPath } })
next({
name: 'auth-required',
query: {
redirect: to.fullPath,
},
})
} else if (to.meta.requiresAdmin && !authStore.isAdmin) {
next({ name: 'forbidden' })
} else if ((to.name === 'login' || to.name === 'register') && authStore.isLoggedIn) {
+7 -3
View File
@@ -5,6 +5,7 @@ import MiniLocationMap from '@/components/cloud/MiniLocationMap.vue'
import QuickUploadModal from '@/components/cloud/QuickUploadModal.vue'
import { supabase } from '@/lib/supabase'
import { loadAMap } from '@/lib/amap'
import { useAuthStore } from '@/stores/auth'
import { NIcon } from 'naive-ui'
import { Adjustments, Calendar, CloudUpload, Refresh, Map, Satellite, X } from '@vicons/tabler'
@@ -24,6 +25,7 @@ interface CloudMarkerData {
}
const mapEl = ref<HTMLDivElement>()
const authStore = useAuthStore()
const previewCloud = ref<CloudMarkerData | null>(null)
const satelliteOn = ref(false)
const statusText = ref('加载中...')
@@ -70,7 +72,6 @@ const archiveTitle = computed(() => {
if (mapMode.value === 'realtime') return '实时'
return archiveKind.value === 'day' ? archiveDay.value : archiveMonth.value
})
function getMinuteOfDay(date: Date) {
return date.getHours() * 60 + date.getMinutes()
}
@@ -344,6 +345,7 @@ function toggleSat() {
}
function openQuickUpload() {
if (authStore.loading || !authStore.isLoggedIn) return
quickUploadOpen.value = true
}
@@ -446,9 +448,11 @@ onUnmounted(() => {
<div class="absolute bottom-6 right-7 z-20 flex w-10 flex-col items-center gap-2">
<button
type="button"
class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50"
title="快速上传图片"
class="w-10 h-10 rounded-lg shadow-md flex items-center justify-center"
:class="authStore.loading || !authStore.isLoggedIn ? 'cursor-not-allowed bg-slate-100 text-slate-300 shadow-none' : 'bg-white hover:bg-gray-50'"
:title="authStore.isLoggedIn ? '快速上传图片' : '请先登录后上传图片'"
aria-label="快速上传图片"
:disabled="authStore.loading || !authStore.isLoggedIn"
@click="openQuickUpload"
>
<NIcon size="20" style="display: inline-flex; vertical-align: middle;">
+50
View File
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { computed } from 'vue'
import { NButton } from 'naive-ui'
import { RouterLink, useRoute } from 'vue-router'
const route = useRoute()
const redirect = computed(() => {
const value = route.query.redirect
return typeof value === 'string' && value.startsWith('/') ? value : '/'
})
const title = computed(() => {
if (redirect.value === '/upload') {
return '上传前先登录'
}
return '需要先登录'
})
const description = computed(() => {
if (redirect.value === '/upload') {
return '上传云图、使用地图快捷上传都需要先登录账号。登录后再回来,这里的上传入口就会开放。'
}
return '这个页面需要先登录才能继续访问。登录成功后可以返回刚才的目标页面继续操作。'
})
</script>
<template>
<div class="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4 py-10">
<div class="w-full max-w-2xl border border-slate-200 bg-white px-8 py-14 text-center shadow-sm">
<div class="text-7xl leading-none">🔐</div>
<p class="mt-6 text-sm font-medium uppercase tracking-[0.24em] text-sky-600">401 Login Required</p>
<h1 class="mt-3 text-4xl font-bold text-slate-900">{{ title }}</h1>
<p class="mx-auto mt-4 max-w-xl text-sm leading-7 text-slate-600">
{{ description }}
</p>
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
<RouterLink :to="{ path: '/login', query: redirect !== '/' ? { redirect } : undefined }">
<NButton type="default" secondary strong class="oc-panel-button oc-panel-button--sky">前往登录</NButton>
</RouterLink>
<RouterLink to="/">
<NButton type="default" secondary strong class="oc-panel-button oc-panel-button--neutral">返回地图</NButton>
</RouterLink>
</div>
</div>
</div>
</template>