115 lines
3.1 KiB
Vue
115 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
import { userClient, type LoginParams } from '@/apis';
|
|
import { ThemeSwitch } from '@/components';
|
|
import { useUserStore } from '@/stores';
|
|
import { parseErrorFeedback, randomNum } from '@/utils';
|
|
import { useMutation } from '@tanstack/vue-query';
|
|
import { NButton, NCard, NFlex, NForm, NFormItem, NInput, NLayout } from 'naive-ui';
|
|
import { reactive } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
|
|
const router = useRouter();
|
|
|
|
const loginParams = reactive<LoginParams>({
|
|
username: '',
|
|
password: '',
|
|
code: '',
|
|
key: randomNum(24, 16),
|
|
grantType: 'PASSWORD',
|
|
});
|
|
|
|
const { mutate: login, isPending: loading } = useMutation({
|
|
mutationFn: async (params: LoginParams) => {
|
|
const userStore = useUserStore();
|
|
await userStore.userLogin(params);
|
|
const [err] = await userClient.post<void>(`/api/ndm/ndmKeepAlive/verify`, {}, { timeout: 5000 });
|
|
if (err) throw err;
|
|
},
|
|
onSuccess: () => {
|
|
window.$message.success('登录成功');
|
|
router.push({ path: '/' });
|
|
},
|
|
onError: (error) => {
|
|
console.error(error);
|
|
const errorFeedback = parseErrorFeedback(error);
|
|
window.$dialog.destroyAll();
|
|
window.$dialog.error({
|
|
closable: false,
|
|
maskClosable: false,
|
|
title: '错误提示',
|
|
content: errorFeedback,
|
|
positiveText: '确认',
|
|
onPositiveClick: () => {
|
|
window.$message.destroyAll();
|
|
},
|
|
});
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<NLayout class="login-page" position="absolute">
|
|
<div class="login-card-wrapper" @keyup.enter="() => login(loginParams)">
|
|
<NCard class="login-card">
|
|
<ThemeSwitch class="theme-switch" />
|
|
<NFlex vertical justify="center" align="center">
|
|
<span class="platform-title">网络设备管理平台</span>
|
|
<span class="login-title">登录</span>
|
|
<NForm class="login-form" :model="loginParams" label-placement="top">
|
|
<NFormItem label="用户名" path="username">
|
|
<NInput v-model:value="loginParams.username" placeholder="请输入用户名" />
|
|
</NFormItem>
|
|
<NFormItem label="密码" path="password">
|
|
<NInput v-model:value="loginParams.password" type="password" show-password-on="click" placeholder="请输入密码" />
|
|
</NFormItem>
|
|
</NForm>
|
|
<NButton type="primary" block strong :loading="loading" @click="() => login(loginParams)">登录</NButton>
|
|
</NFlex>
|
|
</NCard>
|
|
</div>
|
|
</NLayout>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.login-page {
|
|
.login-card-wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.login-card {
|
|
width: 400px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
padding: 32px;
|
|
position: relative;
|
|
}
|
|
|
|
.theme-switch {
|
|
position: absolute;
|
|
bottom: 16px;
|
|
right: 16px;
|
|
}
|
|
|
|
.platform-title {
|
|
font-size: 28px;
|
|
font-weight: 500;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.login-title {
|
|
font-size: 16px;
|
|
color: #999;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.login-form {
|
|
width: 100%;
|
|
margin-bottom: 16px;
|
|
}
|
|
}
|
|
</style>
|