Vue之完整Dome

项目介绍:

一个前后端分离的管理系统。前端使用vue框架,后端使用Java的springboot框架。

项目框架:SpringBoot+SpringSecurity+jwt+mybatis-plus+swagger2
环境:JDK1.8
软件: idea
后端教程:Java后端管理系统

前端框架:vue ,vue_cli,element
软件: HBuilder x

1.创建项目vue-cli-3.0

cmd中输入命令:

vue create 项目名

结构:
在这里插入图片描述
结构描述见:Vue之vue-cli新和旧版本代码生成的项目区别

2.引入element

官方地址:https://element.eleme.cn/#/zh-CN/component/installation

安装element插件:

npm i element-ui -S

在main.js中引入:

//引入element
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

3.配置路由router

相关资料:【Vue-cli3】路由基础

修改App.vue使用router-view

<template>
  <div id="app">
  <!-- router-view路由页面组件展示区域 -->
     <router-view></router-view>
  </div>
</template>

<script>
</script>

<style>
</style>

在main.js中新增路由配置

import Vue from 'vue'
import App from './App.vue'
import router from "./router/index.js" //引入路由配置 在router文件下的index.js

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router //添加路由
}).$mount('#app')

创建路由 src-》router-》index.js

import Vue from 'vue' 
import Router from 'vue-router'
  
Vue.use(Router)
 
export default new Router({
  routes: [
    // 配置First的路由
    {
      path: '/',
      name: 'Login', 
      component: () => import("@/components/views/Login.vue"),
    },
  ]
})

4.引入axios和qs配置

安装:

npm install axios --save-dev 
npm install qs

main.js添加axios配置:

import axios from 'axios' //引入axios
import qs from 'qs'  
Vue.prototype.$axios = axios

var base="http://localhost:8090"
export const POST = (url, params) => {
   axios.defaults.transformRequest = [
     function (data) {
       return qs.stringify(data)
     }
   ];
  console.log(url);
  return axios.post(`${base}${url}`, params).then(res => {
    console.log(res);
    if (res.status == 200)
      return res.data;
    else
      console.log("操作失败,服务端出现异常错误!") 
  })
}

Vue.prototype.$ajax = { 
  POST(url, params) {
         return POST(url, params);
  }, 
}

页面使用:

submitForm() {
        var _this=this
        this.$ajax
         .POST("/login", {
               username: this.loginForm.username,
               password: this.loginForm.password
              }).then((data) => {
                 console.info(data)
                 if(data.success){ 
                      //处理
                      _this.$router.push("/home") //跳转
                 } 
            })  
},

4.登录和主页页面

登录页面Login.vue
在这里插入图片描述

4.1 使用全局的css设置整体样式

在 assets-》css文件下创建global.css

html,body,#app{
    height: 100%;
    margin:0 ;
    padding: 0;
}

在man.js中引入:

import './assets/css/global.css' //引入全局样式

4.2 创建Login.vue页面

使用滑块验证插件,使用教程SlideVerify

<template>
    <!-- 登录布局 -->
    <div class="login_container"> 
        <!-- 登录区域 -->
        <div class="login_box">
            <!-- 头像 -->
            <div class="avatar_box">
                <img src="../assets/img/timg.gif" />
            </div>
            <!-- 表单 -->
            <el-form :model="loginForm" :rules="loginRules" ref="loginForm" label-width="0px" class="login_Form">
              
              <el-form-item   prop="username">
                <el-input v-model="loginForm.username" prefix-icon="el-icon-user-solid"></el-input>
              </el-form-item>
              
              <el-form-item  prop="password">
                <el-input type="password" v-model="loginForm.password" autocomplete="off" prefix-icon="el-icon-lock"></el-input>
              </el-form-item>
              
              <el-form-item   prop="verifyCode">
                  <div class="verify_box">
                     <el-popover
                       placement="top-start"
                       width="380"
                       trigger="click" 
                       v-model="SlideVerify">
                       <slide-verify ref="slideblock"
                                      :l="42"
                                      :r="10"
                                      :w="380"
                                      :h="185"
                                      @success="onSuccess"
                                      @fail="onFail"
                                      @refresh="onRefresh"
                                      @again="onAgain"
                                      :slider-text="text"
                                      ></slide-verify>
                        <el-button style="width: 410px;" :type="codeType" @click="handleClick" :disabled="disabled"  slot="reference">{{TipMsg}}</el-button>
                     </el-popover>
                  </div>
                   
               </el-form-item>
              
              <el-form-item class="login_btn">
                <el-button type="primary" @click="submitForm()">提交</el-button>
                <el-button @click="resetForm('loginForm')">重置</el-button>
              </el-form-item> 
                
            </el-form>
        </div>
    </div>
</template>

<script> 
    export default{
        name: 'Login',
        data() {
              return {
                  fullscreenLoading: false,
                SlideVerify:false,
                text: '向右滑',
                codeType:"",
                TipMsg:"点击验证",
                disabled:false, 
                loginForm: {
                  username: '',
                  password: '',
                  verifyCode: ''
                },
                loginRules: {
                    username: [
                      { required:true , message: "请输入用户名称",trigger: 'blur' },
                      { min :3 ,max:5, message: "长度在 3 到 5 个字符",trigger: 'blur'}
                    ],
                  password: [
                    { required:true , message: "请输入密码",trigger: 'blur' },
                    { min :3 ,max:5, message: "长度在 3 到 5 个字符",trigger: 'blur'}
                  ],
                  verifyCode:[
                      { required:true , message: "请输入验证码",trigger: 'blur'}
                  ]
                }
              };
            },
            methods: { 
                onSuccess(){
                       console.info('成功!')
                       this.loginForm.verifyCode=true; 
                       this.SlideVerify=false; 
                       this.codeType="success";
                       this.TipMsg="验证成功";
                       // this.disabled=true;
                },
                onFail(){
                        console.info('失败!')
                        this.loginForm.verifyCode=false;
                        this.codeType="danger";
                        this.TipMsg="验证失败";
                 },
                 onRefresh(){
                        console.info('刷新!')
                        this.loginForm.verifyCode=false;
                        this.codeType="";
                        this.TipMsg="点击验证";
                         // this.disabled=false;
                },
                onAgain(){
                  console.log("检测到非人为操作的哦!"); 
                  this.$refs.slideblock.reset();
                  this.onRefresh();
                },
                 handleClick() {
                      // 父组件直接可以调用刷新方法
                      this.$refs.slideblock.reset();
                      this.TipMsg="验证中";
                      this.codeType="";
                    },
              submitForm() {
                 var _this=this
                 if(_this.loginForm.verifyCode==true){
                     console.info("验证成功!")
                     
                     //登录时加载样式
                     const loading = this.$loading({
                       lock: true,
                       // text: 'Loading',
                       // spinner: 'el-icon-loading',
                       background: 'rgba(255, 255, 255, 0.7)' 
                     });
                     
                     this.$ajax
                     .POST("/login", {
                       username: this.loginForm.username,
                       password: this.loginForm.password
                     })
                     .then((data) => {
                          console.info(data)
                         if(data.success){
                             _this.$message.success("登录成功!");
                             console.info("存储token")
                             localStorage.setItem("token",data.data.token)
                             
                              loading.close();//关闭加载样式
                             _this.$router.push("/") 
                         } else{ 
                             _this.$message.error("登录失败!账号密码错误!");
                             _this.onRefresh();
                         }
                        })
                 }else{
                     this.$message.info("请点击验证!");
                 }
                  
              },
              resetForm(formName) {
                this.$refs[formName].resetFields();
              }
            }
          
    }
</script>

<style>
    .el-loading-spinner {
        /* // 这个是自己想设置的 gif 加载动图 */
      background-image:url('../assets/img/loading.gif'); 
        /* //设置背景图 不重复 */
      background-repeat: no-repeat; 
      height:10%;
      /* //设置背景 定位  为居中 */
      background-position:center; 
       /* //覆盖 element-ui  默认的 50%    因为此处设置了height:100%,所以不设置的话,会只显示一半,因为被top顶下去了 */
      top:0; 
     
    }
    .el-loading-spinner .circular {
        /* //隐藏 之前  element-ui  默认的 loading 动画 */
      display: none;  
    }
</style>
<!-- 内部样式 -->
<style lang="less" scoped >
    
     
    
    .login_container{
        width: 100%;
        height: 100%;
        background-color:aquamarine;
    }
    
    .login_box{
        width: 450px;
        height: 380px;
        background-color: #FFFFFF;
        border-radius: 3px;
        position: absolute;
        left: 50%;
        top: 50%;
        transform:  translate(-50%,-50%);
    }
    .avatar_box{
        width: 130px;
        height: 130px;
        border: 1px solid #EEEEEE;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
        margin:  -65px auto;
        background-color: #FFFFFF;
        img{
            width: 100%;
            height: 100%;
            border-radius: 50%;
            background-color: #eeeeee;
        }
    }
    .login_Form{
        position: absolute;
        bottom: 0px;
        width: 100%;
        padding: 0px 20px;
        box-sizing: border-box;
        .login_btn{
            display: flex;
            justify-content: flex-end;
        }
        
    }
    .verify_box{
        display: flex;
        .verify_code{
            width: 70%;
            justify-content: left;
        }
        .verify_img{
            width: 30%;
            height: 45px;
            justify-content: flex-end;
        }
        
    }
   
   
</style>


使用localStorage.setItem(“token”,token) 存储token,利用拦截器每次请求带上token ,过期删除跳转登录页面。

4.3 创建Home.vue页面

首先在路由router-》index.js中配置

export default new Router({
  routes: [
    
    // 配置First的路由
    {
      path: '/',
      name: 'Login', 
      component: () => import("@/components/views/Login.vue"),
    }, 
    {
      path: '/home',
      name: 'Home', 
      component: () => import("@/components/views/Home.vue"),
    }, 
  ]
})

使用element的Container 布局容器

<template>
        <el-container class="el-container">
          <!-- 头部布局 -->
          <el-header>
              <!-- logo和项目名 -->
              <div class="left_box">
                  <img src="../../assets/img/timg.gif" />
                  <span>集元库房管理系统</span>
              </div>
             
              <!-- 用户登录展示头像 -->
              <div class="right_box"> 
                 <i class="el-icon-full-screen" @click="toggleFullscreen"></i> 
                  <!-- 下拉菜单 -->
                  <el-dropdown>
                    <img src="../../assets/img/timg.gif" />
                    <el-dropdown-menu slot="dropdown">
                      <el-dropdown-item icon="el-icon-user">个人信息</el-dropdown-item>
                      <el-dropdown-item icon="el-icon-edit-outline">修改密码</el-dropdown-item>
                      <el-dropdown-item icon="el-icon-switch-button"><a href="/">退出登录</a></el-dropdown-item>
                    
                    </el-dropdown-menu>
                  </el-dropdown>
              </div>
              
          </el-header>
          <el-container>
            <el-aside :width="isCollapse?'60px':'200px'" > 
                <!--展开/收起-->
                <div class="toggle_box" @click="toggleCollapse">|||</div>
                <el-menu
                      class="el-menu-vertical-demo"
                      @open="handleOpen"
                      @close="handleClose"
                      background-color="#001529"
                      text-color="#fff"
                      active-text-color="#ffd04b"
                      :collapse="isCollapse"
                      :default-active="activePath"
                      :collapse-transition="false"
                      :unique-opened="true"
                      :router="true">
                      
                      <el-submenu index="1">
                        <template slot="title">
                          <i class="el-icon-s-tools"></i>
                          <span>系统管理</span>
                        </template>
                         <el-menu-item-group>
                           <el-menu-item index="/user"><i class="el-icon-user" ></i>用户管理</el-menu-item>
                           <el-menu-item index="1-2"><i class="el-icon-star-off"></i>权限管理</el-menu-item>
                         </el-menu-item-group>
                       </el-submenu>
                      
                      <el-submenu index="2">
                        <template slot="title">
                          <i class="el-icon-message-solid"></i>
                          <span>日志管理</span>
                        </template>
                        <el-menu-item-group>
                          <el-menu-item index="2-1"><i class="el-icon-view"></i>sql日志</el-menu-item>
                          <el-menu-item index="2-2"><i class="el-icon-edit"></i>用户日志</el-menu-item>
                        </el-menu-item-group>
                      </el-submenu>
                    </el-menu>
            </el-aside>
            
            <el-main>
              <!-- 路由视图 -->
              <router-view></router-view>
            </el-main>
          </el-container>
        </el-container>
 
</template>

<script>
  
     export default{
        name: 'Main',
         data(){
             return {
               isCollapse: false,
               activePath: '',
                }
        },
        components: {
           
        },
        methods: {
          toggleCollapse () {
            this.isCollapse = !this.isCollapse
          },
          handleOpen (key, keyPath) {
            console.log(key, keyPath)
          },
          handleClose (key, keyPath) {
            console.log(key, keyPath)
          },
           toggleFullscreen() {
           let _this = this;
           let el = document.documentElement;
                if (document.fullscreenElement === null) {
                    _this.openFullscreen(el);
                } else {
                    _this.quitFullscreen();
                }
           },
           openFullscreen(element) {
                if (element.requestFullscreen) {
                    element.requestFullscreen();
                } else if (element.webkitRequestFullScreen) {
                    element.webkitRequestFullScreen();
                } else if (element.mozRequestFullScreen) {
                    element.mozRequestFullScreen();
                } else if (element.msRequestFullscreen) {
                    // IE11
                    element.msRequestFullscreen();
            }
           },
           quitFullscreen() {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.webkitCancelFullScreen) {
                    document.webkitCancelFullScreen();
                } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                }
           },
           getKeyDown() {
                let _this = this;
                if (event.keyCode === 122) {
                    event.preventDefault() || (event.returnValue = false);
                    _this.toggleFullscreen(); // 触发全屏的按钮
                } else if (event.keyCode === 27) {
                    if (document.fullscreenElement !== null) {
                        _this.quitFullScreen();
                    }
                }
           }
        }, 
        mounted() {
            window.addEventListener("keydown", this.getKeyDown);
        }
     }
</script>

<style lang="less" scoped>
    /*设置整个容器*/
    .el-container{
        width: 100%;
        height: 100%;
    }
    /*头部布局*/
    .el-header {
       background-color: #001529;
       display: flex;
       justify-content: space-between;
       padding-left:0 ;
       color: #FFFFFF;
      align-items: center;
      font-size:  20px;
     }
       /*侧边栏*/
     .el-aside {
       background-color: #001529;
       color: #333;
      //去掉边框
      .el-menu {
        border-right: none;
      }
      
      /*展开/收起*/
      .toggle_box {
        background-color: chartreuse;
        font-size: 15px;
        font-weight: bold;
        line-height: 24px;
        color: #FFFFFF;
        letter-spacing: 0.2em;
        text-align: center;
        cursor: pointer;
      
      }
      
     }
    
     
       /*内容*/
     .el-main {
       background-color: #E9EEF3;
        
     }
     
     body > .el-container {
       margin-bottom: 40px;
     }
     
      /*左边logo和标题*/
     .left_box{
         display:  flex;
         align-items: center;
         /*logo*/
         img{
             width: 60px;
             height: 60px;
             margin: 0px 0px 10px 15px;
         }
         /*标题*/
         span{
             margin-left: 15px;
         }
     }
    
     /*右边的登录头像*/
     .right_box{
         /*全屏按钮、主题换色*/
         // el-icon-full-screen
         // 头像
         .el-dropdown > img{
             height: 60px;
             width: 60px;
             border-radius:50% ;
             background-color: #FFFFFF;
              margin:0px 15px 0px 0px ;
         }
        
     }
     // 下拉菜单
      .el-dropdown-link {
         cursor: pointer;
         color: #409EFF;
       }
       .el-icon-arrow-down {
         font-size: 12px;
       }
      a {
       text-decoration: none;
       color: #000000;
      }
</style>

登录成功跳转home页面
在这里插入图片描述

5.token使用

在登录成功后使用localStorage.setItem(“token”,token) 存储,每次请求由拦截器带上token。

5.1 配置请求拦截器

在main.js中

let token="";
axios.defaults.headers.common['token'] = token;

// 添加请求拦截器 
axios.interceptors.request.use(function (config) {
  let token=localStorage.getItem('token')
  console.info("每次带token")
  console.info(token)
  if(token)
    config.headers.common['token'] = token;
    
  return config;
}, function (error) {
  // Do something with request error
  Message.error('网络连接异常,请稍后再试!');
  console.info("error: ");
  console.info(error);
  return Promise.reject(error); 
});


// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  // Do something with response error
  if (error.response.status == 404) {
    //找不到请求的服务
    Message.error('找不到请求的服务!');
  } 
  else if (error.response.status == 401) {
    //没有token需要登录
    Message.error('登录凭证失效,请重新登录!'); 
    window.location.href = "/login";
  } else if (error.response.status == 403) {
     Message.error('token过期,请重新登录!');
     localStorage.removeItem("token"); 
     window.location.href = "/login";
  }  
  return Promise.reject(error);
})

进行登录:
这里是在配置了初始token为空,可以选择不带
在这里插入图片描述

5.2 创建User.vue页面

在路由router-》index.js中配置user路由
放在home下为子标签, Container 布局容器的main部分会改变显示

{
      path: '/home',
      name: 'Home', 
      component: () => import("@/components/views/Home.vue"),
      // 子标签:children。 页面使用<router-view></router-view> 显示
      children :[
        {
             path: '/', //默认显示
             name: 'User',
             //使用() => import('xx')引入
             component: () => import('../components/users/User.vue')
         }, 
      ]
    }

User.vue

<template>
   <div>
  <!-- 面包屑 -->
       <el-breadcrumb separator-class="el-icon-arrow-right" style="padding-left: 10px;padding-bottom: 10px;">
         <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
         <el-breadcrumb-item>系统管理</el-breadcrumb-item>
         <el-breadcrumb-item>用户管理</el-breadcrumb-item>
       </el-breadcrumb>
       <!-- 用户列表卡片 -->
       <el-card class="box-card">
          <el-form :inline="true" :model="UserInfoVo" class="demo-form-inline">
            <el-form-item label="部门" label-width="70px" >
               <el-select v-model="UserInfoVo.depId" clearable  placeholder="请选择"  >
                  <el-option
                    v-for="item in departments"
                    :key="item.dpId"
                    :label="item.departmentName"
                    :value="item.dpId" >
                    <span style="float:left">{{item.departmentName}}</span>
                    <span style="float: right;color: #409EFF;font-size: 13px;">
                        <span class="el-tag el-tag--success el-tag--mini el-tag--plain">{{item.deptCount}}</span>
                    </span>
                  </el-option>
              </el-select>
             </el-form-item>
             
            <!-- <el-form-item label="用户名" label-width="70px">
              <el-input clearable  v-model="UserInfoVo.userName" placeholder="请输入用户名"></el-input>
            </el-form-item> -->
            
            <el-form-item label="邮箱" label-width="70px">
              <el-input v-model="UserInfoVo.email" clearable placeholder="请输入邮箱"></el-input>
            </el-form-item>
            
             <el-radio-group v-model="UserInfoVo.sex" label-width="70px">
                <el-radio :label="0"></el-radio>
                <el-radio :label="1"></el-radio>
                <el-radio label="">全部</el-radio>
              </el-radio-group>
                <br />
              <el-form-item label="昵称" label-width="70px">
                <el-input clearable  v-model="UserInfoVo.nickname" placeholder="请输入昵称"></el-input>
              </el-form-item>
            <!-- 按钮 -->
            <el-form-item style="margin-left: 10px;">
                <el-row>
                  <el-button icon="el-icon-refresh" @click="resetUserInfoVO()">重置</el-button>
                  <el-button type="primary" icon="el-icon-search" @click="findLists()">查询</el-button>
                  <el-button type="success" icon="el-icon-plus" @click="show">添加</el-button>
                  <el-button type="warning" icon="el-icon-download">导出</el-button> 
                </el-row>
            </el-form-item>
          </el-form>
          
       <!--  // 表格显示 -->
          <el-table
              :data="userList"
              border
              style="width: 100%;height:600px;">
              <el-table-column
                prop="userId"
                label="#"
                width="150">
              </el-table-column>
              <el-table-column
                sortable
                prop="departmentName"
                label="部门"
                width="150">
              </el-table-column>
              <el-table-column
                sortable
                prop="nickname"
                label="昵称"
                width="150">
              </el-table-column>
              <el-table-column
                prop="sex"
                label="性别"
                 width="150">
                 <template slot-scope="scope">{{scope.row.sex==0?'男':(scope.row.sex==1?'女':'保密')}}</template>
                 
              </el-table-column>
              <el-table-column
                prop="birthday"
                label="生日"
                 width="150" >
                  <template slot-scope="scope">{{scope.row.birthday| dateYMDFormat}}</template>

              </el-table-column>
              <el-table-column
                prop="phone"
                label="手机号"
                 width="150">
              </el-table-column>
              <el-table-column
                prop="email"
                label="邮箱"
                 width="150">
              </el-table-column>
              <el-table-column
                prop="delflag"
                label="是否禁用"
                 width="100">
                
                   <template slot-scope="scope">
                     <el-switch
                       v-model="scope.row.delflag"
                       active-color="#13ce66"
                       inactive-color="#ff4949"
                     >
                     </el-switch>
                   </template>
                 
                  
              </el-table-column>
              
              <el-table-column
                label="操作">
                <el-button type="primary" size="mini" icon="el-icon-edit"></el-button>
                <el-button type="danger" size="mini" icon="el-icon-delete"></el-button>
                <el-button type="warning" size="mini" icon="el-icon-s-tools"></el-button>
              </el-table-column>
            </el-table>
            
            <!-- 新增编辑弹框子组件 -->
            <!--<user-info-add :addOrUpdateVisible="addOrUpdateVisible" @changeShow="showAddOrUpdate" ref="UserInfoAdd"></user-info-add> -->

            <!-- 分页 -->
            <div class="block">
                
                <el-pagination
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
                  :current-page="currentPage"
                  :page-sizes="[10, 20, 30, 40]"
                  :page-size="pageSize"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="total">
                </el-pagination>
              </div>
       </el-card>
   </div>
</template>

<script>
    // import Home from "../views/Home" 
    import UserInfoAdd from './UserInfoAdd.vue'
   
    export default{
        
         name: 'Main',
         data() {
              return {
                value:'',
                //每页显示条数
                pageSize:10,
                //总记录条数
                total:7,
                //初始页数      
                currentPage:1,  
                 
                // 用户信息集合 静态数据
                userList: [{
                          userId: '1',
                          departmentName: '研发部',
                          nickname: '张小虎',
                          sex:1,
                          birthday:"2016-05-02",
                          phone:"13452850922",
                          email:"13452850922@qq.com"
                        }],
                departments: [],
                UserInfoVo:{
                      "depId": "",
                      "departmentName": "",
                      "email": "",
                      "nickname": "",
                      "phone": "",
                      "sex": "",
                      "userName": ""
                    },
                // 控制新增编辑弹窗的显示与隐藏
                addOrUpdateVisible: false
                
              }
            },
            //创建组件时调用方法
            created(){
                  this.findLists();
                  this.findDeptAndCount();
            },
           // 使用子组件
              components:{
                  UserInfoAdd
              },
            methods: {
                //当每页条数改变时
               handleSizeChange(val) {
                   this.pageSize=val;
                   this.findLists();
                   console.log(`每页 ${val} 条`);
                  },
                // 当页码改变时
                handleCurrentChange(val) {
                      this.currentPage=val;
                      this.findLists();
                      console.log(`当前页: ${val}`);
                    }, 
                 // 异步
                 async  findLists(){ 
                        var _this=this
                        console.log(this.UserInfoVo);
                        this.$ajax
                        .POST("/department/findLists", {
                          currentPage: this.currentPage,
                          pageSize: this.pageSize,
                          UserInfoVo:this.UserInfoVo
                        })
                        .then((data) => {
                             console.info(data)
                            if(data.success){  
                                _this.userList = data.data.userInfoList;
                                _this.total=data.data.total;
                            } 
                           })   
                    },
                async  findDeptAndCount(){
                    var _this=this;
                    this.$ajax
                    .POST("/department/findDeptAndCount", {
                      currentPage: this.currentPage,
                      pageSize: this.pageSize
                    })
                    .then((data) => {
                         console.info(data)
                        if(data.success){  
                            _this.departments = data.data.departments; 
                        } 
                       })  
                           
                },
                // 重置查询条件
                resetUserInfoVO(){ 
                    this.UserInfoVo.depId="",
                    this.UserInfoVo.departmentName= "",
                    this.UserInfoVo.email= "",
                    this.UserInfoVo.nickname= "",
                    this.UserInfoVo.phone= "",
                    this.UserInfoVo.sex= "",
                    this.UserInfoVo.userName= ""
                    this.findLists();
                },
                 // 按钮点击事件 显示新增编辑弹窗组件
                show(){
                    this.addOrUpdateVisible = true
                },
                // 监听 子组件弹窗关闭后触发,有子组件调用
                showAddOrUpdate(data){
                    if(data === 'false'){
                        this.addOrUpdateVisible = false
                    }else{
                        this.addOrUpdateVisible = true
                    }
                }
            }
    }
</script>

<style>
</style>

在这里插入图片描述
配置:Vue element el-table-column标签中对日期进行格式化(全局过滤器)

6.使用elment标签页

相关文章:
1.elementui tab标签管理路由页面
2.Vue之标签页
3.Vue之Element标签页保留用户操作缓存。

效果:

在这里插入图片描述

6.1 创建标签页路由store.js

import Vue from 'vue'
import Vuex from 'vuex'
import store from './store'  
import router from './router.js'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    openTab:[],//所有打开的路由
    activeIndex:'/main', //当前激活状态
    curContextTabId:"", //右击当前标签页id 
    excludeList:[],//keep-alive的黑名单
    includeList:[]//keep-alive的白名单
  },
  mutations: {
    // 添加tabs
    add_tabs (state, data) { 
        this.state.openTab.push(data); 
    },
    // 删除tabs
    delete_tabs (state, route) { 
        
      let index = 0;
      for (let option of state.openTab) {
        if (option.route === route) {
          break;
        }
        index++;
      }
       this.state.openTab.splice(index, 1);
    },
    // 设置当前激活的tab
    set_active_index (state, index) {
      this.state.activeIndex = index;
    },
     // 保存右键点击tab的id
     saveCurContextTabId(state, curContextTabId) {
         this.state.curContextTabId = curContextTabId
     },
    // 关闭所有标签
    closeAllTabs(state) {
          this.state.openTab = [];  
          //想实现标签和页面同步,以下三句代码
          store.commit('add_tabs', {route: '/main', name: '首页'}); //添加进入标签页数组
          store.commit('set_active_index', '/main'); // 激活/main的标签页
          router.push({path: '/main'});// 跳转路由页面 显示页面 
          
    },
    
    // 关闭其它标签页
    closeOtherTabs(state, par) { 
          let tabs = state.openTab;
          let length = tabs.length;
          let curContextTabId =  state.curContextTabId;
          let activeTabItem =  state.activeIndex
          // console.log("当前激活:"+activeTabItem)
          // console.log("右击选中:"+curContextTabId)
          let id; // 右键点击时的tab在整个tabs数组中的id
          let curId // 左键点击时的tab在整个tabs数组中的id 
          tabs.forEach((tab, index) => {
            if (tab.route == curContextTabId) {
              id = index
            }
            if (tab.route == activeTabItem) {
              curId = index
            } 
          })
          
           // console.log("激活:"+activeTabItem+" id:"+id+"|| 右击选中:"+curContextTabId+" curid:"+ curId) 
           store.commit('set_active_index', curContextTabId);//激活显示当前页面
           router.push({path: curContextTabId});//激活路由转换页面
           
          if (par == "left") {
            // console.info("关闭左边! 显示当前标签:"+curContextTabId)  
            this.state.openTab= state.openTab.slice(id, length) //截取当前标签到末尾标签
           
            //使首页一直存在第一位置
            if(curContextTabId !="/main")
            {
                this.state.openTab.unshift({route: '/main', name: '首页'}); //导入首页在数组头部
            }
            
            
          }
          if (par == "right") { 
           // console.info("关闭右边! 显示当前标签:"+curContextTabId)  
           this.state.openTab = state.openTab.slice(0, id + 1) //截取0到当前选择标签
          }
          if (par == "other") { 
            // console.info("关闭其他! 激活当前标签:"+curContextTabId) 
            this.state.openTab = [state.openTab[id]] //只留当前标签和首页
            if(curContextTabId !="/main")
            {
                this.state.openTab.unshift({route: '/main', name: '首页'});
            } 
          } 
          
        }
  },
  actions: {

  }
})

6.2 修改Home页面

新增:标签页和标签页右键菜单功能、用户操作缓存、关闭标签页清除缓存。
使用以下布局结合标签页
在这里插入图片描述

<template>
 <el-container class="el-container"> 
        <!-- 头部布局 -->
        <el-header> 
            <nav-top></nav-top> 
        </el-header>
        
         <!-- 左侧导航栏 -->
        <el-container> 
            <slider></slider> 
      
            <el-main>
               <!-- 内容区 -->
               <div class="app-wrap">
                   <!-- 此处放置el-tabs 标签代码 -->
                   <div >
                     <el-tabs
                       v-model="activeIndex"
                       type="border-card"
                       closable
                       v-if="openTab.length"
                        @tab-click='tabClick'
                        @tab-remove='tabRemove'
                        @contextmenu.prevent.native="openContextMenu($event)"
                       >
                       
                       <el-tab-pane
                         :key="item.name"
                         v-for="(item, index) in openTab"
                         :label="item.name"
                         :name="item.route"
                        >
                       </el-tab-pane>
                       <div class="content-wrap"> 
                       <!-- 缓存页面不刷新 -->
                        <keep-alive  :include="includeList"> 
                            <router-view></router-view>
                        </keep-alive>
                         
                       </div>
                     </el-tabs>
                   </div>
                   
               </div>
                <ul
                              v-show="contextMenuVisible"
                              :style="{left:left+'px',top:top+'px'}"
                              class="contextmenu"
                            >
                              <li @click="closeAllTabs">关闭所有</li>
                              <li @click="closeOtherTabs('left')">关闭左边</li>
                              <li @click="closeOtherTabs('right')">关闭右边</li>
                              <li @click="closeOtherTabs('other')">关闭其他</li> 
                            </ul>
           </el-main>
       </el-container>
   </el-container> 
</template>

<script>
// @ is an alias to /src
import Slider from '@/components/Slider.vue'
import navTop from '@/components/Header.vue'
import store from '../store.js'

export default {
  name: 'Home',
  components: {
    Slider,navTop,store
  },
  data(){
    return{
        contextMenuVisible:false,
        currentContextTabId:'',
        left:0,
        top:0, 
        includeList:[]
    }
  }, 
  mounted () {
      //监控标签页点击空白事件
      document.body.addEventListener("click", this.closeContextMenu);
     
      window.addEventListener("keydown", this.getKeyDown);
      
    // 刷新时以当前路由做为tab加入tabs
    // 当前路由不是首页时,添加首页以及另一页到store里,并设置激活状态
    // 当当前路由是首页时,添加首页到store,并设置激活状态 
    if (this.$route.path != '/' && this.$route.path != '/main') {
      // console.log('1'); 
      this.$store.commit('add_tabs', {route: '/main', name: '首页'}); //添加到标签页中
      this.$store.commit('set_active_index', '/main');//激活显示页面
      
      this.$store.commit('add_tabs', {route: this.$route.path , name: this.$route.name });
      this.$store.commit('set_active_index', this.$route.path);
    } else {
      // console.log('2');
      this.$store.commit('add_tabs', {route: '/main', name: '首页'}); 
      this.$router.push('/');
    }

    this.updateIncludeList();
  }, 
  methods:{
        // 标签页右击菜单
        openContextMenu(e) {
           //console.log(e.srcElement);
           if (e.srcElement.id) {
             let currentContextTabId = e.srcElement.id.split("-")[1];
             this.contextMenuVisible = true;
             // console.info("标签id:"+currentContextTabId);
             this.$store.commit("saveCurContextTabId", currentContextTabId);
             this.currentContextTabId=currentContextTabId;
             this.left = e.clientX;
             this.top = e.clientY + 10;
           }
         }, 
        // 关闭所有标签页
        closeAllTabs() {
          this.$store.commit("closeAllTabs");
          this.contextMenuVisible = false;
           this.updateIncludeList();
        },
        // 关闭其它标签页
        closeOtherTabs(par) {
          this.$store.commit("closeOtherTabs", par);
          this.contextMenuVisible = false;
           this.updateIncludeList();
        }, 
        
        
    // 关闭右击菜单
    closeContextMenu() {
          this.contextMenuVisible = false;
    }, 
    
    //tab标签点击时,切换相应的路由
    tabClick(tab){
      // console.log("tab",tab);
      console.log('active',this.activeIndex);
      this.$router.push({path: this.activeIndex});
    },
    //移除tab标签
    tabRemove(targetName){
       console.log("tabRemove",targetName);
      
      this.blackList=targetName;
      //首页不删
      if(targetName == '/'||targetName == '/main'){
        return
      }
      this.$store.commit('delete_tabs', targetName);
      if (this.activeIndex === targetName) {
        // 设置当前激活的路由
        // console.info("当前标签:"+this.openTab.length)
        if (this.openTab && this.openTab.length >= 1) {
          // console.log('=============',this.openTab[this.openTab.length-1].route)
          this.$store.commit('set_active_index', this.openTab[this.openTab.length-1].route);
          this.$router.push({path: this.activeIndex});
        } else {
          this.$router.push({path: '/'});
        }
      }
       this.updateIncludeList();
    },
    //更新缓存白名单
    updateIncludeList(){
        var arr=[];
        for (var i = 0; i < this.openTab.length; i++) {
            var temp=this.openTab[i].route.replace("/","");
            console.log("temp:"+temp);
            arr.push(temp)  
        }
        this.includeList=arr;
    }
  },
  computed: {
    openTab () {
      return this.$store.state.openTab;
    },
    activeIndex:{
      get () {
        return this.$store.state.activeIndex;
      },
      set (val) {
        this.$store.commit('set_active_index', val);
      }
    }
  },
  watch:{
    '$route'(to,from){
         
        //判断路由是否已经打开
        //已经打开的 ,将其置为active
        //未打开的,将其放入队列里
        let flag = false;
        for(let item of this.openTab){
          console.log("item.name",item.name)
          // console.log("t0.name",to.name)

          if(item.name === to.name){
            // console.log('to.path',to.path);
            this.$store.commit('set_active_index',to.path)
            flag = true;
            break;
          }
        }

        if(!flag){
          // console.log('to.path',to.path);
          this.$store.commit('add_tabs', {route: to.path, name: to.name});
          this.$store.commit('set_active_index', to.path);
         
        }
        this.updateIncludeList();
    }
  }


}
</script>
<style  >
 /*设置整个容器*/
 .el-container{
     width: 100%;
     height: 100%;
 }
 /*头部布局*/
 .el-header {
    background-color: #001529;
    display: flex;
    justify-content: space-between;
    padding-left:0 ;
    color: #FFFFFF;
   align-items: center;
   font-size:  20px;
  }
    /*侧边栏*/
  .el-aside {
    background-color: #001529;
    color: #333;
   /* //去掉边框 */
   .el-menu {
     border-right: none;
   }
   
   /*展开/收起*/
   .toggle_box {
     background-color: chartreuse;
     font-size: 15px;
     font-weight: bold;
     line-height: 24px;
     color: #FFFFFF;
     letter-spacing: 0.2em;
     text-align: center;
     cursor: pointer;
   
   }
   
  } 
    /*内容*/
  .el-main {
    background-color: #E9EEF3;
     
  }
  
  body > .el-container {
    margin-bottom: 40px;
  }
  
   /*左边logo和标题*/
  .left_box{
      display:  flex;
      align-items: center;
      /*logo*/
      img{
          width: 60px;
          height: 60px;
          margin: 0px 0px 10px 15px;
      }
      /*标题*/
      span{
          margin-left: 15px;
      }
  }
 
  /*右边的登录头像*/
  .right_box{
      /*全屏按钮、主题换色*/
      // el-icon-full-screen
      // 头像
      .el-dropdown > img{
          height: 60px;
          width: 60px;
          border-radius:50% ;
          background-color: #FFFFFF;
           margin:0px 15px 0px 0px ;
      }
     
  }
  /* // 下拉菜单 */
   .el-dropdown-link {
      cursor: pointer;
      color: #409EFF;
    }
    .el-icon-arrow-down {
      font-size: 12px;
    }
  
   a {
    text-decoration: none;
    color: #000000;
   }
   
   /* 标签页右击菜单样式 */
   .contextmenu {
     width: 100px;
     margin: 0;
     border: 1px solid #ccc;
     background: #fff;
     z-index: 3000;
     position: absolute;
     list-style-type: none;
     padding: 5px 0;
     border-radius: 4px;
     font-size: 14px;
     color: #333;
     box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.2);
   }
   .contextmenu li {
     margin: 0;
     padding: 7px 16px;
   }
   .contextmenu li:hover {
     background: #f2f2f2;
     cursor: pointer;
   }
   
</style>



6.3 Header部分-Header.vue

<template>
    <div class="header"> 
        <!-- logo和项目名 -->
        <div class="left_box">
            <img src="../assets/img/timg.gif" />
            <span>XX管理系统</span>
        </div>
                     
        <!-- 用户登录展示头像 -->
        <div class="right_box"> 
           <i class="el-icon-full-screen" @click="toggleFullscreen"></i> 
            <!-- 下拉菜单 -->
            <el-dropdown>
              <img src="../assets/img/timg.gif" />
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item icon="el-icon-user">个人信息</el-dropdown-item>
                <el-dropdown-item icon="el-icon-edit-outline">修改密码</el-dropdown-item>
                <el-dropdown-item icon="el-icon-switch-button" ><span @click='logout'>退出登录</span></el-dropdown-item>
              
              </el-dropdown-menu>
            </el-dropdown>
        </div>
    </div>
    
    
</template>

<script>
export default {
    name:'navTop',
    methods:{
        logout(){
            console.info("退出登录!清除token")
            this.$store.state.openTab = [];
            // this.$store.state.activeIndex = '/main';
            //清除token
            localStorage.removeItem('token');
            this.$router.push('/login') 
        },
        toggleFullscreen() {
        let _this = this;
        let el = document.documentElement;
             if (document.fullscreenElement === null) {
                 _this.openFullscreen(el);
             } else {
                 _this.quitFullscreen();
             }
        },
        openFullscreen(element) {
             if (element.requestFullscreen) {
                 element.requestFullscreen();
             } else if (element.webkitRequestFullScreen) {
                 element.webkitRequestFullScreen();
             } else if (element.mozRequestFullScreen) {
                 element.mozRequestFullScreen();
             } else if (element.msRequestFullscreen) {
                 // IE11
                 element.msRequestFullscreen();
         }
        },
    }
}
</script>

<style lang="less" scoped>

/*头部布局*/
    .header {
       background-color: #001529;
       display: flex;
       justify-content: space-between;
       padding-left:0 ;
       color: #FFFFFF;
      align-items: center;
      font-size:  20px; 
      width: 100%;
     }
     
     /*左边logo和标题*/
     .left_box{
         display:  flex;
         align-items: center;
         /*logo*/
         img{
             width: 60px;
             height: 60px;
             margin: 0px 0px 10px 15px;
         }
         /*标题*/
         span{
             margin-left: 15px;
         }
     }
         
     /*右边的登录头像*/
     .right_box{
         
         /*全屏按钮、主题换色*/
         /* // el-icon-full-screen */
         /* // 头像 */ 
         .el-dropdown > img{
             height: 60px;
             width: 60px;
             border-radius:50% ;
             background-color: #FFFFFF;
             margin:0px 15px 0px 0px ;
         }
    }
</style>

6.4 Asider部分-Slider.vue

<template>
    
    <el-aside class="aside" :width="isCollapse?'60px':'200px'" >
        <!--展开/收起-->
        <div class="toggle_box" @click="toggleCollapse">|||</div>
        <el-menu
              class="el-menu-vertical-demo"
              @open="handleOpen"
              @close="handleClose"
              background-color="#001529"
              text-color="#fff"
              active-text-color="#ffd04b"
              :collapse="isCollapse"
              :default-active="activePath"
              :collapse-transition="false"
              :unique-opened="true"
              :router="true">
              
              <el-menu-item index="/main" >
                <i class="el-icon-menu"></i>
                <span slot="title">首页</span>
              </el-menu-item>
               
               <el-submenu  v-for="(item,index) in menuList" 
                             :key="index" 
                             :index='item.id'
                             v-if="item.show && item.children!=''">
                        <!-- 主菜单 -->
                      <template slot="title">
                        <i :class="item.icon"></i>
                        <span>{{item.title}}</span>
                      </template>
                      <!-- 子菜单 -->
                      <el-menu-item-group >
                        <el-menu-item v-for="child in item.children" 
                                      :key="child.id" 
                                      :index='child.index' 
                                      v-if="child.show" >
                          <i :class="child.icon"></i><span>{{child.childtitle}}</span>
                        </el-menu-item>
                      </el-menu-item-group> 
                      
               </el-submenu>
              
              
            </el-menu>
    </el-aside>
    
</template>

<script>
export default {
  name: 'Slider',
  data(){
    return {
      isCollapse: false,
      activePath: '',
      menuList:[ 
        {
          id:'1',
          title: '系统管理',
          icon:'el-icon-s-tools',
          show:true,
          children:[
            {
              index:'/user',
              childtitle:'用户管理',
              icon:'el-icon-user',
              show:true,//是否显示子菜单页面
            },
            {
              index:'/page2',
              childtitle:'权限管理',
              icon:'el-icon-star-off',
              show:true,
            },
          ]
        },
        {
          id:'2',
          title: '日志管理',
          icon:'el-icon-message-solid',
          show:true,
          children:[
            {
              index:'/page3',
              childtitle:'sql日志',
              icon:'el-icon-view',
              show:true,
            },
            {
              index:'/page4',
              childtitle:'用户日志',
              icon:'el-icon-edit',
              show:true,
            },
          ]
        }
      ]
    }
  },
  methods:{
       toggleCollapse () {
         this.isCollapse = !this.isCollapse
       },
       handleOpen (key, keyPath) {
         // console.log(key, keyPath)
       },
       handleClose (key, keyPath) {
         // console.log(key, keyPath)
       },
       
        quitFullscreen() {
             if (document.exitFullscreen) {
                 document.exitFullscreen();
             } else if (document.webkitCancelFullScreen) {
                 document.webkitCancelFullScreen();
             } else if (document.mozCancelFullScreen) {
                 document.mozCancelFullScreen();
             } else if (document.msExitFullscreen) {
                 document.msExitFullscreen();
             }
        },
        getKeyDown() {
             let _this = this;
             if (event.keyCode === 122) {
                 event.preventDefault() || (event.returnValue = false);
                 _this.toggleFullscreen(); // 触发全屏的按钮
             } else if (event.keyCode === 27) {
                 if (document.fullscreenElement !== null) {
                     _this.quitFullScreen();
                 }
             }
        },
  }
}
</script>
<style lang="less" scoped>
 
    /*侧边栏*/
     .el-aside {
       background-color: #001529;
       color: #333;
          /* //去掉边框 */
          .el-menu {
            border-right: none;
          }
      
      /*展开/收起*/
      .toggle_box {
        background-color: chartreuse;
        font-size: 15px;
        font-weight: bold;
        line-height: 24px;
        color: #FFFFFF;
        letter-spacing: 0.2em;
        text-align: center;
        cursor: pointer;
        
            .i{
                 font-size: 15px;
            }
        }
      }
</style>

6.5 main.js修改

新增:限制标签页条数,路由拦截跳转。

import Vue from 'vue'
import App from './App.vue'
import router from './router' //引入路由
import store from './store'     //引入标签
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import './assets/css/global.css' //引入全局样式

//验证码插件
import SlideVerify from 'vue-monoplasty-slide-verify';
Vue.use(SlideVerify);

Vue.use(ElementUI);
Vue.config.productionTip = false


import { Message } from 'element-ui';

//引入日期标签格式化插件
import moment from 'moment'
Vue.use(require('vue-moment'));
Vue.prototype.moment = moment
Vue.filter('dateYMDHMSFormat',function(dateStr,pattern='YYYY-MM-DD HH:mm:ss'){
  return moment(dateStr).format(pattern);
})


import axios from 'axios' //引入axios
import qs from 'qs'
  
var base="http://localhost:8090" 
export const POST = (url, params) => {
   axios.defaults.transformRequest = [
     function (data) {
       return qs.stringify(data)
     }
   ];
  console.log(url);
  return axios.post(`${base}${url}`, params).then(res => {
    console.log(res);
    if (res.status == 200)
      return res.data;
    else
     this.$message.error("操作失败,服务端出现异常错误!") 
  })
}

Vue.prototype.$ajax = { 
  POST(url, params) {
         return POST(url, params);
  },
}

// 添加请求拦截器 
axios.interceptors.request.use(function (config) {
  let token=localStorage.getItem('token')
  // console.info("每次带token")
  // console.info(token)
  if(token)
    config.headers.common['token'] = token; 
    
  return config;
}, function (error) {
  // Do something with request error
  Message.error('网络连接异常,请稍后再试!');
  console.info("error: ");
  console.info(error);
  return Promise.reject(error); 
});


// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  // Do something with response error
  if (error.response.status == 404) {
    //找不到请求的服务
    Message.error('找不到请求的服务!');
  } 
  else if (error.response.status == 401) {
    //没有token需要登录
    Message.error('登录凭证失效,请重新登录!'); 
    localStorage.removeItem("token"); 
    window.location.href = "/login";
  } else if (error.response.status == 403) {
     Message.error('token过期,请重新登录!');
     localStorage.removeItem("token"); 
     window.location.href = "/login";
  }  
  return Promise.reject(error);
})

router.beforeEach((to, from, next) => { 
     console.info("当前标签数:"+store.state.openTab.length)
     //限制最大标签页数 超过不跳转路由~
     if(store.state.openTab.length>12){
         Message.info('标签页已达到最大12页');
         return
     } 
      
     //根据字段判断是否进行路由过滤(即是否需要登录才能访问) 
    if (localStorage.getItem('token') != null || to.name == 'login') {
        next()
    } else { 
       console.info("token失效跳转登录页面") 
        next({
            path: '/login',
        });
    } 
});


new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

6.6 router.js修改

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import main from './views/main.vue'
import user from './views/user.vue'
import page2 from './views/page2.vue'
import page3 from './views/page3.vue'
import page4 from './views/page4.vue'
import login from './views/Login'
Vue.use(Router)

export default new Router({
  routes: [
    {
      path:'/login',
      component:login,
      name:'login'
    },
    { 
      path: '/', 
      component: Home,
      redirect: 'main',
      children: [
        {
          path: 'main',
          name: '首页',
          component: main
        },
        {
          path: 'user',
          name: '用户管理',
          component: user
        },
        {
          path: 'page2',
          name: 'page2',
          component: page2
        },
        {
          path: 'page3',
          name: 'page3',
          component: page3
        },
        {
          path: 'page4',
          name: 'page4',
          component: page4
        }
      ]
    }
  ]
})

const originalPush = Router.prototype.push
  Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}


到此vue主体框架完成…
github地址:https://github.com/123ac/tabRouter
– 未完待续

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值