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

3.1 KiB
Raw Blame History

9.8 登录流程

:::tip 概述 本节说明前端登录流程的实现,包括登录表单提交、Token 存储、路由守卫和登出处理。 :::

前端登录流程

1. 用户输入用户名、密码、验证码
2. 调用 POST /api/login 接口
3. 成功 → 存储 Token 到 localStorage
4. 路由守卫检测到 Token → 获取用户信息
5. 生成动态路由 → 跳转首页

登录页面代码

<!-- 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 存储

// 登录成功后存储
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');
}