Vue2后台管理:项目开发全流程(一)

​🌈个人主页:前端青山
🔥系列专栏:vue篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来vue篇专栏内容:Vue2后台管理:项目开发全流程(一)

目录

Vue项目开发

项目架构搭建

1、创建项目

2、技术栈说明

初始化项目包

3、UI组件库

4、路由规划和配置

二、功能实现

1、注册功能

2、登录功能

3、后台首页布局

4、实现菜单跳转路由

5、注销登录

6、防止翻墙越权

7、回车确认提交

Vue项目开发

Vue开发项目核心点使用JS开发项目,前端页面。

Vue和原生JS及其jQuery的区别:换一种操作语法,尽量少的操作DOM

拥有了组件化,工程化的概念。可以进打包,压缩,混淆代码。。。。。 单管理功能......针对不同的业务模块设置和开发的各种功能

项目架构搭建

前后端分离项目

1、创建项目
# 安装脚手架
npm i -g @vue/cli

# 生成项目包  在哪儿生成项目包 在哪儿执行
vue create 项目名称

选择创建项目包的参数选项

2、技术栈说明

客户端:

Vuejs 渲染库

Vue-Router SPA 单页面应用路由管理

Vuex 组件状态共享和管理

axios ajax请求的发送和封装

momentjs 处理时间格式化

scss css预处理器

服务端:

1、nodejs结合mongodb实现API接口

2、json-server 模拟API数据结构

3、使用服务端人员开发的接口

初始化项目包

根据需求删除项目包中无用的文件和内容

3、UI组件库

UI组件库 能够帮助开发者快速实现页面效果的一种方式。封装好的一些组件开源出来,供大家使用。

自己调查一下,上网查,问问你的朋友同学他们在公司用什么?

组件库名称使用端框架
vantUI 有赞移动端Vue,React,微信小程序,支付宝小程序
mintUI 饿了么移动端Vue
elementUI 饿了么PC端Vue,React,Angular
antdesign 蚂蚁金服PC端Vue,React,Angular
antmobile 蚂蚁金服移动端React
nuxtui 京东移动端Vue,React

本次使用elementUI:https://element.eleme.io/#/zh-CN

①安装组件库

npm i element-ui -S

②完整引入elementUI

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 1、添加引入以下两行
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false
// 2、使用组件库
Vue.use(ElementUI)
new Vue({
  router,
  store,
  render: (h) => h(App)
}).$mount('#app')

③测试使用

App.vue 测试按钮组件引入使用情况

<template>
  <div>
    <el-row>
      <el-button>默认按钮</el-button>
      <el-button type="primary">主要按钮</el-button>
      <el-button type="success">成功按钮</el-button>
      <el-button type="info">信息按钮</el-button>
      <el-button type="warning">警告按钮</el-button>
      <el-button type="danger">危险按钮</el-button>
    </el-row>

    <el-row>
      <el-button plain>朴素按钮</el-button>
      <el-button
        type="primary"
        plain
      >主要按钮</el-button>
      <el-button
        type="success"
        plain
      >成功按钮</el-button>
      <el-button
        type="info"
        plain
      >信息按钮</el-button>
      <el-button
        type="warning"
        plain
      >警告按钮</el-button>
      <el-button
        type="danger"
        plain
      >危险按钮</el-button>
    </el-row>

    <el-row>
      <el-button round>圆角按钮</el-button>
      <el-button :round="true">圆角按钮</el-button>
      <el-button
        type="primary"
        round
      >主要按钮</el-button>
      <el-button
        type="success"
        round
      >成功按钮</el-button>
      <el-button
        type="info"
        round
      >信息按钮</el-button>
      <el-button
        type="warning"
        round
      >警告按钮</el-button>
      <el-button
        type="danger"
        round
      >危险按钮</el-button>
    </el-row>

    <el-row>
      <el-button
        icon="el-icon-search"
        circle
      ></el-button>
      <el-button
        type="primary"
        icon="el-icon-edit"
        circle
      ></el-button>
      <el-button
        type="success"
        icon="el-icon-check"
        circle
      ></el-button>
      <el-button
        type="info"
        icon="el-icon-message"
        circle
      ></el-button>
      <el-button
        type="warning"
        icon="el-icon-star-off"
        circle
      ></el-button>
      <el-button
        type="danger"
        icon="el-icon-delete"
        circle
      ></el-button>
    </el-row>
  </div>
</template>

<script>
export default {}
</script>

<style lang="scss" scoped>
</style>

4、路由规划和配置

功能URL备注
注册/register
登录/login
管理功能/admin父级路由
用户管理功能/admin/user嵌套子路由

注意:在配置好路由文件后,一定要记得在App.vue添加渲染标签 否则无法切换显示

<router-view></router-view>

二、功能实现

1、注册功能

①表单选项布局

根据注册功能需要的表单选项进行表单的设置及其校验工作。收集数据并通过ajax提交

src\views\Register.vue

<template>
    <div class="container">
        <div class="title">综合数据运营管理平台</div>
        <!-- 注册表单 -->
        <!-- card组件 -->
        <el-card style="margin-top: 20px;padding: 10px 30px">
            <!-- 表单组件 -->
            <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px">
                <!-- 表单每个元素 -->
                <el-form-item prop="username">
                    <!-- 输入框 -->
                    <el-input v-model="ruleForm.username" placeholder="用户名"></el-input>
                </el-form-item>
                <el-form-item prop="pass">
                    <el-input type="password" v-model="ruleForm.pass" autocomplete="off" placeholder="密码"></el-input>
                </el-form-item>
                <el-form-item prop="checkPass">
                    <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off" placeholder="确认密码"></el-input>
                </el-form-item>

                <el-form-item style="display: flex;justify-content:space-around;">
                    <el-button type="primary" style="width: 220px;" @click="submitForm('ruleForm')">注册</el-button>
                    <!-- <el-button @click="resetForm('ruleForm')">重置</el-button> -->
                </el-form-item>
            </el-form>
        </el-card>
    </div>
</template>

<script>
export default {
    data() {
        // 校验用户名
        var checkUsername = (rule, value, callback) => {
            if (!value) {
                return callback(new Error('用户名不能为空'));
            }
            return callback()
        };
        // 校验密码
        var validatePass = (rule, value, callback) => {
            if (value === '') {
                callback(new Error('请输入密码'));
            } else if (value.length < 6) {
                callback(new Error('密码长度最少6位'))
            }
            else {
                if (this.ruleForm.checkPass !== '') {
                    this.$refs.ruleForm.validateField('checkPass');
                }
                callback();
            }
        };
        // 校验确认密码
        var validatePass2 = (rule, value, callback) => {
            if (value === '') {
                callback(new Error('请再次输入密码'));
            } else if (value !== this.ruleForm.pass) {
                callback(new Error('两次输入密码不一致!'));
            } else {
                callback();
            }
        };
        return {
            // 表单双向绑定数据
            ruleForm: {
                username: '',
                pass: '',
                checkPass: '',
            },
            // 校验规则及其校验时机
            rules: {
                pass: [
                    { validator: validatePass, trigger: 'blur' }
                ],
                checkPass: [
                    { validator: validatePass2, trigger: 'blur' }
                ],
                username: [
                    { validator: checkUsername, trigger: 'blur' }
                ]
            }
        };
    },
    methods: {
        submitForm(formName) {
            this.$refs[formName].validate((valid) => {
                if (valid) {
                    alert('submit!');
                } else {
                    console.log('error submit!!');
                    return false;
                }
            });
        },
        resetForm(formName) {
            this.$refs[formName].resetFields();
        }
    }
}
</script>

<style lang="scss" scoped>
.container {
    height: 100vh;
    background: url('../assets/images/bg.jpg');
    background-size: cover;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    .title {
        font-size: 28px;
        padding: 15px;
        border-radius: 10px;
        font-weight: bold;
        color: white;
        background-color: rgba($color: #000000, $alpha: 0.4);
    }
}

/* 使用::v-deep(选择器) 实现样式向下级组件穿透 */
::v-deep(.el-form-item__content) {
    margin-left: 0px !important;
}
</style>

②安装axios

npm i axios

③发送根据请求进行判断业务逻辑

methods: {
        // 提交表单
        submitForm(formName) {
            // ref获取到子组件的实例  validate el-from表单组件里的校验方法
            this.$refs[formName].validate((valid) => {
                if (valid) {
                    // 通过就提交表单
                    // alert('submit!');
                    // 收集数据
                    console.log(this.ruleForm);
                    const { username, pass } = this.ruleForm
                    // 组合接口需要的数据结构
                    const data = { username, password: pass }
                    axios.post('http://localhost:5000/api/v1/register', data).then(res => {
                        console.log(res);
                        // message提示框组件
                        this.$message({
                            type: res.data.code === 0 ? 'success' : 'error',
                            duration:1000,
                            message: res.data.msg,
                            onClose: () => {
                                // 跳转到登录页面
                                this.$router.push('/login')
                            }
                        });
                    })
                } else {
                    // 校验不通过
                    console.log('error submit!!');
                    return false;
                }
            });
        },
    }

请求方法的拦截器封装

src\utils\request.js

/**
 *  axios拦截器
 *  请求拦截器   统一配置请求参数信息
 *  响应拦截器   统一处理响应返回数据
 * 
 */

import axios from 'axios'
import { Loading } from 'element-ui'
var loading
const instance = axios.create({
    // 接口基础地址  接口的公共地址 一般域名
    // baseURL:'',
    // 超时时间
    // timeout:5000
})
// 请求拦截器
instance.interceptors.request.use(cfg => {
    // 封装请求加载状态
    loading = Loading.service({
        text: "loading...",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.8)"
    });
    return cfg
})

// 响应拦截器
instance.interceptors.response.use(res => {
    setTimeout(() => {
        // 请求加载状态停止
        loading.close()
    }, 700)

    return res
})

export default instance

接口地址封装

src\config\url.js

/**
 *  封装接口url地址
 *  方便统一管理
 * 
 */

const prefix = 'http://localhost:5000/api/v1'
const url = {
    // 注册
    Register: prefix + '/register'
}
export default url

2、登录功能

①表单收集用户登录信息

②发送请求给服务端接口

③根据返回结果处理业务逻辑 服务端

​ 1> 登录成功返回token

​ 2> 登录失败提示错误

④存储token和用户信息方便后续使用

响应拦截器 \backend\src\http\req.js

axios.interceptors.response.use((res) => {
  // 统一存储token
  if (res.data.code === 0) {
    localStorage.setItem('token', res.data.token)
  }
  return res.data
})

登录成功后设置 \backend\src\views\Login.vue

if (res.code === 0) {
                localStorage.setItem('username',res.data.username)
                this.$message({
                  message: '登录成功',
                  type: 'success',
                  duration:1000,
                  onClose: () => {
                    this.$router.push('/admin')
                  }
                })
} 

3、后台首页布局

布局实现

src\views\Admin\Admin.vue

<template>
    <div>
        <!-- 整个容器布局 -->
        <el-container>
            <!-- 左侧菜单 -->
            <el-aside width="200px">
                <el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose"
                    background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
                    <el-submenu index="1">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span>导航一</span>
                        </template>
                        <el-menu-item-group>
                            <template slot="title">分组一</template>
                            <el-menu-item index="1-1">选项1</el-menu-item>
                            <el-menu-item index="1-2">选项2</el-menu-item>
                        </el-menu-item-group>
                        <el-menu-item-group title="分组2">
                            <el-menu-item index="1-3">选项3</el-menu-item>
                        </el-menu-item-group>
                        <el-submenu index="1-4">
                            <template slot="title">选项4</template>
                            <el-menu-item index="1-4-1">选项1</el-menu-item>
                        </el-submenu>
                    </el-submenu>
                    <el-menu-item index="2">
                        <i class="el-icon-menu"></i>
                        <span slot="title">导航二</span>
                    </el-menu-item>
                    <el-menu-item index="3" disabled>
                        <i class="el-icon-document"></i>
                        <span slot="title">导航三</span>
                    </el-menu-item>
                    <el-menu-item index="4">
                        <i class="el-icon-setting"></i>
                        <span slot="title">导航四</span>
                    </el-menu-item>
                </el-menu>
            </el-aside>
            <!-- 右侧容器 -->
            <el-container>
                <!-- 头部 -->
                <el-header>Header</el-header>
                <!-- 主体 -->
                <el-main>Main</el-main>
                <!-- 底部 -->
                <el-footer>Footer</el-footer>
            </el-container>
        </el-container>
    </div>
</template>

<script>
export default {

}
</script>

<style lang="scss" scoped>
.el-container {
    /* 设置容器的高度 */
    height: 100vh;
}

.el-menu {
    /* 默认菜单右侧1px边框 去掉 */
    border-right: 0;
}

.el-header,
.el-footer {
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    line-height: 60px;
}

.el-aside {
    background-color: #D3DCE6;
    color: #333;
    text-align: center;
    line-height: 200px;
}

.el-main {
    background-color: #E9EEF3;
    color: #333;
    text-align: center;
    line-height: 160px;
}


.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
    line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
    line-height: 320px;
}
</style>

左侧列表菜单

使用组件:https://element.eleme.io/#/zh-CN/component/menu

NavMenu 导航菜单

el-menu 整个菜单组件

el-menu-item 每个菜单组件

src\views\Admin\Admin.vue

<template>
    <div>
        <!-- 整个容器布局 -->
        <el-container>
            <!-- 左侧菜单 -->
            <el-aside width="200px">
                <div class="logo">
                    <div>
                        <img src="@/assets/logo.png" alt="" srcset="">
                    </div>
                    <div>综合数据管理平台</div>
                </div>
                <!-- 菜单 -->
                <el-menu router default-active="2" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
                    <!-- index 开启router路由模式 会作为路由跳转的路径 -->
                    <el-menu-item index="/admin/dashboard">
                        <!-- icon图标 菜单左侧 -->
                        <i class="el-icon-menu"></i>
                        <span slot="title">控制台</span>
                    </el-menu-item>
                    <!-- <el-submenu index="/admin/user">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span slot="title">用户管理</span>
                        </template> -->
                    <!-- <el-menu-item index="4">
                            <i class="el-icon-setting"></i>
                            <span slot="title">管理员管理</span>
                        </el-menu-item> -->
                    <el-menu-item index="/admin/user">
                        <i class="el-icon-setting"></i>
                        <span slot="title">会员管理</span>
                    </el-menu-item>
                    <!-- </el-submenu> -->
                </el-menu>
            </el-aside>
            <!-- 右侧容器 -->
            <el-container>
                <!-- 头部 -->
                <el-header>Header</el-header>
                <!-- 主体 -->
                <el-main>
                    <!-- 嵌套路由渲染容器 -->
                    <router-view></router-view>
                </el-main>
                <!-- 底部 -->
                <el-footer>Footer</el-footer>
            </el-container>
        </el-container>
    </div>
</template>

<script>
export default {

}
</script>

<style lang="scss" scoped>
.el-container {
    /* 设置容器的高度 */
    height: 100vh;
}

.el-menu {
    /* 默认菜单右侧1px边框 去掉 */
    border-right: 0;
}

.el-aside {
    background-color: #D3DCE6;
    color: #333;
    text-align: center;
}

.logo {
    height: 60px;
    display: flex;
    justify-content: space-around;
    align-items: center;
    color: white;
    font-weight: bold;
    background-color: rgb(84, 92, 100);

    >div:first-child {
        width: 40px;
        height: 40px;

        >img {
            width: 100%;
        }
    }
}

.el-main {
    background-color: #E9EEF3;
    color: #333;
}

.el-header,
.el-footer {
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    line-height: 60px;
}



.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
    line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
    line-height: 320px;
}
</style>

4、实现菜单跳转路由

①在后台首页面中添加渲染容器

\src\views\Admin\Admin.vue

 <el-main>
    <!-- 子路由渲染容器 -->
    <router-view></router-view>
 </el-main>

②根据嵌套路由的配置规则创建对应路由和页面组件

src\router\index.js

{
    path:'/admin',
    component:()=>import('@/views/Admin/Admin.vue'),
    children:[
      {
        path:'dashboard',
        component:()=>import('@/views/admin/Dashboard.vue'),
      },
      {
        path:'user',
        component:()=>import('@/views/admin/User.vue'),
      },
      {
        path:'goods',
        component:()=>import('@/views/admin/Goods.vue'),
      },
    ]
  }

③开启菜单路由功能,并且配置对应的index参数

\backend\src\views\admin\Index.vue

<el-menu
            default-active="/admin/dashboard"
            class="el-menu-vertical-demo"
            @open="handleOpen"
            @close="handleClose"
            background-color="#001529"
            text-color="#fff"
            active-text-color="#409eff"
            router
          >
            <el-menu-item index="/admin/dashboard">
              <!-- <i class="el-icon-platform-eleme" style="color:#409eff"></i> -->
              <i class="el-icon-platform-eleme"></i>
              <span slot="title">控制台</span>
            </el-menu-item>
            <el-menu-item index="/admin/user">
              <i class="el-icon-user-solid"></i>
              <span slot="title">用户管理</span>
            </el-menu-item>
            <el-menu-item index="/admin/goods">
              <i class="el-icon-s-goods"></i>
              <span slot="title">商品管理</span>
            </el-menu-item>
          </el-menu>

5、注销登录

思路:

1、按钮点击触发事件 处理注销

2、登录依据是判断是否有token,及其是否过期,退出登录的核心就是把token删除

退出按钮

<!-- 显示管理登录信息及其下拉菜单 -->
                    <el-dropdown @command="handleCommand">
                        <span class="el-dropdown-link">
                            <el-avatar size="medium"
                                src="https://img1.baidu.com/it/u=3096599450,2589974591&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"></el-avatar>
                            <span style="font-weight: bold;">{{ username }}</span><i
                                class="el-icon-arrow-down el-icon--right"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item command="ucenter">个人中心</el-dropdown-item>
                            <el-dropdown-item command="logout">退出</el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>

js代码部分删除token

 handleCommand(value) {
            console.log(value);
            if (value === 'logout') {
                this.$confirm('确认退出吗?')
                    // 确定
                    .then(_ => {
                        // 将用户的token和记录信息全部清空掉
                        localStorage.removeItem('token')
                        localStorage.removeItem('username')
                        this.$message({
                            message: '退出成功',
                            type: 'success',
                            duration: 700,
                            onClose: () => {
                                this.$router.replace('/login')
                            }
                        })
                    })
                    // 取消
                    .catch(_ => {
                        // console.log(_);
                        // console.log('2222');
                    });
            }
        }

6、防止翻墙越权

注意校验是否登录权限,需要携带token信息在请求头

// 请求拦截器
instance.interceptors.request.use(cfg => {
    // 如果token存在,则统一添加token到请求头信息
    if (localStorage.getItem('token')) {
        cfg.headers.Authorization = localStorage.getItem('token')
    }
    // 封装请求加载状态
    loading = Loading.service({
        text: "loading...",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.8)"
    });
    return cfg
})

实现方法:

方法一:父级路由组件的beforeCreate生命周期拦截

beforeCreate() {
    if (!localStorage.getItem('token')) {
      this.$tip('请先登录', 'error')
      this.$router.replace('/login')
    } else {
      this.$http.get('http://127.0.0.1:5000/api/v1/profile').then((res) => {
        if (res.data.code === 401) {
          this.$tip('请先登录', 'error')
          this.$router.replace('/login')
        } else {
          localStorage.setItem('userInfo', JSON.stringify(res.data.data))
        }
      })
    }
  },

方法二:使用路由守卫进行拦截

全局路由前置守卫

src\router\index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
import Register from '../views/Register.vue'
import Login from '../views/Login.vue'
import Admin from '../views/Admin/Admin.vue'
import Dashboard from '../views/Admin/Dashboard.vue'
import User from '../views/Admin/User.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/register'
  },
  {
    path: '/register',
    name: 'register',
    component: Register,
    meta: {
      isAuth: false
    }
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
    // 路由元信息 路由传参
    meta: {
      isAuth: false
    }
  },
  {
    path: '/admin',
    name: 'admin',
    component: Admin,
    children: [
      {
        // 嵌套路由中 path 不需要写/  
        path: 'dashboard',
        name: 'dashboard',
        component: Dashboard
      },
      {
        path: 'user',
        name: 'user',
        component: User
      }
    ],
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
  console.log(to, from);
  // 根据路由元信息 判断哪些路由是需要校验登录
  if (to.meta.isAuth === false) {
    next()
  } else {
    // 如果本地存储未报错token 肯定没有登录
    if (!localStorage.getItem('token')) {
      Message({
        message: '未登录,请先登录',
        type: 'error',
        duration: 1000,
        onClose: () => {
          // 跳转到登录界面
          next('/login')
        }
      })
    } else {
      // 校验token的有效性
      req.get(url.Profile).then(res => {
        console.log(res);
        if (res.data.code === 0) {
          // 存储管理员登录信息
          localStorage.setItem('username', res.data.data.username)
        } else {
          Message({
            message: '登录失效,重新登录',
            type: 'error',
            duration: 1000,
            onClose: () => {
              // 跳转到登录界面
              next('/login')
            }
          })
        }
      })
      next()
    }
  }

})

export default router

路由独享守卫

src\router\index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
import Register from '../views/Register.vue'
import Login from '../views/Login.vue'
import Admin from '../views/Admin/Admin.vue'
import Dashboard from '../views/Admin/Dashboard.vue'
import User from '../views/Admin/User.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/register'
  },
  {
    path: '/register',
    name: 'register',
    component: Register,
    meta: {
      isAuth: false
    }
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
    // 路由元信息 路由传参
    meta: {
      isAuth: false
    }
  },
  {
    path: '/admin',
    name: 'admin',
    component: Admin,
    children: [
      {
        // 嵌套路由中 path 不需要写/  
        path: 'dashboard',
        name: 'dashboard',
        component: Dashboard
      },
      {
        path: 'user',
        name: 'user',
        component: User
      }
    ],
    // 路由 独享守卫
    beforeEnter: (to, from, next) => {
      // console.log(to);
      // // 如果本地存储未报错token 肯定没有登录
      if (!localStorage.getItem('token')) {
        Message({
          message: '未登录,请先登录',
          type: 'error',
          duration: 1000,
          onClose: () => {
            // 跳转到登录界面
            next('/login')
          }
        })
      } else {
        // 校验token的有效性
        req.get(url.Profile).then(res => {
          console.log(res);
          if (res.data.code === 0) {
            // 存储管理员登录信息
            localStorage.setItem('username', res.data.data.username)
              next()
          } else {
            Message({
              message: '登录失效,重新登录',
              type: 'error',
              duration: 1000,
              onClose: () => {
                // 跳转到登录界面
                next('/login')
              }
            })
          }
        })
      }
    },
  }
  // {
  //   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/AboutView.vue')
  // }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
export default router

组件内部的前置守卫

src\views\Admin\Admin.vue

<template>
    <div>
        <!-- 整个容器布局 -->
        <el-container>
            <!-- 左侧菜单 -->
            <el-aside width="200px">
                <div class="logo">
                    <div>
                        <img src="@/assets/logo.png" alt="" srcset="">
                    </div>
                    <div>综合数据管理平台</div>
                </div>
                <!-- 菜单 -->
                <!-- default-active 根据路由路径匹配 选中的对应的菜单高亮 -->
                <el-menu router :default-active="$route.path" background-color="#001529" text-color="#ccc">
                    <!-- index 开启router路由模式 会作为路由跳转的路径 -->
                    <el-menu-item index="/admin/dashboard">
                        <!-- icon图标 菜单左侧 -->
                        <i class="el-icon-data-line"></i>
                        <span slot="title">控制台</span>
                    </el-menu-item>
                    <!-- <el-submenu index="/admin/user">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span slot="title">用户管理</span>
                        </template> -->
                    <!-- <el-menu-item index="4">
                            <i class="el-icon-setting"></i>
                            <span slot="title">管理员管理</span>
                        </el-menu-item> -->
                    <el-menu-item index="/admin/user">
                        <i class="el-icon-user"></i>
                        <span slot="title">会员管理</span>
                    </el-menu-item>
                    <!-- </el-submenu> -->
                </el-menu>
            </el-aside>
            <!-- 右侧容器 -->
            <el-container>
                <!-- 头部 -->
                <el-header>Header</el-header>
                <!-- 主体 -->
                <el-main>
                    <!-- 嵌套路由渲染容器 -->
                    <div>
                        <router-view></router-view>
                    </div>
                </el-main>
                <!-- 底部 -->
                <el-footer>Footer</el-footer>
            </el-container>
        </el-container>
    </div>
</template>

<script>
import { Message } from 'element-ui';
import url from '@/config/url';
import req from '@/utils/request'
export default {
    // 拦截操作 判断用户是否登录
    // 路由 组件内置守卫  内部获取不到组件的this 此时组件还未被创建
    beforeRouteEnter(to, form, next) {
        // 如果本地存储未报错token 肯定没有登录
        if (!localStorage.getItem('token')) {
            Message({
                message: '未登录,请先登录',
                type: 'error',
                duration: 1000,
                onClose: () => {
                    // 跳转到登录界面
                    next('/login')
                }
            })
        } else {
            // 校验token的有效性
            req.get(url.Profile).then(res => {
                console.log(res);
                if (res.data.code === 0) {
                    // 存储管理员登录信息
                    localStorage.setItem('username',res.data.data.username)
                } else {
                    Message({
                        message: '登录失效,重新登录',
                        type: 'error',
                        duration: 1000,
                        onClose: () => {
                            // 跳转到登录界面
                            next('/login')
                        }
                    })
                }
            })
            next()
        }
    }
}
</script>

<style lang="scss" scoped>
.el-container {
    /* 设置容器的高度 */
    height: 100vh;
}

.el-menu {
    /* 默认菜单右侧1px边框 去掉 */
    border-right: 0;
}

.el-aside {
    background-color: #001529;
    color: #333;
    /* text-align: center; */
}

.logo {
    height: 60px;
    display: flex;
    /* justify-content: space-around; */
    align-items: center;
    color: white;
    font-weight: bold;
    background-color: #001529;
    padding: 5px;

    >div:first-child {
        width: 40px;
        height: 40px;

        >img {
            width: 100%;
        }
    }

    >div:nth-child(2) {
        margin-left: 10px;
    }
}

.el-main {
    background-color: #f5f5f5;

    >div {
        background-color: #fff;
        min-height: calc(100vh - 60px - 60px - 20px - 20px - 20px);
        margin-top: 20px;
    }
}

.el-menu-item {
    height: 40px;
    line-height: 40px;
    margin: 5px 10px;
    border-radius: 10px;
}

.el-menu-item.is-active {
    color: white !important;
    background-color: #1677ff !important;
}

.el-header {
    background-color: #ffffff;
    text-align: center;
    line-height: 60px;
}

.el-footer {
    background-color: #f5f5f5;
    text-align: center;
    line-height: 60px;
}
</style>

7、回车确认提交

mounted() {
    // 文档对象监听键盘事件   回车键
    document.onkeyup = (event) => {
      var e = event || window.event
      if (e && e.keyCode == 13) {
        // console.log('回车')
        this.submitForm('ruleForm')
      }
    }
  },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/703222.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SolidWorks 2016 SP5安装教程

软件介绍 Solidworks软件功能强大&#xff0c;组件繁多。 Solidworks有功能强大、易学易用和技术创新三大特点&#xff0c;这使得SolidWorks 成为领先的、主流的三维CAD解决方案。 SolidWorks 能够提供不同的设计方案、减少设计过程中的错误以及提高产品质量。SolidWorks 不仅…

开发移动端常见的问题:VW适配问题,基于 postcss 插件 实现项目vw适配

当你开发移动端的时候有一个问题是避免不了的&#xff0c;那就是当屏幕大小无论怎么变化时&#xff0c;内部尺寸也要随之发生改变&#xff0c;也就是适配问题。这里我们讲的是最新的VW适配&#xff0c;也就是用vw作为单位&#xff0c;100vw是整个页面的大小。而在开发的设计图中…

Mysql学习(九)——存储引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 七、存储引擎7.1 MySQL体系结构7.2 存储引擎简介7.3 存储引擎特点7.4 存储引擎选择7.5 总结 七、存储引擎 7.1 MySQL体系结构 连接层&#xff1a;最上层是一些客户…

SAP SO定价上面2个ZPR1 其中一个不活跃

查看价格表 取定价的时候排除不活动的 即可

永磁同步电机滞环电流控制(PI双闭环)matlab仿真模型

微♥“电击小子程高兴的MATLAB小屋”获取模型 1.滞环电流控制的原理 将给定的电流信号与反馈的电流信号进行比较&#xff0c;然后控制它俩之间的差值稳定在一个滞环范围内&#xff0c;若超出范围&#xff0c;则进行相应的调节操作。 操作如下叙述&#xff1a;假设以三相中的A相…

DockerCompose+Jenkins+Pipeline流水线打包SpringBoot项目(解压安装配置JDK、Maven等)入门

场景 DockerCompose中部署Jenkins&#xff08;Docker Desktop在windows上数据卷映射&#xff09;&#xff1a; DockerCompose中部署Jenkins&#xff08;Docker Desktop在windows上数据卷映射&#xff09;-CSDN博客 DockerJenkinsGiteeMaven项目配置jdk、maven、gitee等拉取代…

论文图片颜色提取

论文绘图的时候有些颜色不知道怎么选取&#xff0c;参考其他论文&#xff0c;将其他论文中的颜色提取下来&#xff0c;用取色器识别出来&#xff0c;记录如下&#xff1a; 颜色代码&#xff1a;#BEAED4 190,174,212 颜色代码&#xff1a;#C4CBCB 196,203,203 颜色代码&am…

51单片机实验05 -点阵

目录 一&#xff0c;熟悉矩阵led小灯 1&#xff0c;点亮矩阵的一只led 2&#xff0c;点亮矩阵的一排led 3&#xff0c;点亮矩阵的全部led static 关键字 unsigned 关键字 4&#xff0c;点阵的静态显示 2&#xff09;心形矩阵显示代码 3&#xff09;效果 二&#xff0c;课…

6.nginx负载均衡

说明 增加服务器的数量,将请求分发到各个服务器上。 将原来请求集中到单个服务器上的情况改为将请求分发到多个服务器上。 案例 浏览器请求地址http://ip/edu/a.html, 负载均衡的效果,平分到8080和8081两台服务上中。 准备工作 tomcat8080配置 tomcat8081配置 直接通过…

----几种接口的使用---

Compareable接口 对于给数组中的变量成员排序&#xff0c;我们能想到用sort&#xff0c;根据成员之间的大小进行排序&#xff0c;那么如果数组中的成员是对象的话&#xff0c;单单只是用sort去排序肯定是步成功的&#xff0c;因为并不知道要根据什么去排序&#xff0c; 这时要…

【C++】编译

三、C编译 前面给大家演示了如何从写C代码到编译代码再到执行代码的全过程。这个过程中非常重要的编译环节&#xff0c;被我们一个按钮或者一个ctrlF7快捷键就给带过了。其实这个环节非常重要&#xff0c;如果你非常了解这个环节&#xff0c;你开发源代码就会更加自信和清醒&a…

CorelDRAW 2024开启设计新纪元,终身永久版与中文破解版的全面解析及安装攻略

当我们谈论图形设计软件时&#xff0c;CorelDRAW无疑是一个响亮的名字。作为一款强大的矢量图形编辑工具&#xff0c;它以其丰富的功能和用户友好的界面赢得了全球设计师的喜爱。随着CorelDRAW 2024的发布&#xff0c;这个备受瞩目的版本带来了前所未有的创新特性&#xff0c;进…

Vulnhub-DC-1,7

靶机IP:192.168.20.141 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 前言 1和7都是Drupal的网站&#xff0c;只写了7&#xff0c;包含1的知识点 信息收集 用nmap扫描端口及版本号 进入主页查看作者给的提示&#xff0c;不是暴力破解的…

ROS2底层机制源码分析

init ->init_and_remove_ros_arguments ->init ->Context::init 保存初始化传入的信号 ->install_signal_handlers→SignalHandler::install 开线程响应信号 ->_remove_ros_arguments 移除ros参数 ->SingleNodeManager::instance().…

条件判断if语句与case语句

一、条件测试 test命令进行条件测试&#xff0c;然后根据返回值来判断条件是否成立。 常用操作符&#xff1a; -e &#xff1a;既可以测试文件又可以测试目录是否存在 -d &#xff1a;测试目录是否存在 -f &#xff1a;测试文件是否存在 -r &#xff1a;测试当前用户是否…

前端加载 动画特效

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>加载动画</title><style type="text/css">/* 设置页面背景颜色 */body {background: #ECF0F1;}/* 定义加载动画容器的样式…

抖店没人做了?不是项目不行了,而是商家们都换思路去玩了

我是王路飞。 有没有发现现在很多抖店新手都在吐槽&#xff0c;抖店不好做了&#xff0c;做不起来&#xff0c;没人做了&#xff0c;太内卷了...... 对这种做不起来还在怪项目本身的&#xff0c;一定要离他远一点&#xff0c;省得被他的负能量给影响到自己的状态。 任何项目…

怎么学习汇川Codesys PLC教程?

前言 各位好&#xff0c;我在B站和抖音上都有发布视频的&#xff0c;搜索我的名称“阿凡工控分享”即可。在CSDN上发表文章也是想把我的一点见解和经验分享出来&#xff0c;进一步的方便大家进行学习。 我是正文 本文主要也是为了方便大家学习汇川的Codesys PLC而制作的&…

Java并发编程之线程池源码解析与实现详解

Java并发编程是现代开发中非常重要的一个领域。使用线程池来管理和控制线程的创建和生命周期&#xff0c;可以更加高效地利用系统资源&#xff0c;提高系统的并发性能。线程池是一种常见的并发编程模型&#xff0c;Java提供了ThreadPoolExecutor类来实现线程池。本文将详细解析…

⌈ 传知代码 ⌋ MonoCon解读与复现

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…