# 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选项要求的数组
-
基本使用
//现在有一个非常普通的路由 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&w=90&h=56&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组件全选和单选并且在底下显示的问题
/**
* 功能是论需求而定的,没有做封装,见谅
*/
/**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前后端共同规定每种按钮的标识符 添加=>Add 修改=>Update 删除=>Delete
在每次加载页面的时候请求接口,拿到按钮级别的权限,存放在Session里面或者公共存储里面
然后通过自定义v-has=”{class:’Add’}”去控制显示和隐藏
使用
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文件即可,同理.根据情况而定