左右布局拖拽(VUE2)

javascript 复制代码
<!-- 通用布局:左右拖拽 -->
<template>
  <div :class="className">
    <div class="box">
      <div v-show="isExpand" ref="leftPane" :style="{ width: leftPaneWidth + 'px' }" class="pane left-pane">
        <div class="el-icon-s-fold shrinkage" @click="setExpandState" />
        <slot name="left" />
      </div>

      <div v-show="isExpand" ref="divider" class="divider" @mousedown="startDrag" />

      <div v-show="!isExpand" class="el-icon-s-unfold openThe2" @click="setExpandState" />

      <div class="pane right-pane">
        <slot name="right" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    className: {
      type: String,
      default: 'boss'
    },
    // 左侧布局最小宽度
    minLeftWidth: {
      type: Number,
      default: 100
    },
    // 左侧初始化宽度
    defaultLeftWidth: {
      type: [Number, String],
      default: 260
    },
    // 右侧布局最小宽度
    minRightWidth: {
      type: Number,
      default: 150
    }
  },
  data() {
    return {
      isExpand: true, // 展开收缩state
      dragging: false,
      startDragX: 0,
      // 添加一个变量来记录上次更新的时间戳
      lastUpdate: 0,
      startLeftPaneWidth: 0,
      startRightPaneWidth: 0
    }
  },
  computed: {
    leftPaneWidth() {
      return Math.max(this.startLeftPaneWidth, this.minLeftWidth)
    },
    rightPaneWidth() {
      const availableWidth = this.$el.offsetWidth - this.$refs.divider.offsetWidth - this.leftPaneWidth
      return Math.max(availableWidth, this.minRightWidth, 0)
    }
  },
  watch: {
    defaultLeftWidth(newVal) {
      this.startLeftPaneWidth = this.calculateWidth(newVal)
    }
  },
  mounted() {
    this.initView()
  },
  beforeUnmount() {
    document.removeEventListener('mousemove', this.handleDrag)
    document.removeEventListener('mouseup', this.stopDrag)

    this.$refs.divider.removeEventListener('mouseup', this.dividerMouseupBlur)
  },
  methods: {
    initView() {
      this.startLeftPaneWidth = this.calculateWidth(this.defaultLeftWidth)
      this.startRightPaneWidth = this.$el.offsetWidth - this.$refs.divider.offsetWidth - this.startLeftPaneWidth
      document.addEventListener('mousemove', this.handleDrag)
      document.addEventListener('mouseup', this.stopDrag)

      this.$refs.divider.addEventListener('mouseup', this.dividerMouseupBlur)
    },
    // 设置展开收缩状态
    setExpandState() {
      this.isExpand = !this.isExpand
    },
    calculateWidth(value) {
      if (typeof value === 'number') {
        return value
      } else if (typeof value === 'string' && value.endsWith('%')) {
        return (this.$el.offsetWidth * parseFloat(value)) / 100
      } else {
        throw new Error('Invalid defaultLeftWidth value. Please use a valid number or a percentage string (e.g., "50%" or 100).')
      }
    },

    /**
     * 开始拖拽
     * @param e
     */
    startDrag(e) {
      this.dragging = true
      this.startDragX = e.clientX
      this.startLeftPaneWidth = this.$refs.leftPane.offsetWidth
      this.startRightPaneWidth = this.$el.offsetWidth - this.$refs.divider.offsetWidth - this.startLeftPaneWidth
    },

    /**
     * 拖动中
     * @param e
     */
    handleDrag(e) {
      if (!this.dragging) return

      const currentTime = performance.now()
      const deltaTime = currentTime - this.lastUpdate

      // 使用节流控制更新频率为8.33ms(120帧/秒)
      // 使用节流控制更新频率为6.67ms(150帧/秒)
      // 使用节流控制更新频率为16ms(60帧/秒)
      if (deltaTime >= 8.33) {
        this.lastUpdate = currentTime

        // 使用requestAnimationFrame在下一帧执行更新
        requestAnimationFrame(() => {
          const deltaX = e.clientX - this.startDragX
          const containerWidth = this.$el.offsetWidth
          const newLeftPaneWidth = this.startLeftPaneWidth + deltaX
          const newRightPaneWidth = this.startRightPaneWidth - deltaX

          const maxLeftPaneWidth = containerWidth - this.minLeftWidth
          const minRightPaneWidth = this.minRightWidth

          if (newLeftPaneWidth >= this.minLeftWidth && newLeftPaneWidth <= maxLeftPaneWidth && newRightPaneWidth >= minRightPaneWidth) {
            this.startLeftPaneWidth = newLeftPaneWidth
            this.startRightPaneWidth = newRightPaneWidth
            this.startDragX = e.clientX
          }
        })
      }
    },

    /**
     * 拖动结束
     */
    stopDrag() {
      this.dragging = false
    },

    /**
     * 拖动完成页面失焦
     */
    dividerMouseupBlur() {
      document.activeElement.blur()
    }
  }
}
</script>

<style lang="scss" scoped>
.boss {
  height: calc(100vh - 128px);
}

.box {
  padding-bottom: 0 !important;
  display: flex;
  width: 100%;
  height: 100%;
  background-color: white;
  .b1 {
    padding: 15px;
    width: 100%;
  }
}

.pane {
  flex-grow: 1;
  overflow: hidden;
  height: 100%;

  position: relative;
}

.left-pane {
  flex-grow: 0;
  flex-shrink: 0;
}

.divider {
  width: 4px;
  background-color: #f2f2f2;
  cursor: ew-resize;
  flex-shrink: 0;
}

/* 展开收起按钮 */
.el-icon-s-fold,
.el-icon-s-unfold {
  cursor: pointer;
  font-size: 20px;
}

.el-icon-s-fold {
  position: absolute;
  top: 13px;
  right: 14px;
  z-index: 99;
}

.el-icon-s-unfold {
  padding: 13px 4px 0;
}
</style>

使用方式:

html 复制代码
// DragLeftRight是引入的左右栏组件名称
<template>
  <DragLeftRight>
    <template slot="left">
      <div class="header">
        名称
      </div>
      <div class="simline">
        <div class="point" />
        <div class="point" />
        <div class="point" />
        <div class="line" />
      </div>
      <el-scrollbar style="height: calc(100vh - 135px); overflow: hidden;" :noresize="true">
        <el-tree
          ref="tree"
          :data="data"
          :highlight-current="true"
          node-key="label"
          :props="defaultProps"
          :default-expand-all="true"
          @node-click="handleNodeClick"
        />
      </el-scrollbar>
    </template>
    <template slot="right">
      <div style="padding: 10px 2px;">
        <div class="btn">
          <div />
          <div style="margin-left: 10px; margin-bottom: 10px;" class="expbtn">
            <el-button theme="lcs" type="primary" icon="el-icon-plus" plain @click="PLAdd">新增</el-button>
          </div>
        </div>
        <!-- 表格 -->
        <div class="tableDiv">
          <el-table
            ref="table"
            class="custom-header-table"
            theme="lcs"
            :data="tableDataList"
            :header-cell-style="{ background: '#F5F7FA', height: '30px' }"
            style="width: 100%; margin: 0 auto"
            height="100%"
            align="center"
            row-key="node_code"
            stripe
            border
          >
            <el-table-column type="index" align="center" label="序号" />
            <template>
              <el-table-column
                v-for="(item, index) in viewColumns"
                :key="index"
                :fixed="item.fixed"
                :prop="item.prop"
                :align="item.align"
                :sortable="item.sortable"
                :label="item.label"
                :min-width="item.width"
                :show-overflow-tooltip="true"
              />
            </template>
            <el-table-column
              align="center"
              label="操作"
              width="80"
              fixed="right"
            >
              <template slot-scope="scope">
                <div>
                  <a class="mc" title="修改" @click="editdiaopen(scope.row)"><em class="el-icon-edit" />修改</a>
                </div>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>
    </template>
  </DragLeftRight>
</template>
css 复制代码
<style lang="scss" scoped>
.header {
  line-height: 40px;
  font-size: 14px;
  font-weight: 700;
  margin-left: 5px;
}
.simline {
  overflow: hidden;
  display: flex;
  flex-wrap: nowrap;
  // margin-top: 10px;
  .point {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: #00b4e1;
    margin-right: 3px;
  }
  .line {
    width: 100%;
    height: 2px;
    background: #00b4e1;
  }
}

::v-deep{
  .el-scrollbar__wrap{
    overflow-x: hidden !important;
  }
  .tableDiv{
    height: calc(100vh - 183px);
  }
}
.pageblock{
  display: flex;
  justify-content: flex-end;
}
</style>
相关推荐
ZHOUPUYU4 小时前
PHP 8.3网关优化:我用JIT将QPS提升300%的真实踩坑录
开发语言·php
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
萧曵 丶8 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
l1t8 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿9 小时前
Jsoniter(java版本)使用介绍
java·开发语言
Amumu121389 小时前
Vue3扩展(二)
前端·javascript·vue.js
NEXT069 小时前
JavaScript进阶:深度剖析函数柯里化及其在面试中的底层逻辑
前端·javascript·面试
ceclar1239 小时前
C++使用format
开发语言·c++·算法
码说AI10 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS10 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化