Files
rxthinkcmf-wiki/RXThinkCMF_ThinkPhp6_NaiveVue/zh/frontend/login.md
T
2026-10-10 19:17:02 +08:00

128 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 9.8 登录流程
:::tip 概述
本节说明前端登录流程的实现,包括登录表单提交、Token 存储、路由守卫和登出处理。
:::
## 前端登录流程
```text
1. 用户输入用户名、密码、验证码
2. 调用 POST /api/login 接口
3. 成功 → 存储 Token 到 localStorage
4. 路由守卫检测到 Token → 获取用户信息
5. 生成动态路由 → 跳转首页
```
## 登录页面代码
```vue
<!-- src/views/login/LoginForm.vue(节选) -->
<template>
<n-form
ref="formRef"
:model="formInline"
:rules="rules"
:show-label="false"
size="large"
>
<n-form-item path="username">
<n-input v-model:value="formInline.username" placeholder="请输入登录账号" />
</n-form-item>
<n-form-item path="password">
<n-input
v-model:value="formInline.password"
type="password"
show-password-on="mousedown"
placeholder="请输入登录密码"
@keyup.enter="handleSubmit"
/>
</n-form-item>
<n-form-item path="code">
<div style="display: flex">
<n-input
v-model:value.trim="formInline.code"
placeholder="验证码"
@keyup.enter="handleSubmit"
/>
<img
style="width: 108px; height: 40px; margin-left: 8px; cursor: pointer"
@click="getCaptcha"
v-if="captchaImg"
:src="captchaImg"
/>
</div>
</n-form-item>
<n-form-item>
<n-button
class="w-full"
type="primary"
size="large"
:loading="loading"
@click="handleSubmit"
>
登录
</n-button>
</n-form-item>
</n-form>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useMessage, type FormRules } from 'naive-ui';
import { useUserStore } from '@/store/modules/user';
import { getInfoCaptcha } from '@/api/common/user';
const router = useRouter();
const message = useMessage();
const userStore = useUserStore();
const formInline = reactive({ username: '', password: '', code: '' });
const rules: FormRules = {
username: [{ required: true, message: '请输入登录账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入登录密码', trigger: 'blur' }],
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
};
const loading = ref(false);
const captchaImg = ref('');
// 获取验证码
const getCaptcha = async () => {
const res = await getInfoCaptcha();
captchaImg.value = res.image;
};
// 登录
const handleSubmit = async () => {
loading.value = true;
try {
await userStore.login(formInline);
router.push('/');
} catch (e) {
message.error(e.message || '登录失败');
} finally {
loading.value = false;
}
};
onMounted(() => getCaptcha());
</script>
```
## Token 存储
```typescript
// 登录成功后存储
storage.set(ACCESS_TOKEN, data.access_token, 7 * 24 * 60 * 60 * 1000); // 7天过期
// 请求拦截器自动携带
config.headers['Authorization'] = `Bearer ${token}`;
// 响应拦截器检测 401
if (response.data.code === 401) {
router.push('/login');
}
```