架构底层优化

pull/59/head
smallchill 7 years ago
parent 35a396c654
commit 51e9429930
  1. 33
      src/cache.js
  2. 1
      src/lang/en.js
  3. 1
      src/lang/zh.js
  4. 1
      src/main.js
  5. 160
      src/page/index/index.vue
  6. 11
      src/page/index/layout.vue
  7. 111
      src/permission.js
  8. 310
      src/router/avue-router.js
  9. 37
      src/router/router.js
  10. 5
      src/util/util.js
  11. 46
      src/views/util/affix.vue
  12. 22
      src/views/util/cache.vue
  13. 59
      src/views/util/crud-form.vue
  14. 40
      src/views/util/form-detail.vue
  15. 28
      src/views/util/top.vue

@ -0,0 +1,33 @@
import Vue from 'vue'
import store from './store';
Vue.mixin({
beforeRouteLeave: function (to, from, next) {
if (this.$route.meta.keepAlive === true) {
const result = this.$route.meta.keepAlive === true && store.state.tags.tagList.some(ele => {
return ele.value === this.$route.fullPath;
})
if (this.$vnode && !result) {
if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache) {
if (this.$vnode.componentOptions) {
var key = this.$vnode.key == null
? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '')
: this.$vnode.key;
var cache = this.$vnode.parent.componentInstance.cache;
var keys = this.$vnode.parent.componentInstance.keys;
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key);
if (index > -1) {
keys.splice(index, 1);
}
}
delete cache[key];
}
}
}
}
}
next();
},
});

@ -59,6 +59,7 @@ export default {
logs: 'logs',
table: 'table',
form: 'form',
top: 'backtop',
data: 'data',
error: 'error',
test: 'test'

@ -58,6 +58,7 @@ export default {
logs: '日志监控',
table: '表格',
form: '表单',
top: '返回顶部',
data: '数据展示',
permission: '权限',
error: '异常页面',

@ -5,6 +5,7 @@ import App from './App';
import router from './router/router';
import './permission'; // 权限
import './error'; // 日志
import './cache';//页面缓存
import store from './store';
import {loadStyle} from './util/util'
import * as urls from '@/config/env';

@ -1,110 +1,100 @@
<template>
<div class="avue-contail"
:class="{'avue--collapse':isCollapse}">
<div class="avue-contail" :class="{'avue--collapse':isCollapse}">
<div class="avue-header">
<!-- 顶部导航栏 -->
<top />
<top/>
</div>
<div class="avue-layout">
<div class="avue-left">
<!-- 左侧导航栏 -->
<sidebar />
<sidebar/>
</div>
<div class="avue-main">
<!-- 顶部标签卡 -->
<tags />
<tags/>
<!-- 主体视图层 -->
<el-scrollbar style="height:100%">
<div style="height:100%;overflow-y:auto;overflow-x:hidden;" id="avue-view">
<keep-alive>
<router-view class="avue-view"
v-if="$route.meta.keepAlive" />
<router-view class="avue-view" v-if="$route.meta.$keepAlive"/>
</keep-alive>
<router-view class="avue-view"
v-if="!$route.meta.keepAlive" />
</el-scrollbar>
<router-view class="avue-view" v-if="!$route.meta.$keepAlive"/>
</div>
</div>
</div>
<!-- <el-footer class="avue-footer">
<img src="/svg/logo.svg"
alt=""
class="logo">
<p class="copyright">© 2018 Avue designed by smallwei</p>
</el-footer> -->
<div class="avue-shade"
@click="showCollapse"></div>
<div class="avue-shade" @click="showCollapse"></div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import tags from "./tags";
import top from "./top/";
import sidebar from "./sidebar/";
import admin from "@/util/admin";
import { validatenull } from "@/util/validate";
import { calcDate } from "@/util/date.js";
import { getStore } from "@/util/store.js";
export default {
components: {
top,
tags,
sidebar
},
name: "index",
data() {
return {
//token
refreshLock: false,
//token
refreshTime: ""
};
},
created() {
//token
this.refreshToken();
},
mounted() {
this.init();
},
computed: mapGetters(["isLock", "isCollapse", "website"]),
props: [],
methods: {
showCollapse() {
this.$store.commit("SET_COLLAPSE");
import {mapGetters} from "vuex";
import tags from "./tags";
import top from "./top/";
import sidebar from "./sidebar/";
import admin from "@/util/admin";
import {validatenull} from "@/util/validate";
import {calcDate} from "@/util/date.js";
import {getStore} from "@/util/store.js";
export default {
components: {
top,
tags,
sidebar
},
//
init() {
this.$store.commit("SET_SCREEN", admin.getScreen());
window.onresize = () => {
setTimeout(() => {
this.$store.commit("SET_SCREEN", admin.getScreen());
}, 0);
name: "index",
data() {
return {
//token
refreshLock: false,
//token
refreshTime: ""
};
this.$store.dispatch("FlowRoutes").then(() => {});
},
refreshToken() {
this.refreshTime = setInterval(() => {
const token = getStore({
name: "token",
debug: true
created() {
//token
this.refreshToken();
},
mounted() {
this.init();
},
computed: mapGetters(["isLock", "isCollapse", "website"]),
props: [],
methods: {
showCollapse() {
this.$store.commit("SET_COLLAPSE");
},
//
init() {
this.$store.commit("SET_SCREEN", admin.getScreen());
window.onresize = () => {
setTimeout(() => {
this.$store.commit("SET_SCREEN", admin.getScreen());
}, 0);
};
this.$store.dispatch("FlowRoutes").then(() => {
});
const date = calcDate(token.datetime, new Date().getTime());
if (validatenull(date)) return;
if (date.seconds >= this.website.tokenTime && !this.refreshLock) {
this.refreshLock = true;
this.$store
.dispatch("refreshToken")
.then(() => {
this.refreshLock = false;
})
.catch(() => {
this.refreshLock = false;
});
}
}, 10000);
},
refreshToken() {
this.refreshTime = setInterval(() => {
const token = getStore({
name: "token",
debug: true
});
const date = calcDate(token.datetime, new Date().getTime());
if (validatenull(date)) return;
if (date.seconds >= this.website.tokenTime && !this.refreshLock) {
this.refreshLock = true;
this.$store
.dispatch("refreshToken")
.then(() => {
this.refreshLock = false;
})
.catch(() => {
this.refreshLock = false;
});
}
}, 10000);
}
}
}
};
};
</script>

@ -1,3 +1,10 @@
<template>
<router-view></router-view>
</template>
<div>
<keep-alive>
<router-view class="avue-view"
v-if="$route.meta.$keepAlive" />
</keep-alive>
<router-view class="avue-view"
v-if="!$route.meta.$keepAlive" />
</div>
</template>

@ -4,78 +4,65 @@
*/
import router from './router/router'
import store from './store'
import { validatenull } from '@/util/validate'
import { getToken } from '@/util/auth'
import {validatenull} from '@/util/validate'
import {getToken} from '@/util/auth'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
NProgress.configure({ showSpinner: false });
NProgress.configure({showSpinner: false});
const lockPage = store.getters.website.lockPage; //锁屏页
router.beforeEach((to, from, next) => {
//缓冲设置
if (to.meta.keepAlive === true && store.state.tags.tagList.some(ele => {
return ele.value === to.fullPath;
})) {
to.meta.$keepAlive = true;
const meta = to.meta || {};
if (getToken()) {
if (store.getters.isLock && to.path != lockPage) { //如果系统激活锁屏,全部跳转到锁屏页
next({path: lockPage})
} else if (to.path === '/login') { //如果登录成功访问登录页跳转到主页
next({path: '/'})
} else {
NProgress.start()
if (to.meta.keepAlive === true && validatenull(to.meta.$keepAlive)) {
to.meta.$keepAlive = true;
} else {
to.meta.$keepAlive = false;
//如果用户信息为空则获取用户信息,获取用户信息失败,跳转到登录页
if (store.getters.token.length === 0) {
store.dispatch('FedLogOut').then(() => {
next({path: '/login'})
})
} else {
const value = to.query.src || to.fullPath;
const label = to.query.name || to.name;
const meta = to.meta || router.$avueRouter.meta || {};
const i18n = to.query.i18n;
if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
store.commit('ADD_TAG', {
label: label,
value: value,
params: to.params,
query: to.query,
meta: (() => {
if (!i18n) {
return meta
}
return {
i18n: i18n
}
})(),
group: router.$avueRouter.group || []
});
}
next()
}
}
const meta = to.meta || {};
if (getToken()) {
if (store.getters.isLock && to.path != lockPage) { //如果系统激活锁屏,全部跳转到锁屏页
next({ path: lockPage })
} else if (to.path === '/login') { //如果登录成功访问登录页跳转到主页
next({ path: '/' })
} else {
//如果用户信息为空则获取用户信息,获取用户信息失败,跳转到登录页
if (store.getters.token.length === 0) {
store.dispatch('FedLogOut').then(() => {
next({ path: '/login' })
})
} else {
const value = to.query.src || to.fullPath;
const label = to.query.name || to.name;
const meta = to.meta || router.$avueRouter.meta || {};
const i18n = to.query.i18n;
if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
store.commit('ADD_TAG', {
label: label,
value: value,
params: to.params,
query: to.query,
meta: (() => {
if (!i18n) {
return meta
}
return {
i18n: i18n
}
})(),
group: router.$avueRouter.group || []
});
}
next()
}
}
} else {
//判断是否需要认证,没有登录访问去登录页
if (meta.isAuth === false) {
next()
} else {
//判断是否需要认证,没有登录访问去登录页
if (meta.isAuth === false) {
next()
} else {
next('/login')
}
next('/login')
}
}
})
router.afterEach(() => {
NProgress.done();
let title = store.getters.tag.label;
let i18n = store.getters.tag.meta.i18n;
title = router.$avueRouter.generateTitle(title, i18n)
//根据当前的标签也获取label的值动态设置浏览器标题
router.$avueRouter.setTitle(title);
NProgress.done();
let title = store.getters.tag.label;
let i18n = store.getters.tag.meta.i18n;
title = router.$avueRouter.generateTitle(title, i18n)
//根据当前的标签也获取label的值动态设置浏览器标题
router.$avueRouter.setTitle(title);
});

@ -1,166 +1,178 @@
let RouterPlugin = function () {
this.$router = null;
this.$store = null;
this.$router = null;
this.$store = null;
};
RouterPlugin.install = function (vue, router, store, i18n) {
this.$router = router;
this.$store = store;
this.$vue = new vue({ i18n });
function isURL(s) {
return /^http[s]?:\/\/.*/.test(s)
}
function objToform(obj) {
let result = [];
Object.keys(obj).forEach(ele => {
result.push(`${ele}=${obj[ele]}`);
})
return result.join('&');
}
this.$router.$avueRouter = {
//全局配置
$website: this.$store.getters.website,
routerList: [],
group: '',
meta: {},
safe: this,
// 设置标题
setTitle: (title) => {
const defaultTitle = this.$vue.$t('title');
title = title ? `${title}-${defaultTitle}` : defaultTitle;
document.title = title;
},
closeTag: (value) => {
let tag = value || this.$store.getters.tag;
if (typeof value === 'string') {
tag = this.$store.getters.tagList.filter(ele => ele.value === value)[0]
}
this.$store.commit('DEL_TAG', tag)
},
generateTitle: (title, key) => {
if (!key) return title;
const hasKey = this.$vue.$te('route.' + key)
if (hasKey) {
// $t :this method from vue-i18n, inject in @/lang/index.js
const translatedTitle = this.$vue.$t('route.' + key)
this.$router = router;
this.$store = store;
this.$vue = new vue({i18n});
return translatedTitle
}
return title
},
//处理路由
getPath: function (params) {
let { src } = params;
let result = src || '/';
if (src.includes("http") || src.includes("https")) {
result = `/myiframe/urlPath?${objToform(params)}`;
}
return result;
},
//正则处理路由
vaildPath: function (list, path) {
let result = false;
list.forEach(ele => {
if (new RegExp("^" + ele + ".*", "g").test(path)) {
result = true
}
function isURL(s) {
return /^http[s]?:\/\/.*/.test(s)
}
function objToform(obj) {
let result = [];
Object.keys(obj).forEach(ele => {
result.push(`${ele}=${obj[ele]}`);
})
return result.join('&');
}
this.$router.$avueRouter = {
//全局配置
$website: this.$store.getters.website,
routerList: [],
group: '',
meta: {},
safe: this,
// 设置标题
setTitle: (title) => {
const defaultTitle = this.$vue.$t('title');
title = title ? `${title}-${defaultTitle}` : defaultTitle;
document.title = title;
},
closeTag: (value) => {
let tag = value || this.$store.getters.tag;
if (typeof value === 'string') {
tag = this.$store.getters.tagList.filter(ele => ele.value === value)[0]
}
this.$store.commit('DEL_TAG', tag)
},
generateTitle: (title, key) => {
if (!key) return title;
const hasKey = this.$vue.$te('route.' + key)
if (hasKey) {
// $t :this method from vue-i18n, inject in @/lang/index.js
const translatedTitle = this.$vue.$t('route.' + key)
return translatedTitle
}
return title
},
//处理路由
getPath: function (params) {
let {src} = params;
let result = src || '/';
if (src.includes("http") || src.includes("https")) {
result = `/myiframe/urlPath?${objToform(params)}`;
}
return result;
},
//正则处理路由
vaildPath: function (list, path) {
let result = false;
list.forEach(ele => {
if (new RegExp("^" + ele + ".*", "g").test(path)) {
result = true
}
})
return result;
},
//设置路由值
getValue: function (route) {
let value = "";
if (route.query.src) {
value = route.query.src;
})
return result;
},
//设置路由值
getValue: function (route) {
let value = "";
if (route.query.src) {
value = route.query.src;
} else {
value = route.path;
}
return value;
},
//动态路由
formatRoutes: function (aMenu = [], first) {
const aRouter = []
const propsConfig = this.$website.menu.props;
const propsDefault = {
label: propsConfig.label || 'name',
path: propsConfig.path || 'path',
icon: propsConfig.icon || 'icon',
children: propsConfig.children || 'children',
meta: propsConfig.meta || 'meta',
}
if (aMenu.length === 0) return;
for (let i = 0; i < aMenu.length; i++) {
const oMenu = aMenu[i];
if (this.routerList.includes(oMenu[propsDefault.path])) return;
let path = (() => {
if (first) {
return oMenu[propsDefault.path].replace('/index', '')
} else {
value = route.path;
}
return value;
},
//动态路由
formatRoutes: function (aMenu = [], first) {
const aRouter = []
const propsConfig = this.$website.menu.props;
const propsDefault = {
label: propsConfig.label || 'name',
path: propsConfig.path || 'path',
icon: propsConfig.icon || 'icon',
children: propsConfig.children || 'children',
meta: propsConfig.meta || 'meta',
return oMenu[propsDefault.path]
}
if (aMenu.length === 0) return;
for (let i = 0; i < aMenu.length; i++) {
const oMenu = aMenu[i];
if (this.routerList.includes(oMenu[propsDefault.path])) return;
const path = (() => {
if (first) {
return oMenu[propsDefault.path].replace('/index', '')
} else {
return oMenu[propsDefault.path]
}
})(),
//特殊处理组件
component = 'views' + oMenu.path,
name = oMenu[propsDefault.label],
icon = oMenu[propsDefault.icon],
children = oMenu[propsDefault.children],
meta = oMenu[propsDefault.meta] || {};
})(),
//特殊处理组件
component = 'views' + oMenu.path,
name = oMenu[propsDefault.label],
icon = oMenu[propsDefault.icon],
children = oMenu[propsDefault.children],
meta = oMenu[propsDefault.meta] || {};
const isChild = children.length !== 0;
const oRouter = {
path: path,
component(resolve) {
// 判断是否为首路由
if (first) {
require(['../page/index'], resolve)
return
// 判断是否为多层路由
} else if (isChild && !first) {
require(['../page/index/layout'], resolve)
return
// 判断是否为最终的页面视图
} else {
require([`../${component}.vue`], resolve)
}
},
name: name,
icon: icon,
meta: meta,
redirect: (() => {
if (!isChild && first && !isURL(path)) return `${path}/index`
else return '';
})(),
// 处理是否为一级路由
children: !isChild ? (() => {
if (first) {
if (!isURL(path)) oMenu[propsDefault.path] = `${path}/index`;
return [{
component(resolve) { require([`../${component}.vue`], resolve) },
icon: icon,
name: name,
meta: meta,
path: 'index'
}]
}
return [];
})() : (() => {
return this.formatRoutes(children, false)
})()
}
aRouter.push(oRouter)
meta = Object.assign(meta, (function () {
if (meta.keepAlive === true) {
return {
$keepAlive: true
}
}
})());
const isChild = children.length !== 0;
const oRouter = {
path: path,
component(resolve) {
// 判断是否为首路由
if (first) {
if (!this.routerList.includes(aRouter[0][propsDefault.path])) {
this.safe.$router.addRoutes(aRouter)
this.routerList.push(aRouter[0][propsDefault.path])
}
require(['../page/index'], resolve)
return
// 判断是否为多层路由
} else if (isChild && !first) {
require(['../page/index/layout'], resolve)
return
// 判断是否为最终的页面视图
} else {
return aRouter
require([`../${component}.vue`], resolve)
}
},
name: name,
icon: icon,
meta: meta,
redirect: (() => {
if (!isChild && first && !isURL(path)) return `${path}/index`
else return '';
})(),
// 处理是否为一级路由
children: !isChild ? (() => {
if (first) {
if (!isURL(path)) oMenu[propsDefault.path] = `${path}/index`;
return [{
component(resolve) {
require([`../${component}.vue`], resolve)
},
icon: icon,
name: name,
meta: meta,
path: 'index'
}]
}
return [];
})() : (() => {
return this.formatRoutes(children, false)
})()
}
aRouter.push(oRouter)
}
if (first) {
if (!this.routerList.includes(aRouter[0][propsDefault.path])) {
this.safe.$router.addRoutes(aRouter)
this.routerList.push(aRouter[0][propsDefault.path])
}
} else {
return aRouter
}
}
}
}
export default RouterPlugin;

@ -14,22 +14,29 @@ import Vue from 'vue';
import i18n from '@/lang' // Internationalization
import Store from '../store/';
let Router = new VueRouter({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = document.body.scrollTop;
}
return {
x: 0,
y: to.meta.savedPosition || 0
}
}
},
routes: []
scrollBehavior(to, from, savedPosition) {
const avueView = document.getElementById('avue-view');
if (!avueView) {
return {
x: 0,
y: 0
}
}
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = avueView.scrollTop
} else {
from.meta.savedPosition = 0;
}
avueView.scrollTop = to.meta.savedPosition
}
},
routes: []
});
AvueRouter.install(Vue, Router, Store, i18n);
Router.$avueRouter.formatRoutes(Store.state.user.menu, true);
Router.addRoutes([...PageRouter, ...ViewsRouter]);
export default Router;
export default Router;

@ -26,6 +26,9 @@ export const getObjType = obj => {
}
return map[toString.call(obj)];
};
export const getViewDom = () => {
return window.document.getElementById('avue-view').getElementsByClassName('el-scrollbar__wrap')[0]
}
/**
* 对象深拷贝
*/
@ -285,4 +288,4 @@ export const openWindow = (url, title, w, h) => {
if (window.focus) {
newWindow.focus()
}
}
}

@ -0,0 +1,46 @@
<template>
<div class="affix">
<avue-affix id="avue-view">
<span class="affix-affix">固定在最顶部</span>
</avue-affix>
<div class="affix-line"></div>
<avue-affix id="avue-view"
:offset-top="50">
<span class="affix-affix">固定在距离顶部 50px 的位置</span>
</avue-affix>
<div class="affix-line"></div>
<avue-affix id="avue-view"
:offset-top="100">
<span class="affix-affix">固定在距离顶部 100px 的位置</span>
</avue-affix>
<div class="affix-line"></div>
<avue-affix id="avue-view"
:offset-top="150">
<span class="affix-affix">固定在距离顶部 150px 的位置</span>
</avue-affix>
<div style="height:2000px;">
<div style="padding:15px 20px;font-size:18px;">往下拉就会出现图钉</div>
</div>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss">
.affix {
position: relative;
background-color: #fff;
&-affix {
display: inline-block;
color: #fff;
padding: 10px 30px;
text-align: center;
background: rgba(0, 153, 229, 0.9);
}
&-line {
height: 100px;
}
}
</style>

@ -0,0 +1,22 @@
<template>
<basic-container>
<h3>这个页面会被 keep-alive</h3>
<el-tag>在下面的输入框输入任意字符后切换到其它页面再回到此页时输入框文字保留证明被缓存</el-tag>
<br /> <br />
<el-tag>同时滚动下拉返回时还会保持滚动条所在的位置</el-tag>
<br /> <br />
<el-input v-model="value" placeholder="input here"></el-input>
<div style="height:1000px;"></div>
</basic-container>
</template>
<script>
export default {
data() {
return {
value: ""
};
}
};
</script>

@ -0,0 +1,59 @@
<template>
<basic-container>
<h3>点击新增或编辑跳转到新的页面</h3>
<avue-crud :option="option"
:data="data">
<template slot="menuLeft">
<el-button type="primary"
size="small"
@click="handleForm()"
icon="el-icon-plus">add</el-button>
</template>
<template slot="menu"
slot-scope="{row}">
<el-button size="small"
type="text"
@click="handleForm(row.id)"
icon="el-icon-edit">edit</el-button>
</template>
</avue-crud>
</basic-container>
</template>
<script>
export default {
data() {
return {
option: {
addBtn: false,
editBtn: false,
column: [
{
label: "姓名",
prop: "name"
}
]
},
data: [
{
id: 1,
name: "small"
}
]
};
},
methods: {
handleForm(id) {
this.$router.push({
path: "/form-detail/index",
query: {
id: id
}
});
}
}
};
</script>
<style>
</style>

@ -0,0 +1,40 @@
<template>
<basic-container>
<h3>{{$route.query.id?'编辑':'新增'}}</h3>
<avue-form :option="option"
v-model="form">
<template slot="menuForm">
<el-button icon="el-icon-back"
@click="handleBack()"> </el-button>
</template>
</avue-form>
</basic-container>
</template>
<script>
export default {
data() {
return {
form: {},
option: {
labelWidth: 110,
column: [
{
label: "姓名",
prop: "name"
}
]
}
};
},
methods: {
handleBack() {
this.$router.$avueRouter.closeTag();
this.$router.back();
}
}
};
</script>
<style>
</style>

@ -0,0 +1,28 @@
<template>
<div>
<div style="height:2000px;background-color:#fff;">
<div style="padding:15px 20px;font-size:18px;">往下拉就会出现返回菜单</div>
</div>
<avue-back-top id="avue-view"></avue-back-top>
<avue-back-top id="avue-view"
:height="100"
:bottom="200">
<div class="top">返回顶端</div>
</avue-back-top>
</div>
</template>
<script>
export default {};
</script>
<style scoped>
.top {
padding: 10px;
font-size: 14px;
background: rgba(0, 153, 229, 0.7);
color: #fff;
text-align: center;
border-radius: 2px;
}
</style>
Loading…
Cancel
Save