PC端实现左右两侧导航双向滚动

效果如图
PC端实现左右两侧导航双向滚动_第1张图片
点击左侧导航菜单,右侧对应的模块会滚动到顶部位置
滚动右侧区域,模块标题到达顶部,对应左侧菜单会高亮
代码如下

<template>
  <div class="wapper-content">
    <header>
      <strong>菜单资源</strong>
      <div class="table-actions">
        <el-button type="primary" :disabled="!$permission('base_menu_add')" icon="iconfont el-icon-icon-system-add" @click="clickAddApp">新增</el-button>
      </div>
    </header>

    <section v-loading="pageLoading">

      <aside>

        <ul>
          <li v-for="(item,idx) in menuGroup" :key="idx" :class="idx == index ? 'selected' : ''" @click.stop="changeId(idx)">
            <div class="row-li">
              {{ item }}
            </div>
          </li>
        </ul>

      </aside>

      <main
        ref="itemList"
        :style="
          index > 0
            ? 'box-shadow: 0 -2px 0 rgba(84, 151, 215,.2)'
            : ''
        "
        @scroll="handleScroll($event)"
      >
        <div ref="rigth">
          <div v-for="item in menuList" :key="item.id">
            <div class="card">

              <strong>{{ item.groupName }}</strong>
              <el-row :gutter="16" style="margin:16px 0">
                <el-col v-for="menu in item.children" :key="menu.id" :span="6">
                  <el-card class="card-content" @click.native="toEdit(menu)">
                    <el-row type="flex" style="margin-bottom:26px">
                      <svg class="icon svg-icon" aria-hidden="true" style="font-size:42px">
                        <use :xlink:href="`#${menu.icon}`" />
                      </svg>
                      <span class="ml-16">
                        <strong>{{ menu.name }}</strong>
                        <article>
                          {{ menu.description }}
                        </article>
                      </span>

                    </el-row>
                    <footer>
                      <el-button class="miniBtn" @click.stop="toEditApp(menu)">
                        编辑
                      </el-button>
                      <el-button class="miniBtn" @click.stop="delApp(menu)">
                        删除
                      </el-button>
                    </footer>

                  </el-card>
                </el-col>
              </el-row>

            </div>
          </div>
        </div>
      </main>

    </section>

    <!-- 新增编辑抽屉 -->
    <menuDrawer v-if="menuParam.visible" v-bind="menuParam" :visible.sync="menuParam.visible" @success="getMenuList" />

  </div>
</template>

<script>
import {

  apiBaseMenuNoPage,
  apiBaseMenuDelApp
} from '@/api/vone/base/meun'
import menuDrawer from './menuDrawer.vue'
export default {
  components: {
    menuDrawer
  },
  data() {
    return {
      menuGroup: [
        '效能管理', '基础应用', '持续交付', '协同管理', '效率工具', '外部应用'
      ],
      menuList: [],
      index: 0,
      scrollY: 0, // 左侧列表滑动的y轴坐标
      scorllEvent: false,
      menuParam: { visible: false },
      pageLoading: true
    }
  },
  watch: {
    scrollY() {
      this.initRightBoxHeight()
    }
  },
  mounted() {
    this.getMenuList()
    // 启动鼠标滚动监听
    window.addEventListener(
      'mousewheel',
      this.setScorllEvent,
      true
    ) ||
      window.addEventListener(
        'DOMMouseScroll',
        this.setScorllEvent,
        false
      )
  },
  methods: {

    // 新增菜单组
    clickAddApp() {
      this.menuParam = { visible: true, title: '新增菜单' }
    },
    toEditApp(item) {
      this.menuParam = { visible: true, title: '编辑菜单', id: item.id }
    },

    changeId(idx) {
      this.index = idx
      // 点击事件标识非滚动事件
      this.scorllEvent = false
      this.initRightBoxHeight()
      // this.$refs.rigth.scrollTop = this.rightLiTops[idx]

      this.$refs['itemList'].scrollTo({
        behavior: 'smooth', // 平滑过渡
        top: this.rightLiTops[idx]
        // block: 'start' // 上边框与视窗顶部平齐。默认值
      })

      // console.log(this.$refs.rigth, ' this.$refs.rigth')

      console.log(this.$refs.rigth.scrollTop, 'this.$refs.rigth.scrollTop2')
    },

    async getMenuList() {
      this.pageLoading = true
      const { data, isSuccess, msg } = await apiBaseMenuNoPage({
        parentId: 0
      })
      this.pageLoading = false
      if (!isSuccess) {
        this.$message.warning(msg)
        return
      }

      const groupMap = {
        base: '基础应用',
        workflow: '基础应用',
        cmdb: '基础应用',
        wiki: '效率工具',
        si: '效率工具',
        project: '协同管理',
        producm: '协同管理',
        projectm: '协同管理',
        reqm_center: '协同管理',
        productfit: '协同管理',
        testm_test_library: '协同管理',
        release: '协同管理',
        code: '持续交付',
        pipeline: '持续交付',
        package: '持续交付',
        measure_center: '效能管理',
        man_hour: '效能管理',
        dashboard: '效能管理'
      }

      data.forEach(element => {
        element.groupName = groupMap[element.code] || '外部应用'
      })

      const map = {}
      data.forEach((item) => {
        map[item.groupName] = map[item.groupName] || []
        map[item.groupName].push(item)
      })

      const DATA = Object.keys(map).map((orgName) => {
        return {
          groupName: orgName,
          children: map[orgName]
        }
      })

      this.menuList = DATA
    },
    setScorllEvent() {
      this.scorllEvent = true
    },
    handleScroll(event) {
      // console.log(this.scorllEvent, 'this.scorllEvent ')
      console.log(event, 'event')
      this.scrollY = event.target.scrollTop

      console.log(this.scrollY, 'this.scrollY ')
    },
    /* 计算每个小盒子高度 */
    initRightBoxHeight() {
      // console.log(this.scorllEvent, '-------12')
      const itemArray = []
      let top = 0
      itemArray.push(top)
      // 获取右边所有子盒子高度集合
      const allList = this.$refs.itemList.getElementsByClassName('card')
      // console.log(allList, 'allList----')
      // allList伪数组转化成真数组
      Array.prototype.slice.call(allList).forEach((li) => {
        top += li.clientHeight // 获取所有li的每一个高度
        itemArray.push(top)
      })
      // console.log(itemArray, 'itemArray-----')
      this.rightLiTops = itemArray
      this.rightLiTops.forEach((item, index) => {
        if (item < this.scrollY) {
          // 当为鼠标滚动时重新赋值
          if (this.scorllEvent) {
            this.index = index
          }
        }
      })
      // console.log(this.index)
    },
    async delApp(item) {
      await this.$confirm(`删除【${item.name}】前请先删除模块下所有的【菜单和功能】`, '提示', {
        type: 'warning'
      })
      const res = await apiBaseMenuDelApp([
        item.id
      ])
      if (!res.isSuccess) {
        this.$message.warning(res.msg)
        return
      }
      this.$message.success('删除成功')
      this.getMenuList()
    },
    toEdit(item) {
      if (!this.$permission('base_menu_config')) return
      this.$router.push({
        name: 'base_menu_config',
        params: {
          id: item.id,
          path: item.path
        }
      })
    }
  }

}
</script>

<style lang="scss" scoped>
@import "~@/styles/variables.scss";

.wapper-content {
  min-height: calc(100vh - #{$hasHeader});
  border-radius: 4px;
  background-color: var(--main-bg-color);
  header {
    height: 54px;
    line-height: 54px;
    padding: 0 16px;
    border-bottom: 1px solid var(--el-divider);
    display: flex;
    justify-content: space-between;
    .table-actions {
      line-height: 54px;
    }
  }
  section {
    display: flex;
    aside {
      width: 216px;
      border-right: 1px solid var(--el-divider);
      height: calc(100vh - #{$hasHeader} - 54px);
      overflow-y: auto;
      ul {
        .selected {
          background-color: var(--tab-bg-color);
          color: var(--main-theme-color);
        }
        li {
          .row-li {
            line-height: 22px;
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: center;
            padding: 8px 16px;
            cursor: pointer;
            span {
              a {
                display: none;
              }
            }
            a {
              color: var(--main-theme-color);
            }

            a + a {
              margin-left: 6px;
            }
          }
          .row-input {
            padding: 8px 16px;
          }

        }
        .row-li:hover {
          background-color: var(--tab-bg-color);
          a {
            display: inline-block;
          }
        }
      }
    }

    main {
      position: relative;
      padding: 16px;
      flex: 1;
      height: calc(100vh - #{$hasHeader} - 54px);
      overflow-y: scroll;

      .el-row + .el-row {
        margin-top: 16px;
      }
      article {
        margin: 10px 0;
        color: var(--auxiliary-font-color);
        line-height: 22px;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box; //使用自适应布局
        -webkit-line-clamp: 2; //设置超出行数,要设置超出几行显示省略号就把这里改成几
        -webkit-box-orient: vertical;
      }
      .miniBtn {
        color: var(--tab-font-color);
        border-color: var(--input-border-color);
      }
      .miniBtn:hover {
        color: var(--main-theme-color);
        border-color: var(--main-theme-color);
      }
      .card-content {
        height: 142px;
        position: relative;

        .svg-icon{
          width: 50px;
        }
        .ml-16{
          flex: 1;
        }
        footer {
          position: absolute;
          bottom: 16px;
          right: 16px;
        }
      }
      .el-col + .el-col {
        margin-bottom: 16px;
      }
      .el-card :hover {
        cursor: pointer;
      }
    }
  }
}
</style>

你可能感兴趣的:(vue,javascript,vue.js,前端)