Explorar o código

🎉添加更多本地化组件,移除骨架屏组件

chuzhixin %!s(int64=4) %!d(string=hai) anos
pai
achega
e769919073

+ 1 - 2
package.json

@@ -61,8 +61,7 @@
     "zx-comparison": "^1.0.3",
     "zx-count": "^0.3.7",
     "zx-icon": "^1.1.9",
-    "zx-keel": "^0.9.4",
-    "zx-layouts": "^0.6.14",
+    "zx-layouts": "^0.6.15",
     "zx-magnifie": "^0.4.0",
     "zx-markdown-editor": "^0.0.2",
     "zx-player": "^1.0.1",

+ 3 - 7
src/config/settings.js

@@ -26,12 +26,10 @@ module.exports = {
   devPort: "80",
   //版本号
   version: process.env.VUE_APP_VERSION,
-  //烦请保留package.json作者信息 保留版权可免费商用 如需去除并自定义为自己企业的版权请联系群主QQ 1204505056 需支付299元 恶意修改发生纠纷及出现任何问题 由修改人自行承担
+  //烦请保留package.json作者信息 即可免费商用
   copyright: process.env.VUE_APP_AUTHOR,
-  //是否显示页面底部版权信息,建议您显示,当然您也可以选择不显示,不管您是付费用户还是未付费用户您都有选择显示或者不显示的权利
-  footerCopyright: process.env.NODE_ENV !== "development",
-  //是否显示右上角github图标
-  githubCorner: false,
+  //是否显示页面底部自定义版权信息
+  footerCopyright: true,
   //是否显示顶部进度条
   progressBar: true,
   //缓存路由的最大数量
@@ -60,8 +58,6 @@ module.exports = {
   themeBar: true,
   //是否显示多标签页
   tabsBar: true,
-  //是否显示骨架屏
-  skeleton: false,
   //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form-urlencoded;charset=UTF-8
   contentType: "application/json;charset=UTF-8",
   //消息框消失时间

+ 103 - 0
src/layouts/components/AppMain/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <section v-if="routerView" class="app-main-container">
+    <transition mode="out-in" name="fade-transform">
+      <keep-alive :include="cachedRoutes" :max="keepAliveMaxNum">
+        <router-view :key="key" class="app-main-height" />
+      </keep-alive>
+    </transition>
+    <footer v-show="footerCopyright" class="footer-copyright">
+      Copyright
+      <vab-icon :icon="['fas', 'copyright']"></vab-icon>
+      xxx-项目 {{ fullYear }}
+    </footer>
+  </section>
+</template>
+
+<script>
+  import { VabKeel, VabKeelHeading, VabKeelText } from "@/plugins/vabKeel";
+  import { mapActions, mapGetters } from "vuex";
+  import {
+    copyright,
+    footerCopyright,
+    keepAliveMaxNum,
+    title,
+  } from "@/config/settings";
+
+  export default {
+    name: "AppMain",
+    data() {
+      return {
+        show: false,
+        fullYear: new Date().getFullYear(),
+        copyright,
+        title,
+        keepAliveMaxNum,
+        routerView: true,
+        footerCopyright,
+      };
+    },
+    computed: {
+      ...mapGetters({
+        visitedRoutes: "tabsBar/visitedRoutes",
+        device: "settings/device",
+      }),
+      cachedRoutes() {
+        const cachedRoutesArr = [];
+        this.visitedRoutes.forEach((item) => {
+          if (!item.meta.noKeepAlive) {
+            cachedRoutesArr.push(item.name);
+          }
+        });
+        return cachedRoutesArr;
+      },
+      key() {
+        return this.$route.path;
+      },
+    },
+    watch: {
+      $route: {
+        handler(route) {
+          if ("mobile" === this.device) this.foldSideBar();
+        },
+        immediate: true,
+      },
+    },
+    created() {
+      //重载所有路由
+      this.$baseEventBus.$on("reload-routerview", () => {
+        this.routerView = false;
+        this.$nextTick(() => {
+          this.routerView = true;
+        });
+      });
+    },
+    mounted() {},
+    methods: {
+      ...mapActions({
+        foldSideBar: "settings/foldSideBar",
+      }),
+    },
+  };
+</script>
+
+<style lang="scss" scoped>
+  .app-main-container {
+    position: relative;
+    width: 100%;
+    overflow: hidden;
+    .vab-keel {
+      margin: $base-padding;
+    }
+    .app-main-height {
+      min-height: $base-app-main-height;
+    }
+
+    .footer-copyright {
+      min-height: 55px;
+      line-height: 55px;
+      color: rgba(0, 0, 0, 0.45);
+      text-align: center;
+      border-top: 1px dashed $base-border-color;
+    }
+  }
+</style>

+ 63 - 0
src/layouts/components/Breadcrumb/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <el-breadcrumb class="breadcrumb-container" separator=">">
+    <el-breadcrumb-item v-for="item in list" :key="item.path">
+      {{ item.meta.title }}
+    </el-breadcrumb-item>
+  </el-breadcrumb>
+</template>
+
+<script>
+  export default {
+    name: "Breadcrumb",
+    data() {
+      return {
+        list: this.getBreadcrumb(),
+      };
+    },
+    watch: {
+      $route() {
+        this.list = this.getBreadcrumb();
+      },
+    },
+    methods: {
+      getBreadcrumb() {
+        return this.$route.matched.filter(
+          (item) => item.name && item.meta.title
+        );
+      },
+    },
+  };
+</script>
+
+<style lang="scss" scoped>
+  .breadcrumb-container {
+    height: $base-nav-bar-height;
+    font-size: $base-font-size-default;
+    line-height: $base-nav-bar-height;
+
+    ::v-deep {
+      .el-breadcrumb__item {
+        .el-breadcrumb__inner {
+          a {
+            display: flex;
+            float: left;
+            font-weight: normal;
+            color: #515a6e;
+
+            i {
+              margin-right: 3px;
+            }
+          }
+        }
+
+        &:last-child {
+          .el-breadcrumb__inner {
+            a {
+              color: #999;
+            }
+          }
+        }
+      }
+    }
+  }
+</style>

+ 152 - 0
src/layouts/components/NavBar/index.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="nav-bar-container">
+    <el-row :gutter="15">
+      <el-col :xs="4" :sm="12" :md="12" :lg="12" :xl="12">
+        <div class="left-panel">
+          <i
+            :class="collapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
+            :title="collapse ? '展开' : '收起'"
+            class="fold-unfold"
+            @click="handleCollapse"
+          ></i>
+          <breadcrumb class="hidden-xs-only" />
+        </div>
+      </el-col>
+      <el-col :xs="20" :sm="12" :md="12" :lg="12" :xl="12">
+        <div class="right-panel">
+          <error-log></error-log>
+          <full-screen-bar @refresh="refreshRoute"></full-screen-bar>
+          <theme-bar class="hidden-xs-only"></theme-bar>
+          <vab-icon
+            title="重载所有路由"
+            :pulse="pulse"
+            :icon="['fas', 'redo']"
+            @click="refreshRoute"
+          ></vab-icon>
+          <avatar></avatar>
+          <!--  <vab-icon
+            title="退出系统"
+            :icon="['fas', 'sign-out-alt']"
+            @click="logout"
+          />-->
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+  import { mapActions, mapGetters } from "vuex";
+
+  import {
+    Avatar,
+    Breadcrumb,
+    ErrorLog,
+    FullScreenBar,
+    ThemeBar,
+  } from "@/layouts/components";
+
+  export default {
+    name: "NavBar",
+    components: {
+      Avatar,
+      Breadcrumb,
+      ErrorLog,
+      FullScreenBar,
+      ThemeBar,
+    },
+    data() {
+      return {
+        pulse: false,
+      };
+    },
+    computed: {
+      ...mapGetters({
+        collapse: "settings/collapse",
+        visitedRoutes: "tabsBar/visitedRoutes",
+        device: "settings/device",
+        routes: "routes/routes",
+      }),
+    },
+    methods: {
+      ...mapActions({
+        changeCollapse: "settings/changeCollapse",
+      }),
+      handleCollapse() {
+        this.changeCollapse();
+      },
+      async refreshRoute() {
+        this.$baseEventBus.$emit("reload-routerview");
+        this.pulse = true;
+        setTimeout(() => {
+          this.pulse = false;
+        }, 1000);
+      },
+    },
+  };
+</script>
+
+<style lang="scss" scoped>
+  .nav-bar-container {
+    position: relative;
+    height: $base-nav-bar-height;
+    padding-right: $base-padding;
+    padding-left: $base-padding;
+    overflow: hidden;
+    user-select: none;
+    background: $base-color-white;
+    box-shadow: $base-box-shadow;
+
+    .left-panel {
+      display: flex;
+      align-items: center;
+      justify-items: center;
+      height: $base-nav-bar-height;
+
+      .fold-unfold {
+        font-size: 20px;
+        color: $base-color-gray;
+        cursor: pointer;
+      }
+
+      ::v-deep {
+        .breadcrumb-container {
+          margin-left: 10px;
+        }
+      }
+    }
+
+    .right-panel {
+      display: flex;
+      align-content: center;
+      align-items: center;
+      justify-content: flex-end;
+      height: $base-nav-bar-height;
+
+      ::v-deep {
+        svg {
+          width: 1em;
+          height: 1em;
+          margin-right: 15px;
+          font-size: $base-font-size-big;
+          color: $base-color-gray;
+          cursor: pointer;
+          fill: $base-color-gray;
+        }
+
+        button {
+          svg {
+            margin-right: 0;
+            color: $base-color-white;
+            cursor: pointer;
+            fill: $base-color-white;
+          }
+        }
+
+        .el-badge {
+          margin-right: 15px;
+        }
+      }
+    }
+  }
+</style>

+ 261 - 0
src/layouts/components/ThemeBar/index.vue

@@ -0,0 +1,261 @@
+<template>
+  <span v-if="themeBar">
+    <vab-icon
+      title="主题配置"
+      :icon="['fas', 'palette']"
+      @click="handleOpenThemeBar"
+    />
+    <div class="theme-bar-setting">
+      <div @click="handleOpenThemeBar">
+        <vab-icon :icon="['fas', 'palette']" />
+        <p>主题配置</p>
+      </div>
+      <div @click="handleGetCode">
+        <vab-icon :icon="['fas', 'laptop-code']"></vab-icon>
+        <p>拷贝源码</p>
+      </div>
+    </div>
+
+    <el-drawer
+      title="主题配置"
+      :visible.sync="drawerVisible"
+      direction="rtl"
+      append-to-body
+      size="470px"
+    >
+      <el-scrollbar style="height: 94vh; overflow: hidden">
+        <div class="el-drawer__body">
+          <el-form ref="form" :model="theme">
+            <el-form-item label="主题">
+              <el-radio-group v-model="theme.name">
+                <el-radio-button label="default">默认</el-radio-button>
+                <el-radio-button label="ocean">海洋之心</el-radio-button>
+                <el-radio-button label="green">绿荫草场</el-radio-button>
+                <el-radio-button label="glory">荣耀典藏</el-radio-button>
+                <el-radio-button label="dark">暗黑之子</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="布局">
+              <el-radio-group v-model="theme.layout">
+                <el-radio-button label="vertical">纵向布局</el-radio-button>
+                <el-radio-button label="horizontal">横向布局</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="头部">
+              <el-radio-group v-model="theme.header">
+                <el-radio-button label="fixed">固定头部</el-radio-button>
+                <el-radio-button label="noFixed">不固定头部</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="多标签">
+              <el-radio-group v-model="theme.tabsBar">
+                <el-radio-button label="true">开启</el-radio-button>
+                <el-radio-button label="false">不开启</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item>
+              <el-button @click="handleSetDfaultTheme">恢复默认</el-button>
+              <el-button type="primary" @click="handleSaveTheme">
+                保存
+              </el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-scrollbar>
+    </el-drawer>
+  </span>
+</template>
+
+<script>
+  import variables from "@/styles/variables.scss";
+  import { mapActions, mapGetters } from "vuex";
+  import { layout as defaultLayout } from "@/config/settings";
+  export default {
+    name: "ThemeBar",
+    data() {
+      return {
+        drawerVisible: false,
+        theme: {
+          name: "default",
+          layout: "",
+          header: "",
+          tabsBar: "",
+        },
+      };
+    },
+    computed: {
+      ...mapGetters({
+        layout: "settings/layout",
+        header: "settings/header",
+        tabsBar: "settings/tabsBar",
+        themeBar: "settings/themeBar",
+      }),
+    },
+    created() {
+      this.$baseEventBus.$on("theme", () => {
+        this.handleOpenThemeBar();
+      });
+      const theme = localStorage.getItem("vue-admin-beautiful-theme");
+      if (null !== theme) {
+        this.theme = JSON.parse(theme);
+        this.handleSetTheme();
+      } else {
+        this.theme.layout = this.layout;
+        this.theme.header = this.header;
+        this.theme.tabsBar = this.tabsBar;
+      }
+    },
+    methods: {
+      ...mapActions({
+        changeLayout: "settings/changeLayout",
+        changeHeader: "settings/changeHeader",
+        changeTabsBar: "settings/changeTabsBar",
+      }),
+      handleIsMobile() {
+        return document.body.getBoundingClientRect().width - 1 < 992;
+      },
+      handleOpenThemeBar() {
+        this.drawerVisible = true;
+      },
+      handleSetTheme() {
+        let { name, layout, header, tabsBar } = this.theme;
+        localStorage.setItem(
+          "vue-admin-beautiful-theme",
+          `{
+            "name":"${name}",
+            "layout":"${layout}",
+            "header":"${header}",
+            "tabsBar":"${tabsBar}"
+          }`
+        );
+        if (!this.handleIsMobile()) this.changeLayout(layout);
+        this.changeHeader(header);
+        this.changeTabsBar(tabsBar);
+        document.getElementsByTagName(
+          "body"
+        )[0].className = `vue-admin-beautiful-theme-${name}`;
+        this.drawerVisible = false;
+      },
+      handleSaveTheme() {
+        this.handleSetTheme();
+      },
+      handleSetDfaultTheme() {
+        let { name } = this.theme;
+        document
+          .getElementsByTagName("body")[0]
+          .classList.remove(`vue-admin-beautiful-theme-${name}`);
+        localStorage.removeItem("vue-admin-beautiful-theme");
+        this.$refs["form"].resetFields();
+        Object.assign(this.$data, this.$options.data());
+        this.changeHeader(defaultLayout);
+        this.theme.name = "default";
+        this.theme.layout = this.layout;
+        this.theme.header = this.header;
+        this.theme.tabsBar = this.tabsBar;
+        this.drawerVisible = false;
+      },
+      handleGetCode() {
+        const url =
+          "https://github.com/chuzhixin/vue-admin-beautiful/tree/master/src/views";
+        let path = this.$route.path + "/index.vue";
+        if (path === "/vab/menu1/menu1-1/menu1-1-1/index.vue") {
+          path = "/vab/nested/menu1/menu1-1/menu1-1-1/index.vue";
+        }
+        if (path === "/vab/icon/awesomeIcon/index.vue") {
+          path = "/vab/icon/index.vue";
+        }
+        if (path === "/vab/icon/remixIcon/index.vue") {
+          path = "/vab/icon/remixIcon.vue";
+        }
+        if (path === "/vab/icon/colorfulIcon/index.vue") {
+          path = "/vab/icon/colorfulIcon.vue";
+        }
+        if (path === "/vab/table/comprehensiveTable/index.vue") {
+          path = "/vab/table/index.vue";
+        }
+        if (path === "/vab/table/inlineEditTable/index.vue") {
+          path = "/vab/table/inlineEditTable.vue";
+        }
+        window.open(url + path);
+      },
+    },
+  };
+</script>
+
+<style lang="scss" scoped>
+  @mixin right-bar {
+    position: fixed;
+    right: 0;
+    z-index: $base-z-index;
+    width: 60px;
+    min-height: 60px;
+    text-align: center;
+    cursor: pointer;
+    background: $base-color-blue;
+    border-radius: $base-border-radius;
+
+    > div {
+      padding-top: 10px;
+      border-bottom: 0 !important;
+
+      &:hover {
+        opacity: 0.9;
+      }
+
+      & + div {
+        border-top: 1px solid $base-color-white;
+      }
+
+      p {
+        padding: 0;
+        margin: 0;
+        font-size: $base-font-size-small;
+        line-height: 30px;
+        color: $base-color-white;
+      }
+    }
+  }
+
+  .theme-bar-setting {
+    @include right-bar;
+
+    top: calc((100vh - 110px) / 2);
+
+    ::v-deep {
+      svg:not(:root).svg-inline--fa {
+        display: block;
+        margin-right: auto;
+        margin-left: auto;
+        color: $base-color-white;
+      }
+
+      .svg-icon {
+        display: block;
+        margin-right: auto;
+        margin-left: auto;
+        font-size: 20px;
+        color: $base-color-white;
+        fill: $base-color-white;
+      }
+    }
+  }
+
+  .el-drawer__body {
+    padding: 20px;
+  }
+</style>
+<style lang="scss">
+  .el-drawer__wrapper {
+    outline: none !important;
+
+    * {
+      outline: none !important;
+    }
+  }
+
+  .vab-color-picker {
+    .el-color-dropdown__link-btn {
+      display: none;
+    }
+  }
+</style>

+ 8 - 4
src/layouts/components/index.js

@@ -15,12 +15,16 @@ export { default as Logo } from "./Logo";
 export { default as Avatar } from "./Avatar";
 //本地化广告组件,如不需要全局搜索ad组件删掉即可
 export { default as Ad } from "./Ad";
-export { default as AppMain } from "zx-layouts/AppMain";
+//本地化AppMain组件
+export { default as AppMain } from "./AppMain";
 export { default as TabsBar } from "zx-layouts/TabsBar";
 export { default as SideBar } from "zx-layouts/SideBar";
-export { default as Breadcrumb } from "zx-layouts/Breadcrumb";
+//本地化Breadcrumb组件
+export { default as Breadcrumb } from "./Breadcrumb";
 export { default as FullScreenBar } from "zx-layouts/FullScreenBar";
 export { default as ErrorLog } from "zx-layouts/ErrorLog";
-export { default as ThemeBar } from "zx-layouts/ThemeBar";
+//本地化ThemeBar组件
+export { default as ThemeBar } from "./ThemeBar";
 export { default as TopBar } from "zx-layouts/TopBar";
-export { default as NavBar } from "zx-layouts/NavBar";
+//本地化NavBar组件
+export { default as NavBar } from "./NavBar";

+ 0 - 8
src/plugins/vabKeel.js

@@ -1,8 +0,0 @@
-import { Heading, Img, Keel, Text } from "zx-keel";
-import "zx-keel/dist/zx-keel.css";
-
-const VabKeel = Keel;
-const VabKeelHeading = Heading;
-const VabKeelImg = Img;
-const VabKeelText = Text;
-export { VabKeel, VabKeelHeading, VabKeelImg, VabKeelText };

+ 2 - 2
src/router/index.js

@@ -398,12 +398,12 @@ const router = new VueRouter({
   routes: constantRoutes,
 });
 //注释的地方是允许路由重复点击,如果你觉得框架路由跳转规范太过严格可选择放开
-/* const originalPush = VueRouter.prototype.push;
+const originalPush = VueRouter.prototype.push;
 VueRouter.prototype.push = function push(location, onResolve, onReject) {
   if (onResolve || onReject)
     return originalPush.call(this, location, onResolve, onReject);
   return originalPush.call(this, location).catch((err) => err);
-}; */
+};
 
 export function resetRouter() {
   router.matcher = new VueRouter({

+ 1 - 3
src/store/modules/settings.js

@@ -5,7 +5,7 @@
 
 import defaultSettings from "@/config/settings";
 
-const { tabsBar, logo, layout, header, themeBar, skeleton } = defaultSettings;
+const { tabsBar, logo, layout, header, themeBar } = defaultSettings;
 const theme =
   JSON.parse(localStorage.getItem("vue-admin-beautiful-theme")) || "";
 const state = {
@@ -14,7 +14,6 @@ const state = {
   collapse: false,
   layout: theme.layout || layout,
   header: theme.header || header,
-  skeleton,
   device: "desktop",
   themeBar,
 };
@@ -26,7 +25,6 @@ const getters = {
   logo: (state) => state.logo,
   tabsBar: (state) => state.tabsBar,
   themeBar: (state) => state.themeBar,
-  skeleton: (state) => state.skeleton,
 };
 const mutations = {
   changeLayout: (state, layout) => {