Files
workspace/code/fms/fms-vue/src/views/login/index.vue
T
2026-08-10 17:22:36 +08:00

151 lines
5.2 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, ref } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useForm } from "vee-validate"
import { toast } from "vue-sonner"
import { loginApi } from "@/services/api"
import { useAuthStore } from "@/stores/auth"
import { Building2, GalleryVerticalEnd, KeyRound, LoaderCircle, User } from "@lucide/vue"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
Form,
FormField,
FormItem,
FormLabel,
FormControl,
FormMessage,
} from "@/components/ui/form"
import { toTypedSchema } from "@vee-validate/zod"
import { z } from "zod"
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const loginHint = computed(() => {
const reason = String(route.query.reason || "")
if (reason === "session-replaced") return "账号已在其他设备登录,请重新登录"
if (reason === "expired") return "登录已过期,请重新登录"
return ""
})
const formSchema = toTypedSchema(
z.object({
orgId: z.string({ required_error: "请输入机构码" }).min(1, "机构码不能为空"),
userId: z.string({ required_error: "请输入账号" }).min(1, "账号不能为空"),
password: z.string().optional(),
}),
)
const form = useForm({
validationSchema: formSchema,
})
const submitting = ref(false)
const orgIdInput = ref<InstanceType<typeof Input> | null>(null)
onMounted(() => {
form.setValues({
orgId: authStore.orgId || '',
userId: authStore.user?.account || '',
password: '',
})
orgIdInput.value?.$el?.focus()
})
async function onSubmit(values: Record<string, unknown>) {
submitting.value = true
try {
const { orgId, userId, password } = values as { orgId: string; userId: string; password?: string }
const res = await loginApi(orgId, userId, password ?? "")
authStore.setSession(
{ token: res.data.token, user: { ...res.data.user, account: userId } },
res.data.orgid ?? orgId,
)
toast.success('登录成功')
router.replace(String(route.query.redirect || "/dashboard"))
} catch (e: unknown) {
if (e instanceof Error) alert(e.message)
} finally {
submitting.value = false
}
}
function login() {
form.handleSubmit(onSubmit)()
}
</script>
<template>
<div class="flex min-h-svh flex-col items-center justify-center bg-background p-6 md:p-10">
<div class="w-full max-w-sm">
<Form class="flex flex-col gap-6" :form="form">
<!-- Header -->
<div class="flex flex-col items-center gap-2 text-center">
<a href="#" class="flex flex-col items-center gap-2 font-medium">
<div class="flex size-8 items-center justify-center rounded-md">
<GalleryVerticalEnd class="size-6" />
</div>
<span class="sr-only">FMS</span>
</a>
<h1 class="text-xl font-bold">登录 FMS</h1>
</div>
<!-- Hint -->
<div
v-if="loginHint"
class="rounded-md bg-amber-50 px-4 py-2.5 text-center text-sm text-amber-700 dark:bg-amber-950 dark:text-amber-300"
>{{ loginHint }}</div>
<!-- orgId -->
<FormField v-slot="{ componentField }" name="orgId">
<FormItem>
<FormLabel>机构码 <span class="text-destructive">*</span></FormLabel>
<FormControl>
<div class="relative">
<span class="absolute inset-y-0 left-3 flex items-center text-muted-foreground"><Building2 class="size-4" /></span>
<Input id="orgId" ref="orgIdInput" v-bind="componentField" placeholder="请输入机构码" class="pl-9" :disabled="submitting" />
</div>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<!-- userId -->
<FormField v-slot="{ componentField }" name="userId">
<FormItem>
<FormLabel>账号 <span class="text-destructive">*</span></FormLabel>
<FormControl>
<div class="relative">
<span class="absolute inset-y-0 left-3 flex items-center text-muted-foreground"><User class="size-4" /></span>
<Input id="userId" v-bind="componentField" placeholder="请输入账号" class="pl-9" :disabled="submitting" />
</div>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<!-- password -->
<FormField v-slot="{ componentField }" name="password">
<FormItem>
<FormLabel>密码</FormLabel>
<FormControl>
<div class="relative">
<span class="absolute inset-y-0 left-3 flex items-center text-muted-foreground"><KeyRound class="size-4" /></span>
<Input id="password" v-bind="componentField" type="password" placeholder="请输入密码" class="pl-9" :disabled="submitting" />
</div>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<Button class="w-full cursor-pointer" :disabled="submitting" @click="login">
<LoaderCircle v-if="submitting" class="size-4 animate-spin" />
登录
</Button>
</Form>
</div>
</div>
</template>