<repo_name>2422002753/8.16<file_sep>/src/user1/mutations-types.js
// user1/mutations-types.js
//  把mutations事件名定义为常量 防止项目过大的时候大家的命名冲突
// eslint-disable-next-line import/prefer-default-export
export const MADD = 'MADD';
// 点击加的事件
<file_sep>/src/api/index.js
// api/index.js
// eslint-disable-next-line import/no-cycle
import myAjax from '../lib/axios';

const getList = () => myAjax.request({
  url: '/list',
});
// 请求路由权限
const getAuth = () => myAjax.request({
  url: '/auth',
});
// eslint-disable-next-line import/prefer-default-export
export { getList, getAuth };
<file_sep>/路由权限.md
component 新建  Icon.vue  


# vue 路由权限  

1. 后端返回权限列表

```js
[
  {
    parentId: -1,
    name: '购物车',
    id: 1,
    auth: 'cart' //auth权限的标志,为了防范auth对应的都是路由path
  },
  {
    parentId: 1,
    id: 2,
    name: '购物车列表',
    auth: 'cart-list'
  },
  {
    parentId: -1,
    name: '我的',
    id: 3,
    auth: 'my' //auth权限的标志
  }
];
```

2. 前端配置好路由
   配置公共路由和权限路由
   代码见:router.js
3.  获取权限

- stroe 定义一个值 这个值是代表有没有获取过权限如果没有就请求权限接口
- 请求接口
- 对比权限路由和权限列表来筛选出我们需要的路由

4. 通过 router.addRoutes 添加动态路由 也就是我们筛选出来的路由列表
<file_sep>/权限管理的副本.MD
Vue 权限菜单及按钮权限

# 一.服务端返回路由权限数据

Vue 权限菜单需要根据后端返回的数据来实现

[
{pid:-1,name:'购物车',id:1,auth:'cart'},
{pid:1,name:'购物车列表',id:4,auth:'cart-list'},
{pid:4,name:'彩票',id:5,auth:'lottery'},
{pid:4,name:'商品',id:6,auth:'product'},
{pid:-1,name:'商店',id:2,auth:'shop'},
{pid:-1,name:'个人中心',id:3,auth:'store'},
];

# 二.静态菜单

使用 element-ui 构建静态菜单

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
<el-menu default-active="2" class="el-menu-vertical-demo">
<el-submenu index="1">
<template slot="title">导航一</template>
<el-submenu index="1-1">
<template slot="title">选项 1-1</template>
<el-menu-item index="1-1-1">选项 1-1-1</el-menu-item>
<el-menu-item index="1-1-2">选项 1-1-2</el-menu-item>
</el-submenu>
<el-menu-item index="1-2">选项 1-2</el-menu-item>
</el-submenu>
<el-menu-item index="2">
导航二
</el-menu-item>
<el-menu-item index="3">
导航三
</el-menu-item>
<el-menu-item index="4">
导航四
</el-menu-item>
</el-menu>
路由配置

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)
export const authRoutes = [ // 权限路由
{
path: '/cart',
name: 'cart',
component: () => import('@/views/Cart'),
children: [
{
path: 'cart-list',
name: 'cart-list',
component: () => import('@/views/CartList'),
children: [
{
path: 'lottery',
name: 'lottery',
component: () => import('@/views/Lottery'),
},
{
path: 'product',
name: 'product',
component: () => import('@/views/Product'),
},
],
},
],
},
];
export default new Router({ // 默认导出 首页和 404 页面
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path:'*',
component:{
render:h=>h('h1',{},'Not Found')
}
}
]
})

# 三.获取权限

根据后端返回的数据，格式化树结构，并提取用户权限

// 默认设置没有获取过权限
export default new Vuex.Store({
state: {
hasPermission:false
},
mutations: {
setPermission(state){
state.hasPermission = true
}
},
})
在路由跳转前看下是否获取过权限，如果没有获取过，就获取权限存入 vuex 中

router.beforeEach(async (to,from,next)=>{
if(!store.state.hasPermission){
// 获取最新路由列表
let newRoutes = await store.dispatch('getRouteList');
router.addRoutes(newRoutes); // 增加新路由
next({...to,replace:true})
}else{
next(); // 获取过就不需要再次获取了
}
})

# 四.获取相关需要数据

const getMenListAndAuth = (menuList)=>{
let menu = [];
let sourceMap = {};
let auth = [];
menuList.forEach(m => {
m.children = []; // 增加孩子列表
sourceMap[m.id] = m;
auth.push(m.auth)
if(m.pid === -1){
menu.push(m); // 根节点
}else{
sourceMap[m.pid] && sourceMap[m.pid].children.push(m)
}
});
return {menu,auth} // 获取菜单数据和权限数据
}
async getRouteList({dispatch,commit}){
let auths = await axios.get('http://localhost:3000/roleAuth');
let menuList = auths.data.menuList;
let {menu,auth} = getMenListAndAuth(menuList);
}

# 五.找到需要添加的路由

import {authRoutes} from './router'
const getRoutes = auth => {
const filter = (authRoutes)=>{
return authRoutes.filter(route=>{
// 包含权限
if(auth.includes(route.name)){
if(route.children){
route.children = filter(route.children);
}
return true;
}
})
}
return filter(authRoutes);
};

// 获取需要添加的路由列表
async getRouteList({ dispatch, commit }) {
let auths = await axios.get("http://localhost:3000/roleAuth");
let menuList = auths.data.menuList;
let { menu, auth } = getMenListAndAuth(menuList);
commit("setMenu", menu); // 将菜单数据保存起来
commit("setPermission"); // 权限获取完毕
// 通过 auth 查找需要添加的路由
return getRoutes(auth);
}

# 六.递归渲染菜单

渲染 Menu 组件提取公共部分

<template>
 <div>
  <el-menu>
   <template v-for="menu in $store.state.menu">
    <el-submenu v-if="menu.children.length" :key="menu.auth" :index="menu.auth">
     <template slot="title">{{menu.name}}</template>
     <!-- 此处需要不停的递归 el-submenu  -->
    </el-submenu>
    <el-menu-item v-else :key="menu.auth" :index="menu.auth">{{menu.name}}</el-menu-item>
   </template>
  </el-menu>
 </div>
</template>
编写递归组件

<template>
    <el-submenu :index="menu.auth">
         <template slot="title">{{menu.name}}</template>
         <template v-for="(child,index) in menu.children">  
            <el-menu-item v-if="!child.children.length" :key="index"> 
               <router-link :to="{name:child.auth}"> {{child.name}}</router-link>
            </el-menu-item>
            <!-- 如果有儿子继续递归组件 -->
            <ResubMenu :menu="child" v-else :key="index"></ResubMenu>
         </template>
    </el-submenu>
</template>
<script>
export default {
    name:'ResubMenu',
    props:{
        menu:{}
    }
}
</script>
# 七.权限按钮控制
state: {
    hasPermission: false,
    menu: [], // 菜单权限
    btnPermission:{ // 按钮权限
        edit:false,
        add:true
    }
},
查看当前按钮是否有权限

<el-button v-has="'edit'">编辑</el-button>
<el-button v-has="'add'">添加</el-button>

自定义指令的使用

directives: {
has: {
inserted(el, bindings, vnode) {
let value = bindings.value;
// 在 vuex 中查看是否有按钮权限
let flag = vnode.context.\$store.state.btnPermission[value];
// 如果没有全选则将按钮删除即可
!flag && el.parentNode.removeChild(el);
}
}
}
<file_sep>/deploy.sh
#!/usr/bin/env sh

# 确保脚本抛出遇到的错误
set -e
echo '开始'
git add -A
echo '添加完成'
git commit -m 'vuex模块2拆分'
echo 'commit 完成'
git push origin master
echo 'push完成'

<file_sep>/src/mock/home.js
// mock/home.js  只处理首页的请求
// request 请求  response  响应
// url: "/list?id=1&name=lili"  {id:1,name:'lili'}
import Mock from 'mockjs';
// 返回路由权限数组
const data = Mock.mock({
  'list|3': [
    {
      name: '@cname',
      email: '@email',
      'age|10-20': 1,
    },
  ],
});
function senList() {
  return {
    code: 200,
    data,
  };
}
function loginApi(req) {
  const { user, pass } = JSON.parse(req.body);
  // 判断用户名和密码是否合法
  if (user === 'lili' && pass === '<PASSWORD>') {
    return { msg: '登录成功' };
  }
  return { msg: '登录失败' };
}

export { senList, loginApi };
<file_sep>/vue组件传值.md
# vue 组件传值

1. props 父传子 数字、对象、数组、布尔值、对象的属性 都需要 v-bind: 缩写成 :
2. $emit 和 $on 子传父

```js
//子组件 更新m的值到200 .sync是一种语法糖
this.$emit('update:m', 200);
//父组件 money 和子组件的m值同步
<Son :m.sync="money"></Son>
```

```js
//子组件 v-model 只能针对value
 this.$emit('input', 200);
// 父组件通过@value @input的事件进行修改
 <Son @value='money'
      @input="change"></Son>
 // 事件
change(value) {
     this.money = value;
}
 //可以简写成下面
 <Son v-model='money'></Son>

```

3. eventbus 简单项目可以用 eventbus 进行传值

- 把\$bus 变成一个 vue 的实例 同一个 vue 实例

```js
Vue.prototype.$bus = new Vue();
this.$bus.$emit('toS');
this.$bus.$on('toS', () => {
  console.log(345);
});
```

4. refs  组件和 dom 上
   ref 如果放在组件上 拿到的是挂到的组件上的实例
   ref 如果放在元素上 拿到是当前元素的 dom

5. $attrs 和 $listeners 从上到下传递
   $attrs 传递属性  继续往下传 用 v-bind 
   $listeners 传递的是事件 继续往下传 v-on
6. slot
7. vuex 所有组件都能共享状态
   //作业 辅助函数的的模块使用 mapActions mapMumations MapGetters
   rootState 和 rootGetters 可以拿到根 vuex 的 state 和 getters
   // commit 派发到全局 作业
<file_sep>/README.md
# axios 封装

axios.create() 会创建一个 axios 实例

# mockjs 的使用

1. src 文件目录下面建立 mock 文件夹，mock 下面创建 index.js 文件
2. main.js 引入 mock/index.js

```js
//根据环境变量决定是否使用mock进行数据模拟
if(process.env.NODE_ENV=='development')import('./mock')
import()  //es6 里面 懒加载
```

3. src 里面建立一个 api 文件夹  文件夹里面创建 index.js (放请求)
4. mockjs 可以拦截 ajax 请求 可以拦截匹配到的路径 可以一个页面  拆分出来一个 js 去做响应处理/或者一个功能模块拆分成一个 js
5. src 建立一个 lib 文件夹 工具类

作业 博客 写 axios 封装过程 vuex 使用



<file_sep>/src/mock/routerauth.js
// routerautu.js
// pid -1 表示一级路由 pid的父请等于上级的id auth 有权限就会有auth 标志
const authlist = () => [
  {
    parentid: -1,
    name: '购物车',
    id: 1,
    auth: 'cart',
  },
  {
    parentid: 1,
    name: '购物车列表',
    id: 4,
    auth: 'cart-list',
  },
  {
    parentid: 4,
    name: '彩票',
    id: 5,
    auth: 'lott',
  },
  {
    parentid: 4,
    name: '商品',
    id: 6,
    auth: 'product',
  },
  {
    parentid: -1,
    name: 'my',
    id: 2,
    auth: 'cart',
  },
];

export { authlist };
export default {};
<file_sep>/src/lib/util.js
const util = {
  /**
   * @param {*} str  type:String
   */
  queryString(str) {
    if (typeof str !== 'string') {
      throw TypeError('str is not String');
    }
    // eslint-disable-next-line no-param-reassign
    str = str.split('?')[1].split('&');
    const obj = {};
    str.forEach((item) => {
      // eslint-disable-next-line no-param-reassign
      item = item.split('=');
      // eslint-disable-next-line prefer-destructuring
      obj[item[0]] = item[1];
    });
    return obj;
  },
};
export default util;
<file_sep>/src/api/a.js
const router = [
  {
    name: 'cart',
    children: [
      {
        name: 'list',
      },
      {
        name: 'a',
      },
    ],
  },
  {
    name: 'ccc',
  },
];
const auth = ['list', 'a'];

function format(router) {
  return router.filter((item) => {
    if (auth.includes(item.name)) {
      // 过滤儿子
      if (item.children) {
        item.children = format(item.children);
      }
      return true;
    }
  });
}
const res = format(router);
console.log(res);
<file_sep>/src/mock/router.js
// routerautu.js
const authlist = () => [
    // pid -1 表示一级路由 pid的父请等于上级的id auth 有权限就会有auth
        {
            pid: -1,
            name: '购物车',
            id: 1,
            auth: 'cart',
        },
        {
            pid: 1,
            name: '购物车列表',
            id: 4,
            auth: 'cart-list',
        },
        {
            pid: 4,
            name: '彩票',
            id: 5,
            auth: 'lott',
        },
        {
            pid: 4,
            name: '商品',
            id: 6,
            auth: 'product',
        },
    ];
export { authlist };
export default {};
<file_sep>/src/store.js
import Vue from 'vue';
import Vuex from 'vuex';
import { getAuth } from './api';
import { authRouer } from './router';
import user from './user';
import user1 from './user1';

Vue.use(Vuex);
function formatRoute(authlist, routers) {
  return routers.filter((router) => {
    if (authlist.includes(router.name)) {
      // 过滤出有权限的儿子
      if (router.children) {
        // eslint-disable-next-line no-param-reassign
        router.children = formatRoute(authlist, router.children);
      }
      return true;
    }
  });
}
export default new Vuex.Store({
  strict: process.env.NODE_ENV === 'development',
  // 如果不是通过mutations修改state会报警告
  state: {
    name: '李雷',
    loading: false,
    hasAuth: false, // 是否请求过路由权限
  },
  getters: {
    xname(state) {
      return `小${state.name}`;
    },
  },
  mutations: {
    hasAuth(state) {
      state.hasAuth = true;
    },
    hideloading(state) {
      state.loading = false;
    },
    showloading(state) {
      state.loading = true;
    },
  },
  actions: {
    minus() {
      console.log('rootactions');
    },
    async getRouterList({ commit }) {
      // 发送请求 得到权限列表
      let authlist = await getAuth();
      authlist = authlist.map(item => item.auth);
      console.log(authlist);
      // 用后台返回的权限对比我们自己的动态路由 确定路由列表
      const routerList = formatRoute(authlist, authRouer);
      // 修改是否获取权限的状态
      commit('hasAuth');
      return routerList;
    },
    hideloading({ commit }) {
      commit('hideloading');
    },
    showloading(store) {
      store.commit('showloading');
    },
  },
  modules: {
    user,
    user1,
  },
});
<file_sep>/src/views/vue组件传值.md
# vue 组件传值

1. props 父传子 数字、对象、数组、布尔值、对象的属性 都需要 v-bind: 缩写成 :
2. $emit 和 $on 子传父

```js
//子组件 更新m的值到200 .sync是一种语法糖
this.$emit('update:m', 200);
//父组件 money 和子组件的m值同步
<Son :m.sync="money"></Son>
```

```js
//子组件 v-model 只能针对value
 this.$emit('input', 200);
// 父组件通过@value @input的事件进行修改
 <Son @value='money'
      @input="change"></Son>
 // 事件
change(value) {
     this.money = value;
}
 //可以简写成下面
 <Son v-model='money'></Son>

```

3. eventbus 简单项目可以用 eventbus 进行传值

- 把\$bus 变成一个 vue 的实例 同一个 vue 实例

```js
Vue.prototype.$bus = new Vue();
this.$bus.$emit('toS');
this.$bus.$on('toS', () => {
  console.log(345);
});
```
<file_sep>/src/user1/acions.js
import * as Types from './mutations-types';

const acions = {
  addc({ commit }, payload) {
    commit(Types.MADD, payload);
  },
};
export default acions;
<file_sep>/src/mock/Icon.md
<Icon type="checkmark"></Icon>  
type	图标的名称	String	
size	图标的大小，单位是 px	Number | String	
color	图标的颜色	String<file_sep>/src/lib/axios.js
// lib/axios.js
import axios from 'axios';
import store from '../store';

class AjaxRequest {
  // eslint-disable-next-line no-useless-constructor
  constructor() {
    // 定义一些公共配置
    // ajax 请求的地址
    this.baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '/';
    // ajax 请求超时时间
    this.timeout = 2000;
  }

  merge(options) {
    // 合并公共参数和用户传过来的参数
    return { baseURL: this.baseURL, timeout: this.timeout, ...options };
  }

  // eslint-disable-next-line class-methods-use-this
  setInterceptors(instance) {
    // 设置响应拦截器
    instance.interceptors.response.use(
      (res) => {
        // 让loading 隐藏
        console.log('请求完成之后');
        store.dispatch('hideloading');
        return res.data;
      },
      err => Promise.reject(err),
    );
    // 设置请求拦截器
    instance.interceptors.request.use(
      (config) => {
        // 让loading显示
        store.dispatch('showloading');
        // eslint-disable-next-line no-param-reassign
        config.headers.authorization = 'token';
        return config;
      },
      err => Promise.reject(err),
    );
  }

  // eslint-disable-next-line class-methods-use-this
  request(options) {
    // 实现axios的实例调用
    const instance = axios.create();
    // setInterceptors 设置拦截器的方法
    this.setInterceptors(instance);
    // opt 就是合并后的参数
    const opt = this.merge(options);
    return instance(opt);
  }
}
export default new AjaxRequest();
// AjaxRequest.request({
//   url:'/list',
//   data:{
//     a:1
//   }
// })
<file_sep>/app.js
// eslint-disable-next-line import/no-extraneous-dependencies
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.get('/list', (req, res) => {
  res.json('hello world');
});
app.listen(3000);
<file_sep>/src/mock/index.js
// mock/index.js
import Mock from 'mockjs';
import { senList, loginApi } from './home';
import { authlist } from './routerauth';
// 配置ajax请求延时时间
Mock.setup({
  timeout: 500,
});
// 正则

Mock.mock(/\/list/, senList);
Mock.mock(/\/login/, loginApi);
Mock.mock(/\/auth/, authlist);
