e563f17283
- 添加JWT认证中间件保护API端点 - 在登录流程中使用bcrypt哈希密码和JWT令牌 - 配置HTTPS服务器使用自签名证书 - 更新前端API调用以包含认证令牌
118 lines
2.9 KiB
Vue
118 lines
2.9 KiB
Vue
<template>
|
|
<div class="login-wrapper">
|
|
<div class="login-card card">
|
|
<h2>Login</h2>
|
|
<form @submit.prevent="handleLogin">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" v-model="username" class="form-input" required />
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<input type="password" id="password" v-model="password" class="form-input" required />
|
|
</div>
|
|
<p class="info">Hint: worker/password or manager/password</p>
|
|
<button type="submit" class="button-primary login-button" :disabled="loading">
|
|
{{ loading ? 'Logging in...' : 'Login' }}
|
|
</button>
|
|
<p v-if="error" class="error-message">{{ error }}</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
const router = useRouter()
|
|
const username = ref('')
|
|
const password = ref('')
|
|
const error = ref('')
|
|
const loading = ref(false)
|
|
|
|
const handleLogin = async () => {
|
|
loading.value = true
|
|
error.value = ''
|
|
try {
|
|
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/auth/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username: username.value, password: password.value }),
|
|
})
|
|
|
|
const data = await response.json()
|
|
|
|
if (response.ok) {
|
|
// Store token and user info in session storage
|
|
sessionStorage.setItem('token', data.token)
|
|
try {
|
|
const decodedToken = JSON.parse(atob(data.token.split('.')[1]))
|
|
sessionStorage.setItem('userId', decodedToken.userId)
|
|
sessionStorage.setItem('userRole', decodedToken.role)
|
|
|
|
if (decodedToken.role === 'worker') {
|
|
router.push('/worker/dashboard')
|
|
} else if (decodedToken.role === 'manager') {
|
|
router.push('/manager/dashboard')
|
|
}
|
|
} catch (e) {
|
|
error.value = 'Invalid token received from server.'
|
|
}
|
|
} else {
|
|
error.value = data.message
|
|
}
|
|
} catch {
|
|
error.value = 'Failed to connect to the server.'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Styles remain the same */
|
|
.login-wrapper {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 70vh;
|
|
}
|
|
.login-card {
|
|
width: 100%;
|
|
max-width: 400px;
|
|
}
|
|
.login-card h2 {
|
|
text-align: center;
|
|
margin-top: 0;
|
|
}
|
|
.form-group {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
font-weight: 500;
|
|
}
|
|
.form-input {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
.info {
|
|
font-size: 0.8rem;
|
|
color: var(--c-text-secondary);
|
|
text-align: center;
|
|
margin: -0.5rem 0 1.5rem 0;
|
|
}
|
|
.login-button {
|
|
width: 100%;
|
|
padding: 12px;
|
|
font-size: 1.1rem;
|
|
}
|
|
.error-message {
|
|
color: var(--c-danger);
|
|
text-align: center;
|
|
margin-top: 1rem;
|
|
}
|
|
</style>
|