You've already forked agentic-coding-workflow
76 lines
2.0 KiB
Vue
76 lines
2.0 KiB
Vue
<template>
|
|
<div class="min-h-screen flex items-center justify-center p-4">
|
|
<div class="w-full max-w-sm space-y-4">
|
|
<h1 class="text-2xl font-semibold">
|
|
登录
|
|
</h1>
|
|
<NForm @submit.prevent="onSubmit">
|
|
<NFormItem label="邮箱">
|
|
<NInput
|
|
v-model:value="email"
|
|
placeholder="admin@local"
|
|
/>
|
|
</NFormItem>
|
|
<NFormItem label="密码">
|
|
<NInput
|
|
v-model:value="password"
|
|
type="password"
|
|
show-password-on="click"
|
|
/>
|
|
</NFormItem>
|
|
<NButton
|
|
type="primary"
|
|
attr-type="submit"
|
|
:loading="loading"
|
|
:disabled="!isValid"
|
|
block
|
|
>
|
|
登录
|
|
</NButton>
|
|
</NForm>
|
|
<p
|
|
v-if="error"
|
|
class="text-red-500 text-sm"
|
|
>
|
|
{{ error }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import { NForm, NFormItem, NInput, NButton } from 'naive-ui'
|
|
import { useRouter } from 'vue-router'
|
|
import { authApi } from '@/api/auth'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { ApiException } from '@/api/types'
|
|
|
|
const email = ref('')
|
|
const password = ref('')
|
|
const loading = ref(false)
|
|
const error = ref('')
|
|
|
|
const isValid = computed(() => email.value.trim().length > 0 && password.value.length > 0)
|
|
|
|
const auth = useAuthStore()
|
|
const router = useRouter()
|
|
|
|
async function onSubmit() {
|
|
loading.value = true
|
|
error.value = ''
|
|
try {
|
|
const out = await authApi.login(email.value, password.value)
|
|
auth.setSession(out.token, out.user)
|
|
const raw = router.currentRoute.value.query.next
|
|
const next =
|
|
typeof raw === 'string' && raw.startsWith('/') && !raw.startsWith('//') ? raw : '/'
|
|
await router.replace(next).catch(() => {})
|
|
} catch (e) {
|
|
error.value = e instanceof ApiException ? e.body.message : '登录失败'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|