前端开发笔记,持续更新中!!! 2020-11-9

# reduce 函数

// 求和
var numbers=[65,44,12,4];
function getSum(total,num){
    retuen total+num
}
function MyFun(item){
    var numrs=numbers.reduce(getSum)
}

#Object.keys()方法

//获得对象中所有的键或者其他方法使用
//PS:IE11包括IE11不支持
var person = {
    name:"张三",
    age:18,
    66:"55"
};
Object.keys(person);//["66","name","age"]
Object.values(person);//["55","张三",18];
/*
* 如果属性名的类型是Number,那么Object.keys返回值是按照key从小到大的顺序排列
* 如果属性名的类型是String,那么Object.keys返回值是按照树形被创建的时间升序排列
* 如果属性名的类型是Symbol,那么逻辑和String的逻辑相同
*/

# vue 的router.addRoutes 方法

router.addRoutes -----函数签名

router.addRoutes(routes:Array) — 动态添加路由规则,参数必须是符合routes选项要求的数组

  1. 基本使用
    //现在有一个非常普通的路由
    const routes = [
        {
            path:"/",
            name:"Home",
            component:Home
        },
        {
            path:"PageA",
            name:"PageA",
            conponent:PageA
        }
    ]
    const router = new VueRouter({
        routes
    })
    export default router;
    
    那么使用router.addRoutes改造上面的配置,实现动态添加PageA,如下
    const router = new VueRouter({
        {
        	path:"/",
        	name:"Home",
        	component:Home
    	}
    });
    let route = [
        {
            path:"PageA",
            name:"PageA",
            component:PageA
        }
    ];
    router.addRoutes(route);
    export default router;
    // 把原来的routes配置照搬到一个新的数组中,就可以作为addRoutes的参数使用,经验证实,通过addRoutes动态配置和普通配置一样
    
    2.路由的权限验证

    如果网页有[管理员,普通用户,其他管理]等多种角色,不同的角色看见的页面页是不同的,比如普通用户不应该看见管理员的控制台,那么这个时候,动态路由就非常有用了,

let PageA,pageB,pageC;
let route = [
    {
        path:"/PageA",
        name:"PageA",
        component:PageA
    },
    {
        path:"/PageB",
        name:"PageB",
        component:PageB
    },
    {
        path:"/PageC",
        name:"PageC",
        component:PageC
    }
];
let commOnUser=["PageA","PageB"];
let commInUserRoute=route.filter(function(page){
    return commOnUser.includes(page.name)
})
console.log(commInUserRoute);
router.addRoues(commInUserRoutes);
//结果
/*
*	(2) [{...},{...}]
*	0:{path:"/PageA",name:"PageA",component:PageA},
*	1:{path:"/PageB",name:"PageB",component:PageB},
*	length:2
*/
//这样就可以完成权限验证,当前如果权限在前台验证会有一定的安全隐患,最好的还是在后台来进行限制访问

PS:router.addRoutes 之后的next() 可能会失效,因为可能next() 的时候路由并没有完全add完成

解决办法:**通过next(to)**解决,这行代码重新进入router.beforeEach这个钩子,这是后再通过 **next()**来释放钩子,这能确保所有的路由都已经挂载完成了

#封装请求和vuex && 路由守卫

1.封装axios请求,设置请求拦截器和响应拦截器

import axios from "axios";
//引入页面上方的进度条
import Nprogress from "nprogress";//需要自己去npm安装环境
import "nprogress/nprogress.css";
const Axios=axios.create({
 baseUrl:"";
 withCredentials:fasle,//请求头需要携带cookies的话,需要设置为true、否则false或者不写
 timeout:5000//请求超时的时间(毫秒),服务器在该时间没有响应会直接超时,执行自己设置超时的事件
})
//请求拦截器,所有的请求都会触发该事件
Axios.interceptors.request.use(
	config => {
     Nprogress.start();
     if (sessionStorage.getItem("token")) {
        	config.headers.token=sessionStorage.getItem("token")
        }
     retuen config
 },
 error => {
     Promise.reject(error)
 }
);
//响应拦截器,所有请求回来的数据都会触发该事件
Axios.interceptors.response.use(
	res => {
     Nprogress.done();
     return res
 },
 error => {
     Nprogress.done();
     //如果请求超时了,对请求超时的处理
     if (error.message,includes("timeout")) {
				console.error("请求超时")
         }
 }
)
export default Axios;//将自己定义的Axios抛出去,方便其他事件引用

2.开始封装xaios请求

在src下创建一个api的文件夹,在里面创建对应的事件

import axios from "@/neteork/axiosUtil";//自己封装axios请求的位置
//登录的封装请求
export function login (data) {
 return axios({
     url:"/api/login",//自己需要请求的地址
     method:"post",//请求的方式,
     data//原来是data:data,可以简写为data
 })
}
//登出的请求
export function loginOut () {
 return axios({
     url:"/api/loginOut",
     method:"post"
 })
}
//获取侧拉菜单
export function getmenu (userCode) {
 return axios({
     url:"/api/menu",
     method:"get",
     params:{userCode}
 })
}

3.登录和登出基本是靠着token去控制的,所以用到好多,封装下token

//我将所有封装事件在src下创建了一个util文件夹,将所有的封装事件封装在里面
//token.js
const tokenKey=sessionStorage.getItem("token") || undefined;
export function setToken (token) {
 return sessionStorage.setItem("token",token);
}
export function getToken () {
 return sessionStorage.getItem("token")
}
export function removeToken () {
 return sessionStorage.removeItem("token")
}

4.在vuex中封装请求

/*
*	在store的文件下创建一个modules文件夹
*	创建user.js文件
*/
//引入封装好的请求
import {login, loginOut, getmenu} from "@/api/user";
//引入关于token的封装
import {getToken, setToken, removeToken} from "@/utils/auth";
const state = {
 token: getToken,//登录够需要存储的token
 name: "",//登录人的用户名
 menus: [],
 userCode: "",//当前登录人的标识码(登录的账号)
 loginF: false,//双层控制是否登录,true代表登录了,false代表没登录
};
const mutations = {
 setToken (state,token) {
     state.token = token;
 },
 setName (state,name) {
     state.name = name;
 },
 setMenu (state,menu) {
     state.menus = menu;
 },
 setUserCode (state,userCode) {
     state.userCode = userCode;
 },
 setLoginF (state,loginF) {
     state.loginF = loginF;
 }
};
const actions = {//所有的ajax请求或者异步事件都在这里面处理
 //用户登录触发的事件
	login ({commit}, menu) {//userInfo事件传递过来的参数
     const { userCode, password} = userInfo;
     return new Promise ((resolve,reject) => {
         login({userCode,password})
         .then(res => {
             const {data} = res;
             commit("setToken",data.token);
             commit("setLoginF",true);
             setToken(data.token);
             resolve();
         })
         .catch(err => {
             reject(err)
         })
     })
 },
 //登录成功后请求侧拉菜单的事件
 getmenu ({commit}, state) {
     retuen new Promise((resolve,reject) => {
         getmenu(state.userCode)
         .then(res => {
             const {data} =res;
             commit("setMenu", data);
             resolve();
         })
         .catch(err => {
             rejece(err)
         })
     })
 },
 //登出的事件
	loginOut ({commit,state}) {
     return new Promise((resolve,reject) => {
         loginOut(state.userCode)
         .then(res => {
            	commit("setToken","");
             commit("setMenu",[]);
             commit("setLoginF",false);
             removeToken();
             resolve();
         })
         .catch(err => {
             reject(err)
         })
     })
 },
 //移除token和菜单和session
 resetToken ({commit}) {
     return new Promise(resolve => {
         commit("setToken","");
         commit("setMenu",[]);
         commit("setLoginF",fasle);
         removeToken();
         resolve();
     })
 }
};
export default {
 namespaced: true,
 state,
 mutataions,
 actions
};

5.在store根文件下创建一个getters.js

//该文件主要让用户直接获取数据用
const getters = {
 token: (state) => {
     state.user.token
 },
 name: (state) => {
     state.user.name
 },
 menus: (state) => {
     state.user.menus
 },
 userCode: (state) => {
     state.user.userCode
 },
 loginF: (state) => {
     state.user.loginF
 }
};
export default getters;

6.在主文件store.js里面处理事件

import Vue from "vue";
import Vuex from "vuex";
import getters frm "./getters";//引入刚才创建的getters文件

Vue.use(Vuex);

const modulesFiles = require.context("./modules", true, /\.js$/);
//不需要将mudules里面封装的vuex在本文件挨着import进来了
//通过本事件处理
const modules = modulesFiles.keys().reduce((modules,modulePath) => {
 //设置将 xx.js 文件设置为 "xx“
 const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/,"$1");
 const value = modulesFiles(modulePath);
 modules[moduleName] = value.default;
 return modules
},{});
const store = new Vuex.Store({
 modules,
 getters
})
export default store;
//----------------------------------------------------------------
//另外一种写法,挨着把文件引入
import Vue from "vue";
import Vuex from "vuex";
import getters frm "./getters";//引入刚才创建的getters文件
import user from "./modules/user";
Vue.use(Vuex);
export default new Vuex.Store({
 modules:{
     user,
     getters
 }
})

7.随后配置路由守卫

//在src文件夹下创建perminnion.js文件
import router from "./router";//router是router配置的文件夹
import store from "./store";
import {Message} from "element-ui";//引入elemen-ui的message组件,可以进行相应的提示
import Npogress from "nprogress";
import "nprogress/nprogress.css";
import {getToken} from "@/utils/auth";
Npogress.configure({showSpinner: false});
router.beforeEach(async (to,from,next) => {
 Npogress.start();
 document.title=to.meta.title;//每个路由都配置的title
 //如果跳转的页面没有,就跳转404页面
 if(to.matched.length === 0) {
   	next("/404");//自己定义的404页面 	
   }
 //校验是否登录,防止部登录,直接进入其他页面
 const HasToken = getToken();//获取token
 const HasLoginF = store.getters.loginF;//获取是否登录
	if(HasToken && HasLoginF){
    	if (to.path === "/login") {
         	next({path:"/home"});//项目设置的首页
         	Npogress.done();
         }else if (to.meta.requireAuth) {
                   	next()
                   }else{
                       next({
                           path: "/login",
                           query: {
                               redirect: to.fullPath
                           }
                       })
                   }
    }else {
        next({
            path:"/login",
            query: {
                redirect: to.fullPath
            }
        })
    }
})

8.登录触发的事件

//因为登录页面可能是从别的页面打回来的,所以在登录页面加一个watch监视路由
watch: {
$route: {
   handler: function (route) {
       const query=route.query;
       if (query) {
        	 this.redirect = query.redirect  	 	
        }
   },
   immediate: true
}  
};
//PS:记得在data中定义redirect:undefinde
//在登录界面点击登录的时候触发封装的事件
this.$store.dispatch("user/login",this.regForm)
.then(res => {
 this.$store.dispatch("user/getmenu",this.regForm.userCode)
 .then(res => {
     this.$router.push({
         path:this.redirect || "/home"
     })
 })
 .catch(err => {
     Promise.reject(err);
 })
})

9.注销触发的事件

logOut () {
 this.$store.dispatch("user/logOut")
 this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}

# watch 初始化不会触发事件问题

//可以添加immediate 属性,这样初始化的时候也同样会触发
watch: {
    searchText: {
        handler: function (newValue, oldValue) {
            //newValue是新数据,oldValue是老数据
            console.log(newValue,oldValue);
        },
        immediate: true//第一次刷新页面的时候就会执行
    }
}

# vue报错Do not use built-in or reserved HTML elements as component id

 // 一版情况是因为组件命名和引入的不知道导致的
export default {
    name:"header"
}
//在组件引入上方组件的时候,将引入的名字需要和name名称保持一致
 import header from "./header" //名称保持一致
/*
* 还有一种情况是名称哟两个大写的英文单词书写造成的
import TestPage from "./TestPage"
*/
//使用的时候
//		<div>
//			<testpage></textpage>
//		</div>
//改使用标签即可
//		<div>
//			<test-page></text-page>
//		</div>

# 在vue中用v-for给src动态绑定图片不显示问题

//用v-for给图片的img动态绑定本地的src属性
						<div
							class="img_carousel"
							v-for="(items, indexs) in item.carousel"
							:key="indexs"
						>
							<img :src="imgSrc(items)" alt="加载失败" />
						</div>
//设置imgSrc事件,将当前本地的图片名字传入,通过事件将图片路径完整的返回
//然后在methods中设置imgSrc事件
methods:{
    imgSrc(item){
        //拼接路径,将路径return出去
        return require(`../assets/carousel/${item}`)
    }
}

# el的日期组件

//element ui 的日期组件,选择区间日期和具体时间
					<el-date-picker
						v-model="userDeatil.Time"
						type="datetimerange"
						start-placeholder="开始时间"
						end-placeholder="结束时间"
						:default-time="['12:00:00', '08:00:00']"
						size="small"
						value-format="yyyy-MM-dd HH:mm:ss"
					></el-date-picker>
// 注意,把选择的去掉一次后,绑定的参数值直接为null

# webpack打包vue项目后,head里面有特别多的link(ref=prefetch和ref=preload)

这种加载影响了加载速度,加载了太多不需要的东西,所以需要优化

// 通过配置wenpack去掉这一种行为
module.export = {
    chainWebpack(config) {
		config.plugins.delete("prefetch");
		config.plugins.delete("preload");
	}
}

然后再次运行项目,就会发现,多余的都去掉了

# vue使用字节流转换为文件(word)

// 首先发送请求,拿到字节流数据
export function ticketDownload(data){
	return new Promise((resolve,reject)=>{
			axios({
				url:"/api/dangan_war/message/exportToWorld",
				method : "post",
				data:{
					userCode:data
				},
				headers: {
          "content-type":
            "application/json;application/octet-stream;charset=utf-8"
        },
        responseType: "blob"//重要!!!!
			})
		.then(res=>{
			resolve(res)
		})
		.catch(err=>{
			reject(err)
		})
	})
}
//然后在页面引用
import { ticketDownload } from "@/api/request";
//执行事件
ticketDownload(row.userCode).then(res => {
        let content = res.data;
        const blob = new Blob([content], { type: "application/msword" });//重要
        let fileName = res.headers["content-disposition"].split("=")[1]; //后端传递的文件名称
        if ("download" in this.elink) {
          this.elink.download = fileName;
          this.elink.style.display = "none";
          this.elink.href = URL.createObjectURL(blob);
            //refs 后面的是页面上设置的ref的值,需要自己去页面设置
          this.$refs.LDQZOne.appendChild(this.elink);
          this.elink.click();
          URL.revokeObjectURL(this.elink.href);
          this.$refs.LDQZOne.removeChild(this.elink);
        } else {
          navigator.msSaveBlob(blob, fileName);
        }
      });

# 在VUE中使用高德地图定位,获取位置的详细信息

//1. 首先在index.html中引入两个文件
	<script type="text/javascript" src="https://webapi.amap.com/maps?v?v=1.4.14&key=efeb01501fd4bc21946fba7439cc1b45&plugin=AMap.Geocoder"></script>
		<script language="javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=efeb01501fd4bc21946fba7439cc1b45&plugin=AMap.ControlBar"></script>
//2. 安装vue-amap插件  npm i vue-amap -s
//3. 在main.js中
import VueAMap from 'vue-amap'   // 这个引入的最好放在引入的vue上面
Vue.use(VueAMap)
VueAMap.initAMapApiLoader({
  key: 'efeb01501fd4bc21946fba7439cc1b45',  // 你的key
  plugin: ['AMap.Autocomplete', 'AMap.PlaceSearch','AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.Geolocation','AMap.Geocoder', 'AMap.AMapManager', 'AMap.Marker'],//应用功能项
  v: '1.4.4', //版本
  uiVersion: '1.0' //ui版本
})
//4.创建vue文件
<template>
  <div id="wrap">
    <div id="searchWrap">
      <div class="searchWrap">
        <el-input style="width:143px" v-model="address" @input="search" size="mini" placeholder="请输入地址" ></el-input>
        <el-button type="primary" @click="search" size="mini">搜索</el-button>
      </div>
      <div id="result" class="amap_lib_placeSearch" v-show="hide">
        <div class="amap_lib_placeSearch_list amap-pl-pc" v-for="(item,index) in poiArr"
           @click="openMarkerTipById(index,$event)"
           @mouseout="onmouseout_MarkerStyle(index+1,$event)"
           :key="index">
          <div class="poibox" style="border-bottom: 1px solid #eaeaea">
            <div class="amap_lib_placeSearch_poi poibox-icon" :class="index==selectedIndex?'selected':''">{{index+1}}</div>
            <div class="poi-img" v-if="item.url" :style="'background-image:url('+item.url+'?operate=merge&amp;w=90&amp;h=56&amp;position=5)'"
            ></div>
            <h3 class="poi-title" >
              <span class="poi-name">{{item.name}}</span>
            </h3>
            <div class="poi-info">
              <p class="poi-addr">地址:{{item.address}}</p>
              <p class="poi-tel">电话:{{item.tel}}</p>
            </div>
            <div class="clear"></div>
          </div>
        </div>
      </div>
    </div>
    <div id="iCenter"></div>
  </div>
</template>
<script>
 export default {
  data() {
   return {
    address:'',//保存地址的汉字名字
    location:{
     P:121.59996,
     Q:31.197646,
    },
    map1: '',
    map:'',//保存地址的经纬度
    poiArr: [],//左边搜索出来的数组
    windowsArr: [],//信息窗口的数组
    marker: [],
    mapObj: "",//地图对象
    selectedIndex: -1,
    hide: false,
    clickType: 1,
   };
  },
  mounted() {
   this.mapInit()
   this.placeSearch(this.address);
  },
  methods: {
    callMethod(){
      this.$emit("mapAdress",this.address)
    },
   showToast(address){
    this.placeSearch(address.address)
    this.location.P =address.lat
    this.location.Q =address.lng
    this.address = address.address
    let that = this;
    new AMap.InfoWindow({
     content:"<h3>" + '当前选中地址' + "</h3>" + that.address,
     size: new AMap.Size(300, 0),
     autoMove: true,
     offset: new AMap.Pixel(-4, -10)
    }).open(that.mapObj,that.location)
   },
   cancelSave(){
    eventBus.$emit('cancelSave')
   },
   saveAddress(){
    let addressName,location;
    if(this.clickType==1){
     let address = this.poiArr[this.selectedIndex]
     addressName = address.name+address.address;
     location = address.location
    }else if(this.clickType==2){
     addressName = this.address;
     location = this.map;
    }else if(this.clickType==3){
     addressName = this.address;
     location = this.map1;
    }
    eventBus.$emit('saveAddress',[addressName,location])
   },
   // 经纬度转化为详细地址
   getAddress(){
    let that = this;
    AMap.plugin('AMap.Geocoder',function(){
     let geocoder = new AMap.Geocoder({
      radius: 100,
      extensions: "all"
     });
     geocoder.getAddress([that.map1.lng,that.map1.lat], function(status, result) {
      if (status === 'complete' && result.info === 'OK') {
       let address = result.regeocode.formattedAddress;
       that.address = result.regeocode.formattedAddress;
       // that.placeSearch(that.address)
      }
     });
    })
   },
   // 地图点击事件
   testevent(){
    let that = this;
    this.clickType = 3
    // var map=new AMap.Map('iCenter');//重新new出一个对象,传入参数是div的id
    AMap.event.addListener(this.mapObj,'click',function (e) { //添加点击事件,传入对象名,事件名,回调函数
     that.map1 = e.lnglat;
     that.getAddress();
     setTimeout(()=>{
      new AMap.InfoWindow({
       content:"<h3>" + '当前选中地址' + "</h3>" + that.address,
       size: new AMap.Size(300, 0),
       autoMove: true,
       offset: new AMap.Pixel(-4, -10)
      }).open(that.mapObj,e.lnglat)
     },100)
    })
   },
   //创建一个map
   mapInit() {
    this.mapObj = new AMap.Map("iCenter", {
     resizeEnable: true,
     zoom: 10,
    })
    this.testevent();
   },
   //根据名字地址去搜索结果
   placeSearch(name) {
    let that = this;
    this.hide = true
    var MSearch;
    this.mapObj.plugin(
     ["AMap.PlaceSearch", "AMap.ToolBar", "AMap.Scale"],
     () => {
      this.mapObj.addControl(new AMap.ToolBar())
      this.mapObj.addControl(new AMap.Scale())
      MSearch = new AMap.PlaceSearch({
       //构造地点查询类
       city: that.address //城市
      });
      AMap.event.addListener(MSearch,"complete",this.keywordSearch_CallBack) //返回地点查询结果
      MSearch.search(name); //关键字查询
     }
    );
   },
   //结果的回调
   keywordSearch_CallBack(data) {
    var poiArr = data.poiList.pois
    var resultCount = poiArr.length
    this.poiArr = poiArr; //左边要渲染的数据
    for (var i = 0; i < resultCount; i++) {
     this.addmarker(i, poiArr[i])
     this.poiArr[i].url = this.poiArr[i].photos? this.poiArr[i].photos[0]? this.poiArr[i].photos[0].url: "": ""
    }
    this.mapObj.setFitView()
   },
   //添加marker&infowindow
   addmarker(i, d) {
    var lngX = d.location.getLng();
    var latY = d.location.getLat();
    var markerOption = {
     map: this.mapObj,
     position: new AMap.LngLat(lngX, latY)
    };
    var mar = new AMap.Marker(markerOption);
    this.marker.push(new AMap.LngLat(lngX, latY));
    var infoWindow = new AMap.InfoWindow({
     content: "<h3>" +'当前选中位置:'+ d.name + "</h3>" + this.TipContents(d.name, d.address),
     size: new AMap.Size(300, 0),
     autoMove: true,
     offset: new AMap.Pixel(0, -30)
    });
    this.windowsArr.push(infoWindow);
    var _this = this;
    var aa = (e) => {
     this.clickType = 2
     var obj = mar.getPosition();
     this.map = obj //这里保存的地址经纬度
     this.address = d.name + d.address //这里保存的是地址名字
     infoWindow.open(_this.mapObj, obj);
    }
    AMap.event.addListener(mar, "click", aa)
   },
   TipContents(name, address) {
    //窗体内容
    if (
     name == "" ||
     name == "undefined" ||
     name == null ||
     name == " undefined" ||
     typeof name == "undefined"
    ) {
     type = "暂无";
    }
    if (
     address == "" ||
     address == "undefined" ||
     address == null ||
     address == " undefined" ||
     typeof address == "undefined"
    ) {
     address = "暂无";
    }
    var str = `地址:${address}`
    return str
   },
   openMarkerTipById(pointid, event) {
    //根据id 打开搜索结果点tip
    this.clickType = 1
    event.currentTarget.style.background = "#CAE1FF";
    this.selectedIndex = pointid
    // this.map = this.marker[pointid]
    this.map1 = this.poiArr[pointid].location
    this.address = this.poiArr[pointid].address + this.poiArr[pointid].name
    this.windowsArr[pointid].open(this.mapObj, this.marker[pointid])
 
 
   },
   onmouseout_MarkerStyle(pointid, event) {
    //鼠标移开后点样式恢复
    event.currentTarget.style.background = ""
   },
   search() {
    this.windowsArr = []
    this.marker = []
    this.mapObj=''
    this.mapInit()
    this.placeSearch(this.address)
   }
  },
 };
</script>
<style lang="scss" scoped>
  #wrap{
    width:100%;
    display: flex;
    #iCenter {
      height: 600px;
      position: relative;
      display: flex;
      flex: 1;
    }
    #searchWrap{
      width:200px;
      position: relative;
      height:600px;
      .searchWrap{
        position: absolute;
        width:300px;
        z-index: 9;
        display: flex;
        align-items: center;
      }
      #result {
        width: 200px;
        position: absolute;
        top:30px;
        height: 570px;
        z-index: 8;
        overflow-y: auto;
        border-right: 1px solid #ccc;
      }
    }
    .amap_lib_placeSearch {
      height: 100%;
      overflow-y: scroll;
      .poibox {
        border-bottom: 1px solid #eaeaea;
        cursor: pointer;
        padding: 5px 1px 5px 10px;
        position: relative;
        min-height: 35px;
        .selected {
          background-image: url(https://webapi.amap.com/theme/v1.3/markers/n/mark_r.png) !important;
        }
        &:hover {
          background: #f6f6f6;
        }
        .poi-img {
          float: right;
          margin: 3px 8px 0;
          width: 90px;
          height: 56px;
          overflow: hidden;
        }
        .poi-title {
          margin-left: 25px;
          font-size: 13px;
          overflow: hidden;
        }
        .poi-info {
          word-break: break-all;
          margin: 0 0 0 25px;
          overflow: hidden;
          p {
            color: #999;
            font-family: Tahoma;
            line-height: 20px;
            font-size: 12px;
          }
        }
        .poibox-icon {
          margin-left: 7px;
          margin-top: 4px;
        }
        .amap_lib_placeSearch_poi {
          background: url(https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png)
          no-repeat;
          height: 31px;
          width: 19px;
          cursor: pointer;
          left: -1px;
          text-align: center;
          color: #fff;
          font: 12px arial, simsun, sans-serif;
          padding-top: 3px;
          position: absolute;
        }
      }
    }
    .btn{
      position: fixed;
      bottom:20px;
      left:50%;
      padding:10px;
    }
  }
</style>

/**
* 该页面可单独使用,可引入弹框使用
*/

# el的table组件全选和单选并且在底下显示的问题

table
/**
*      功能是论需求而定的,没有做封装,见谅
*/
/**1.进入列表,点击全选,将全选的数据显示到下方已选择的里面
*  首先分析,用户有几种选择的情况
*      1.1 全选和全部取消选择
*      1.2 单选单独取消
*/
/**
*		所以,我们拟定了两个事件,一个是点击全选和全不选的
*       一个是单独点击选择和单独取消的事件
*/
<el-table
          :data="tableDataTHREE"
          style="width:100%"
          ref="multipleTableTHREE"
          tooltip-effect="dark"
          border
          @select-all="selectAllThree"  //这是监听全选和全不选的事件
          @select="selectThree"         // 这是监听单独选择和单独取消的事件
        >
</el-table>
//  注!!!  所有的对比都是name关键字
/**
*   首先是全选的事件
*	qianDaoListThree(Array):是下方显示已选择的绑定值,所有数据均在此出现
*/
selectAllThree(val) {
      // 第三步的列表点击全选的事件
      // 如果当前选中的没有数据,就将选中的数据全部放进去
      if (this.qianDaoListThree.length == 0) {
          //注意不能直接赋值,不然两个数据绑定会出现bug
        val.forEach(item => {
          this.qianDaoListThree.push(item);
        });
      } else {
        // 这个是全选后点击了取消全选的事件
        if (val.length == 0) {
          for (let i = 0; i < this.qianDaoListThree.length; i++) {
            for (let j = 0; j < this.tableDataTHREE.length; j++) {
              if (
                this.qianDaoListThree[i].name == this.tableDataTHREE[j].name
              ) {
                this.qianDaoListThree.splice(i, 1);
              }
            }
          }
        }
        //为了保证数据的准确性,有两种情况,因为下方显示的数据还有后台返回,所以有可能比列表多,
        // 这样的话,保证循环的准确性,分为两种情况
        // 1.选择的长度大于有的数据的长度(小于0)
        if (this.qianDaoListThree.length - val.length < 0) {
          val.forEach(item => {
            for (let i = 0; i < this.qianDaoListThree.length; i++) {
              if (this.qianDaoListThree[i].name !== item.name) {
                this.qianDaoListThree.push(item);
              }
            }
          });
        } else {
          // 2.有的数据的长度小于选择的长度(大于等于0)
          this.qianDaoListThree.forEach(item => {
            for (let j = 0; j < val.length; j++) {
              if (val[j].name !== item.name) {
                if (j == val.length) {
                  this.qianDaoListThree.push(item);
                }
              }
            }
          });
        }
      }
    },
// 其次是单选的事件
 selectThree(rows, row) {
      // 第三步单选
     // 这是判断是点击点选是取消还是添加的,为false或者0都是取消
      let selected = rows.length && rows.indexOf(row) !== -1;
      if (selected == false || selected == 0) {
        for (let i = 0; i < this.qianDaoListThree.length; i++) {
          if (this.qianDaoListThree[i].name == row.name) {
            this.qianDaoListThree.splice(i, 1);
            return;
          }
        }
      } else {
        // 代表是添加的
        if (this.qianDaoListThree.length == 0) {
          this.qianDaoListThree.push(row);
        } else {
          for (let i = 0; i < this.qianDaoListThree.length; i++) {
            if (row.name !== this.qianDaoListThree[i].name) {
              this.qianDaoListThree.push(row);
            }
          }
        }
      }
    },

# 如何让el-table保持持久化的勾选(复选框)

 <el-table
          :data="tableDataTHREE"
          style="width:100%"
          ref="multipleTableTHREE"
          tooltip-effect="dark"
          border
          :row-key="(row)=>{return row.name}" //name是你数据绑定的关键词
          @selection-change="handleSelectThree"
        >
     <!-- 复选框 -->
     <!-- reserve-selection为true -->
      <el-table-column type="selection" :reserve-selection="true"></el-table-column>
</el-table>
/*
*  这样换页的时候也会保持勾选状态
*/

# vue全局过滤器的使用(时间戳转yyyy-MM-dd HH:mm:ss)

// 首先在src下创建一个plugins文件夹,创建一个filter.js文件(过滤器)
// 在里面定义自己需要的过滤器
/*
 *  PS:本文件时全局过滤器
 *  需要定义直接let就可以,然后export把过滤器方法出去
 *   引入在main.js里面引入,再导入全局
 */

// 根据后台返回的数据进行过滤,把毫秒转换为2020-5-13的格式
let formDate = value => {
  value = Number(value);
  let date = new Date(value);
  let year = date.getFullYear();
  let Month = date.getMonth() + 1;
  Month = Month < 10 ? "0" + Month : Month;
  let Hours=date.getHours().toString().padStart(2,0);
  let minutes=date.getMinutes().toString().padStart(2,0);
  let seconds=date.getSeconds().toString().padStart(2,0);
  let day = date.getDay();
  day = day < 10 ? "0" + day : day;
  return year + "-" + Month + "-" + day +" "+Hours+":"+minutes+":"+seconds;
};
// 根据返回的性别值判断男女

export default {
  formDate
};
/**
*   然后在main.js里面引入
*/
import filter from "./plugins/filter"
Object.keys(filter).forEach(key => {
  Vue.filter(key, filter[key]);
});
/**
*  在页面使用
*/
 <span>{{scope.row.registerEndTime | formDate(scope.row.registerEndTime)}}</span>
//  scope.row.registerEndTime是我的时间戳,这样的话就直接经过转换就ok

# vue+elementui动态生成侧拉导航栏

一共两种办法,根据情况而定,首先第一种

首先,拿到菜单树结构数据

 nav: [
        {
          icon: "el-icon-s-platform",
          title: "首页",
          path: "/welcome"
        },
        {
          icon: "el-icon-s-operation",
          title: "test",
          path: "/examAdmin"
        },
        {
          icon: "el-icon-setting",
          title: "admin",
          child: [
            {
              title: "testadmin",
              child: [
                {
                  title: "seeexam",
                  path: "/SeeExam"
                },
                {
                  title: "locat",
                  path: "/ExamAllocat"
                },
                {
                  title: "xxc",
                  child: [
                    {
                      title: "aab",
                      path: "/testPaperList"
                    },
                    {
                      title: "bbc",
                      path: "/ExamAllTestPaper"
                    }
                  ]
                },
                {
                  title: "数据包管理",
                  child: [
                    {
                      title: "数据包导出",
                      path: "/DataPacket"
                    }
                  ]
                }
              ]
            },
            {
              title: "rfhm",
              child: [
                {
                  title: "sdfbvn",
                  path: "/SeepracticalExam"
                },
                {
                  title: "rfbn",
                  path: "/TestPract"
                },
                {
                  title: "ola",
                  child: [
                    {
                      title: "dfjk",
                      path: "/SeeTestAsk"
                    }
                  ]
                }
              ]
            },
            {
              title: "qaxcd",
              child: [
                {
                  title: "fsdgfd",
                  path: "/SeeSynthesizeExam"
                },
                {
                  title: "baax",
                  path: "/TestExamPract"
                },
                {
                  title: "sad",
                  child: [
                    {
                      title: "t",
                      path: "/TestExamDataPacket"
                    }
                  ]
                }
              ]
            },
            {
              title: "s",
              child: [
                {
                  title: "r证",
                  path: "/admissionTicket"
                },
                {
                  title: "u",
                  path: "/ExamCollect"
                }
              ]
            },
            {
              title: "q",
              child: [
                {
                  title: "p",
                  child: [
                    {
                      title: "o",
                      path: "/gradeAccept"
                    }
                  ]
                },
                {
                  title: "n",
                  child: [
                    {
                      title: "m",
                      path: "/practicalGrade"
                    }
                  ]
                },
                {
                  title: "l",
                  child: [
                    {
                      title: "k",
                      path: "/reviewGrade"
                    }
                  ]
                }
              ]
            },
            {
              title: "j",
              child: [
                {
                  title: "i",
                  child: [
                    {
                      title: "h",
                      path: "/SeeTestPlan"
                    },
                    {
                      title: "g",
                      path: "/SetTestSign"
                    }
                  ]
                },
                {
                  title: "f",
                  child: [
                    {
                      title: "e",
                      path: "/SeeTestPlanKP"
                    },
                    {
                      title: "d",
                      path: "/SetTestPlanKP"
                    }
                  ]
                },
                {
                  title: "c",
                  child: [
                    {
                      title: "b",
                      path: "/SeeTestPlanDD"
                    },
                    {
                      title: "a",
                      path: "/SetTestPlanDD"
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]

拿到树形结构后进行挂载解析操作

 <el-menu
      :default-active="$route.path"
      class="el-menu-vertical"
      ref="menu"
      router
    >
      <!--动态生成sideItem-->
      <template v-for="(item, parentIndex) in nav">
        <SideNav :item="item" :index="parentIndex"  :key="parentIndex"></SideNav>
      </template>
    </el-menu>

// SideNav是子组件,将数据循环并且传递到子组件中
import SideNav from "./SideNav";
  components: {
    SideNav
  }
# sideNav子组件结构(其中,fragment标签是vue编码但是不渲染,不用div避免导航渲染问题)

fragment 是插件 vue-fragment

npm i -s vue-fragment

然后在main.js里面引入即可

import Fragment from “vue-fragment”;

Vue.use(Fragment.Plugin);

具体使用方法可以搜索该标签用法

<template>
  <fragment>
    <!--没有子导航-->
    <el-menu-item v-if="!item.child" :index="item.path">
      <i v-if="item.icon" :class="item.icon"></i>
      <span slot="title">{{ item.title }}</span>
    </el-menu-item>
    <!--有子导航-->
    <el-submenu v-else :index="String(index + 1)+item.title">
      <template slot="title">
        <!--如果item有icon才添加icon图标-->
        <i v-if="item.icon" :class="item.icon"></i>
        <span slot="title">{{ item.title }}</span>
      </template>
      <!--判断子导航是否存在下一级的子导航,如果没有则渲染自己,如果有则递归组件-->
      <!--如果子导航中没有存在下一级的子导航 则是<el-menu-item> 否则为<el-submenu>-->
      <template v-for="(cItem, cIndex) in item.child" :index="String(index + 1 + '-' + cIndex + 1)">
        <el-menu-item v-if="!cItem.child" :index="cItem.path" :key="cItem.path">{{ cItem.title }}</el-menu-item>
        <el-submenu
          v-else
          :index="String(index + 1 + '-' + cIndex + 1)"
          :key="String(index + 1 + '-' + cIndex + 1)"
        >
          <i v-if="item.icon" :class="cItem.icon"></i>
          <span slot="title">{{ cItem.title }}</span>
          <!--递归自己 遍历子..导航-->
          <template v-for="(item, parentIndex) in cItem.child">
            <SideNav :item="item" :index="parentIndex+1" :key="parentIndex"></SideNav>
          </template>
        </el-submenu>
      </template>
    </el-submenu>
  </fragment>
</template>

<script>
export default {
  name: "SideNav",
  props: {
    item: {
      type: Object,
      required: true
    },
    index: {
      type: Number,
      required: true
    }
  }
};
</script>
<style scoped>
.el-submenu [class^="fa"] {
  vertical-align: middle;
  margin-right: 5px;
  width: 24px;
  text-align: center;
  font-size: 16px;
}
</style>

# 第二种动态生成

首先创建父组件去渲染数据

<template>
  <div>
    <el-menu>
      <navigationitem v-for="(menu,i) in adminMenus" :key="i" :item="menu" />
    </el-menu>
  </div>
</template>
<script>
export default {
  data() {
    return {
      adminMenus: [
        {
          id: 1,
          path: "/admin",
          nameZh: "首页",
          parentId: 0,
          iconCls: null,
          children: null
        },
        {
          id: 2,
          path: "/adminbbb",
          nameZh: "用户管理",
          iconCls: null,
          parentId: 0,
          children: [
            {
              id: 6,
              path: "/bgfbfbgf",
              iconCls: null,
              nameZh: "用户信息",
              parentId: 2,
              children: null
            },
            {
              id: 7,
              path: "/gbfbdb",
              iconCls: null,
              nameZh: "角色配置",
              parentId: 2,
              children: null
            }
          ]
        },
        {
          id: 3,
          path: "/dgbdbdb",
          nameZh: "内容管理",
          iconCls: null,
          parentId: 0,
          children: [
            {
              id: 9,
              path: "/dbfdbdbf",
              nameZh: "就业部类",
              iconCls: null,
              parentId: 3,
              children: null
            },
            {
              id: 10,
              path: "/dfbdbdfbdfb",
              nameZh: "学生类",
              iconCls: null,
              parentId: 3,
              children: [
                {
                  id: 20,
                  path: "/dfbdbdfbd",
                  nameZh: "毕业生信息",
                  iconCls: null,
                  parentId: 10,
                  children: null
                }
              ]
            },
            {
              id: 11,
              path: "/dbfdbszbfdz",
              name: "Enterprise",
              nameZh: "企业类",
              iconCls: null,
              component: "content/enterprise",
              parentId: 3,
              children: null
            }
          ]
        },
        {
          id: 4,
          path: "/dfbhdhdfh",
          name: "System",
          nameZh: "系统管理",
          iconCls: "el-icon-s-tools",
          component: "AdminIndex",
          parentId: 0,
          children: [
            {
              id: 12,
              path: "/dfgdhbdfb",
              name: "Run",
              nameZh: "运行情况",
              iconCls: null,
              component: "system/run",
              parentId: 4,
              children: null
            },
            {
              id: 13,
              path: "/dfgbdfhbdfbd",
              name: "Data",
              nameZh: "备份恢复数据库",
              iconCls: null,
              component: "system/data",
              parentId: 4,
              children: null
            },
            {
              id: 14,
              path: "/safgbgfcbcxvbd",
              name: "Log",
              nameZh: "操作日志",
              iconCls: null,
              component: "system/log",
              parentId: 4,
              children: null
            }
          ]
        },
        {
          id: 5,
          path: "/dsgdfhgfdhb",
          name: "Link",
          nameZh: "链接",
          iconCls: null,
          component: "AdminIndex",
          parentId: 0,
          children: null
        }
      ]
    };
  }
};
</script>

组件创建完毕后,在main.js里面将组建注册为全局组件

/**
* 	main.js
*/

import navigationitem from "./components/submen.vue";
Vue.component("navigationitem", navigationitem);

# 创建子组件,去循环父组件传递的数据

<template>
  <div>
    <!--叶子级菜单-->
    <template v-if="item.children==null">
      <el-menu-item :key="item.id" :index="item.path">{{item.nameZh}}</el-menu-item>
    </template>
    <!--父级菜单-->
    <el-submenu v-else :index="item.path" style="text-align: left">
      <span slot="title" style="font-size: 17px;">
        <i :class="item.iconCls"></i>
        {{item.nameZh}}
      </span>
      <template v-for="child in item.children">
        <navigationitem
          v-if="child.children && child.children.length>0"
          :key="child.id"
          :item="child"
        />
        <el-menu-item v-else :key="child.id" :index="child.path">
          <i :class="child.icon"></i>
          {{child.nameZh}}
        </el-menu-item>
      </template>
    </el-submenu>
  </div>
</template>
 
<script>
export default {
  name: "NavigationItem",
  props: {
    item: {
      type: Object,
      required: true
    }
  }
};
</script>

一共两种,欢迎补充,根据情况而定

# vue 按钮级别权限的实现

  1. 按钮级的权限

    1.1前后端共同规定每种按钮的标识符 添加=>Add 修改=>Update 删除=>Delete

    ​ 在每次加载页面的时候请求接口,拿到按钮级别的权限,存放在Session里面或者公共存储里面

    ​ 然后通过自定义v-has=”{class:’Add’}”去控制显示和隐藏

  2. 使用

    2.1在src下的utils里面把js文件放进去,随后在main.js里面引入,

    ​ import { has } from “./utils/hasPermission”;

    2.2 在页面的按钮级权限的里面v-has="{class:‘admin’}" 去控制

** 在src下创建util文件夹创建hasPermission.js文件**

import Vue from "vue";
/**
 * 按钮级别的权限控制 -- 比对元素 -- 元素绑定角色值---登录后获取角色的值
 * 元素绑定角色值,接收字符串或者数组
 */
/** 权限指令 */
let has = Vue.directive("has", {
	inserted: function(el, binding) {
		//注意使用inserted周期,不要使用bind 不然无法删除元素
		let ClassName = binding.value.class; //v-has 接收数组对象
		if (el.parentNode && !Vue.prototype.$_has(ClassName)) {
			el.parentNode.removeChild(el);
		}
	},
});
// 元素指令值与角色值对比,如果不匹配,返回false
// 权限检查
Vue.prototype.$_has = function(val) {
	let isShow = false;
	let bthPerminnions = sessionStorage.getItem("bthPermissions");
	if (bthPerminnions === undefined || bthPerminnions == null) {
		return false;
	}
	if (typeof val === "string") {
		if (val === bthPerminnions) {
			isShow = true;
		}
		return isShow;
	} else {
		for (let i = 0; i < val.length; i++) {
			const showIs = val[i];
			if (showIs === bthPerminnions) {
				isShow = true;
			}
		}
		return isShow;
	}
};
export { has };

我是放在了session里面,如果放在vuex中直接修改js文件即可,同理.根据情况而定

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值