feat: 部署初版测试
This commit is contained in:
66
web/src/views/Login.vue
Normal file
66
web/src/views/Login.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Activity } from 'lucide-vue-next'
|
||||
|
||||
const router = useRouter()
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const errorMsg = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
const handleLogin = async () => {
|
||||
errorMsg.value = ''
|
||||
loading.value = true
|
||||
|
||||
const apiBase = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api/v1'
|
||||
|
||||
try {
|
||||
const res = await fetch(`${apiBase}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email: email.value, password: password.value })
|
||||
})
|
||||
|
||||
if (!res.ok) throw new Error('Invalid credentials')
|
||||
|
||||
const data = await res.json()
|
||||
if (data.token) {
|
||||
localStorage.setItem('jwt_token', data.token)
|
||||
router.push('/dashboard')
|
||||
}
|
||||
} catch (err: any) {
|
||||
errorMsg.value = err.message || 'Login failed'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="h-screen w-full flex items-center justify-center relative bg-[#0B1120]">
|
||||
<div class="glass-panel w-full max-w-sm p-8 z-10 flex flex-col items-center">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-brand-primary to-blue-600 flex items-center justify-center mb-6">
|
||||
<Activity color="white" :size="24" />
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-white mb-2">Welcome Back</h2>
|
||||
<p class="text-zinc-400 text-sm text-center mb-8">Sign in to orchestrate your AI reply strategy.</p>
|
||||
|
||||
<form @submit.prevent="handleLogin" class="w-full space-y-4">
|
||||
<div v-if="errorMsg" class="bg-red-500/10 text-red-400 p-3 rounded-lg text-sm text-center">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-zinc-500 uppercase tracking-widest block mb-2">Email</label>
|
||||
<input v-model="email" type="email" required placeholder="founder@startup.com" class="w-full bg-black/20 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-zinc-600 focus:outline-none focus:border-brand-primary/50 focus:ring-1 focus:ring-brand-primary/50 transition-all cursor-text pointer-events-auto" style="pointer-events: auto;" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-zinc-500 uppercase tracking-widest block mb-2">Password</label>
|
||||
<input v-model="password" type="password" required placeholder="••••••••" class="w-full bg-black/20 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-zinc-600 focus:outline-none focus:border-brand-primary/50 focus:ring-1 focus:ring-brand-primary/50 transition-all cursor-text pointer-events-auto" style="pointer-events: auto;" />
|
||||
</div>
|
||||
<button type="submit" :disabled="loading" class="w-full bg-brand-primary hover:bg-blue-500 disabled:opacity-50 text-white font-medium rounded-lg px-4 py-3 mt-4 transition-all hover:shadow-[0_0_20px_rgba(59,130,246,0.3)] duration-300">
|
||||
{{ loading ? 'Signing In...' : 'Sign In' }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user