loulijun2021
2022-12-31 df744014031c57bf69fd1d94767a3c6e07f2ecb8
1.横向竖向菜单模式切换功能
已添加1个文件
已修改8个文件
586 ■■■■ 文件已修改
src/layout/components/Navbar.vue 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Settings/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Sidebar/MenuItemEx.vue 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Sidebar/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/settings.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/settings.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/sidebar.scss 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/variables.scss 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue
@@ -1,113 +1,141 @@
<template>
  <!--  <div class="navbar">-->
  <div class="navbar" :style="{background: $store.state.settings.headBackgroundColorValue? '#304156':'#fff'}">
    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
    <breadcrumb class="breadcrumb-container" />
    <div class="right-menu">
      <!--      <template v-if="device!=='mobile'">-->
      <template>
        <search id="header-search" class="right-menu-item" />
        <!--        <error-log class="errLog-container right-menu-item hover-effect" />-->
        <screenfull id="screenfull" class="right-menu-item hover-effect" />
        <!--        <el-tooltip content="Global Size" effect="dark" placement="bottom">-->
        <!--          <size-select id="size-select" class="right-menu-item hover-effect" />-->
        <!--        </el-tooltip>-->
      </template>
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <!--          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">-->
          <div style="display: flex;align-items: center;margin-top: -5px">
            <i class="el-icon-user" style="font-weight: bolder;font-size: 18px;margin-right: 5px;color:#A7A7A7" />
            <div style=" font-size: 18px;font-family: 'Microsoft YaHei';color:#AAAAAA">{{ username }}</div>
          </div>
          <!--          <i class="el-icon-caret-bottom" />-->
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown" style="text-align: center">
          <!--          <router-link to="/">-->
          <!--            <el-dropdown-item>-->
          <!--              Home-->
          <!--            </el-dropdown-item>-->
          <!--          </router-link>-->
          <!--          <a target="_blank" href="https://github.com/PanJiaChen/vue-admin-template/">-->
          <!--            <el-dropdown-item>Github</el-dropdown-item>-->
          <!--          </a>-->
          <!--          <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">-->
          <!--            <el-dropdown-item>Docs</el-dropdown-item>-->
          <!--          </a>-->
          <el-dropdown-item @click.native="editPassword">
            <span style="display:block;">修改密码</span>
          </el-dropdown-item>
          <el-dropdown-item divided @click.native="logout">
            <span style="display:block;font-weight: bolder">退出</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
    <el-dialog
      title="修改密码"
      :visible.sync="dialogVisible"
      width="500px"
      :close-on-click-modal="false"
      @close="handleClose"
      @closed="handleClose"
  <div>
    <div
      class="navbar"
      :class="{'display_btw':$store.state.settings.menuIsHorizontal}"
      :style="{background: $store.state.settings.headBackgroundColorValue? '#304156':'#fff'}"
    >
      <el-form ref="dialogForm" :rules="formRules" :model="form" label-width="100px">
        <!--        <el-form-item label="用户编码:">-->
        <!--          <div>{{ usercode }}</div>-->
        <!--        </el-form-item>-->
        <!--        <el-form-item label="用户名称:">-->
        <!--          <div> {{ username }}</div>-->
        <!--        </el-form-item>-->
        <el-form-item label="原密码:" prop="password">
          <el-input v-model="form.password" style="width: 220px;" />
        </el-form-item>
        <el-form-item label="新密码:" prop="newpassword">
          <el-input v-model="form.newpassword" style="width: 220px;" />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <div class="footerButton">
          <el-button @click="dialogVisibleCancel">返回</el-button>
          <el-button type="primary" @click="dialogVisibleConfirm">ç¡® å®š</el-button>
        </div>
      </span>
    </el-dialog>
      <hamburger
        v-if="!$store.state.settings.menuIsHorizontal"
        :is-active="sidebar.opened"
        class="hamburger-container"
        @toggleClick="toggleSideBar"
      />
      <breadcrumb
        class="breadcrumb-container"
        :style="{marginLeft:$store.state.settings.menuIsHorizontal?'20px':''}"
      />
      <el-menu
        v-if="$store.state.settings.menuIsHorizontal"
        router
        class="menuHorizontal el-menu-demo"
        mode="horizontal"
        :default-active="activeMenu"
        :text-color="$store.state.settings.headBackgroundColorValue?'#fff':'#87909c'"
        :background-color="$store.state.settings.headBackgroundColorValue?variables.menuBg:'#fff'"
        :active-text-color="variables.menuActiveText"
        @select="handleSelect"
      >
        <!--        menu-trigger="click"-->
        <!--        :text-color="variables.menuText"-->
        <menu-item-ex
          v-for="route in permission_routes"
          :key="route.path"
          :item="route"
          :base-path="route.path"
        />
      </el-menu>
      <div class="right-menu">
        <!--      <template v-if="device!=='mobile'">-->
        <template>
          <search id="header-search" class="right-menu-item" />
          <!--        <error-log class="errLog-container right-menu-item hover-effect" />-->
          <screenfull id="screenfull" class="right-menu-item hover-effect" />
          <!--        <el-tooltip content="Global Size" effect="dark" placement="bottom">-->
          <!--          <size-select id="size-select" class="right-menu-item hover-effect" />-->
          <!--        </el-tooltip>-->
        </template>
        <el-dropdown class="avatar-container" trigger="click">
          <div class="avatar-wrapper">
            <!--          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">-->
            <div style="display: flex;align-items: center;margin-top: -5px">
              <i class="el-icon-user" style="font-weight: bolder;font-size: 18px;margin-right: 5px;color:#A7A7A7" />
              <div style=" font-size: 18px;font-family: 'Microsoft YaHei';color:#AAAAAA">{{ username }}</div>
            </div>
            <!--          <i class="el-icon-caret-bottom" />-->
          </div>
          <el-dropdown-menu slot="dropdown" class="user-dropdown" style="text-align: center">
            <!--          <router-link to="/">-->
            <!--            <el-dropdown-item>-->
            <!--              Home-->
            <!--            </el-dropdown-item>-->
            <!--          </router-link>-->
            <!--          <a target="_blank" href="https://github.com/PanJiaChen/vue-admin-template/">-->
            <!--            <el-dropdown-item>Github</el-dropdown-item>-->
            <!--          </a>-->
            <!--          <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">-->
            <!--            <el-dropdown-item>Docs</el-dropdown-item>-->
            <!--          </a>-->
            <el-dropdown-item @click.native="editPassword">
              <span style="display:block;">修改密码</span>
            </el-dropdown-item>
            <el-dropdown-item divided @click.native="logout">
              <span style="display:block;font-weight: bolder">退出</span>
            </el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
      <el-dialog
        title="修改密码"
        :visible.sync="dialogVisible"
        width="500px"
        :close-on-click-modal="false"
        @close="handleClose"
        @closed="handleClose"
      >
        <el-form ref="dialogForm" :rules="formRules" :model="form" label-width="100px">
          <!--        <el-form-item label="用户编码:">-->
          <!--          <div>{{ usercode }}</div>-->
          <!--        </el-form-item>-->
          <!--        <el-form-item label="用户名称:">-->
          <!--          <div> {{ username }}</div>-->
          <!--        </el-form-item>-->
          <el-form-item label="原密码:" prop="password">
            <el-input v-model="form.password" style="width: 220px;" />
          </el-form-item>
          <el-form-item label="新密码:" prop="newpassword">
            <el-input v-model="form.newpassword" style="width: 220px;" />
          </el-form-item>
        </el-form>
        <span slot="footer" class="dialog-footer">
          <div class="footerButton">
            <el-button @click="dialogVisibleCancel">返回</el-button>
            <el-button type="primary" @click="dialogVisibleConfirm">ç¡® å®š</el-button>
          </div>
        </span>
      </el-dialog>
    </div>
  </div>
</template>
<script>
const SER_HZ = /^[\u4e00-\u9fa5]+$/
import { mapGetters } from 'vuex'
import MenuItemEx from './Sidebar/MenuItemEx'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import { getCookie } from '@/utils/auth'
import { UpdateUserPassword } from '@/api/user'
import Search from '@/components/HeaderSearch'
import Screenfull from '@/components/Screenfull'
import variables from '@/styles/variables.scss'
const SER_HZ = /^[\u4e00-\u9fa5]+$/
export default {
  components: {
    MenuItemEx,
    Breadcrumb,
    Hamburger,
    Search,
    Screenfull
  },
  computed: {
    ...mapGetters([
      'sidebar',
      'avatar',
      'device'
    ])
  },
  created() {
    this.usercode = getCookie('navTabId')
    this.username = getCookie('username')
  },
  data() {
    const validatePassword1 = (rule, value, callback) => {
@@ -150,7 +178,43 @@
      }
    }
  },
  created() {
    this.usercode = getCookie('navTabId')
    this.username = getCookie('username')
  },
  computed: {
    ...mapGetters([
      'sidebar',
      'avatar',
      'permission_routes'
    ]),
    routes() {
      return this.$router.options.routes
    },
    activeMenu() {
      const route = this.$route
      const { meta, path } = route
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu
      }
      // const p = '/' + path.split('/')[1]
      // console.log(p)
      return path
    },
    variables() {
      console.log(variables, 2333)
      // å…ˆè¾“出这个variables值  ç„¶åŽä¿®æ”¹å…¶å±žæ€§å€¼
      variables.menuActiveText = this.$store.state.settings.theme
      // background: rgb(48, 65, 86);
      // variables.menuBg = this.$store.state.settings.headBackgroundColorValue && this.$store.state.settings.menuIsHorizontal ? `rgb(48,65,86)` : '#fff'
      return variables
    }
  },
  methods: {
    handleSelect(key, keyPath) {
      // console.log(key, keyPath)
    },
    toggleSideBar() {
      this.$store.dispatch('app/toggleSideBar')
    },
@@ -174,7 +238,6 @@
            password: this.form.password,
            newpassword: this.form.newpassword
          }
          console.log(data)
          UpdateUserPassword(data).then(res => {
            if (res.code === '200') {
              this.$message.success('修改成功!')
@@ -192,9 +255,11 @@
  }
}
</script>
<style lang="scss" scoped>
$main_color: #42b983;
.display_btw {
  display: flex;
  justify-content: space-between;
}
.navbar {
  height: 50px;
@@ -220,6 +285,30 @@
  .breadcrumb-container {
    float: left;
    width: 200px;
  }
  .menuHorizontal {
    display: flex !important;
    justify-content: center !important;
    ::v-deep .svg-icon {
      margin-right: 8px;
      //margin-left: 8px !important;
    }
    ::v-deep .el-submenu__title > span {
      //margin-right: 8px !important;
    }
    ::v-deep .submenu-title-noDropdown > span {
      //margin-right: 8px !important;
    }
    ::v-deep .el-submenu__icon-arrow {
      display: none;
    }
  }
  .right-menu {
@@ -227,6 +316,7 @@
    height: 100%;
    line-height: 50px;
    display: flex;
    //min-width: 300px;
    &:focus {
      outline: none;
@@ -275,33 +365,12 @@
      }
    }
  }
  //.footerButton {
  //  display: flex;
  //  justify-content: end;
  //}
  //
  //::v-deep .el-button--primary {
  //  background-color: $main_color !important;
  //  height: 30px;
  //  display: flex;
  //  align-items: center;
  //  //border: 1px solid $main_color;
  //  border: none;
  //  padding: 0 20px;
  //}
  //
  //::v-deep .el-button--default {
  //  background-color: #ffffff !important;
  //  height: 30px;
  //  display: flex;
  //  align-items: center;
  //  padding: 0 20px;
  //}
  //
  //::v-deep .el-input__inner {
  //  height: 30px;
  //  line-height: 30px;
  //}
}
</style>
<style>
.el-menu--horizontal .el-menu {
  background-color: transparent !important;
  margin-top: -2px;
}
</style>
src/layout/components/Settings/index.vue
@@ -19,6 +19,17 @@
          inactive-text="浅色"
        />
      </div>
      <div class="drawer-item" style="display: flex;justify-content: space-between">
        <span>菜单模式</span>
        <el-switch
          v-model="$store.state.settings.menuIsHorizontal"
          style="display: block"
          :active-color="$store.state.settings.theme"
          inactive-color="#ccc"
          active-text="横屏"
          inactive-text="竖屏"
        />
      </div>
      <!--      æ­¤ä¸‰ä¸ªåŠŸèƒ½æš‚æ—¶ä¸ç”¨-->
      <!--      <div class="drawer-item">-->
src/layout/components/Sidebar/MenuItemEx.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
<template>
  <div v-if="!item.hidden" :class="isNest === true ? 'dropdown' : 'noDropdown'">
    <template
      v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"
    >
      <app-link v-if="onlyOneChild.meta" :to="onlyOneChild.disabled ? '' : resolvePath(onlyOneChild.path)">
        <el-menu-item
          :index="resolvePath(onlyOneChild.path)"
          :class="{'submenu-title-noDropdown':!isNest}"
          :disabled="onlyOneChild.disabled"
        >
          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
        </el-menu-item>
      </app-link>
    </template>
    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
      <template slot="title">
        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
      </template>
      <menu-item-ex
        v-for="child in item.children"
        :key="child.path"
        :is-nest="true"
        :item="child"
        :base-path="resolvePath(child.path)"
        class="nest-menu"
      />
    </el-submenu>
  </div>
</template>
<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
  name: 'MenuItemEx',
  components: { Item, AppLink },
  mixins: [FixiOSBug],
  props: {
    // route object
    item: {
      type: Object,
      required: true
    },
    isNest: {
      type: Boolean,
      default: false
    },
    basePath: {
      type: String,
      default: ''
    }
  },
  data() {
    // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
    // TODO: refactor with render function
    this.onlyOneChild = null
    return {}
  },
  methods: {
    hasOneShowingChild(children = [], parent) {
      const showingChildren = children.filter(item => {
        if (item.hidden) {
          return false
        } else {
          // Temp set(will be used if only has one showing child)
          this.onlyOneChild = item
          return true
        }
      })
      // When there is only one child router, the child router is displayed by default
      if (showingChildren.length === 1) {
        return true
      }
      // Show parent if there are no child router to display
      if (showingChildren.length === 0) {
        this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
        return true
      }
      return false
    },
    resolvePath(routePath) {
      if (isExternal(routePath)) {
        return routePath
      }
      if (isExternal(this.basePath)) {
        return this.basePath
      }
      return path.resolve(this.basePath, routePath)
    }
  }
}
</script>
<style lang="scss" scoped>
//::v-deep .el-submenu__title:hover {
//  /* background-color: #ecf5ff; */
//  background-color: rgba(255, 255, 255, 0.3) !important;
//}
//
//.nest-menu .el-menu-item {
//  color: rgb(255, 255, 255);
//  background-color: rgb(84, 92, 100);
//}
//
//.nest-menu .el-menu-item:hover {
//  color: rgb(255, 255, 255);
//  background-color: rgba(84, 92, 100, 0.9);
//}
//
//.dropdown {
//  cursor: pointer;
//}
//
//.noDropdown {
//  float: left;
//  cursor: pointer;
//}
</style>
src/layout/components/Sidebar/index.vue
@@ -3,16 +3,17 @@
    <logo v-if="showLogo" :collapse="isCollapse" />
    <div v-if="$store.state.app.sidebar.opened" class="layoutLogo" />
    <div v-if="$store.state.app.sidebar.opened&&!$store.state.settings.menuIsHorizontal" class="layoutLogo" />
    <el-scrollbar wrap-class="scrollbar-wrapper" class="scrollbarWrapperClass">
      <el-menu
        v-if="!$store.state.settings.menuIsHorizontal"
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        :unique-opened="false"
        mode="vertical"
      >
        <!--        <sidebar-item-->
@@ -32,7 +33,7 @@
      </el-menu>
    </el-scrollbar>
    <div style="position: absolute;bottom: 20px;left: 30px;">
    <div v-if="!$store.state.settings.menuIsHorizontal" style="position: absolute;bottom: 20px;left: 30px;">
      <div>
        <el-button v-if="$store.state.app.sidebar.opened" type="text" style="font-size: 14px;cursor: pointer">
          æ–°å‡¯è¿ªåˆ¶é€ V1.0.01
@@ -127,9 +128,9 @@
//}
//#app .sidebar-container .el-submenu .el-menu-item.is-active {
  //background: $main_color !important;
  //color: #fff !important;
  //font-weight: bolder !important;
//background: $main_color !important;
//color: #fff !important;
//font-weight: bolder !important;
//}
//
//#app .sidebar-container .el-submenu .el-menu-item:hover {
src/layout/index.vue
@@ -1,8 +1,12 @@
<template>
  <div :class="classObj" class="app-wrapper">
    <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
    <sidebar class="sidebar-container" />
    <div class="main-container">
    <sidebar
      :class="$store.state.settings.menuIsHorizontal?'sidebar-container-sideBarWidthIsMenuHorizontal':'sidebar-container'"
    />
    <div
      :class="$store.state.settings.menuIsHorizontal?'main-container-sideBarWidthIsMenuHorizontal': 'main-container'"
    >
      <div :class="{'fixed-header':fixedHeader}">
        <navbar />
        <tags-view v-if="needTagsView" />
src/settings.js
@@ -26,6 +26,8 @@
   */
  tagsView: true,
  headBackgroundColorValue: false // å¤´éƒ¨èƒŒæ™¯é¢œè‰²
  headBackgroundColorValue: false, // å¤´éƒ¨èƒŒæ™¯é¢œè‰²
  menuIsHorizontal: true// èœå•是否是横屏
}
src/store/modules/settings.js
@@ -1,10 +1,11 @@
import defaultSettings from '@/settings'
import variables from '@/styles/element-variables.scss'
const { showSettings, tagsView, fixedHeader, sidebarLogo, headBackgroundColorValue } = defaultSettings
const { showSettings, tagsView, fixedHeader, sidebarLogo, headBackgroundColorValue, menuIsHorizontal } = defaultSettings
const state = {
  headBackgroundColorValue: headBackgroundColorValue, // å¤´éƒ¨èƒŒæ™¯é¢œè‰²
  menuIsHorizontal: menuIsHorizontal, // èœå•是否是横屏
  theme: variables.theme,
  showSettings: showSettings,
  fixedHeader: fixedHeader,
src/styles/sidebar.scss
@@ -7,6 +7,13 @@
    position: relative;
  }
  .main-container-sideBarWidthIsMenuHorizontal {
    min-height: 100%;
    transition: margin-left .28s;
    margin-left: $sideBarWidthIsMenuHorizontal;
    position: relative;
  }
  .sidebar-container {
    transition: width 0.28s;
    width: $sideBarWidth !important;
@@ -66,6 +73,90 @@
      border: none;
      height: 100%;
      width: 100% !important;
      }
    // menu hover
    .submenu-title-noDropdown,
    .el-submenu__title {
      &:hover {
        background-color: $menuHover !important;
      }
    }
    .is-active>.el-submenu__title {
      color: $subMenuActiveText !important;
    }
    & .nest-menu .el-submenu>.el-submenu__title,
    & .el-submenu .el-menu-item {
      min-width: $sideBarWidth !important;
      background-color: $subMenuBg !important;
      &:hover {
        background-color: $subMenuHover !important;
      }
    }
  }
  .sidebar-container-sideBarWidthIsMenuHorizontal {
    transition: width 0.28s;
    width: $sideBarWidthIsMenuHorizontal !important;
    background-color: $menuBg;
    height: 100%;
    position: fixed;
    font-size: 0px;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 1001;
    overflow: hidden;
    // reset element-ui css
    .horizontal-collapse-transition {
      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
    }
    .scrollbar-wrapper {
      overflow-x: hidden !important;
    }
    .el-scrollbar__bar.is-vertical {
      right: 0px;
    }
    .el-scrollbar {
      height: 100%;
    }
    &.has-logo {
      .el-scrollbar {
        height: calc(100% - 50px);
      }
    }
    .is-horizontal {
      display: none;
    }
    a {
      display: inline-block;
      width: 100%;
      overflow: hidden;
    }
    .svg-icon {
      margin-right: 16px;
    }
    .sub-el-icon {
      margin-right: 12px;
      margin-left: -2px;
    }
    .el-menu {
      border: none;
      height: 100%;
      width: 100% !important;
    }
    // menu hover
src/styles/variables.scss
@@ -1,15 +1,17 @@
// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
$menuText: #bfcbd9;
$menuActiveText: #409EFF;
$subMenuActiveText: #f4f4f5; //https://github.com/ElemeFE/element/issues/12951
$menuBg:#304156;
$menuHover:#263445;
$menuBg: #304156;
$menuHover: #263445;
$subMenuBg:#1f2d3d;
$subMenuHover:#001528;
$subMenuBg: #1f2d3d;
$subMenuHover: #001528;
$sideBarWidth: 210px;
$sideBarWidthIsMenuHorizontal: 0; //当菜单横屏时的侧边栏高度
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
@@ -22,4 +24,5 @@
  subMenuBg: $subMenuBg;
  subMenuHover: $subMenuHover;
  sideBarWidth: $sideBarWidth;
  sideBarWidthIsMenuHorizontal:$sideBarWidthIsMenuHorizontal;
}