当前位置: 首页>后端>正文

ant design transfer修改title样式 ant design menu

antd框架——实现自定义菜单功能

最近在写后台管理系统,看到同事写的antd的框架,是在antd原有框架的基础上进行的修改。由于原有的框架中的菜单是menu.js的写法,类似于react的形式,后面要进行改动样式,因此自定义了一个menuNew的菜单组件,方便用于样式的修改。

ant design transfer修改title样式 ant design menu,ant design transfer修改title样式 ant design menu_ide,第1张

1.左侧菜单可以通过a-layout-sider组件来展示

ant design transfer修改title样式 ant design menu,ant design transfer修改title样式 ant design menu_css3_02,第2张

代码如下:

1.1 html部分的代码
<a-layout-sider
    :theme="sideTheme"
    :class="['side-menu', isMobile ? null : 'shadow']"
    width="220px"
    :collapsible="collapsible"
    v-model="collapsed"
    :trigger="null"
  >
    <div :class="['logo', theme]">
      <router-link to="/dashboard/workplace">
        <img src="@/assets/img/logo.png" />
        <h1>{{ systemName }}</h1>
      </router-link>
    </div>
    <div :class="['side-menu-content', 'beauty-scroll']">
      <MenuItem
        :theme="theme"
        :collapsed="collapsed"
        :options="menuData"
        @select="onSelect"
        class="menu"
      />
    </div>
  </a-layout-sider>
1.2 js部分的代码
<script>
import MenuItem from './MenuItem';
import { mapState } from 'vuex';
export default {
  name: 'SideMenu',
  components: { MenuItem },
  props: {
    collapsible: {
      type: Boolean,
      required: false,
      default: false,
    },
    collapsed: {
      type: Boolean,
      required: false,
      default: false,
    },
    menuData: {
      type: Array,
      required: true,
    },
    theme: {
      type: String,
      required: false,
      default: 'dark',
    },
  },
  computed: {
    sideTheme() {
      return this.theme == 'light' ? this.theme : 'dark';
    },
    ...mapState('setting', ['isMobile', 'systemName']),
  },
  methods: {
    onSelect(obj) {
      this.$emit('menuSelect', obj);
    },
  },
};
</script>
1.3 css部分的代码
// .shadow{
//   box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
// }
.side-menu{
  min-height: 100vh;
  // overflow-y: auto;
  z-index: 9;
  .logo{
    height: 64px;
    position: relative;
    line-height: 64px;
    padding-left: 24px;
    -webkit-transition: all .3s;
    transition: all .3s;
    overflow: hidden;
    background-color: #0B2540;
    &.light{
      background-color: #0B2540;
      h1{
        color: @primary-color;
      }
    }
    h1{
      color: @menu-dark-highlight-color;
      font-size: 20px;
      margin: 0 0 0 12px;
      display: inline-block;
      vertical-align: middle;
    }
    img{
      width:159px;
      vertical-align: middle;
    }
  }
  .side-menu-content{
    overflow-y: auto;
    height: calc(100vh - 64px);
  }
}
.ant-layout-sider{background: #213346;}
// .menu{
//   padding: 16px 0;
//   background-color: #fff;
// }

2.menuItem组件部分

2.1 html部分代码
<template>
  <div class="menu-wrap">
    <div
      class="menu-item-wrap"
      v-for="(item, index) in menuData"
      :key="`menu-${index}`"
    >
      <div
        class="menu-item"
        :class="{ active: item.active }"
        @click="handleRouter($event, item)"
        @mouseover="mouseover($event, item)"
      >
        <SvgIcon
          class="menu-icon"
          v-if="item.meta && item.meta.icon"
          :iconClass="item.meta.icon"
        />
        <div class="menu-text">{{ item.name }}</div>
        <SubItem
          class="menu-sub-item"
          :style="{ left: '80px', top: pageY + 'px' }"
          v-if="
            item.children &&
              item.children.length > 0 &&
              item.meta.type != 'link'
          "
          :list="item.children"
          :index="index"
          :collapsed="collapsed"
        />
      </div>
    </div>
  </div>
</template>
2.2 js部分代码
<script>
import SubItem from './SubItem'
export default {
  components: {
    SubItem
  },
  props: {
    options: {
      type: Array,
      required: true
    },
    theme: {
      type: String,
      required: false,
      default: 'dark'
    },
    collapsed: {
      type: Boolean,
      required: false,
      default: false
    }
  },
  watch: {
    $route() {
      this.menuData.forEach(item => {
        let active = false
        if (this.$route.fullPath.startsWith(item.fullPath)) {
          active = true
        }
        item.active = active
      })
    }
  },
  data() {
    return {
      menuData: [],
      openSelect: false,
      pageY: undefined,
      currentItem: null
    }
  },
  created() {
    this.menuData = JSON.parse(JSON.stringify(this.options))
      .map(item => {
        let active = false
        if (this.$route.fullPath.startsWith(item.fullPath)) {
          active = true
        }
        return {
          ...item,
          active: active
        }
      })
      .filter(item => {
        if (item.meta.type == 'link') {
          return true
        }
        if (item.meta.invisible) {
          return false
        }
        if (item.children?.length > 0) {
          return this.isShow(item.children)
        } else if (item.children) {
          return false
        } else {
          return true
        }
      })
  },
  methods: {
    isShow(list) {
      return list.some(item => {
        if (item.children?.length > 0) {
          return this.isShow(item.children)
        } else {
          return !item.meta.invisible
        }
      })
    },
    mouseover(e, item) {
      if (this.currentItem == item.name) {
        return
      }
      let height = window.innerHeight || document.body.clientHeight
      let pageY
      let doms
      if (e.currentTarget.getAttribute('class').includes('menu-item-wrap')) {
        pageY = e.clientY - e.offsetY
      } else if (
        e.currentTarget.parentElement
          .getAttribute('class')
          .includes('menu-item-wrap')
      ) {
        doms = e.currentTarget.parentElement
        pageY = doms.getBoundingClientRect().top
      }

      let domHeight
      if (doms && doms.getElementsByClassName('sub-item-content')[0]) {
        domHeight = doms
          .getElementsByClassName('sub-item-content')[0]
          .getBoundingClientRect().height
      } else {
        domHeight = 0
      }
      if (domHeight + pageY > height) {
        this.pageY = height - domHeight
      } else if (pageY < 64) {
        this.pageY = 64
      } else {
        this.pageY = pageY
      }

      this.currentItem = item.name
    },
    handleRouter(e, item) {
      if (item.redirect) {
        this.$router.push(item.fullPath)
      } else {
        if (item.meta.type == 'link') {
          this.$router.push(item.fullPath)
        }
      }
    }
  }
}
</script>

这个代码中有以下相关内容:

2.2.1 vue项目——鼠标移入时判断是否含有某个类名
e.currentTarget.getAttribute('class').includes('menu-item-wrap')
2.2.2 vue项目——鼠标移入时判断当前元素的父级元素是否含有某个类名

当鼠标移入时,需要判断当前元素是否含有指定类名,如果不含有,则需要判断该元素的父级元素是否含有指定类名

e.currentTarget.parentElement.getAttribute('class').includes('menu-item-wrap')
判断当前元素的父级元素是否含有某个类名
2.2.3 vue项目——判断父级元素距离页面顶部的距离
let doms = e.currentTarget.parentElement;//获取当前元素的父级元素
let pageY = doms.getBoundingClientRect().top
//通过doms.getBoundingClientRect()可以获取该元素的很多参数,包含宽度高度 距离顶部 距离左边的大小等。
判断父级元素距离页面顶部的距离
2.2.4 vue项目——鼠标移入时判断当前元素距离页面顶部的距离
let pageY = e.clientY - e.offsetY
判断当前元素距离页面订单的距离
2.2.5 vue项目——判断当前元素含有sub-item-content的元素的高度
let domHeight = doms.getElementsByClassName('sub-item-content')[0].getBoundingClientRect().height
判断当前元素的父级元素且类名为sub-item-content的元素的高度,如果该元素距离页面顶部的距离+元素高度超过了屏幕的高度,则该元素距离页面顶部的距离要改成 屏幕的可视高度-元素的高度,也就是元素与底部贴合,否则元素就是根据距离顶部的距离来渲染。

ant design transfer修改title样式 ant design menu,ant design transfer修改title样式 ant design menu_ide_03,第3张

ant design transfer修改title样式 ant design menu,ant design transfer修改title样式 ant design menu_html_04,第4张

ant design transfer修改title样式 ant design menu,ant design transfer修改title样式 ant design menu_sed_05,第5张

2.2.6 vue项目——获取浏览器的可视区域的高度
let height = window.innerHeight || document.body.clientHeight
获取浏览器的可视区域的高度
2.3 css部分代码
<style lang="less" scoped>
.menu-wrap {
  position: relative;
  padding-top: 13px;
  .menu-item {
    width: 64px;
    height: 64px;
    padding-top: 12px;
    margin: 0 auto 10px;
    border-radius: 8px;
    text-align: center;
    cursor: pointer;
    .menu-icon {
      font-size: 20px;
      color: #fff;
    }
    .menu-text {
      color: #fff;
      font-size: 14px;
      line-height: 19px;
    }
    .menu-sub-item {
      display: none;
    }
  }
  .menu-item-wrap {
    &:hover {
      .menu-item {
        background-color: #2b3e51;
      }
      .menu-sub-item {
        display: block;
        position: fixed;
      }
    }
  }
  .menu-item.active {
    background-color: #2b3e51;
  }
}
</style>

3.SubItem组件代码

3.1 html部分代码
<template>
  <div
    class="sub-item-wrap"
    :style="{ left: collapsed ? '80px' : '220px' }"
    v-if="visible"
  >
    <!-- :style="{top: top + 'px',left:collapsed ? '80px' : '220px'}" -->
    <div class="sub-item-content">
      <div class="item-box">
        <div
          class="item-list"
          v-for="(item, index) in subMenuData"
          :key="index"
        >
          <div
            class="sub-item"
            :class="{
              'parent-node': subItem.hasChild,
              'child-node': !subItem.hasChild,
              'hide-node': subItem.childFlag,
              active: $route.path == subItem.fullPath
            }"
            v-for="(subItem, i) in item"
            :key="i"
            @click.stop="handleRouter(subItem)"
          >
            <span>{{ subItem.name }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
3.2 js部分代码
<script>
export default {
  props: {
    collapsed: {
      type: Boolean,
      required: false,
      default: false
    },
    list: Array,
    index: Number
  },
  data() {
    return {
      visible: false,
      subMenuData: [],
      top: 77
    }
  },
  mounted() {
    this.showSubItem(this.list, this.index)
  },
  methods: {
    showSubItem(data, index) {
      let top = 64 + 13 + index * 74 // 头部高度+搜索高度+当前点击下标*菜单高度
      const arr = this.getMenuData(JSON.parse(JSON.stringify(data)))
      const num = arr.length
      const contentHeight = 40 * num // 子菜单内容高度
      const clientHeight =
        document.documentElement.clientHeight || document.body.clientHeight // 可视区高度
      if (top + contentHeight + 16 + 5 <= clientHeight) {
        this.top = top
      } else {
        if (contentHeight + 16 + 64 <= clientHeight) {
          this.top = clientHeight - contentHeight - 16 - 5
        } else {
          this.top = 69
        }
      }
      this.visible = true
      // 处理多列子菜单
      let subMenuData = []
      let i = 0
      subMenuData[i] = []
      arr.forEach((item, index) => {
        subMenuData[i].push(item)
        if ((index + 1) % 6 == 0) {
          i++
          if (arr[index + 1]) {
            subMenuData[i] = []
          }
        }
      })
      this.subMenuData = subMenuData
    },
    getMenuData(data) {
      const arr = []
      data.forEach((item, i) => {
        if (!item.meta?.invisible) {
          arr.push({
            ...item,
            hasChild: item.children?.length > 0
          })
          if (item.children && item.children.length > 0) {
            arr.push(...this.getMenuData(item.children))
          }
        }
      })
      return arr
    },
    handleRouter(v) {
      if (v.hasChild) return
      if (this.$route.fullPath == v.fullPath) {
        return false
      }
      this.$router.push(v.fullPath)
    }
  }
}
</script>
3.3 css部分代码
<style lang="less" scoped>
.sub-item-wrap {
  position: absolute;
  top: 10px;
  .sub-item-content {
    width: 200px;
    // height: calc(100vh - 84px);
    max-height: 500px;
    padding: 8px 10px;
    box-sizing: border-box;
    box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.16);
    border-radius: 8px;
    overflow: auto;
    background-color: #fff;
    border-radius: 10px;
    position: fixed;
  }
  .sub-item-close {
    position: absolute;
    top: 12px;
    right: 14px;
  }
  .sub-item {
    height: 40px;
    padding: 0 38px;
    box-sizing: border-box;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .parent-node {
    font-size: 14px;
    color: @primary-color;
    justify-content: flex-start;
    color: #999999;
    padding-left: 20px;
    &::before {
      content: '';
      display: block;
      width: 5px;
      height: 5px;
      background: #999999;
      border-radius: 50%;
      margin-right: 6px;
    }
  }
  .hide-node {
    display: none;
  }
  .child-node {
    color: #333;
    cursor: pointer;
    padding-left: 44px;
    &:hover {
      color: #f90 !important;
      background-color: #f5f5f5;
      border-radius: 4px;
    }
  }
  .sub-item.active {
    color: #f90 !important;
    // background-color: #f90;
    border-radius: 4px;
  }
}
</style>

效果图如下:

完成!!!多多积累,多多收获!!!



https://www.xamrdz.com/backend/36g1923213.html

相关文章: