mirror of
				https://gitee.com/chu1204505056/vue-admin-beautiful.git
				synced 2025-11-04 05:12:09 +08:00 
			
		
		
		
	🚀 feat: add layouts
This commit is contained in:
		
							parent
							
								
									349e4c8902
								
							
						
					
					
						commit
						ae66791f74
					
				
							
								
								
									
										7
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
src/assets
 | 
			
		||||
src/icons
 | 
			
		||||
public
 | 
			
		||||
dist
 | 
			
		||||
node_modules
 | 
			
		||||
vab-icon
 | 
			
		||||
layouts
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -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
									
								
								layouts/Permissions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								layouts/Permissions/index.js
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/Permissions/permissions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								layouts/Permissions/permissions.js
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabColorfullIcon/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								layouts/VabColorfullIcon/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabErrorLog/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								layouts/VabErrorLog/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabFullScreenBar/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								layouts/VabFullScreenBar/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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>
 | 
			
		||||
							
								
								
									
										75
									
								
								layouts/VabGithubCorner/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								layouts/VabGithubCorner/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <a
 | 
			
		||||
    href="https://github.com/chuzhixin/vue-admin-beautiful"
 | 
			
		||||
    target="_blank"
 | 
			
		||||
    class="github-corner"
 | 
			
		||||
    aria-label="View source on Github"
 | 
			
		||||
  >
 | 
			
		||||
    <svg
 | 
			
		||||
      width="80"
 | 
			
		||||
      height="80"
 | 
			
		||||
      viewBox="0 0 250 250"
 | 
			
		||||
      class="github-color"
 | 
			
		||||
      aria-hidden="true"
 | 
			
		||||
    >
 | 
			
		||||
      <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
 | 
			
		||||
      <path
 | 
			
		||||
        d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
 | 
			
		||||
        fill="currentColor"
 | 
			
		||||
        style="transform-origin: 130px 106px"
 | 
			
		||||
        class="octo-arm"
 | 
			
		||||
      />
 | 
			
		||||
      <path
 | 
			
		||||
        d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
 | 
			
		||||
        fill="currentColor"
 | 
			
		||||
        class="octo-body"
 | 
			
		||||
      />
 | 
			
		||||
    </svg>
 | 
			
		||||
  </a>
 | 
			
		||||
</template>
 | 
			
		||||
<script>
 | 
			
		||||
  export default {
 | 
			
		||||
    name: 'VabGithubCorner',
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
  .github-corner {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    z-index: $base-z-index - 3;
 | 
			
		||||
 | 
			
		||||
    .octo-arm {
 | 
			
		||||
      animation: octocat-wave 560ms ease-in-out infinite;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:hover {
 | 
			
		||||
      .octo-arm {
 | 
			
		||||
        animation: octocat-wave 560ms ease-in-out infinite;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .github-color {
 | 
			
		||||
      color: #fff;
 | 
			
		||||
      fill: $base-color-blue;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @keyframes octocat-wave {
 | 
			
		||||
    0%,
 | 
			
		||||
    100% {
 | 
			
		||||
      transform: rotate(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    20%,
 | 
			
		||||
    60% {
 | 
			
		||||
      transform: rotate(-25deg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    40%,
 | 
			
		||||
    80% {
 | 
			
		||||
      transform: rotate(100deg);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										20
									
								
								layouts/VabQueryForm/VabQueryFormBottomPanel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								layouts/VabQueryForm/VabQueryFormBottomPanel.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabQueryForm/VabQueryFormLeftPanel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								layouts/VabQueryForm/VabQueryFormLeftPanel.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabQueryForm/VabQueryFormRightPanel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								layouts/VabQueryForm/VabQueryFormRightPanel.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabQueryForm/VabQueryFormTopPanel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								layouts/VabQueryForm/VabQueryFormTopPanel.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabQueryForm/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								layouts/VabQueryForm/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabRemixIcon/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								layouts/VabRemixIcon/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabSideBar/components/VabMenuItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								layouts/VabSideBar/components/VabMenuItem.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabSideBar/components/VabSideBarItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								layouts/VabSideBar/components/VabSideBarItem.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabSideBar/components/VabSubmenu.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								layouts/VabSideBar/components/VabSubmenu.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabSideBar/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								layouts/VabSideBar/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabTabsBar/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								layouts/VabTabsBar/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/VabTopBar/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								layouts/VabTopBar/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								layouts/index.js
									
									
									
									
									
										Normal file
									
								
							@ -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
									
								
								layouts/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								layouts/package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "layouts",
 | 
			
		||||
  "main": "index.js"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								layouts/prettier.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								layouts/prettier.config.js
									
									
									
									
									
										Normal file
									
								
							@ -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',
 | 
			
		||||
}
 | 
			
		||||
@ -146,7 +146,7 @@ const data = [
 | 
			
		||||
    timestamp: '2020-07-11',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    content: '修改zx-layouts引入方式',
 | 
			
		||||
    content: '修改layouts引入方式',
 | 
			
		||||
    timestamp: '2020-07-15',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
 | 
			
		||||
@ -158,36 +158,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',
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								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",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								plopfile.js
									
									
									
									
									
								
							
							
						
						
									
										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)
 | 
			
		||||
}
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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 = ''
 | 
			
		||||
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
import VabMagnifier from 'zx-magnifie'
 | 
			
		||||
 | 
			
		||||
export default VabMagnifier
 | 
			
		||||
@ -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
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
import { VabPlayerMp4, VabPlayerHls, VabPlayerFlv } from 'zx-player'
 | 
			
		||||
 | 
			
		||||
export { VabPlayerMp4, VabPlayerHls, VabPlayerFlv }
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
import 'zx-quill/dist/zx-quill.css'
 | 
			
		||||
import VabQuill from 'zx-quill'
 | 
			
		||||
 | 
			
		||||
export default VabQuill
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
import VabVerify from 'zx-verify'
 | 
			
		||||
import 'zx-verify/dist/zx-verify.css'
 | 
			
		||||
 | 
			
		||||
export default VabVerify
 | 
			
		||||
@ -155,12 +155,6 @@ export const asyncRoutes = [
 | 
			
		||||
        component: () => import('@/views/vab/tree/index'),
 | 
			
		||||
        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'),
 | 
			
		||||
@ -190,40 +184,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',
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -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'
 | 
			
		||||
        )
 | 
			
		||||
      },
 | 
			
		||||
      handleShowHtml(html) {
 | 
			
		||||
        this.html = html
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -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
									
								
								vab-icon
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								vab-icon
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
			
		||||
D:/Development/VSCodeProjects/vue-admin-beautiful/node_modules/_vab-icon@0.0.1@vab-icon
 | 
			
		||||
@ -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')
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user