Bladeren bron

:zap: 架构底层优化

smallchill 6 jaren geleden
bovenliggende
commit
51e9429930

+ 33 - 0
src/cache.js

@@ -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();
+  },
+});

+ 1 - 0
src/lang/en.js

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

+ 1 - 0
src/lang/zh.js

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

+ 1 - 0
src/main.js

@@ -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';

+ 75 - 85
src/page/index/index.vue

@@ -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>

+ 9 - 2
src/page/index/layout.vue

@@ -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>

+ 49 - 62
src/permission.js

@@ -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);
 });

+ 161 - 149
src/router/avue-router.js

@@ -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;

+ 22 - 15
src/router/router.js

@@ -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;

+ 4 - 1
src/util/util.js

@@ -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()
     }
-}
+}

+ 46 - 0
src/views/util/affix.vue

@@ -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>

+ 22 - 0
src/views/util/cache.vue

@@ -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>

+ 59 - 0
src/views/util/crud-form.vue

@@ -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>

+ 40 - 0
src/views/util/form-detail.vue

@@ -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>

+ 28 - 0
src/views/util/top.vue

@@ -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>