既然加入了状态管理,就要用到尽,不用不就白浪费了。随着模块更新,持续更新
目录
一、顶部tag的应用
顶部tag的操作:
①如何开始定义模块
首先我们要给tag定义一个基本的属性(虽然这个属性是总体定义),某些规则适配业务与一般逻辑性。
eg: 例如tag仅剩一个时不能关闭
tips:
①下方 tags.js -->store/modules/tag.js
②下方tags.vue --> components/lay-out/tags.vue
// tag.js
//给 tag 标立一个基本对象属性
const tagObj = {
label: '', //标题名称
value: '', //标题的路径
params: '', //标题的路径参数
query: '', //标题的参数
close: false //是否显示关闭按钮
};
//最后一个标签就不必存在关闭按钮了
let setFistTag = ((list) => {
if (list.length == 1) {
list[0].close = false;
} else {
list.some(a => {
a.close = true
})
}
})
//tags.vue
//对应页面也要适配上去
<i class="el-icon-close tag-close" v-if="item.close"/>
②定义模块所需状态值
我们需要的状态值包括:
1、标签列表,以保证刷新后的数据能重新回填
2、当前tag的值
//tags.js
//作本地存储用,后面会贴出来
import {getStore, removeStore, setStore} from '@/util/store'
//若本地/会话存储内不存在,则为空,因为刷新后会清空内存
state: {
tagList: getStore({name: 'tagList'}) || [],
tag: getStore({name: 'tag'}) || tagObj
}
③操作状态值
下列操作仅需要同步操作即可,因故基本为 mutation,若有非此,会注释表明。
tips: 下方 默认存在 getStore, removeStore, setStore 方法,仅仅是封装了sessionStorage/localStorage,第四点会提及这个工具类
1、增加标签
新增的标签,通常是其他地方新增的页面,将最新的更替为当前值
①如果列表中没有,我们就新增,插入到列表中,如果存在,则无需插入
②保险起见,还是要调用上方 setFistTag
来校验一下
ADD_TAG: (state, action) => {
state.tag = action;
setStore({ name: 'tag', content: state.tag, type: 'session' });
if (state.tagList.some(ele => isObjectValueEqual(ele, action))) return;
state.tagList.push(action);
setFistTag(state.tagList);
setStore({ name: 'tagList', content: state.tagList, type: 'session' })
}
2、删除标签
整体是利用过滤器对传进来的标签进行判断
①如果传入的对象属于 object对象就可以对其解析,判断属性是否相同,如果存在则删除
②调用上方setFistTag
来校验
DEL_TAG: (state, action) => {
state.tagList = state.tagList.filter(item => {
if (typeof(action) === 'object') {
let a = Object.assign({}, item);
let b = Object.assign({}, action);
delete a.close;
//delete b.__ob__; 使用了asign 之后就不必要这句,复制后就木有了
//__ob__:Observer这些数据是vue这个框架对数据设置的监控器,一般都是不可枚举的,木有了指的是这个
return !isObjectValueEqual(a, b)//是最终用于判定每个属性是否相同的
} else {
return item.value !== action
}
});
setFistTag(state.tagList);
setStore({ name: 'tagList', content: state.tagList, type: 'session' })
}
//如果不喜欢这样,还有其他方法,例如自行遍历,但是要注意对比时如果全部属性,记得__ob__带来的问题
3、删除所有标签
就很直接,清空标签栏
DEL_ALL_TAG: (state) => {
state.tagList = [];
removeStore({ name: 'tagList' });
}
4、删除其他标签
这个是为了备选需要删除其他标签,仅仅留下当前标签的,也是很清晰
DEL_TAG_OTHER: (state) => {
state.tagList = state.tagList.filter(item => item.value === state.tag.value);
setFistTag(state.tagList);
setStore({ name: 'tagList', content: state.tagList, type: 'session' })
}
上述 isObjectValueEqual
方法
//主要用于判断对象中属性是否完全相同,逻辑如代码所示,简单判别格式化与字符串相等等
export const isObjectValueEqual = (a, b) => {
let result = true;
Object.keys(a).forEach(ele => {
const type = typeof(a[ele]);
if (type === 'string' && a[ele] !== b[ele]) result = false;
else if (type === 'object' && JSON.stringify(a[ele]) !== JSON.stringify(b[ele])) result = false;
})
return result;
};
涉及的本地/会话存储:
为什么要用本地/会话存储?
① vuex存储在内存(刷新GG),localstorage
(本地存储)则以文件的方式存储在本地,永久保存;sessionstorage
( 会话存储 ) ,临时保存页面关闭GG。
② 同时,及时响应的数据驱动也是 localstorage
与 sessionstorage
无法达到的
判空方法
//懒得写箭头还要设置名称
function validatenull(val) {
if (typeof val == 'boolean') {
return false;
}
if (val instanceof Array) {
if (val.length == 0) return true;
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true;
} else {
if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true;
return false;
}
return false;
}
①存储
/**
* 存储local/session Storage
* name : 对应名称
* content: 内容
* type: 主要是 seesion ,不然就默认本地存储,好吧其实是不为空
* datetime: 虽然这里可能遇到ios的无敌问题,但是我不考虑,如果考虑就用moment.js
*/
export const setStore = (params) => {
let {
name,
content,
type,
datetime
} = params;
let obj = {
dataType: typeof(content),
content: content,
type: type,
datetime: new Date().getTime()
};
if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
else window.localStorage.setItem(name, JSON.stringify(obj));
};
②获取某个
/**
* 获取local/session Storage
*/
export const getStore = (params) => {
let {
name,
type
} = params;
let obj = {},
content;
obj = window.localStorage.getItem(name);
if (validatenull(obj)) obj = window.sessionStorage.getItem(name);
if (validatenull(obj)) return;
obj = JSON.parse(obj);
if (obj.dataType == 'string') {
content = obj.content;
} else if (obj.dataType == 'number') {
content = Number(obj.content);
} else if (obj.dataType == 'boolean') {
content = eval(obj.content);
} else if (obj.dataType == 'object') {
content = obj.content;
}
return content;
};
③删除某个
/**
* 删除local/session Storage
* 默认可以不写type,删除全部,当然也可以选择,随自己意思吧
*/
export const removeStore = params => {
let {
name,
type
} = params;
if (type) {
if('session'===type){
window.sessionStorage.removeItem(name);
}else{
window.localStorage.removeItem(name);
}
} else {
window.localStorage.removeItem(name);
window.sessionStorage.removeItem(name);
}
};
④获取全部
/**
* 获取全部local/session Storage
*/
export const getAllStore = (params) => {
let list = [];
let {
type
} = params;
for (let i = 1; i <= window.sessionStorage.length; i++) {
if (type) {
list.push({
name: window.sessionStorage.key(i),
content: getStore({
name: window.sessionStorage.key(i),
type: 'session'
})
})
} else {
list.push(getStore({
name: window.localStorage.key(i),
content: getStore({
name: window.localStorage.key(i),
})
}))
}
}
return list;
};
⑤清空全部
/**
* 清空全部local/session Storage
*/
export const clearStore = (params) => {
let {
type
} = params;
if (type) {
window.sessionStorage.clear();
return;
}
window.localStorage.clear()
};
顺便掺和路由:
这里怎么说呢。标签与路由分离了,标签就滑来滑去就没有意义了(不这样我怎么掺和进来),所以就掺进来。
如何进行绑定?
既然要与路由掺和,就要选定路由改变的时候去掺和,如果存在,就给它(这个路由)特别展示出来,不存在就给他新增一个(标签)出来。这样才能算的上掺和
①对router-plugin改造
之前路由定义的内容,不是很适配接下来的掺和,所以需要略微改动。
主要是修改前置导航守卫,来适配标签
// src/router/router-plugin.js
// 1、移除了前置导航守卫
// 2、修正了校验路由方法 validPath,不再使用foreach,改用some遍历
// 3、新增 setTitle 方法,用于设置浏览器头标题
let RouterPlugin = function () {
this.$router = null
}
RouterPlugin.install = function (router) {
this.$router = router
this.$router.$avueRouter = {
//正则处理路由
validPath: function (list, path) {
let result = false;
list.some(ele => {
if (new RegExp("^" + ele + ".*", "g").test(path)) {
result = true
return true
}
});
return result;
},
}
// 设置标题
setTitle: function(title) {
title = title ? `${title}` : '兰楹花';
document.title = title;
},
}
export default RouterPlugin;
② 对标签进行路由绑定
在每次路由变更的时候,对当前路由进行判定,若存在则展示点亮对应标签,否则,新增标签。
import website from "@/config/website";
import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress'
NProgress.configure({showSpinner: false});
router.beforeEach((to, from, next) => {
NProgress.start()
if (router.$avueRouter.validPath(website.whiteList, to.path)) {
const value = to.query.src ? to.query.src : to.path;
const label = to.query.name ? to.query.name : to.name;
store.commit('ADD_TAG', {
label: label,
value: value,
params: to.params,
query: to.query
});
next()
} else {
store.commit('ADD_TAG', {
label: "主页",
value: "/base/hi",
params: to.params,
query: to.query
});
next('/base/hi')
}
})
router.afterEach(() => {
NProgress.done();
let title = store.getters.tag;
title = title == undefined ? null : title
router.$avueRouter.setTitle(title);
});
③对组件tag进行改造
引入vuex,对会话/本地存储中的标签数据提取并展示出来
<template>
...
<div ref="tag" class="tag-item" v-for="item in tagList">
<span class="tag-text">{{item.label}}</span>
<i class="el-icon-close tag-close" v-if="item.close"/>
</div>
...
<template>
<script>
....
import {mapGetters} from 'vuex'
computed: {
...mapGetters([ 'tagList'])
},
...
</script>