Browse Source

🚀 feat: add layouts

chuzhixin 2 years ago
parent
commit
ae66791f74
45 changed files with 1557 additions and 501 deletions
  1. 7 0
      .eslintignore
  2. 1 1
      .gitignore
  3. 13 0
      layouts/Permissions/index.js
  4. 13 0
      layouts/Permissions/permissions.js
  5. 65 0
      layouts/VabColorfullIcon/index.vue
  6. 128 0
      layouts/VabErrorLog/index.vue
  7. 53 0
      layouts/VabFullScreenBar/index.vue
  8. 75 0
      layouts/VabGithubCorner/index.vue
  9. 20 0
      layouts/VabQueryForm/VabQueryFormBottomPanel.vue
  10. 25 0
      layouts/VabQueryForm/VabQueryFormLeftPanel.vue
  11. 25 0
      layouts/VabQueryForm/VabQueryFormRightPanel.vue
  12. 20 0
      layouts/VabQueryForm/VabQueryFormTopPanel.vue
  13. 63 0
      layouts/VabQueryForm/index.vue
  14. 69 0
      layouts/VabRemixIcon/index.vue
  15. 84 0
      layouts/VabSideBar/components/VabMenuItem.vue
  16. 108 0
      layouts/VabSideBar/components/VabSideBarItem.vue
  17. 60 0
      layouts/VabSideBar/components/VabSubmenu.vue
  18. 141 0
      layouts/VabSideBar/index.vue
  19. 293 0
      layouts/VabTabsBar/index.vue
  20. 224 0
      layouts/VabTopBar/index.vue
  21. 35 0
      layouts/index.js
  22. 4 0
      layouts/package.json
  23. 16 0
      layouts/prettier.config.js
  24. 1 1
      mock/controller/changeLog.js
  25. 0 24
      mock/controller/router.js
  26. 4 21
      package.json
  27. 0 16
      plopfile.js
  28. 1 9
      src/components/VabCharge/index.vue
  29. 1 1
      src/layouts/export.js
  30. 1 3
      src/plugins/index.js
  31. 1 1
      src/plugins/support.js
  32. 0 3
      src/plugins/vabMagnifier.js
  33. 0 5
      src/plugins/vabMarkdownEditor.js
  34. 0 3
      src/plugins/vabPlayer.js
  35. 0 4
      src/plugins/vabQuill.js
  36. 0 4
      src/plugins/vabVerify.js
  37. 0 34
      src/router/index.js
  38. 4 20
      src/views/index/index.vue
  39. 0 156
      src/views/vab/editor/index.vue
  40. 0 35
      src/views/vab/magnifier/index.vue
  41. 0 51
      src/views/vab/markdownEditor/index.vue
  42. 0 73
      src/views/vab/player/index.vue
  43. 0 35
      src/views/vab/verify/index.vue
  44. 1 0
      vab-icon
  45. 1 1
      vue.config.js

+ 7 - 0
.eslintignore

@@ -0,0 +1,7 @@
+src/assets
+src/icons
+public
+dist
+node_modules
+vab-icon
+layouts

+ 1 - 1
.gitignore

@@ -17,7 +17,7 @@ yarn-error.log*
 public/video
 *.zip
 *.7z
-/src/layouts/components/zx-layouts
+/src/layouts/components/layouts
 /zx-templates
 /package-lock.json
 /src/styles/themes/green.scss

+ 13 - 0
layouts/Permissions/index.js

@@ -0,0 +1,13 @@
+import permissions from './permissions'
+
+const install = function (Vue) {
+  Vue.directive('permissions', permissions)
+}
+
+if (window.Vue) {
+  window['permissions'] = permissions
+  Vue.use(install)
+}
+
+permissions.install = install
+export default permissions

+ 13 - 0
layouts/Permissions/permissions.js

@@ -0,0 +1,13 @@
+import store from '@/store'
+
+export default {
+  inserted(element, binding) {
+    const { value } = binding
+    const permissions = store.getters['user/permissions']
+    if (value && value instanceof Array && value.length > 0) {
+      const hasPermission = permissions.some((role) => value.includes(role))
+      if (!hasPermission)
+        element.parentNode && element.parentNode.removeChild(element)
+    }
+  },
+}

+ 65 - 0
layouts/VabColorfullIcon/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <img
+    v-if="isExternal"
+    :src="styleExternalIcon"
+    class="svg-external-icon svg-icon"
+    v-on="$listeners"
+  />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+  import { isExternal } from '@/utils/validate'
+
+  export default {
+    name: 'VabColorfulIcon',
+    props: {
+      iconClass: {
+        type: String,
+        required: true,
+      },
+      className: {
+        type: String,
+        default: '',
+      },
+    },
+    computed: {
+      isExternal() {
+        return isExternal(this.iconClass)
+      },
+      iconName() {
+        return `#colorful-icon-${this.iconClass}`
+      },
+      svgClass() {
+        if (this.className) {
+          return 'svg-icon ' + this.className
+        } else {
+          return 'svg-icon'
+        }
+      },
+      styleExternalIcon() {
+        return this.iconClass
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .svg-icon {
+    width: 1em;
+    height: 1em;
+    overflow: hidden;
+    vertical-align: -0.15em;
+    fill: currentColor;
+
+    &:hover {
+      opacity: 0.8;
+    }
+  }
+
+  .svg-external-icon {
+    display: inline-block;
+  }
+</style>

+ 128 - 0
layouts/VabErrorLog/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <div v-if="errorLogs.length > 0">
+    <el-badge
+      :value="errorLogs.length"
+      @click.native="dialogTableVisible = true"
+    >
+      <el-button type="danger">
+        <vab-icon :icon="['fas', 'bug']" />
+      </el-button>
+    </el-badge>
+
+    <el-dialog
+      :visible.sync="dialogTableVisible"
+      append-to-body
+      width="70%"
+      title="vue-admin-beautiful异常捕获(温馨提示:错误必须解决)"
+    >
+      <el-table :data="errorLogs">
+        <el-table-column label="报错路由">
+          <template slot-scope="{ row }">
+            <a :href="row.url" target="_blank">
+              <el-tag type="success">{{ row.url }}</el-tag>
+            </a>
+          </template>
+        </el-table-column>
+        <el-table-column label="错误信息">
+          <template slot-scope="{ row }">
+            <el-tag type="danger">{{ decodeUnicode(row.err.message) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="错误详情" width="120">
+          <template slot-scope="scope">
+            <el-popover placement="top-start" trigger="hover">
+              <div style="color: red">
+                {{ scope.row.err.stack }}
+              </div>
+              <el-button slot="reference">查看</el-button>
+            </el-popover>
+          </template>
+        </el-table-column>
+        <el-table-column width="380" label="操作">
+          <template slot-scope="{ row }">
+            <a
+              v-for="(item, index) in searchList"
+              :key="index"
+              :href="item.url + decodeUnicode(row.err.message)"
+              target="_blank"
+            >
+              <el-button style="margin-left: 5px" type="primary">
+                <vab-icon :icon="['fas', 'search']" />
+                {{ item.title }}
+              </el-button>
+            </a>
+          </template>
+        </el-table-column>
+      </el-table>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogTableVisible = false">取 消</el-button>
+        <el-button type="danger" icon="el-icon-delete" @click="clearAll">
+          暂不显示
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import { abbreviation, title } from '@/config'
+  import { mapGetters } from 'vuex'
+
+  export default {
+    name: 'VabErrorLog',
+
+    data() {
+      return {
+        dialogTableVisible: false,
+        title: title,
+        abbreviation: abbreviation,
+        searchList: [
+          {
+            title: '百度搜索',
+            url: 'https://www.baidu.com/baidu?wd=',
+          },
+          {
+            title: '谷歌搜索',
+            url: 'https://www.google.com/search?q=',
+          },
+          {
+            title: 'Magi搜索',
+            url: 'https://magi.com/search?q=',
+          },
+        ],
+      }
+    },
+
+    computed: {
+      ...mapGetters({
+        errorLogs: 'errorLog/errorLogs',
+      }),
+    },
+    methods: {
+      clearAll() {
+        this.dialogTableVisible = false
+        this.$store.dispatch('errorLog/clearErrorLog')
+      },
+      decodeUnicode(str) {
+        str = str.replace(/\\/g, '%')
+        str = unescape(str)
+        str = str.replace(/%/g, '\\')
+        str = str.replace(/\\/g, '')
+        return str
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  ::v-deep {
+    .el-badge {
+      .el-button {
+        display: flex;
+        align-items: center;
+        justify-items: center;
+        height: 28px;
+      }
+    }
+  }
+</style>

+ 53 - 0
layouts/VabFullScreenBar/index.vue

@@ -0,0 +1,53 @@
+<template>
+  <span :title="isFullscreen ? '退出全屏' : '进入全屏'">
+    <vab-icon
+      :icon="[
+        'fas',
+        isFullscreen ? 'compress-arrows-alt' : 'expand-arrows-alt',
+      ]"
+      @click="click"
+    ></vab-icon>
+  </span>
+</template>
+
+<script>
+  import screenfull from 'screenfull'
+
+  export default {
+    name: 'VabFullScreenBar',
+    data() {
+      return {
+        isFullscreen: false,
+      }
+    },
+    mounted() {
+      this.init()
+    },
+    beforeDestroy() {
+      this.destroy()
+    },
+    methods: {
+      click() {
+        if (!screenfull.isEnabled) {
+          this.$baseMessage('开启全屏失败', 'error')
+          return false
+        }
+        screenfull.toggle()
+        this.$emit('refresh')
+      },
+      change() {
+        this.isFullscreen = screenfull.isFullscreen
+      },
+      init() {
+        if (screenfull.isEnabled) {
+          screenfull.on('change', this.change)
+        }
+      },
+      destroy() {
+        if (screenfull.isEnabled) {
+          screenfull.off('change', this.change)
+        }
+      },
+    },
+  }
+</script>

File diff suppressed because it is too large
+ 75 - 0
layouts/VabGithubCorner/index.vue


+ 20 - 0
layouts/VabQueryForm/VabQueryFormBottomPanel.vue

@@ -0,0 +1,20 @@
+<template>
+  <el-col :span="24">
+    <div class="bottom-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  export default {
+    name: "VabQueryFormBottomPanel",
+    props: {},
+    data() {
+      return {};
+    },
+    created() {},
+    mounted() {},
+    methods: {},
+  };
+</script>

+ 25 - 0
layouts/VabQueryForm/VabQueryFormLeftPanel.vue

@@ -0,0 +1,25 @@
+<template>
+  <el-col :xs="24" :sm="24" :md="24" :lg="span" :xl="span">
+    <div class="left-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  export default {
+    name: 'VabQueryFormLeftPanel',
+    props: {
+      span: {
+        type: Number,
+        default: 14,
+      },
+    },
+    data() {
+      return {}
+    },
+    created() {},
+    mounted() {},
+    methods: {},
+  }
+</script>

+ 25 - 0
layouts/VabQueryForm/VabQueryFormRightPanel.vue

@@ -0,0 +1,25 @@
+<template>
+  <el-col :xs="24" :sm="24" :md="24" :lg="span" :xl="span">
+    <div class="right-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  export default {
+    name: 'VabQueryFormRightPanel',
+    props: {
+      span: {
+        type: Number,
+        default: 10,
+      },
+    },
+    data() {
+      return {}
+    },
+    created() {},
+    mounted() {},
+    methods: {},
+  }
+</script>

+ 20 - 0
layouts/VabQueryForm/VabQueryFormTopPanel.vue

@@ -0,0 +1,20 @@
+<template>
+  <el-col :span="24">
+    <div class="top-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  export default {
+    name: 'VabQueryFormTopPanel',
+    props: {},
+    data() {
+      return {}
+    },
+    created() {},
+    mounted() {},
+    methods: {},
+  }
+</script>

+ 63 - 0
layouts/VabQueryForm/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <el-row :gutter="0" class="vab-query-form">
+    <slot></slot>
+  </el-row>
+</template>
+
+<script>
+  export default {
+    name: 'VabQueryForm',
+    props: {},
+    data() {
+      return {}
+    },
+    created() {},
+    mounted() {},
+    methods: {},
+  }
+</script>
+
+<style lang="scss" scoped>
+  @mixin panel {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    justify-content: flex-start;
+  }
+
+  .vab-query-form {
+    margin-bottom: 10px;
+
+    ::v-deep {
+      .top-panel {
+        @include panel;
+      }
+
+      .bottom-panel {
+        @include panel;
+
+        padding-top: 14px;
+        border-top: 1px solid #dcdfe6;
+      }
+
+      .left-panel {
+        @include panel;
+
+        > .el-button,
+        .el-form-item {
+          margin: 5px;
+        }
+      }
+
+      .right-panel {
+        @include panel;
+
+        justify-content: flex-end;
+
+        .el-form-item {
+          margin: 5px;
+        }
+      }
+    }
+  }
+</style>

+ 69 - 0
layouts/VabRemixIcon/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div
+    v-if="isExternal"
+    :style="styleExternalIcon"
+    class="svg-external-icon svg-icon"
+    v-on="$listeners"
+  />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+  import { isExternal } from '@/utils/validate'
+
+  export default {
+    name: 'VabRemixIcon',
+    props: {
+      iconClass: {
+        type: String,
+        required: true,
+      },
+      className: {
+        type: String,
+        default: '',
+      },
+    },
+    computed: {
+      isExternal() {
+        return isExternal(this.iconClass)
+      },
+      iconName() {
+        return `#remix-icon-${this.iconClass}`
+      },
+      svgClass() {
+        if (this.className) {
+          return 'svg-icon ' + this.className
+        } else {
+          return 'svg-icon'
+        }
+      },
+      styleExternalIcon() {
+        return {
+          mask: `url(${this.iconClass}) no-repeat 50% 50%`,
+          '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`,
+        }
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .svg-icon {
+    width: 1.125em;
+    height: 1.125em;
+    overflow: hidden;
+    fill: currentColor;
+
+    &:hover {
+      opacity: 0.8;
+    }
+  }
+
+  .svg-external-icon {
+    display: inline-block;
+    background-color: currentColor;
+    mask-size: cover !important;
+  }
+</style>

+ 84 - 0
layouts/VabSideBar/components/VabMenuItem.vue

@@ -0,0 +1,84 @@
+<template>
+  <el-menu-item :index="handlePath(routeChildren.path)" @click="handleLink">
+    <vab-icon
+      v-if="routeChildren.meta.icon"
+      :icon="['fas', routeChildren.meta.icon]"
+      class="vab-fas-icon"
+    />
+    <span>{{ routeChildren.meta.title }}</span>
+    <el-tag
+      v-if="routeChildren.meta && routeChildren.meta.badge"
+      type="danger"
+      effect="dark"
+    >
+      {{ routeChildren.meta.badge }}
+    </el-tag>
+  </el-menu-item>
+</template>
+
+<script>
+  import { isExternal } from '@/utils/validate'
+  import path from 'path'
+
+  export default {
+    name: 'VabMenuItem',
+    props: {
+      routeChildren: {
+        type: Object,
+        default() {
+          return null
+        },
+      },
+      item: {
+        type: Object,
+        default() {
+          return null
+        },
+      },
+      fullPath: {
+        type: String,
+        default: '',
+      },
+    },
+    methods: {
+      handlePath(routePath) {
+        if (isExternal(routePath)) {
+          return routePath
+        }
+        if (isExternal(this.fullPath)) {
+          return this.fullPath
+        }
+        return path.resolve(this.fullPath, routePath)
+      },
+      handleLink() {
+        const routePath = this.routeChildren.path
+        const target = this.routeChildren.meta.target
+
+        if (target === '_blank') {
+          if (isExternal(routePath)) {
+            window.open(routePath)
+          } else if (isExternal(this.fullPath)) {
+            window.open(this.fullPath)
+          } else if (
+            this.$route.path !== path.resolve(this.fullPath, routePath)
+          ) {
+            let routeData = this.$router.resolve(
+              path.resolve(this.fullPath, routePath)
+            )
+            window.open(routeData.href)
+          }
+        } else {
+          if (isExternal(routePath)) {
+            window.location.href = routePath
+          } else if (isExternal(this.fullPath)) {
+            window.location.href = this.fullPath
+          } else if (
+            this.$route.path !== path.resolve(this.fullPath, routePath)
+          ) {
+            this.$router.push(path.resolve(this.fullPath, routePath))
+          }
+        }
+      },
+    },
+  }
+</script>

+ 108 - 0
layouts/VabSideBar/components/VabSideBarItem.vue

@@ -0,0 +1,108 @@
+<template>
+  <component
+    :is="menuComponent"
+    v-if="!item.hidden"
+    :item="item"
+    :full-path="fullPath"
+    :route-children="routeChildren"
+  >
+    <template v-if="item.children && item.children.length">
+      <vab-side-bar-item
+        v-for="route in item.children"
+        :key="route.path"
+        :full-path="handlePath(route.path)"
+        :item="route"
+      />
+    </template>
+  </component>
+</template>
+
+<script>
+  import { isExternal } from '@/utils/validate'
+  import path from 'path'
+
+  export default {
+    name: 'VabSideBarItem',
+    props: {
+      item: {
+        type: Object,
+        required: true,
+      },
+      fullPath: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      this.onlyOneChild = null
+      return {}
+    },
+    computed: {
+      menuComponent() {
+        if (
+          this.handleChildren(this.item.children, this.item) &&
+          (!this.routeChildren.children ||
+            this.routeChildren.notShowChildren) &&
+          !this.item.alwaysShow
+        ) {
+          return 'VabMenuItem'
+        } else {
+          return 'VabSubmenu'
+        }
+      },
+    },
+    methods: {
+      handleChildren(children = [], parent) {
+        if (children === null) children = []
+        const showChildren = children.filter((item) => {
+          if (item.hidden) {
+            return false
+          } else {
+            this.routeChildren = item
+            return true
+          }
+        })
+        if (showChildren.length === 1) {
+          return true
+        }
+
+        if (showChildren.length === 0) {
+          this.routeChildren = {
+            ...parent,
+            path: '',
+            notShowChildren: true,
+          }
+          return true
+        }
+        return false
+      },
+      handlePath(routePath) {
+        if (isExternal(routePath)) {
+          return routePath
+        }
+        if (isExternal(this.fullPath)) {
+          return this.fullPath
+        }
+        return path.resolve(this.fullPath, routePath)
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .vab-nav-icon {
+    margin-right: 4px;
+  }
+
+  ::v-deep {
+    .el-tag {
+      float: right;
+      height: 16px;
+      padding-right: 4px;
+      padding-left: 4px;
+      margin-top: calc((#{$base-menu-item-height} - 16px) / 2);
+      line-height: 16px;
+      border: 0;
+    }
+  }
+</style>

+ 60 - 0
layouts/VabSideBar/components/VabSubmenu.vue

@@ -0,0 +1,60 @@
+<template>
+  <el-submenu
+    ref="subMenu"
+    :index="handlePath(item.path)"
+    :popper-append-to-body="false"
+  >
+    <template slot="title">
+      <vab-icon
+        v-if="item.meta && item.meta.icon"
+        :icon="['fas', item.meta.icon]"
+        class="vab-fas-icon"
+      />
+      <vab-remix-icon
+        v-if="item.meta && item.meta.remixIcon"
+        :icon-class="item.meta.remixIcon"
+        class="vab-remix-icon"
+      />
+      <span>{{ item.meta.title }}</span>
+    </template>
+    <slot />
+  </el-submenu>
+</template>
+
+<script>
+  import { isExternal } from '@/utils/validate'
+  import path from 'path'
+
+  export default {
+    name: 'VabSubmenu',
+    props: {
+      routeChildren: {
+        type: Object,
+        default() {
+          return null
+        },
+      },
+      item: {
+        type: Object,
+        default() {
+          return null
+        },
+      },
+      fullPath: {
+        type: String,
+        default: '',
+      },
+    },
+    methods: {
+      handlePath(routePath) {
+        if (isExternal(routePath)) {
+          return routePath
+        }
+        if (isExternal(this.fullPath)) {
+          return this.fullPath
+        }
+        return path.resolve(this.fullPath, routePath)
+      },
+    },
+  }
+</script>

+ 141 - 0
layouts/VabSideBar/index.vue

@@ -0,0 +1,141 @@
+<template>
+  <el-scrollbar class="side-bar-container" :class="{ 'is-collapse': collapse }">
+    <vab-logo />
+    <el-menu
+      :background-color="variables['menu-background']"
+      :text-color="variables['menu-color']"
+      :active-text-color="variables['menu-color-active']"
+      :default-active="activeMenu"
+      :collapse="collapse"
+      :collapse-transition="false"
+      :default-openeds="defaultOpens"
+      :unique-opened="uniqueOpened"
+      mode="vertical"
+    >
+      <template v-for="route in routes">
+        <vab-side-bar-item
+          :key="route.path"
+          :full-path="route.path"
+          :item="route"
+        />
+      </template>
+    </el-menu>
+  </el-scrollbar>
+</template>
+<script>
+  import variables from '@/styles/variables.scss'
+  import { mapGetters } from 'vuex'
+  import { defaultOopeneds, uniqueOpened } from '@/config'
+
+  export default {
+    name: 'VabSideBar',
+    data() {
+      return {
+        uniqueOpened,
+      }
+    },
+    computed: {
+      ...mapGetters({
+        collapse: 'settings/collapse',
+        routes: 'routes/routes',
+      }),
+      defaultOpens() {
+        if (this.collapse) {
+        }
+        return defaultOopeneds
+      },
+      activeMenu() {
+        const route = this.$route
+        const { meta, path } = route
+        if (meta.activeMenu) {
+          return meta.activeMenu
+        }
+        return path
+      },
+      variables() {
+        return variables
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  @mixin active {
+    &:hover {
+      color: $base-color-white;
+      background-color: $base-menu-background-active !important;
+    }
+
+    &.is-active {
+      color: $base-color-white;
+      background-color: $base-menu-background-active !important;
+    }
+  }
+
+  .side-bar-container {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    z-index: $base-z-index;
+    width: $base-left-menu-width;
+    height: 100vh;
+    overflow: hidden;
+    background: $base-menu-background;
+    box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
+    transition: width $base-transition-time;
+
+    &.is-collapse {
+      width: $base-left-menu-width-min;
+      border-right: 0;
+
+      ::v-deep {
+        .el-menu {
+          transition: width $base-transition-time;
+        }
+
+        .el-menu--collapse {
+          border-right: 0;
+
+          .el-submenu__icon-arrow {
+            right: 10px;
+            margin-top: -3px;
+          }
+        }
+      }
+    }
+
+    ::v-deep {
+      .el-scrollbar__wrap {
+        overflow-x: hidden;
+      }
+
+      .el-menu {
+        border: 0;
+
+        .vab-fas-icon {
+          padding-right: 3px;
+          font-size: $base-font-size-default;
+        }
+
+        .vab-remix-icon {
+          padding-right: 3px;
+          font-size: $base-font-size-default + 2;
+        }
+      }
+
+      .el-menu-item,
+      .el-submenu__title {
+        height: $base-menu-item-height;
+        overflow: hidden;
+        line-height: $base-menu-item-height;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        vertical-align: middle;
+      }
+
+      .el-menu-item {
+        @include active;
+      }
+    }
+  }
+</style>

+ 293 - 0
layouts/VabTabsBar/index.vue

@@ -0,0 +1,293 @@
+<template>
+  <div id="tabs-bar-container" class="tabs-bar-container">
+    <el-tabs
+      v-model="tabActive"
+      type="card"
+      class="tabs-content"
+      @tab-click="handleTabClick"
+      @tab-remove="handleTabRemove"
+    >
+      <el-tab-pane
+        v-for="item in visitedRoutes"
+        :key="item.path"
+        :label="item.meta.title"
+        :name="item.path"
+        :closable="!isAffix(item)"
+      ></el-tab-pane>
+    </el-tabs>
+
+    <el-dropdown @command="handleCommand">
+      <span style="cursor: pointer">
+        更多操作
+        <i class="el-icon-arrow-down el-icon--right"></i>
+      </span>
+      <el-dropdown-menu slot="dropdown" class="tabs-more">
+        <el-dropdown-item command="closeOtherstabs">
+          <vab-icon :icon="['fas', 'times-circle']" />
+          关闭其他
+        </el-dropdown-item>
+        <el-dropdown-item command="closeLefttabs">
+          <vab-icon :icon="['fas', 'arrow-alt-circle-left']"></vab-icon>
+          关闭左侧
+        </el-dropdown-item>
+        <el-dropdown-item command="closeRighttabs">
+          <vab-icon :icon="['fas', 'arrow-alt-circle-right']"></vab-icon>
+          关闭右侧
+        </el-dropdown-item>
+        <el-dropdown-item command="closeAlltabs">
+          <vab-icon :icon="['fas', 'ban']"></vab-icon>
+          关闭全部
+        </el-dropdown-item>
+      </el-dropdown-menu>
+    </el-dropdown>
+  </div>
+</template>
+
+<script>
+  import path from 'path'
+  import { mapGetters } from 'vuex'
+
+  export default {
+    name: 'VabTabsBar',
+    data() {
+      return {
+        affixtabs: [],
+        tabActive: '',
+      }
+    },
+
+    computed: {
+      ...mapGetters({
+        visitedRoutes: 'tabsBar/visitedRoutes',
+        routes: 'routes/routes',
+      }),
+    },
+    watch: {
+      $route: {
+        handler(route) {
+          this.inittabs()
+          this.addtabs()
+          let tabActive = ''
+          this.visitedRoutes.forEach((item, index) => {
+            if (item.path === this.$route.path) {
+              tabActive = item.path
+            }
+          })
+          this.tabActive = tabActive
+        },
+        immediate: true,
+      },
+    },
+    mounted() {
+      //console.log(this.visitedRoutes);
+    },
+    methods: {
+      async handleTabRemove(tabActive) {
+        let view
+        this.visitedRoutes.forEach((item, index) => {
+          if (tabActive == item.path) {
+            view = item
+          }
+        })
+        const { visitedRoutes } = await this.$store.dispatch(
+          'tabsBar/delRoute',
+          view
+        )
+        if (this.isActive(view)) {
+          this.toLastTag(visitedRoutes, view)
+        }
+      },
+      handleTabClick(tab) {
+        const route = this.visitedRoutes.filter((item, index) => {
+          if (tab.index == index) return item
+        })[0]
+        if (this.$route.path !== route.path) {
+          this.$router.push({
+            path: route.path,
+            query: route.query,
+            fullPath: route.fullPath,
+          })
+        } else {
+          return false
+        }
+      },
+      isActive(route) {
+        return route.path === this.$route.path
+      },
+      isAffix(tag) {
+        return tag.meta && tag.meta.affix
+      },
+      filterAffixtabs(routes, basePath = '/') {
+        let tabs = []
+        routes.forEach((route) => {
+          if (route.meta && route.meta.affix) {
+            const tagPath = path.resolve(basePath, route.path)
+            tabs.push({
+              fullPath: tagPath,
+              path: tagPath,
+              name: route.name,
+              meta: { ...route.meta },
+            })
+          }
+          if (route.children) {
+            const temptabs = this.filterAffixtabs(route.children, route.path)
+            if (temptabs.length >= 1) {
+              tabs = [...tabs, ...temptabs]
+            }
+          }
+        })
+        return tabs
+      },
+      inittabs() {
+        const affixtabs = (this.affixtabs = this.filterAffixtabs(this.routes))
+        for (const tag of affixtabs) {
+          if (tag.name) {
+            this.$store.dispatch('tabsBar/addVisitedRoute', tag)
+          }
+        }
+      },
+      addtabs() {
+        const { name } = this.$route
+        if (name) {
+          this.$store.dispatch('tabsBar/addVisitedRoute', this.$route)
+        }
+        return false
+      },
+      handleCommand(command) {
+        switch (command) {
+          case 'refreshRoute':
+            this.refreshRoute()
+            break
+          case 'closeOtherstabs':
+            this.closeOtherstabs()
+            break
+          case 'closeLefttabs':
+            this.closeLefttabs()
+            break
+          case 'closeRighttabs':
+            this.closeRighttabs()
+            break
+          case 'closeAlltabs':
+            this.closeAlltabs()
+            break
+        }
+      },
+      async refreshRoute() {
+        this.$baseEventBus.$emit('reloadrouter-view')
+      },
+      async closeSelectedTag(view) {
+        const { visitedRoutes } = await this.$store.dispatch(
+          'tabsBar/delRoute',
+          view
+        )
+        if (this.isActive(view)) {
+          this.toLastTag(visitedRoutes, view)
+        }
+      },
+      async closeOtherstabs() {
+        const view = await this.toThisTag()
+        await this.$store.dispatch('tabsBar/delOthersRoutes', view)
+      },
+      async closeLefttabs() {
+        const view = await this.toThisTag()
+        await this.$store.dispatch('tabsBar/delLeftRoutes', view)
+      },
+      async closeRighttabs() {
+        const view = await this.toThisTag()
+        await this.$store.dispatch('tabsBar/delRightRoutes', view)
+      },
+      async closeAlltabs() {
+        const view = await this.toThisTag()
+        const { visitedRoutes } = await this.$store.dispatch(
+          'tabsBar/delAllRoutes'
+        )
+        if (this.affixtabs.some((tag) => tag.path === view.path)) {
+          return
+        }
+        this.toLastTag(visitedRoutes, view)
+      },
+      toLastTag(visitedRoutes, view) {
+        const latestView = visitedRoutes.slice(-1)[0]
+        if (latestView) {
+          this.$router.push(latestView)
+        } else {
+          this.$router.push('/')
+        }
+      },
+      async toThisTag() {
+        const view = this.visitedRoutes.filter((item, index) => {
+          if (item.path === this.$route.fullPath) {
+            return item
+          }
+        })[0]
+        if (this.$route.path !== view.path) this.$router.push(view)
+        return view
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .tabs-bar-container {
+    position: relative;
+    box-sizing: border-box;
+    display: flex;
+    align-content: center;
+    align-items: center;
+    justify-content: space-between;
+    height: $base-tabs-bar-height;
+    padding-right: $base-padding;
+    padding-left: $base-padding;
+    user-select: none;
+    background: $base-color-white;
+    border-top: 1px solid #f6f6f6;
+
+    ::v-deep {
+      .fold-unfold {
+        margin-right: $base-padding;
+      }
+    }
+
+    .tabs-content {
+      width: calc(100% - 90px);
+      height: $base-tag-item-height;
+
+      ::v-deep {
+        .el-tabs__nav-next,
+        .el-tabs__nav-prev {
+          height: $base-tag-item-height;
+          line-height: $base-tag-item-height;
+        }
+
+        .el-tabs__header {
+          border-bottom: 0;
+
+          .el-tabs__nav {
+            border: 0;
+          }
+
+          .el-tabs__item {
+            box-sizing: border-box;
+            height: $base-tag-item-height;
+            margin-right: 5px;
+            line-height: $base-tag-item-height;
+            border: 1px solid $base-border-color;
+            border-radius: $base-border-radius;
+            transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
+
+            &.is-active {
+              border: 1px solid $base-color-blue;
+            }
+          }
+        }
+      }
+    }
+
+    .more {
+      display: flex;
+      align-content: center;
+      align-items: center;
+      cursor: pointer;
+    }
+  }
+</style>

+ 224 - 0
layouts/VabTopBar/index.vue

@@ -0,0 +1,224 @@
+<template>
+  <div class="top-bar-container">
+    <div class="vab-main">
+      <el-row>
+        <el-col :xl="7" :lg="7" :md="7" :sm="7" :xs="7">
+          <vab-logo />
+        </el-col>
+        <el-col :xl="12" :lg="12" :md="12" :sm="12" :xs="12">
+          <el-menu
+            :background-color="variables['menu-background']"
+            :text-color="variables['menu-color']"
+            :active-text-color="variables['menu-color-active']"
+            :default-active="activeMenu"
+            mode="horizontal"
+            menu-trigger="hover"
+          >
+            <template v-for="route in routes">
+              <vab-side-bar-item
+                v-if="!route.hidden"
+                :key="route.path"
+                :full-path="route.path"
+                :item="route"
+              />
+            </template>
+          </el-menu>
+        </el-col>
+        <el-col :xl="5" :lg="5" :md="5" :sm="5" :xs="5">
+          <div class="right-panel">
+            <vab-error-log />
+            <vab-full-screen-bar @refresh="refreshRoute" />
+            <vab-theme-bar class="hidden-md-and-down" />
+            <vab-icon
+              title="重载路由"
+              :pulse="pulse"
+              :icon="['fas', 'redo']"
+              @click="refreshRoute"
+            />
+            <vab-avatar />
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+  import variables from '@/styles/variables.scss'
+  import { mapGetters } from 'vuex'
+
+  export default {
+    name: 'VabTopBar',
+    data() {
+      return {
+        pulse: false,
+        menuTrigger: 'hover',
+      }
+    },
+    computed: {
+      ...mapGetters({
+        routes: 'routes/routes',
+        visitedRoutes: 'tabsBar/visitedRoutes',
+      }),
+      activeMenu() {
+        const route = this.$route
+        const { meta, path } = route
+        if (meta.activeMenu) {
+          return meta.activeMenu
+        }
+        return path
+      },
+      variables() {
+        return variables
+      },
+    },
+    methods: {
+      async refreshRoute() {
+        this.$baseEventBus.$emit('reload-router-view')
+        this.pulse = true
+        setTimeout(() => {
+          this.pulse = false
+        }, 1000)
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  .top-bar-container {
+    display: flex;
+    align-items: center;
+    justify-items: flex-end;
+    height: $base-top-bar-height;
+    background: $base-menu-background;
+
+    .vab-main {
+      background: $base-menu-background;
+
+      ::v-deep {
+        .el-menu {
+          &.el-menu--horizontal {
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            height: $base-top-bar-height;
+            border-bottom: 0 solid transparent !important;
+
+            .el-menu-item,
+            .el-submenu__title {
+              padding: 0 15px;
+            }
+
+            @media only screen and (max-width: 767px) {
+              .el-menu-item,
+              .el-submenu__title {
+                padding: 0 8px;
+              }
+
+              li:nth-child(4),
+              li:nth-child(5) {
+                display: none !important;
+              }
+            }
+
+            > .el-menu-item {
+              height: $base-top-bar-height;
+              line-height: $base-top-bar-height;
+            }
+
+            > .el-submenu {
+              .el-submenu__title {
+                height: $base-top-bar-height;
+                line-height: $base-top-bar-height;
+              }
+            }
+          }
+
+          svg {
+            width: 1rem;
+            margin-right: 3px;
+          }
+
+          &--horizontal {
+            .el-menu {
+              .el-menu-item,
+              .el-submenu__title {
+                height: $base-menu-item-height;
+                line-height: $base-menu-item-height;
+              }
+            }
+
+            .el-submenu,
+            .el-menu-item {
+              &.is-active {
+                background-color: $base-color-blue !important;
+                border-bottom: 0 solid transparent !important;
+
+                .el-submenu__title {
+                  border-bottom: 0 solid transparent !important;
+                }
+              }
+            }
+
+            > .el-menu-item {
+              .el-tag {
+                margin-top: calc(#{$base-top-bar-height} / 2 - 7.5px);
+                margin-left: 5px;
+              }
+
+              @media only screen and (max-width: 1199px) {
+                .el-tag {
+                  display: none;
+                }
+              }
+
+              &.is-active {
+                background-color: transparent !important;
+                border-bottom: 3px solid $base-color-blue !important;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .right-panel {
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      height: $base-top-bar-height;
+
+      ::v-deep {
+        .user-name {
+          color: rgba($base-color-white, 0.9);
+        }
+
+        .user-name + i {
+          color: rgba($base-color-white, 0.9);
+        }
+
+        svg {
+          width: 1em;
+          height: 1em;
+          margin-right: 15px;
+          font-size: $base-font-size-big;
+          color: rgba($base-color-white, 0.9);
+          cursor: pointer;
+          fill: rgba($base-color-white, 0.9);
+        }
+
+        button {
+          svg {
+            margin-right: 0;
+            color: rgba($base-color-white, 0.9);
+            cursor: pointer;
+            fill: rgba($base-color-white, 0.9);
+          }
+        }
+
+        .el-badge {
+          margin-right: 15px;
+        }
+      }
+    }
+  }
+</style>

+ 35 - 0
layouts/index.js

@@ -0,0 +1,35 @@
+module.exports = {
+  webpackBarName: 'vue-admin-better',
+  webpackBanner:
+    ' build: vue-admin-better \n vue-admin-beautiful.com \n https://gitee.com/chu1204505056/vue-admin-better \n time: ',
+  donationConsole() {
+    const chalk = require('chalk')
+    console.log(
+      chalk.green(
+        `> 欢迎使用vue-admin-better,github开源地址:https://github.com/chuzhixin/vue-admin-better`
+      )
+    )
+    console.log(
+      chalk.green(
+        `> 欢迎使用vue-admin-better,码云开源地址:https://gitee.com/chu1204505056/vue-admin-better`
+      )
+    )
+
+    console.log(
+      chalk.green(`> pro版演示地址:http://vue-admin-beautiful.com/admin-pro`)
+    )
+
+    console.log(
+      chalk.green(`> plus版演示地址:http://vue-admin-beautiful.com/admin-plus`)
+    )
+
+    console.log(
+      chalk.green(
+        `> 使用中出现任何问题可加QQ群反馈,获取基础版、文档,请我们喝杯咖啡(如若情况不允许,请勿勉强):https://gitee.com/chu1204505056/vue-admin-better#-%E5%89%8D%E7%AB%AF%E8%AE%A8%E8%AE%BA-qq-%E7%BE%A4`
+      )
+    )
+
+    console.log(chalk.green(`> 如果您不希望显示以上信息,可在config中配置关闭`))
+    console.log('\n')
+  },
+}

+ 4 - 0
layouts/package.json

@@ -0,0 +1,4 @@
+{
+  "name": "layouts",
+  "main": "index.js"
+}

+ 16 - 0
layouts/prettier.config.js

@@ -0,0 +1,16 @@
+module.exports = {
+  printWidth: 80,
+  tabWidth: 2,
+  useTabs: false,
+  semi: false,
+  singleQuote: true,
+  quoteProps: 'as-needed',
+  jsxSingleQuote: false,
+  trailingComma: 'es5',
+  bracketSpacing: true,
+  jsxBracketSameLine: false,
+  arrowParens: 'always',
+  htmlWhitespaceSensitivity: 'ignore',
+  vueIndentScriptAndStyle: true,
+  endOfLine: 'lf',
+}

+ 1 - 1
mock/controller/changeLog.js

@@ -146,7 +146,7 @@ const data = [
     timestamp: '2020-07-11',
   },
   {
-    content: '修改zx-layouts引入方式',
+    content: '修改layouts引入方式',
     timestamp: '2020-07-15',
   },
   {

+ 0 - 24
mock/controller/router.js

@@ -159,36 +159,12 @@ const data = [
         ],
       },
       {
-        path: 'magnifier',
-        name: 'Magnifier',
-        component: '@/views/vab/magnifier/index',
-        meta: { title: '放大镜', permissions: ['admin'] },
-      },
-      {
         path: 'loading',
         name: 'Loading',
         component: '@/views/vab/loading/index',
         meta: { title: 'loading', permissions: ['admin'] },
       },
       {
-        path: 'player',
-        name: 'Player',
-        component: '@/views/vab/player/index',
-        meta: { title: '视频播放器', permissions: ['admin'] },
-      },
-      {
-        path: 'markdownEditor',
-        name: 'MarkdownEditor',
-        component: '@/views/vab/markdownEditor/index',
-        meta: { title: 'markdown编辑器', permissions: ['admin'] },
-      },
-      {
-        path: 'editor',
-        name: 'Editor',
-        component: '@/views/vab/editor/index',
-        meta: { title: '富文本编辑器', permissions: ['admin'], badge: 'New' },
-      },
-      {
         path: 'backToTop',
         name: 'BackToTop',
         component: '@/views/vab/backToTop/index',

+ 4 - 21
package.json

@@ -1,26 +1,17 @@
 {
-  "name": "vue-admin-beautiful",
+  "name": "vue-admin-better",
   "version": "1.0.0",
-  "author": "vue-admin-beautiful",
+  "author": "vue-admin-better",
   "participants": [],
   "homepage": "https://chu1204505056.gitee.io/vue-admin-better",
-  "publishConfig": {
-    "registry": "https://npm.pkg.github.com/"
-  },
   "scripts": {
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",
-    "build:report": "vue-cli-service build --report",
-    "globle": "npm install -g cnpm  --registry=http://mirrors.cloud.tencent.com/npm/&&cnpm i rimraf npm-check-updates nrm -g&&rimraf node_modules&&cnpm i",
     "lint": "vue-cli-service lint",
-    "lint:style": "stylelint-config-prettier-check",
-    "inspect": "vue-cli-service inspect",
     "clear": "rimraf node_modules&&npm install  --registry=http://mirrors.cloud.tencent.com/npm/",
     "image-webpack-loader": "cnpm i image-webpack-loader -D",
     "update": "ncu -u --reject sass-loader,sass,screenfull,eslint,chalk,vue-echarts,vue,vue-template-compiler,vue-router,vuex,@vue/cli-plugin-babel,@vue/cli-plugin-eslint,@vue/cli-service,eslint-plugin-vue --registry=http://mirrors.cloud.tencent.com/npm/&&npm i --registry=http://mirrors.cloud.tencent.com/npm/",
-    "update:globle": "ncu -g --concurrency 10 --timeout 80000",
-    "push": "start ./push.sh",
-    "deploy": "start ./deploy.sh"
+    "push": "start ./push.sh"
   },
   "repository": {
     "type": "git",
@@ -58,14 +49,7 @@
     "vue-router": "^3.5.3",
     "vue-template-compiler": "~2.6.14",
     "vuex": "^3.6.2",
-    "zx-count": "^0.3.7",
-    "zx-layouts": "^0.6.29",
-    "zx-magnifie": "^0.4.0",
-    "zx-markdown-editor": "^0.0.2",
-    "zx-player": "^1.0.2",
-    "zx-quill": "^0.0.3",
-    "zx-templates": "^0.0.26",
-    "zx-verify": "^0.0.2"
+    "layouts": "file:layouts"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "^4.5.15",
@@ -83,7 +67,6 @@
     "filemanager-webpack-plugin": "^8.0.0",
     "image-webpack-loader": "^8.1.0",
     "lint-staged": "^13.1.0",
-    "plop": "^3.1.1",
     "prettier": "^2.8.1",
     "sass": "~1.32.13",
     "sass-loader": "^10.1.1",

+ 0 - 16
plopfile.js

@@ -1,16 +0,0 @@
-/**
- * @author https://vue-admin-beautiful.com (不想保留author可删除)
- * @description 代码生成机
- */
-const viewGenerator = require('zx-templates/view/prompt')
-const curdGenerator = require('zx-templates/curd/prompt')
-const componentGenerator = require('zx-templates/component/prompt')
-const mockGenerator = require('zx-templates/mock/prompt')
-const vuexGenerator = require('zx-templates/vuex/prompt')
-module.exports = (plop) => {
-  plop.setGenerator('view', viewGenerator)
-  plop.setGenerator('curd', curdGenerator)
-  plop.setGenerator('component', componentGenerator)
-  plop.setGenerator('mock&api', mockGenerator)
-  plop.setGenerator('vuex', vuexGenerator)
-}

+ 1 - 9
src/components/VabCharge/index.vue

@@ -2,15 +2,7 @@
   <div class="content">
     <div class="g-container" :style="styleObj">
       <div class="g-number">
-        <vab-count
-          :start-val="startVal"
-          :end-val="endVal"
-          :duration="duration"
-          :separator="separator"
-          :prefix="prefix"
-          :suffix="suffix"
-          :decimals="decimals"
-        />
+        {{ endVal }}
       </div>
       <div class="g-contrast">
         <div class="g-circle"></div>

+ 1 - 1
src/layouts/export.js

@@ -12,7 +12,7 @@ requireComponents.keys().forEach((fileName) => {
   Vue.component(componentName, componentConfig.default || componentConfig)
 })
 
-const requireZxLayouts = require.context('zx-layouts', true, /\.vue$/)
+const requireZxLayouts = require.context('layouts', true, /\.vue$/)
 requireZxLayouts.keys().forEach((fileName) => {
   const componentConfig = requireZxLayouts(fileName)
   const componentName = componentConfig.default.name

+ 1 - 3
src/plugins/index.js

@@ -8,10 +8,8 @@ import '@/colorfulIcon'
 import '@/config/permission'
 import '@/utils/errorLog'
 import './vabIcon'
-import VabPermissions from 'zx-layouts/Permissions'
+import VabPermissions from 'layouts/Permissions'
 import Vab from '@/utils/vab'
-import VabCount from 'zx-count'
 
 Vue.use(Vab)
 Vue.use(VabPermissions)
-Vue.use(VabCount)

+ 1 - 1
src/plugins/support.js

@@ -16,5 +16,5 @@ if (!!window.ActiveXObject || 'ActiveXObject' in window) {
     dangerouslyUseHTMLString: true,
   })
 }
-if (!dependencies['vab-icon'] || !dependencies['zx-layouts'])
+if (!dependencies['vab-icon'] || !dependencies['layouts'])
   document.body.innerHTML = ''

+ 0 - 3
src/plugins/vabMagnifier.js

@@ -1,3 +0,0 @@
-import VabMagnifier from 'zx-magnifie'
-
-export default VabMagnifier

+ 0 - 5
src/plugins/vabMarkdownEditor.js

@@ -1,5 +0,0 @@
-import ZxMarkdownEditor from 'zx-markdown-editor'
-import 'zx-markdown-editor/dist/zx-markdown-editor.css'
-
-const VabMarkdownEditor = ZxMarkdownEditor
-export default VabMarkdownEditor

+ 0 - 3
src/plugins/vabPlayer.js

@@ -1,3 +0,0 @@
-import { VabPlayerMp4, VabPlayerHls, VabPlayerFlv } from 'zx-player'
-
-export { VabPlayerMp4, VabPlayerHls, VabPlayerFlv }

+ 0 - 4
src/plugins/vabQuill.js

@@ -1,4 +0,0 @@
-import 'zx-quill/dist/zx-quill.css'
-import VabQuill from 'zx-quill'
-
-export default VabQuill

+ 0 - 4
src/plugins/vabVerify.js

@@ -1,4 +0,0 @@
-import VabVerify from 'zx-verify'
-import 'zx-verify/dist/zx-verify.css'
-
-export default VabVerify

+ 0 - 34
src/router/index.js

@@ -156,12 +156,6 @@ export const asyncRoutes = [
         meta: { title: '树', permissions: ['admin'] },
       },
       {
-        path: 'verify',
-        name: 'Verify',
-        component: () => import('@/views/vab/verify/index'),
-        meta: { title: '验证码', permissions: ['admin'] },
-      },
-      {
         path: 'menu1',
         component: () => import('@/views/vab/nested/menu1/index'),
         name: 'Menu1',
@@ -191,40 +185,12 @@ export const asyncRoutes = [
         ],
       },
       {
-        path: 'magnifier',
-        name: 'Magnifier',
-        component: () => import('@/views/vab/magnifier/index'),
-        meta: { title: '放大镜', permissions: ['admin'] },
-      },
-      {
         path: 'loading',
         name: 'Loading',
         component: () => import('@/views/vab/loading/index'),
         meta: { title: 'loading', permissions: ['admin'] },
       },
       {
-        path: 'player',
-        name: 'Player',
-        component: () => import('@/views/vab/player/index'),
-        meta: { title: '视频播放器', permissions: ['admin'] },
-      },
-      {
-        path: 'markdownEditor',
-        name: 'MarkdownEditor',
-        component: () => import('@/views/vab/markdownEditor/index'),
-        meta: { title: 'markdown编辑器', permissions: ['admin'] },
-      },
-      {
-        path: 'editor',
-        name: 'Editor',
-        component: () => import('@/views/vab/editor/index'),
-        meta: {
-          title: '富文本编辑器',
-          permissions: ['admin'],
-          badge: 'New',
-        },
-      },
-      {
         path: 'backToTop',
         name: 'BackToTop',
         component: () => import('@/views/vab/backToTop/index'),

+ 4 - 20
src/views/index/index.vue

@@ -29,15 +29,7 @@
             <span>
               日均访问量:
 
-              <vab-count
-                :start-val="config1.startVal"
-                :end-val="config1.endVal"
-                :duration="config1.duration"
-                :separator="config1.separator"
-                :prefix="config1.prefix"
-                :suffix="config1.suffix"
-                :decimals="config1.decimals"
-              />
+              {{ config1.endVal }}
             </span>
           </div>
         </el-card>
@@ -51,15 +43,7 @@
           <div class="bottom">
             <span>
               总授权数:
-              <vab-count
-                :start-val="config2.startVal"
-                :end-val="config2.endVal"
-                :duration="config2.duration"
-                :separator="config2.separator"
-                :prefix="config2.prefix"
-                :suffix="config2.suffix"
-                :decimals="config2.decimals"
-              />
+              {{ config2.endVal }}
             </span>
           </div>
         </el-card>
@@ -154,8 +138,8 @@
               <td>{{ dependencies['mockjs'] }}</td>
             </tr>
             <tr>
-              <td>zx-layouts版本</td>
-              <td>{{ dependencies['zx-layouts'] }}</td>
+              <td>layouts版本</td>
+              <td>{{ dependencies['layouts'] }}</td>
               <td>lodash版本</td>
               <td>{{ dependencies['lodash'] }}</td>
             </tr>

+ 0 - 156
src/views/vab/editor/index.vue

@@ -1,156 +0,0 @@
-<template>
-  <div class="editor-container">
-    <el-form ref="form" :model="form" :rules="rules" label-width="100px">
-      <el-form-item label="标题" prop="title">
-        <el-input v-model="form.title" maxlength="20"></el-input>
-      </el-form-item>
-      <el-form-item label="所属模块" prop="module">
-        <el-select v-model="form.module">
-          <el-option label="新闻动态" value="1"></el-option>
-          <el-option label="实时热点" value="2"></el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="内容" prop="content" class="vab-quill-content">
-        <vab-quill
-          v-model="form.content"
-          :min-height="400"
-          :options="options"
-        ></vab-quill>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="handleSee">预览效果</el-button>
-        <el-button type="primary" @click="handleSave">保存</el-button>
-      </el-form-item>
-    </el-form>
-    <el-dialog title="预览效果" :visible.sync="dialogTableVisible">
-      <div style="min-height: 60vh">
-        <h1 class="news-title">{{ form.title }}</h1>
-        <div class="news-content" v-html="form.content"></div>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-  import vabQuill from '@/plugins/vabQuill'
-  export default {
-    name: 'Editor',
-    components: { vabQuill },
-    data() {
-      return {
-        options: {
-          theme: 'snow',
-          bounds: document.body,
-          debug: 'warn',
-          modules: {
-            toolbar: [
-              ['bold', 'italic', 'underline', 'strike'],
-              [{ header: [1, 2, 3, 4, 5, 6, false] }],
-              [{ size: ['small', false, 'large', 'huge'] }],
-              [{ color: [] }, { background: [] }],
-              ['blockquote', 'code-block'],
-              [{ list: 'ordered' }, { list: 'bullet' }],
-              [{ script: 'sub' }, { script: 'super' }],
-              [{ indent: '-1' }, { indent: '+1' }],
-              [{ align: [] }],
-              [{ direction: 'rtl' }],
-              [{ font: [] }],
-              ['clean'],
-              ['link', 'image'],
-            ],
-          },
-          placeholder: '内容...',
-          readOnly: false,
-        },
-        borderColor: '#dcdfe6',
-        dialogTableVisible: false,
-        form: {
-          title: '',
-          module: '',
-          content: '',
-        },
-        rules: {
-          title: [
-            {
-              required: true,
-              message: '请输入标题',
-              trigger: 'blur',
-            },
-          ],
-          module: [
-            {
-              required: true,
-              message: '请选择模块',
-              trigger: 'change',
-            },
-          ],
-          content: [
-            {
-              required: true,
-              message: '请输入内容',
-              trigger: 'blur',
-            },
-          ],
-        },
-      }
-    },
-    methods: {
-      handleSee() {
-        this.$refs['form'].validate((valid) => {
-          this.$refs.form.validateField('content', (errorMsg) => {})
-          if (valid) {
-            this.dialogTableVisible = true
-          } else {
-            return false
-          }
-        })
-      },
-      handleSave() {
-        this.$refs['form'].validate((valid) => {
-          this.$refs.form.validateField('content', (errorMsg) => {
-            this.borderColor = '#dcdfe6'
-            if (errorMsg) {
-              this.borderColor = '#F56C6C'
-            }
-          })
-          if (valid) {
-            this.$baseMessage('submit!', 'success')
-          } else {
-            return false
-          }
-        })
-      },
-    },
-  }
-</script>
-<style lang="scss" scoped>
-  .editor-container {
-    .news {
-      &-title {
-        text-align: center;
-      }
-
-      &-content {
-        ::v-deep {
-          p {
-            line-height: 30px;
-
-            img {
-              display: block;
-              margin-right: auto;
-              margin-left: auto;
-            }
-          }
-        }
-      }
-    }
-
-    .vab-quill-content {
-      ::v-deep {
-        .el-form-item__content {
-          line-height: normal;
-        }
-      }
-    }
-  }
-</style>

+ 0 - 35
src/views/vab/magnifier/index.vue

@@ -1,35 +0,0 @@
-<template>
-  <div class="magnifier-container">
-    <el-row :gutter="20">
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header"><span>放大镜1</span></div>
-          <vab-magnifier
-            url="https://picsum.photos/960/540?random=1"
-            type="circle"
-          ></vab-magnifier>
-        </el-card>
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header"><span>放大镜2</span></div>
-          <vab-magnifier
-            url="https://picsum.photos/960/540?random=2"
-            type="square"
-          ></vab-magnifier>
-        </el-card>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script>
-  import VabMagnifier from '@/plugins/vabMagnifier.js'
-
-  export default {
-    name: 'Magnifier',
-    components: {
-      VabMagnifier,
-    },
-  }
-</script>

+ 0 - 51
src/views/vab/markdownEditor/index.vue

@@ -1,51 +0,0 @@
-<template>
-  <div class="markdown-editor-container">
-    <el-row :gutter="20">
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <vab-markdown-editor
-          ref="mde"
-          v-model="value"
-          @show-html="handleShowHtml"
-        ></vab-markdown-editor>
-        <el-button @click="handleAddText">增加文本</el-button>
-        <el-button @click="handleAddImg">增加图片</el-button>
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header">
-            <span>markdown转换html实时演示区域</span>
-          </div>
-          <div v-html="html"></div>
-        </el-card>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script>
-  import VabMarkdownEditor from '@/plugins/vabMarkdownEditor'
-
-  export default {
-    name: 'MarkdownEditor',
-    components: { VabMarkdownEditor },
-    data() {
-      return {
-        value: '# vue-admin-beautiful',
-        html: '<h1 id="vue-admin-beautiful">vue-admin-beautiful</h1>',
-      }
-    },
-    methods: {
-      handleAddText() {
-        this.$refs.mde.add('\n### 新增加的内容')
-      },
-      handleAddImg() {
-        this.$refs.mde.add(
-          '\n![](https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-2.png)'
-        )
-      },
-      handleShowHtml(html) {
-        this.html = html
-      },
-    },
-  }
-</script>

+ 0 - 73
src/views/vab/player/index.vue

@@ -1,73 +0,0 @@
-<template>
-  <div class="player-container">
-    <el-divider content-position="left">
-      视频地址采用cdn加速服务,开发时需部署到到本地
-    </el-divider>
-    <el-row :gutter="20">
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header">播放传统MP4</div>
-          <vab-player-mp4 :config="config1" @player="Player1 = $event" />
-        </el-card>
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header">播放m3u8,且不暴露视频地址</div>
-          <vab-player-hls
-            :config="config2"
-            @player="Player2 = $event"
-          ></vab-player-hls>
-        </el-card>
-      </el-col>
-      <!--<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <el-card shadow="hover">
-          <div slot="header">播放flv,且不暴露视频地址</div>
-          <vab-player-flv
-            :config="config3"
-            @player="Player3 = $event"
-          ></vab-player-flv>
-        </el-card>
-      </el-col>-->
-    </el-row>
-  </div>
-</template>
-
-<script>
-  import { VabPlayerMp4, VabPlayerHls } from '@/plugins/vabPlayer.js'
-
-  export default {
-    name: 'Player',
-    components: {
-      VabPlayerMp4,
-      VabPlayerHls,
-    },
-    data() {
-      return {
-        config1: {
-          id: 'mse1',
-          url: 'https://fastly.jsdelivr.net/gh/chuzhixin/videos@master/video.mp4',
-          volume: 1,
-          autoplay: false,
-        },
-        Player1: null,
-        config2: {
-          id: 'mse2',
-          url: 'https://fastly.jsdelivr.net/gh/chuzhixin/videos@master/video.m3u8',
-          volume: 1,
-          autoplay: false,
-        },
-        Player2: null,
-        config3: {
-          id: 'mse3',
-          url: 'https://fastly.jsdelivr.net/gh/chuzhixin/videos@master/video.flv',
-          volume: 1,
-          autoplay: false,
-        },
-        Player3: null,
-      }
-    },
-    created() {},
-    mounted() {},
-    methods: {},
-  }
-</script>

+ 0 - 35
src/views/vab/verify/index.vue

@@ -1,35 +0,0 @@
-<template>
-  <div class="verify-container">
-    <vab-verify
-      ref="slideDiv"
-      :w="350"
-      :slider-text="text"
-      :h="175"
-      @success="handleSuccess"
-      @fail="handleError"
-    ></vab-verify>
-  </div>
-</template>
-
-<script>
-  import VabVerify from '@/plugins/vabVerify'
-  export default {
-    name: 'Verify',
-    components: { VabVerify },
-    data() {
-      return {
-        text: '向右滑动',
-      }
-    },
-    created() {},
-    mounted() {},
-    methods: {
-      handleSuccess() {
-        this.$baseMessage('校验成功', 'success')
-      },
-      handleError() {
-        this.$baseMessage('校验失败', 'error')
-      },
-    },
-  }
-</script>

+ 1 - 0
vab-icon

@@ -0,0 +1 @@
+D:/Development/VSCodeProjects/vue-admin-beautiful/node_modules/_vab-icon@0.0.1@vab-icon

+ 1 - 1
vue.config.js

@@ -18,7 +18,7 @@ const {
   donation,
   imageCompression,
 } = require('./src/config')
-const { webpackBarName, webpackBanner, donationConsole } = require('zx-layouts')
+const { webpackBarName, webpackBanner, donationConsole } = require('layouts')
 
 if (donation) donationConsole()
 const { version, author } = require('./package.json')