This commit is contained in:
2026-03-22 14:22:57 +08:00
parent c41154c93a
commit 7325106ae7
6 changed files with 147 additions and 21 deletions

View File

@@ -9,7 +9,12 @@
"mcp__server-mysql__execute",
"Bash(git rm:*)",
"Bash(git add:*)",
"Bash(npx tsc:*)"
"Bash(npx tsc:*)",
"Bash(pnpm vue-tsc:*)",
"Bash(find node_modules/.pnpm -name *.mjs -path *vue-router*)",
"Bash(grep -r \"routeMeta\\\\|definePage\\\\|<route\" node_modules/.pnpm/vue-router*/node_modules/vue-router/dist/*.mjs)",
"Bash(grep -r \"route block\\\\|customBlock\\\\|defineCustomBlock\" node_modules/.pnpm/vue-router*/dist/*.mjs)",
"Bash(find node_modules/.pnpm -path *vue-router* -name *.mjs)"
],
"additionalDirectories": [
"/Users/sion/Desktop/projects/monisuo/monisuo-admin/.git"

View File

@@ -19,7 +19,7 @@ export function useAuth() {
}
function toHome() {
router.push({ path: '/dashboard' })
router.push({ path: '/monisuo/dashboard' })
}
async function login(username: string, password: string) {

View File

@@ -0,0 +1,8 @@
<template>
<router-view />
</template>
<route lang="yaml">
meta:
layout: false
</route>

View File

@@ -0,0 +1,97 @@
<script setup lang="ts">
import { Eye, EyeOff, Lock, User } from 'lucide-vue-next'
import { useAuth } from '@/composables/use-auth'
const username = ref('')
const password = ref('')
const showPassword = ref(false)
const { login, loading, error, isLogin } = useAuth()
const router = useRouter()
// 已登录则跳转首页
watchEffect(() => {
if (unref(isLogin)) {
router.push('/dashboard')
}
})
async function handleSubmit() {
if (!username.value || !password.value)
return
await login(username.value, password.value)
}
</script>
<template>
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-background to-muted p-4">
<UiCard class="w-full max-w-md">
<UiCardHeader class="space-y-1 text-center">
<div class="flex justify-center mb-4">
<div class="size-12 rounded-xl bg-primary flex items-center justify-center">
<Lock class="size-6 text-primary-foreground" />
</div>
</div>
<UiCardTitle class="text-2xl font-bold">
Monisuo Admin
</UiCardTitle>
<UiCardDescription>
管理员登录
</UiCardDescription>
</UiCardHeader>
<UiCardContent>
<form @submit.prevent="handleSubmit" class="space-y-4">
<div class="space-y-2">
<UiLabel for="username">用户名</UiLabel>
<div class="relative">
<User class="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<UiInput
id="username"
v-model="username"
type="text"
placeholder="请输入用户名"
class="pl-9"
:disabled="loading"
/>
</div>
</div>
<div class="space-y-2">
<UiLabel for="password">密码</UiLabel>
<div class="relative">
<Lock class="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<UiInput
id="password"
v-model="password"
:type="showPassword ? 'text' : 'password'"
placeholder="请输入密码"
class="pl-9 pr-9"
:disabled="loading"
/>
<button
type="button"
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
@click="showPassword = !showPassword"
>
<Eye v-if="!showPassword" class="size-4" />
<EyeOff v-else class="size-4" />
</button>
</div>
</div>
<UiAlert v-if="error" variant="destructive">
<UiAlertDescription>{{ error }}</UiAlertDescription>
</UiAlert>
<UiButton type="submit" class="w-full" :disabled="loading">
<UiSpinner v-if="loading" class="size-4 mr-2" />
{{ loading ? '登录中...' : '登录' }}
</UiButton>
</form>
</UiCardContent>
</UiCard>
</div>
</template>
<route lang="yaml">
meta:
layout: blank
</route>

View File

@@ -5,33 +5,22 @@ import { storeToRefs } from 'pinia'
import pinia from '@/plugins/pinia/setup'
import { useAuthStore } from '@/stores/auth'
// 需要认证的 Monisuo 路由前缀
const MONISUO_AUTH_ROUTES = ['/monisuo/']
// 需要认证的路由前缀
const AUTH_ROUTES = ['/monisuo/', '/dashboard', '/users', '/tasks', '/settings']
export function authGuard(router: Router) {
router.beforeEach((to, _from) => {
const authStore = useAuthStore(pinia)
const { isLogin } = storeToRefs(authStore)
// 检查是否是需要认证的 Monisuo 路由
const isMonisuoRoute = MONISUO_AUTH_ROUTES.some(prefix => to.path.startsWith(prefix))
// 检查是否是需要认证的路由
const needsAuth = to.meta.auth || AUTH_ROUTES.some(prefix => to.path.startsWith(prefix))
// 如果页面需要登录但用户未登录,重定向到登录页并记录原始目标页面
if ((to.meta.auth || isMonisuoRoute) && !unref(isLogin)) {
// Monisuo 路由重定向到 Monisuo 登录页
if (isMonisuoRoute && to.name !== '/auth/monisuo-sign-in') {
return {
name: '/auth/monisuo-sign-in',
query: { redirect: to.fullPath },
}
}
// 其他需要认证的路由重定向到默认登录页
if (to.meta.auth && to.name !== '/auth/sign-in') {
return {
name: '/auth/sign-in',
query: { redirect: to.fullPath },
}
if (needsAuth && !unref(isLogin) && to.name !== '/auth/sign-in') {
return {
name: '/auth/sign-in',
query: { redirect: to.fullPath },
}
}
})

View File

@@ -40,6 +40,20 @@ declare module 'vue-router/auto-routes' {
{ path: ParamValue<false> },
| never
>,
'/auth': RouteRecordInfo<
'/auth',
'/auth',
Record<never, never>,
Record<never, never>,
| '/auth/sign-in'
>,
'/auth/sign-in': RouteRecordInfo<
'/auth/sign-in',
'/auth/sign-in',
Record<never, never>,
Record<never, never>,
| never
>,
'/dashboard/': RouteRecordInfo<
'/dashboard/',
'/dashboard',
@@ -128,6 +142,19 @@ declare module 'vue-router/auto-routes' {
views:
| never
}
'src/pages/auth.vue': {
routes:
| '/auth'
| '/auth/sign-in'
views:
| 'default'
}
'src/pages/auth/sign-in.vue': {
routes:
| '/auth/sign-in'
views:
| never
}
'src/pages/dashboard/index.vue': {
routes:
| '/dashboard/'