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

效果如图
在这里插入图片描述
点击左侧导航菜单,右侧对应的模块会滚动到顶部位置
滚动右侧区域,模块标题到达顶部,对应左侧菜单会高亮
代码如下

<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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF 左侧抽屉菜单是一种常见的 UI 组件,它通常用于显示和隐藏系统的导航栏菜单。实现该组件的关键在于使用 WPF 的布局管理器和控件模板。 首先,在 XAML 文件中创建一个根布局容器,例如 `<Grid>` 或 `<StackPanel>`。接着,在该容器的左侧添加一个按钮,该按钮的大小、样式和位置决定了抽屉菜单的开关按钮的外观。这个按钮的点击事件会触发抽屉菜单的显示或隐藏。 然后,在该容器的右侧,创建一个子控件用于显示菜单项,例如 `<ListBox>` 或 `<TreeView>`。该控件的边界应该与整个容器的边界对齐,并包含一些菜单项。当抽屉菜单被显示时,该控件会向右滑出,因此需要将其水平位移设置为负的宽度(例如 `-200px`)。 最后,在 XAML 文件中定义抽屉菜单的控件模板。该模板应包含开关按钮和菜单项控件。同时,该模板应使用触发器或绑定器等技术来实现按钮的点击事件和菜单项的选中事件。可以使用数据绑定技术来动态生成菜单项,将菜单项的文本、图标和命令绑定到 ViewModel 中的属性和方法上。 当用户单击开关按钮时,可以使用动画效果来显示或隐藏抽屉菜单。例如可以使用 WPF 的转换动画(TranslateTransform)来将菜单的位置从左侧滑动到右侧或从右侧滑动到左侧。同时,该转换动画可以配合时间线动画(StoryBoard)一起使用,从而实现更加自然的菜单显示隐藏效果。 总之,WPF 左侧抽屉菜单的实现需要用到布局管理器、控件模板、数据绑定和动画效果等技术,通过精心的设计和实现,可以使应用程序的用户界面更加美观和易用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值