-
管理员登录
+
用户登录
-
+
-
+
默认账号: admin / admin123
@@ -44,12 +55,14 @@
import { ref, onMounted, provide } from 'vue'
const isLoggedIn = ref(false)
+const isAdmin = ref(false)
const showLogin = ref(false)
const loginForm = ref({ username: '', password: '' })
// 提供给子组件使用
provide('showLogin', showLogin)
provide('isLoggedIn', isLoggedIn)
+provide('isAdmin', isAdmin)
// 获取存储的 token
const getToken = () => localStorage.getItem('authToken') || ''
@@ -67,9 +80,9 @@ const login = async () => {
if (res.ok) {
const data = await res.json()
isLoggedIn.value = true
+ isAdmin.value = data.is_admin
showLogin.value = false
localStorage.setItem('authToken', data.token)
- localStorage.setItem('isAdmin', 'true')
} else {
alert('用户名或密码错误')
}
@@ -80,27 +93,31 @@ const login = async () => {
const logout = () => {
isLoggedIn.value = false
+ isAdmin.value = false
localStorage.removeItem('authToken')
- localStorage.removeItem('isAdmin')
}
// 检查登录状态(验证 token 有效性)
onMounted(async () => {
const token = localStorage.getItem('authToken')
- if (!token) return
+ if (!token) {
+ showLogin.value = true
+ return
+ }
try {
const res = await fetch('/api/auth/me', {
headers: { 'Authorization': `Bearer ${token}` },
})
if (res.ok) {
+ const data = await res.json()
isLoggedIn.value = true
+ isAdmin.value = data.is_admin
} else {
- // token 无效或过期,清除
localStorage.removeItem('authToken')
- localStorage.removeItem('isAdmin')
+ showLogin.value = true
}
} catch {
- // 网络错误时不清除,保持登录状态
+ // 网络错误时保持当前状态
}
})
@@ -116,12 +133,17 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
.nav a.router-link-active { background: #16213e; color: white; }
.nav a:hover { color: white; }
.user-info { display: flex; align-items: center; gap: 10px; }
-.user-avatar { width: 32px; height: 32px; background: #1890ff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 14px; }
+.user-avatar { width: 32px; height: 32px; background: #1890ff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; }
.user-name { font-size: 14px; }
.btn-login { background: #1890ff; color: white; border: none; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-size: 13px; }
.btn-logout { background: transparent; color: #a0a0a0; border: 1px solid #444; padding: 6px 16px; border-radius: 4px; cursor: pointer; font-size: 13px; }
.btn-logout:hover { border-color: #ff4d4f; color: #ff4d4f; }
+.login-required { display: flex; align-items: center; justify-content: center; min-height: calc(100vh - 64px); }
+.login-card { background: white; border-radius: 12px; padding: 48px; text-align: center; box-shadow: 0 2px 12px rgba(0,0,0,0.08); }
+.login-card h2 { font-size: 20px; color: #1a1a2e; margin-bottom: 8px; }
+.login-card p { color: #999; margin-bottom: 24px; }
+
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.login-modal { background: white; border-radius: 12px; padding: 32px; width: 400px; }
.login-modal h2 { font-size: 20px; margin-bottom: 24px; text-align: center; color: #1a1a2e; }
diff --git a/frontend/src/views/BuildView.vue b/frontend/src/views/BuildView.vue
index c9f2172..a932a68 100644
--- a/frontend/src/views/BuildView.vue
+++ b/frontend/src/views/BuildView.vue
@@ -138,9 +138,19 @@