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

news2024/11/23 18:47:54

​🌈个人主页:前端青山
🔥系列专栏: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')
      }
    }
  },

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA上MySQL的jar包导入教程

jar包下载网址——》https://mvnrepository.com/ 1.进入界面&#xff0c;点击搜索框&#xff0c;搜索mysql&#xff1a; 外国网站&#xff0c;可能有点慢,耐心等待即可。 2.点击查询结果&#xff1a; 进入界面&#xff0c;点击前两个结果的其中一个&#xff0c;两个都可以 …

《Brave New Words 》9.2 如何让孩子在 AI 未来职场中茁壮成长

Part IX: Work and What Comes Next 第九部分&#xff1a;工作及其未来发展 How to Prepare Kids to Thrive in the AI-Future Workplace 如何让孩子在 AI 未来职场中茁壮成长 It is several months before we launch Khanmigo, and I am speaking at the Stanford Computer Sc…

0603 BJT射极耦合差分式放大电路

6.2.3 BJT射极耦合差分式放大电路 电路组成 静态分析 动态分析 仅差模信号输入时 双端输出时电压增益 单端输出时电压增益 单端输入时 差模输入电阻 输出电阻 仅共模信号输入时 带有源负载的射极耦合差分式放大电路

【中间件】Pulsar集群安装

目录 一、Pulsar介绍 1.1 Pulsar基本介绍 1.2 Pulsar架构 Producer & Consumer Apache Zookeeper Pulsar Brokers Apache Bookkeeper 二、Zookeeper集群安装 三、Pulsar集群安装 3.1 bookie与broker配置 3.1.1 修改bookie配置文件 3.1.2 修改broker配置文件 3…

Python写UI自动化--playwright(安装)

Playwright是微软推出的开源自动化测试工具&#xff0c;专为跨浏览器端到端测试设计。Playwright可以在多个浏览器引擎上运行测试&#xff0c;包括基于Chromium的浏览器&#xff08;如Chrome、Edge&#xff09;、Firefox和WebKit&#xff08;Safari的技术基础&#xff09;。支持…

CTFHUB-SQL注入-MySQL结构

目录 sqlmap工具夺flag 查看数据库名 查看数据库中表名 查看第一个表中数据 查看第二个表的数据 手动注入 判断是否存在注入 判断字段数量 查询注入点 查询数据库版本 查询数据库名 查看所有数据库 查看表名 查看表中字段 查看表中数据 本题用到sqlmap工具&…

使用距离传感器数据预测驾驶路线(BOSCH)

使用距离传感器数据预测驾驶路线 Werner Uhler, Michael Scherl and Bernd Lichtenberg Robert Bosch GmbH ABSTRACT 将距离传感器检测到的车辆分配到相对于本车的车道是未来驾驶辅助系统&#xff08;如自适应巡航控制 (ACC)&#xff09;的一项重要且必要的任务。通过车辆前…

企业ERP系统的核心目的是什么?

如今&#xff0c;随着企业业务和规模的逐步扩大&#xff0c;传统的管理模式显然不能满足企业可持续发展的需要。如果不及时改变&#xff0c;很可能会被市场淘汰。ERP系统是当前企业信息管理的必要工具。它可以提高企业的办公效率&#xff0c;帮助企业摆脱传统管理的束缚&#x…

论文阅读Rolling-Unet,卷积结合MLP的图像分割模型

这篇论文提出了一种新的医学图像分割网络Rolling-Unet&#xff0c;目的是在不用Transformer的前提下&#xff0c;能同时有效提取局部特征和长距离依赖性,从而在性能和计算成本之间找到良好的平衡点。 论文地址&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/2…

小红书评论爬取使用教程[八猪采集器]

如何使用八猪采集器爬虫小红书评论, 以下是教程. 目录 1. 下载使用 2. 注册账号 3. 登录 4. 配置任务 5. 执行采集任务 6. 校验执行是否成功 7. 查询和操作评论数据 8. 导出评论数据 9. 下载软件 1. 下载使用 下载后解压缩到任意地址, 双击8zhu_collector.exe即可…

15. STUN协议和ICE工作原理

NET介绍 NAT是一种地址转换技术&#xff0c;它可以将IP数据报文头中的IP地址转换为另一个IP地址&#xff0c;并通过转换端口号达到地址重用的目的。 在大多数网络环境中&#xff0c;我们都需要通过 NAT 来访问 Internet。 NAT作为一种缓解IPv4公网地址枯竭的过渡技术&#xff…

UE4/UE5像素流送云推流|程序不稳定、弱网画面糊怎么办?

看到不少反馈说像素流送技术在实际的应用中&#xff0c;存在一些问题&#xff0c;比如程序崩溃、画面模糊等。本文将探讨这些问题&#xff0c;并介绍点量云流软件提供了哪些有效的解决方案。 常见的像素流送技术问题 画面质量问题&#xff1a; 当快速操作UE模型或初次启动像素…

Mongodb使用$pop删除数组中的元素

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第67篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

IDEA项目上传Github流程+常见问题解决

一、Github上创建仓库 项目创建好后如图所示 二、IDEA连接Github远程仓库 管理远程 复制远程地址 定义远程 登录Github 点击进入File->Settings->Version Control->Github登录自己的账号并勾上“√” 三、推送项目 点击推送 修改为main 点击确定&#xff0c;打开远程…

Kimichat使用案例014:用kimichat自动批量重命名三体字幕文件(零代码编程)

文章目录 一、介绍二、字幕文件三、kimi输入内容四、kimi输出内容五、运行程序一、介绍 网上下载了Netflix的三体电视剧,但没有字幕,又从字幕网站下载了字幕文件,由于字幕文件和视频文件标题有差异,需要全部进行重命名。 二、字幕文件 从字幕网站下载了字幕文件如下: 三、…

基于 Element UI 的高效前端表格模板开发

在前端开发中&#xff0c;表格展示和搜索功能是常见的需求之一。为了提高开发效率&#xff0c;我们可以通过创建一个可复用的表格模板来应对各种类似的场景。本文将分享一个基于 Element UI 的表格模板&#xff0c;它包含了搜索、分页和操作按钮等功能&#xff0c;可以作为未来…

基于zemax的圆孔、矩形衍射、双缝干涉与自定义孔径面型应用分析

自学案例节选自某乎 1&#xff1a;圆孔、矩形衍射、双缝干涉与自定义孔径面型应用分析 讨论一些基本的光学衍射、干涉现象&#xff0c;在加深对光学知识理解的同时&#xff0c;重点介绍一下自定义孔径的用法&#xff0c;主要是为了对软件的运用更为了解&#xff0c;并不针对某…

图说设计模式:单例模式

更多C学习笔记&#xff0c;关注 wx公众号&#xff1a;cpp读书笔记 5. 单例模式 单例模式 模式动机模式定义模式结构时序图代码分析模式分析实例优点缺点适用环境模式应用模式扩展总结 5.1. 模式动机 对于系统中的某些类来说&#xff0c;只有一个实例很重要&#xff0c;例如…

Linux:多线程的操作

多线程操作 进程与线程线程的创建 create_pthread创建线程池给线程传入对象的指针 线程等待 pthread_join退出线程 pthread_exit线程等待参数 retval 与 线程退出参数 retval 线程中断 pthread_cancel获取线程编号 pthread_self线程分离 pthread_detach 进程与线程 进程是资源…

【学习笔记】Kali

纯个人总结&#xff0c;有什么不对的地方欢迎指正。 笔记根据个人学习进度持续更新… 一、 认识Kali 基础了解 Kali是一套基于Debian发行版的一款操作系统。&#xff08;这里讲一个误区&#xff0c;我以前一直以为kali就属于deepin下的操作系统&#xff0c;但是我知道deepin是…