【vue3】:前端登录功能解决方案

2022/6/26 方案vue3element-plusJWT

# # 后台登录功能解决方案

# # 配置生产环境和开发环境

// .env.development
# 标志
ENV = 'development'

# base api
VUE_APP_BASE_API = '/api'
1
2
3
4
5
6
// .env.production
# 标志
ENV = 'production'

# base api
VUE_APP_BASE_API = '/prod-api'
1
2
3
4
5
6

# # 配置接口代理

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "后台地址",
        changeOrigin: true, // 是否跨域
      },
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11

# # 封装 axios 模块

// require.js
import axios from "axios";
import { ElMessage } from "element-plus";
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000,
});
// 请求接口拦截
service.interceptors.request.use((config) => {
  // 统一添加请求参数
  config.headers.code = "参数值";
  return config;
});
// 响应拦截器
service.interceptors.response.use(
  (response) => {
    const { success, message, data } = response.data;
    if (success) {
      // 请求成功
      return data;
    } else {
      ElMessage.error(message);
      return Promise.reject(new Error(message));
    }
  },
  (error) => {
    ElMessage.error(error.message);
    return Promise.reject(error);
  }
);
export default service;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# # 基于 localstorage 封装保存服务端返回的 token

// 设置值
export const setItem = (key, value) => {
  if (typeof value === "object") {
    value = JSON.stringify(value);
  }
  window.localStorage.setItem(key, value);
};

// 获取值
export const getItem = (key) => {
  const data = window.localStorage.getItem(key);
  try {
    return JSON.parse(data);
  } catch (err) {
    return data;
  }
};

// 删除值
export const removeItem = (key) => {
  window.localStorage.renoveItem(key);
};

// 删除所有数据
export const removeAllItem = (key) => {
  window.localStorage.clear();
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# # 封装 接口请求 模块

// sys.js
import request from "@/utils/require";
// 登录 API
export const login = (data) => {
  return request({
    url: "/sys/login",
    method: "POST",
    data,
  });
};
1
2
3
4
5
6
7
8
9
10

# # 封装登录请求动作

单独将有关用户部分的请求封装到 vuex中的user模块中

import { login } from "@/api/sys";
import md5 from "md5";
import { setItem, getItem } from "@/utils/storage";
import router from "@/router";
export default {
  namespaced: true, // 该模块为单独的模块
  state: () => ({
    token: getItem(TOKEN) || "",
  }),
  mutations: {
    setToken(state, token) {
      state.token = token;
      setItem("token", token);
    },
  },
  actions: {
    // 登录请求动作
    login(context, userInfo) {
      const { username, password } = userInfo;
      return new Promise((resolve, reject) => {
        login({
          username,
          password: md5(password),
        })
          .then((data) => {
            // 登录成功 调用 setToken 将 值存放到 localstorage 及 vuex
            this.commit("user/setToken", data.token);
            // 跳转
            router.push("/");
            resolve();
          })
          .catch((err) => {
            reject(err);
          });
      });
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

在 store 入口文件index.js中注册 user 模块

import { createStore } from "vuex";
import user from "./modules/user.js";
import getters from "./getters";
export default createStore({
  getters,
  modules: {
    user,
  },
});
1
2
3
4
5
6
7
8
9

# # 登录鉴权

在访问项目时,需要判断用户是否已经登录了,登录了则直接进入到主页。反则,跳转到登录到页面。

使用 vue-router 前置路由来进行登录鉴权

// permission.js
import router from "./router";
import store from "./store";

// 白名单 (无需登录,即可访问的页面)
const whiteList = ["/login"];

router.beforeEach(async (to, from, next) => {
  if (store.getters.token) {
    // 已登录
    if (to.path === "/login") {
      // 访问登录界面
      next("/"); // 直接进入到主页
    } else {
      next(); // 正常
    }
  } else {
    // 未登录
    if (whiteList.indexOf(to.path) > -1) {
      next();
    } else {
      next("/login");
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// main.js
// 导入路由鉴权
import "./permission";
1
2
3

# # 使用

<el-button type="primary" :loading="loading" @click="handleLogin"
  >登录</el-button
>
<script setup>
  import { useStore } from "vuex";
  const handleLogin = () => {
    store
      .dispatch("user/login", loginForm.value)
      .then(() => {
        console.log("登录成功");
      })
      .catch((err) => {
        console.log(err);
      });
  };
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16