tnblog
首页
视频
资源
登录

vue3左右布局,左右两边宽度不固定,可以拖动中间的分割线实现拖动改变宽度

247人阅读 2025/5/28 9:22 总访问:931274 评论:0 收藏:0 手机
分类: 前端

第一个版本如下

  1. <template>
  2. <div class="split-panel-container">
  3. <div class="panel-left" :style="{ width: `${leftWidth}%` }">
  4. <div class="panel-content" v-bind:style="isResizing ? noSelectStyle : {}">
  5. <slot name="left">
  6. <div class="default-content">左侧面板内容</div>
  7. </slot>
  8. </div>
  9. </div>
  10. <div class="resize-handle" @mousedown="startResize">
  11. <div class="handle-icon"></div>
  12. </div>
  13. <div class="panel-right" :style="{ width: `${rightWidth}%` }">
  14. <div class="panel-content" v-bind:style="isResizing ? noSelectStyle : {}">
  15. <slot name="right">
  16. <div class="default-content">右侧面板内容</div>
  17. </slot>
  18. </div>
  19. </div>
  20. </div>
  21. </template>
  22. <script setup lang="ts">
  23. import { ref, computed, onMounted, onUnmounted } from 'vue'
  24. const props = defineProps({
  25. defaultLeftWidth: {
  26. type: Number,
  27. default: 70
  28. },
  29. minLeftWidth: {
  30. type: Number,
  31. default: 30
  32. },
  33. minRightWidth: {
  34. type: Number,
  35. default: 20
  36. }
  37. })
  38. const leftWidth = ref(props.defaultLeftWidth)
  39. const rightWidth = ref(100 - props.defaultLeftWidth)
  40. const isResizing = ref(false)
  41. // 禁止选择样式
  42. const noSelectStyle = computed(() => ({
  43. 'user-select': 'none',
  44. '-webkit-user-select': 'none',
  45. 'pointer-events': 'none'
  46. }))
  47. const startResize = (e: MouseEvent) => {
  48. isResizing.value = true
  49. document.body.style.cursor = 'col-resize'
  50. document.addEventListener('mousemove', handleResize)
  51. document.addEventListener('mouseup', stopResize)
  52. e.preventDefault() // 阻止默认行为,防止选中内容
  53. }
  54. const handleResize = (e: MouseEvent) => {
  55. if (!isResizing.value) return
  56. const container = document.querySelector('.split-panel-container') as HTMLElement
  57. const containerRect = container.getBoundingClientRect()
  58. const containerWidth = containerRect.width
  59. // 计算鼠标在容器中的相对位置百分比
  60. let mouseX = e.clientX - containerRect.left
  61. let newLeftWidth = (mouseX / containerWidth) * 100
  62. // 应用最小宽度限制
  63. if (newLeftWidth < props.minLeftWidth) newLeftWidth = props.minLeftWidth
  64. if (newLeftWidth > 100 - props.minRightWidth) newLeftWidth = 100 - props.minRightWidth
  65. leftWidth.value = newLeftWidth
  66. rightWidth.value = 100 - newLeftWidth
  67. }
  68. const stopResize = () => {
  69. isResizing.value = false
  70. document.body.style.cursor = ''
  71. document.removeEventListener('mousemove', handleResize)
  72. document.removeEventListener('mouseup', stopResize)
  73. }
  74. onMounted(() => {
  75. // 初始化宽度
  76. leftWidth.value = props.defaultLeftWidth
  77. rightWidth.value = 100 - props.defaultLeftWidth
  78. })
  79. onUnmounted(() => {
  80. document.removeEventListener('mousemove', handleResize)
  81. document.removeEventListener('mouseup', stopResize)
  82. })
  83. </script>
  84. <style scoped lang="scss">
  85. .split-panel-container {
  86. display: flex;
  87. width: 100%;
  88. height: 100%;
  89. min-height: 300px;
  90. overflow: hidden;
  91. .panel-left, .panel-right {
  92. position: relative;
  93. overflow: auto;
  94. transition: width 0.1s ease;
  95. /* 自定义滚动条 */
  96. &::-webkit-scrollbar {
  97. width: 8px;
  98. height: 8px;
  99. }
  100. &::-webkit-scrollbar-track {
  101. background: transparent;
  102. }
  103. &::-webkit-scrollbar-thumb {
  104. background: rgba(150, 150, 150, 0.5);
  105. border-radius: 4px;
  106. transition: background 0.2s;
  107. }
  108. &::-webkit-scrollbar-thumb:hover {
  109. background: rgba(100, 100, 100, 0.7);
  110. }
  111. }
  112. .panel-left {
  113. /* 关键调整:将左侧面板的右侧padding设置为负数,使内容可以扩展到滚动条区域 */
  114. padding-right: -8px;
  115. margin-right: 8px; /* 补偿padding,保持整体宽度不变 */
  116. /* 让左侧面板的滚动条与分割线重叠 */
  117. &::-webkit-scrollbar {
  118. position: absolute;
  119. right: 0;
  120. width: 8px; /* 滚动条宽度 */
  121. }
  122. }
  123. .resize-handle {
  124. position: relative;
  125. width: 6px;
  126. background-color: #f0f0f0;
  127. cursor: col-resize;
  128. border-left: 1px solid #ddd;
  129. border-right: 1px solid #ddd;
  130. z-index: 20; /* 确保分割线在最上层 */
  131. /* 鼠标悬停和拖动时的样式 */
  132. &:hover, &:active {
  133. background-color: #e0e0e0;
  134. }
  135. .handle-icon {
  136. position: absolute;
  137. top: 50%;
  138. left: 50%;
  139. transform: translate(-50%, -50%);
  140. width: 2px;
  141. height: 30px;
  142. background-color: #ccc;
  143. &::before, &::after {
  144. content: '';
  145. position: absolute;
  146. width: 2px;
  147. height: 30px;
  148. background-color: #ccc;
  149. }
  150. &::before {
  151. left: -6px;
  152. }
  153. &::after {
  154. left: 6px;
  155. }
  156. }
  157. }
  158. .panel-content {
  159. padding: 16px;
  160. min-height: 100%;
  161. }
  162. .default-content {
  163. padding: 20px;
  164. background-color: #f9f9f9;
  165. border-radius: 4px;
  166. margin: 10px;
  167. }
  168. }
  169. </style>

欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

vue3vue组件props给一个对象参数。vue组件间传参数vue父组件给子组件传参数。组件参数类型。父组件调用子组件的方法。vue组件事件监听给子组件传递方法子组件调用父组件方法

[TOC]组件可以使用props给组件传值,可以同时传递多个,可以是任意类型,比如字符串或者对象。 下面是个简单的例子: &lt...

vue3最基础的数据加载表格table

vue3表格加载一点静态数据 &lt;template&gt; &lt;el-table :data=&quot;tableData&quot; style=&quot;width: 100%&quot...

vue3 Element Plus 表单输入框放到一行

当垂直方向空间受限且表单较简单时,可以在一行内放置表单。 通过设置 inline 属性为 true 可以让表单域变为行内的表单域...

vue3 Element ui Plus 表格 分页vue3 el-pagination分页

其实就是el-pagination控件的使用而已 &lt;template&gt; &lt;div&gt; &lt;el-table :data=&quot;tableData&quot; ...

vue触发a标签的点击事件。vue3 dom操作 触发点击事件 。文件选择库只会触发一次change事件的问题

[TOC]vue触发a标签的点击事件直接操作dom节点的方式比较简单 &lt;button @click=&quot;handleBtnClick&quot;&gt;点击按钮&...

vue3 ref的使用多个ref的使用。通过ref触发点击事件

多个ref获取的方法可以使用一样的,通过变量名来区分就行了 const vabUploadRef = ref() const multipleTableRef = ref() ...

vue elementuivue3 element plus 文件上传的时候设置其他参数。后台.net接收传递的额外参数。图片上传

比如上传文件的时候额外传递两个select选择的值 前台前面上传文件的时候要提供默认参数很简单,el-upload绑定一个data即可...

vuevue3 打开新页面页面跳转。vue跳转到一个新页面。vue路由传参vue3路由传参vue3 获取路由参数

[TOC]VUE页面跳转本地页面跳转 goApplicationCenter() { //进行页面跳转 let path = &quot;/application-center&quo...

vuevue3组件封装vue组件模板。简单组件模板。基础组件模板。vue引入自定义的组件。vue使用自定义的组件。插槽slot使用。vue封装格子效果一块一块的grid布局效果

[TOC]vue封装组件的简单模板贴一个简单模板方便自定义组件的时候直接复制。 注意:vue2引用组件除了引入还需要在components...

.net6 Signalr+vue3 的运用(上)

.net6 Signalr+Vue3 的运用(上)[TOC] 什么是 SignalR?ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加...

.net6 Signalr+vue3 的运用(下)

.net6 Signalr+Vue3 的运用(下)[TOC] 上篇链接:https://www.tnblog.net/hb/article/details/7961SignalR 中的用户 Sig...

.net6 Signalr+vue3 配合Ingress Nginx的运用

.net6 Signalr+Vue3 配合Ingress Nginx的运用[TOC] 结合上篇:https://www.tnblog.net/hb/article/details/7963 项目打...

vue3 Element Plus 表格使用vue3常用界面搭配。vue3基础模板使用

一个简单的表格加时间搜索界面效果如下: 代码如下: &lt;template&gt; &lt;div class=&quot;app-container&quot;&g...

vue3 如何加prototype。vue3使用globalProperties

在2.X版本中创建一个vue 实例是通过 new Vue()来实现的,到了3.X中则是通过使用createApp这个 API返回一个应用实例,并且可...
这一生多幸运赶上过你.
排名
8
文章
252
粉丝
7
评论
7
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术