欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

3.使用ElementUI搭建前端系统框架三

程序员文章站 2022-05-10 21:02:35
...

本节内容是参数化布局管理器的菜单和响应事件,需要用到新技术Vuex,Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,Vuex细节参考https://vuex.vuejs.org/zh/,我们在此只做简单介绍,重点是如何在前端项目中应用Vuex。

使用VUE设计前端项目,各组件的数据是彼此分离的,无法很好地共享数据,使用Vuex可以在不同vue组件间共享数据。

vuex包括state,mutation,action,getter,module。
state
用于存储数据,可以存储扁平和树状结构对象,各组件间使用全局变量可以共享state数据,同时支持mapState映射所有的数据变量

const state = {
    token: '',
    navList: [],
    permissionList:[],
    openedList:[]
}

mutation
用于修改state数据,vue组件必须使用mutation才可以修改vuex中的数据。mutation相对于计算属性,它的第一个参数必须是state,第二个参数是赋值对象

const mutations = {
    setToken: (state, data) => {
        state.token = data
    },
    setNavList: (state, data) => {
        state.navList = data
        sessionStorage.setItem('navList', data);
    },
    setPermissionList: (state, data) => {
        state.permissionList = data
        sessionStorage.setItem('permissionList', data);
    },
    addPage(state, data){
        if (state.openedList.some(v => v.path === data.path)) 
            return
        data.visiable=true
        data.url = data.path
        state.openedList.push(data)
    },
    removePage(state, data){
        if(data){
            for(let [i, v] of state.openedList.entries()){
                if(v.path === data.path){
                    state.openedList.splice(i, 1)
                }
            }
        } else{
            state.openedList = []        
        }
    }
}

在vue组件中调用mutation必须使用commit方法

this.$store.commit("auth/removePage", item)

action
Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
    Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
    也可以第一个参数定义为commit,同时支持mapAction映射。
const actions = {
    getNavList({commit}){
        return new Promise((resolve) =>{
            axios({
                url: '/acllist',
                methods: 'post',
                data: {}
            }).then((res) => {
                commit("setNavList", res.navList)
                commit("setPermissionList", res.permissionList)
                resolve(res)
            })
        })
    },
    login({ commit }, userInfo) {
        return new Promise((resolve) => {
            axios({
                url: '/login',
                method: 'post',
                data: {
                    ...userInfo
                }
            }).then(res => {
                if(res && res.login){
                    commit('setToken', res.token)
                    sessionStorage.setItem('isLogin', res.login);
                    localStorage.setItem('userId', res.uid);
                    localStorage.setItem('orgName', res.orgname);
                    localStorage.setItem('userName', res.name);
                    localStorage.setItem('token', res.token);
                }
                resolve(res)
            })
        });
    },

getter
访问vuex 状态数据的一种方式,可以对数据进行过滤处理。支持mapGetter操作

const getters = {
    getNavList: state => {
        return sessionStorage.getItem('navList');
    },
    getPermissionList: state => {
        return sessionStorage.getItem('permissionList');
    }
}

module
当数据结构比较复杂是,vuex支持使用module分组存储

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

使用Vuex步骤

1、新建目录/src/store
2、分module存储vuex状态数据,新建/src/store/modules/auth存储认证相关数据
3、在/src/store/modules/auth目录新建index.js文件,定义vuex中规定的组件state,mutation,action等,这个文件比较大,后面介绍。
4、在/src/store/modules/目录下新建index.js,引入模块auth中定义组件

import auth from './auth'

export default {
    auth: auth
}

5、在/src/store目录下新建index.js,声明vuex

import Vue from 'vue'
import Vuex from 'vuex'
import vuexModules from './modules'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: vuexModules
})

6、在/src/main.js中引入vuex全局变量store
import store from ‘./store’

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import store from './store'

// 引入CSS
import 'static/css/gf-default.scss'

Vue.config.productionTip = false

Vue.use(ElementUI)

/* eslint-disable no-new */
//new Vue({
//  el: '#app',
//  router,
//  components: { App },
//  template: '<App/>'
//})

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

第三步中定义的vuex组件

import Cookies from 'js-cookie'

const state = {
    token: '',//登录成功后获取的令牌
    navList: [],//菜单对象集合
    permissionList:[],//授权访问菜单对象集合
    openedList:[]//在TabPane中打开的页面集合
}

const getters = {
    getNavList: state => {
        return sessionStorage.getItem('navList');
    },
    getPermissionList: state => {
        return sessionStorage.getItem('permissionList');
    }
}

const mutations = {
    //登录成功后设置token,第一个参数为state对象,第二个是赋值对象
    setToken: (state, data) => {
        state.token = data
    },
    //登录成功后,从后台获取菜单列表
    setNavList: (state, data) => {
        state.navList = data
        sessionStorage.setItem('navList', data);
    },
    //登录成功后,从后台获取授权访问菜单列表
    setPermissionList: (state, data) => {
        state.permissionList = data
        sessionStorage.setItem('permissionList', data);
    },
    //当点击左侧导航菜单时,向openedList中添加页面,TabPane组件上会相应显示
    addPage(state, data){
        if (state.openedList.some(v => v.path === data.path)) 
            return
        data.visiable=true
        data.url = data.path
        state.openedList.push(data)
    },
    //当点击TabPane组件上页面的关闭图标,从openedList中删除页面,TabPane组件上会相应显示
    removePage(state, data){
        if(data){
            for(let [i, v] of state.openedList.entries()){
                if(v.path === data.path){
                    state.openedList.splice(i, 1)
                }
            }
        } else{
            state.openedList = []        
        }
    }
}

const actions = {
    //支持异步操作,通过Ajax从后台获取菜单数据
    getNavList({commit}){
        return new Promise((resolve) =>{

        })
    },
    //登录操作
    login({ commit }, userInfo) {
        return new Promise((resolve) => {

        });
    },
    logout({commit}) {
        return new Promise((resolve) => {
            sessionStorage.removeItem('isLogin');
            localStorage.removeItem('userId'); 
            localStorage.removeItem('orgName'); 
            localStorage.removeItem('userName'); 
            localStorage.removeItem('token');  
            resolve()
        })
    },
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}

3.使用ElementUI搭建前端系统框架三
在/src/main.js中定义vuex全局访问变量store
修改LeftPane.vue组件
3.使用ElementUI搭建前端系统框架三

<template>
    <div class="sidediv">
      <div @mouseenter="fn" class="floatdiv">
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo2.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo3.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo4.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo5.png" alt style="width:25px;" />
        </div>
        <div style="margin-left:0.3rem;margin-top:1.875rem">
          <img src="~static/images/demo6.png" alt style="width:25px;" />
        </div>
      </div>
      <div class="fly" v-show="hidde" @mouseleave="leave()">
        <div style="height:25px"></div>
        <ul class="uls">
          <li v-for="(item,i) in arr" @click="fun(i)">{{item.name}}</li>
        </ul>
      </div>

      <div class="menudiv">
        <el-menu
          router
          ref="navbar"
          :default-active="defActive"
          :mode="navMode"
          menu-trigger="click"
          @select="selectMenu"
          @open="openMenu"
          @close="closeMenu"
          unique-opened>
          <div class="nav_css">
            <left-menu  v-for="(item, n) in navList" :item="item" :navIndex="String(n)" :key="n"></left-menu>
          </div>
        </el-menu>
      </div>
    </div>
</template>
<style scoped>
.sidediv{
  
}
.menudiv {
  z-index: -1;
  margin-left: 2.4rem;
  width: 7rem;
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto;
}
.nav_css{
  max-height:33rem;
  overflow: hidden;
  width: 9rem;
  overflow-y: scroll;
  text-align:left;
  line-height:33px;
  border:0;
}
.floatdiv {
  position: absolute;
  width: 2.4rem;
  height: 100%;
  background: #001529;
  float: left;
}
.fly {
  width: 7rem;
  height: 100%;
  float: left;
  background: #001529;
  position: absolute;
  left: 2.4rem;
  z-index: 100;
}
.uls li {
  width: 6rem;
  height: 40px;
  line-height: 40px;
  padding-left: 5px;
  margin-bottom: 14.5px;
  color: white;
  cursor:pointer;
}
</style>
<script>
import { mapState } from "vuex";
import LeftMenu from "./LeftMenu";

export default {
  data() {
    return {
      navBgShow: false,
      hidde: false,
      tagNavList: [],
      hometag: {},
      currenttag: {},
      arr: [
        { name: "待办工作" },
        { name: "关闭页签" },
        { name: "导航切换" }
      ],
    };
  },
  props: ["layout"],
  computed: {
    ...mapState({
      navList: state => state.auth.navList
    }),
    defActive() {
      return this.$route.path;
    },
    navMode() {
      if (this.layout == "left") {
        return "vertical";
      }
      if (this.layout == "top") {
        return "horizontal";
      }
    },
    isDark() {
      return true;
    }
  },
  watch: {
    $route() {

    }
  },
  methods: {
    fn() {
      this.hidde = !this.hidde;
    },
    leave() {
      this.hidde = false;
    },
    fun(i) {
      if (i == 0) {
        this.$router.push("/home");
        this.hidde = false;
      }else if (i == 1) {
         this.closeAllTag();
      }else if (i == 2) {
         this.changeMenu();
      }

      // this.$router.push('/'+this.arr.url[i])
    },
    selectMenu(index, indexPath) {
      let openedMenus = this.$refs.navbar.openedMenus;
      let openMenuList;
      if (indexPath.length > 1) {
        let parentPath = indexPath[indexPath.length - 2];
        openMenuList = openedMenus.slice(openedMenus.indexOf(parentPath) + 1);
      } else {
        openMenuList = openedMenus;
      }
      openMenuList = openMenuList.reverse();
      openMenuList.forEach(ele => {
        this.$refs.navbar.closeMenu(ele);
      });
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    },
    openMenu() {
      if (this.navMode == "horizontal") {
        this.navBgShow = true;
      }
    },

    closeAllTag() {
      this.hometag={};
      this.currenttag={};
      this.tagNavList = this.$store.state.tagNav.openedPageList;

      this.tagNavList.forEach(item =>{
          if (item.path == '/home'){
            this.hometag = item;
          }

          if (item.path == this.$route.path){
            this.currenttag = item;
          }
      })

      this.$store.commit("tagNav/removeTagNav", null);
      this.$store.commit("tagNav/addTagNav", this.hometag);
      this.$store.commit("tagNav/addTagNav", this.currenttag);
    },

    changeMenu() {
      if (this.isBig) {
        this.isBig= false;
      }else{
        this.isBig= true;
      }

      if (this.isSmall) {
        this.isSmall= false;
      }else{
        this.isSmall= true;
      }
    },

    closeMenu() {
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    },
    closeAll() {
      let openMenu = this.$refs.navbar.openedMenus.concat([]);
      openMenu = openMenu.reverse();
      openMenu.forEach(ele => {
        this.$refs.navbar.closeMenu(ele);
      });
      if (this.navMode == "horizontal") {
        this.navBgShow = false;
      }
    }
  },
  components: { LeftMenu }
};
</script>

修改TabPane.vue组件
3.使用ElementUI搭建前端系统框架三

<template>
    <div class="tag-nav">
        <tab-scroll-bar ref="scrollBar">
            <router-link ref="tag" class="tag-nav-item"
                :class="isActive(item) ? 'cur' : ''" v-for="(item, index) in openedList" 
                :to="item.path" :key="index">
                <span class='ivu-tag-dot-inner'></span>
                    {{item.title}}
                <span class='el-icon-close' @click.prevent.stop="closePage(item, index)"></span>
            </router-link>
        </tab-scroll-bar>
    </div>
</template>

<script>
import TabScrollBar from './TabScrollBar'

export default {
    data(){
        return {
            defaultPage: '/gf/home'
        }
    },
    computed: {
        openedList(){
            return this.$store.state.auth.openedList;
        }
    },
    mounted(){
        //this.openPage()
    },
    watch: {
        $route(){
            this.openPage()
            this.scrollToCurPage()
        }
    },
    methods: {
        openPage(){
            if(this.$router.getMatchedComponents()[1])
            {
                this.$store.commit("auth/addPage", {
                    name: this.$router.getMatchedComponents()[1].name,
                    path: this.$route.path,
                    title: this.$route.meta.name
                })
            }
        },
        isActive(item){
            return item.path === this.$route.path
        },
        closePage(item, index){
            this.$store.commit("auth/removePage", item)
            if(this.$route.path == item.path){
                if(index){
                    this.$router.push(this.openedList[index-1].path)
                } else {
                    this.$router.push(this.defaultPage)
                    if(this.$route.path == "/gf/home"){
                        this.openPage()
                    }
                }
            } 
        },
        scrollToCurPage(){
            this.$nextTick(() =>{
                for (let item of this.$refs.tag) {
                    if (item.to === this.$route.path) {
                        this.$refs.scrollBar.scrollToCurPage(item.$el)
                        break
                    }
                }
            })
        }
    },
    components: {TabScrollBar}
}
</script>

修改/src/store/modules/auth/index.js
3.使用ElementUI搭建前端系统框架三
路由需要定义beforeEach事件,处理页面添加逻辑
3.使用ElementUI搭建前端系统框架三

import Vue from 'vue'
import Router from 'vue-router'
import staticRoute from './staticRoutes'
import store from '../store'
import NProgress from 'nprogress'

Vue.use(Router)

const router = new Router({
  mode: 'hash',
  routes: staticRoute
})


const whiteList = [
  '/login',
  '/sso',
  '/logout',
]

let permissionList = []

function initRoute(router){
  return new Promise((resolve) => {
    store.dispatch('auth/getNavList').then((res) => {
      permissionList = res.permissionList;
      permissionList.forEach(function(v)
      {
        if(v.url)
        {
          let routeItem = router.match(v.url)
          if(routeItem){
            routeItem.meta.permission = v.permission ? v.permission : []
            routeItem.meta.name = v.name
          }
        }
        resolve()
      })
    })
  })
}

router.beforeEach((to, from, next) => {
  NProgress.start();
  if (true) {
    console.log('登录成功 from='+from.path+',to='+to.path);
    if (to.path === '/login') {
      next({path: "/gf/home", replace: true})
    } else if(to.path.indexOf("/error") >= 0){
        next()
    } else {
      initRoute(router).then(() => {
        let isPermission = false
        permissionList.forEach((v) => {
          if(v.url == to.fullPath){
            isPermission = true
          }
        })
        isPermission = true;
        if(!isPermission){
          next({path: "/error/401", replace: true})
        } else {
          next()
        }
      })
    }
  } else {
    console.log('未登录 from='+from.path+',to='+to.path);
    if (whiteList.indexOf(to.path) >= 0) {
      next()
    } else {
      console.warn('当前未处于登录状态,请登录')
      next({path: "/login", replace: true})
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done(); // 结束Progress
})

export default router

刷新网页
3.使用ElementUI搭建前端系统框架三
新建产品管理组件,/src/product/prdtlist.vue
3.使用ElementUI搭建前端系统框架三
添加路由
3.使用ElementUI搭建前端系统框架三
修改/src/store/modules/auth/index.js
定义菜单集合,此时还没有从后台获取
3.使用ElementUI搭建前端系统框架三
刷新页面可以演示效果

3.使用ElementUI搭建前端系统框架三
gfvue_v1.4.zip 代码下载

链接:https://pan.baidu.com/s/14hO3ZZv6KIcp1FmKEb0Dzg
提取码:o445