vue3+element-plus+登录逻辑token环境搭建

  • 安装脚手架工具

1 npm i @vue/cli@4.5.13 -g
  • 验证是否安装成功

1 vue -V # 输出 @vue/cli 4.5.13
  • 脚手架初始化目录结构

1 vue create jd-shop-manager

  • 手动配置需要的功能

  • 进入到项目目录

1 cd jd-shop-manager
  • 附加其他依赖

1 npm i axios element-plus pinia -S
  • 运行项目

1 npm run serve

浏览器输入localhost:8080查看效果

配置第三方库,main.js根目录

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

// 1. 引入 { createPinia }
import { createPinia } from 'pinia'

import router from './router'

createApp(App)
// 2. 安装插件
.use(createPinia())
.use(ElementPlus)
.use(router)
.mount('#app')

创建文件utils/request.js,关于token认证,

  1. 获取token
  2. 保存token
  3. 后续携带toke
import Axios from 'axios';

const req = Axios.create({
// baseURL:'http://localhost:3000' // 跨域
baseURL:'/api' // 实例独特的表示,走代理解决跨域
});
// 拦截器 = 公共行为: 请求loading 响应关闭loading
// 响应的错误常规处理 401 => 无权访问, 路由跳转到401页面
// 请求时 如果有token, 自动添加到请求头,响应时自动存储token
req.interceptors.response.use((response)=>{
// response:{ data,config:{ headers } }
if (response.data.token) {
// 保存到本地存储: sessionStorage, localStorage
sessionStorage.setItem('token',response.data.token)
}
// 处理响应的业务异常
return response;
},(err)=>{
// 4xx 5xx异常
// 非业务范畴的通用异常提示
console.log('响应异常:',err)
})
// 请求使用token
req.interceptors.request.use((config)=>{
const token = sessionStorage.getItem('token');
if (token) {
config.headers.token = token;
}
return config;
});
export default req;

Axios,vue.config.js配置代理,去玩微博里面找

创建文件api/user.js

1
// 引入3000端口的实例
import req from '@/utils/request';

export const test = ()=> {
return req.get('/')
}
export const login = (data)=>{
return req({
url:'/login',
method:'post',
data
})
}

export const loadMenu = ()=>{
return req.get('/menus/build')
}

pinia插件配置,创建文件store/user.js

// 3. 定义一个Store
// 3.1 引入定义Store的函数
import { defineStore } from 'pinia';
// 4.按约定俗成 返回useXxx
export const useUser = defineStore({
id:'user',
state(){
return {
userInfo:{},
userMenu:[]
}
},
// 修改方式
actions:{
setUserInfo(user){
this.userInfo = user;
},
// { userMenu:值 }
setUserMenu(patchData) {
this.$patch(patchData);
}
}
})

配置路由文件

创建router/index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
import { useUser } from '@/store/user'
import { loadMenu } from '@/api/user';
import Layout from '@/views/Layout.vue'

function checkLogin() {
// 前端验证token有效性
// 请求后端验证token有效性
return window.sessionStorage.getItem('token');
}

function asyncRouteHandler(routes) {
return routes.map(route=>{
// 判断Componet是不是一个布局组件名 Layout
if (route.component === 'Layout') {
route.component = Layout;
} else {
const compPath = route.component;
route.component = () => import(`../views/${compPath}.vue`)
}
// 处理children
if (route.children && route.children.length > 0) {
route.children = asyncRouteHandler(route.children);
}
// 返回route
return route;
});
}

const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/login',
name: 'Login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')
}
]

const router = createRouter({
history: createWebHashHistory(),
routes
})

async function loadAsyncMenu(to, next) {
const user = useUser();
// 动态根据用户的角色或者id拿到他能看到的页面,并生成路由
const res = await loadMenu();
console.log(res.data,'路由数据'); // []

const asyncRoutes = asyncRouteHandler(res.data);
// Todo: 保存菜单到pinia
user.setUserMenu({ userMenu:res.data });

// 添加到原来的路由对象中
asyncRoutes.forEach(r=>{
router.addRoute(r);
});
// 重新进入路由 , replace避免多一个上一步
next({...to,replace:true});
}
// 路由守卫, 在每次确认路由和跳转页面之前
router.beforeEach((to, from, next) => {
const user = useUser();
// next(); 放行
// next(route对象|| stringPath ); 重定向
// next(false); 取消用户导航行为
// 不调用 白屏卡住
// to 代表当前的访问路径 from 代表从哪里来 to|from.path 具体路径

// 访问白名单(可以是个数组)不需要验证 /login
if (to.path === '/login' || to.path === '/register') {
// 不验证/不重定向 => 放行
return next();
}
// 需要验证
if (!checkLogin()) {
// 去登录
// return next('/login?redirect='+ to.path);
return next({
name: 'Login', query: {
redirect: to.path
}
});
}
// 登录了, 判断是否已生成路由
if (user.userMenu && user.userMenu.length > 0) {
return next(); // 放行
}
// 没有菜单,加载菜单
loadAsyncMenu(to, next)
})
export default router

实现一个基于ElementPlus的基础登录框

创建Login.vue中写

<template> 
  <el-form label-width="80px" ref="formRef" :model="form" :rules="rules">
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username"></el-input>
    </el-form-item>
    <el-form-item label="密码" prop="password">
      <el-input v-model="form.password"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button @click="submit(formRef)">提交</el-button>
      <el-button @click="reset(formRef)">重置</el-button>
    </el-form-item>
  </el-form>
</template>
<script setup>
//基本 内部多属性
import { ref, reactive } from 'vue';
import { test, login } from '@/api/user'
import { useUser } from '@/store/user';
import { useRoute,useRouter } from 'vue-router'

// 一定要在方法内使用 useUser
const user = useUser(); // 相当于setup方法
const route = useRoute();
const router = useRouter();

// 单向数据输出,双向数据绑定,还需要API方法的调用
const formSize = ref('default'); // formSize.value
const ruleForm = reactive({ // 直接跟原始结构一样 ruleForm.username
username: 'Hello123',
password: '123456'
})
// 需要改变的复杂对象, reactive, 性能优化,直接使用固定对象
const rules = {
username: [
{ required: true, message: '必须输入用户名', trigger: 'blur' },
],
password: [
{ required: true, message: '必须输入密码', trigger: 'blur' },
]
}
// 获取页面组件对象
const ruleFormRef = ref();

// const doSubmit = () => {
// // 需要注意
// ruleFormRef.value.validate((valid, fields) => {
// if (valid) {
// console.log('submit!')
// } else {
// console.log('error submit!', fields)
// }
// })
// }

// 传递参数的方式
const doSubmit = (form) => {
// 需要注意
form.validate(async (valid, fields) => {
if (valid) {
console.log('submit!');
// login
let res = await login(ruleForm);
console.log(res, '测试数据');
// 登录成功
// 修改Pinia里面的数据状态
// user.setUserInfo({
// username: 'Green'
// });

// 跳转到首页 或者 [之前地址栏的页面] 路由参数redirect
router.push(route.query.redirect || '/');

} else {
console.log('error submit!', fields)
}
})
}

样式篇

  • 初始化样式

html,body { height: 100%; width: 100%;}a { text-decoration: none; }ul {padding:0; }li {list-style: none; }html,body,h1,h2,h3,h4,h5,h6 { padding:0; margin:0; }span {display:inline-block; }

vue3+element-plus+登录逻辑token+环境搭建的更多相关文章

  1. 【Vue3+Express实战】项目开发环境搭建

    大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...

  2. [精华][推荐]CAS SSO 单点登录框架学习 环境搭建

    1.了解单点登录  SSO 主要特点是: SSO 应用之间使用 Web 协议(如 HTTPS) ,并且只有一个登录入口. SSO 的体系中有下面三种角色: 1) User(多个) 2) Web 应用( ...

  3. ssh运行环境搭建及测试

    一.运行环境 1.Spring环境 Spring是一站式开发框架,在SSH中主要有以下作用,就像一个大管家: 控制反转(Inversion of Control):类不再自己进行类创建,而是交给Spr ...

  4. web 单一平台登录逻辑

    单点登录逻辑token=cookie('login_token');serverToken=get(token);if(serverToken!='注销'){ set('userid_已登录token ...

  5. ASP.NET WebAPI 双向token实现对接小程序登录逻辑

    最近在学习用asp.net webapi搭建小程序的后台服务,因为基于小程序端和后台二者的通信,不像OAuth(开放授权),存在第三方应用.所以这个token是双向的,一个是对用户的,一个是对接口的. ...

  6. Shrio00 Shiro认证登录、权限管理环境搭建

    基础环境准备: JDK -> java version "1.8.0_101" MAVEN -> Apache Maven 3.5.0 1 导入依赖 mysql驱动 m ...

  7. Windows 环境搭建 PostgreSQL 逻辑复制高可用架构数据库服务

    本文主要介绍 Windows 环境下搭建 PostgreSQL 的主从逻辑复制,关于 PostgreSQl 的相关运维文章,网络上大多都是 Linux 环境下的操作,鲜有在 Windows 环境下配置 ...

  8. Struts2开发环境搭建,及一个简单登录功能实例

    首先是搭建Struts2环境. 第一步 下载Struts2去Struts官网 http://struts.apache.org/ 下载Struts2组件.截至目前,struts2最新版本为2.3.1. ...

  9. 1、IOS开发--iPad之仿制QQ空间(登录界面搭建+登录逻辑实现)

    开始搭建登录界面 登录界面效果图: 相关的图片资源下载百度云备份链接: http://pan.baidu.com/s/1o71cvMU 密码: 2h7e 步骤开始: 设置辅助窗口的位置在下方 快捷键o ...

随机推荐

  1. "蔚来杯"2022牛客暑期多校训练营9 G Magic Spells【马拉车+哈希】

    四川今天又上热搜了,继南部疫情的未雨绸缪后,龙槽沟是真的倾盆大雨了.我没有兴趣虚伪矫情地对罹难的游人表达同情,因为人与人互不相通徒增谈资:我也没有兴趣居高临下地对擅闯的愚人表达不屑,因为你我皆为乌合之 ...

  2. 如何自定义一个Collector

    Collectors类提供了很多方便的方法,假如现有的实现不能满足需求,我们如何自定义一个Collector呢?   Collector接口提供了一个of方法,调用该方法就可以实现定制Collecto ...

  3. 基于bert训练自己的分词系统

    前言 在中文分词领域,已经有着很多优秀的工具,例如: jieba分词 SnowNLP 北京大学PKUse 清华大学THULAC HanLP FoolNLTK 哈工大LTP 斯坦福分词器CoreNLP ...

  4. Spire.Cloud 私有化部署教程(三) - Windows 系统

    本教程主要介绍如何在 Windows 系统上实现 Spire.Cloud 私有化部署. 详细步骤如下: 一.安装依赖 我们的私有部署的依赖有 Nodejs.MySQL.Redis 和 RabbitMQ ...

  5. Linux之firewalld防火墙规则

    一, 什么是防火墙规则? 允许哪些服务端口被放行,怎么放行,及哪些服务端口被阻拦,如何阻拦的一组网络安全规则.支持ipv4和ipv6,且分为直接规则和富规则两种. 二, 如何管理firewalld 1 ...

  6. 数据结构与算法【Java】05---排序算法总结

    前言 数据 data 结构(structure)是一门 研究组织数据方式的学科,有了编程语言也就有了数据结构.学好数据结构才可以编写出更加漂亮,更加有效率的代码. 要学习好数据结构就要多多考虑如何将生 ...

  7. 自定义View4-塔防小游戏第一篇:一个防御塔+多个野怪(简易版)*

    塔防小游戏 第一篇:一个防御塔+多个野怪(简易版)    1.canvas画防御塔,妖怪大道,妖怪行走路线    2.防御塔攻击范围是按照妖怪与防御塔中心距离计算的,大于防御塔半径则不攻击,小于则攻击 ...

  8. KingbaseES应对表年龄增长过快导致事务回卷

    背景 前几天碰到这样一个场景,在KingbaseES数据库当作数据同步节点.其特点是接收过来的数据量巨大,其更新超级频繁,最大的数据库达到6TB.这还不是主要的,主要导致问题发生原因是同步数据库有很多 ...

  9. STL再回顾(非常见知识点)

    目录 为人熟知的pair类型 再谈STL 迭代器的使用 常用的STL容器 顺序容器 vector(向量) 构造方式 拥有的常用的成员函数(java人称方法) string 构造方式 成员函数 dequ ...

  10. Openstack neutron:目录

    为什么? 最近一直在学习SDN方面的知识,本着"最好的学习就是分享"的精神,记录下本系列的文章,尝试更好地去理解SDN这一正当红的技术. 如何? SDN领域现在已经充斥了大量的公司 ...