Vue3实现面板分割
下面是将你提供的 Vue 组件使用 SCSS,并以 Vue 3 的组合式 API 形式的面板分割代码。
1、建立组件相关的文件夹
2、将下面代码拷贝到index.vue中
<template> <div class="g-split" ref="gSplit"> <!-- 水平方向 --> <div class="horizontal" v-if="showHorizontal"> <div class="left-panel position" :style="horizontalLeftPanel"> <slot name="left"></slot> </div> <div class="horizontal-trigger-panel position" :style="horizontaltriggerPanel" ref="horizontalTriggerPanel" > <slot name="trigger" v-if="$slots.trigger"></slot> <div class="trigger-content-default-wrap" v-else> <div class="trigger-content"> <i class="trigger-bar" v-for="n in 7" :key="n"></i> </div> </div> </div> <div class="right-panel position" :style="horizontalRightPanel"> <slot name="right"></slot> </div> </div> <!-- 垂直方向 --> <div class="vertical" v-if="showVertical"> <div class="top-panel position" :style="verticalTopPanel"> <slot name="top"></slot> </div> <div class="vertical-trigger-panel position" :style="verticaltriggerPanel" ref="verticalTriggerPanel" > <slot name="trigger" v-if="$slots.trigger"></slot> <div class="trigger-content-default-wrap" v-else> <div class="trigger-content"> <i class="trigger-bar" v-for="n in 7" :key="n"></i> </div> </div> </div> <div class="bottom-panel position" :style="verticalBottomPanel"> <slot name="bottom"></slot> </div> </div> </div> </template> <script setup> import { ref, computed, onMounted } from 'vue'; // 注意:页面偏移量单位统一使用百分比计算 const props = defineProps({ value: { type: [String, Number], default: 0.5, }, mode: { type: String, validator(value) { return ["horizontal", "vertical"].includes(value); }, default: "horizontal", }, min: { type: [String, Number], default: 0.1, }, max: { type: [String, Number], default: 0.9, }, }); import { defineEmits } from 'vue'; const emit = defineEmits(['onMoveStart', 'onMoving', 'onMoveEnd']); const gSplit = ref(null); const horizontalTriggerPanel = ref(null); const verticalTriggerPanel = ref(null); const left = ref(props.value); const top = ref(props.value); const gSplitWidth = ref(0); const gSplitHeight = ref(0); const horizontalTriggerPanelWidth = ref(0); const verticalTriggerPanelHeight = ref(0); const showHorizontal = computed(() => props.mode === "horizontal"); const showVertical = computed(() => props.mode === "vertical"); const horizontalLeftPanel = computed(() => ({ left: 0, right: (1 - left.value) * 100 + "%", })); const horizontalRightPanel = computed(() => ({ left: (left.value + horizontalTriggerPanelWidth.value / gSplitWidth.value) * 100 + "%", })); const horizontaltriggerPanel = computed(() => ({ left: left.value * 100 + "%", })); const verticalTopPanel = computed(() => ({ top: 0, bottom: (1 - top.value) * 100 + "%", })); const verticalBottomPanel = computed(() => ({ top: (top.value + verticalTriggerPanelHeight.value / gSplitHeight.value) * 100 + "%", })); const verticaltriggerPanel = computed(() => ({ top: top.value * 100 + "%", })); const initDom = () => { gSplitWidth.value = gSplit.value.clientWidth; gSplitHeight.value = gSplit.value.clientHeight; if (props.mode === "horizontal") { horizontalTriggerPanelWidth.value = horizontalTriggerPanel.value.clientWidth; } else { verticalTriggerPanelHeight.value = verticalTriggerPanel.value.clientHeight; } }; const bindEvent = () => { if (props.mode === "horizontal") { bindHorizontalTriggerPanelEvent(); } else { bindVerticalTriggerPanelEvent(); } }; const preventSelectedOnMouseMove = (e) => e.preventDefault(); const bindHorizontalTriggerPanelEvent = () => { resolveMouseFn("horizontal", horizontalTriggerPanel.value); }; const bindVerticalTriggerPanelEvent = () => { resolveMouseFn("vertical", verticalTriggerPanel.value); }; const resolveMouseFn = (type, element) => { const mousedown = (e) => { document.addEventListener("selectstart", preventSelectedOnMouseMove); emit("onMoveStart", e); const pos = type === "horizontal" ? "left" : "top"; const distance = type === "horizontal" ? e.clientX - element.offsetLeft : e.clientY - element.offsetTop; const mousemove = (e) => { emit("onMoving", e); const gSplitSize = type === "horizontal" ? gSplitWidth.value : gSplitHeight.value; const newPos = (type === "horizontal" ? e.clientX - distance : e.clientY - distance) / gSplitSize; if (newPos < props.min) newPos = props.min; if (newPos > 1 - props.min) newPos = 1 - props.min; if (pos === "left") left.value = newPos; else top.value = newPos; }; const mouseup = () => { emit("onMoveEnd", e); document.removeEventListener("mousemove", mousemove); document.removeEventListener("mouseup", mouseup); document.removeEventListener("selectstart", preventSelectedOnMouseMove); }; document.addEventListener("mousemove", mousemove); document.addEventListener("mouseup", mouseup); }; element.addEventListener("mousedown", mousedown); }; onMounted(() => { bindEvent(); initDom(); }); </script> <style lang="scss"> .g-split { height: 100%; overflow: hidden; .position { position: absolute; } .horizontal { position: relative; height: 100%; .left-panel, .right-panel { height: 100%; } .horizontal-trigger-panel { cursor: col-resize; height: 100%; .trigger-content-default-wrap { background-color: #f8f8f9; height: 100%; position: relative; width: 7px; .trigger-content { position: absolute; top: 50%; transform: translateY(-50%); .trigger-bar { width: 7px; height: 1px; display: block; background: rgba(23, 35, 61, 0.25); margin-top: 3px; } } } } } .vertical { position: relative; height: 100%; .top-panel, .bottom-panel { width: 100%; } .vertical-trigger-panel { width: 100%; .trigger-content-default-wrap { width: 100%; position: relative; height: 7px; cursor: row-resize; background-color: #f8f8f9; .trigger-content { position: absolute; left: 50%; top: 0; transform: translateX(-50%); height: 100%; .trigger-bar { width: 1px; height: 100%; display: inline-block; background: rgba(23, 35, 61, 0.25); margin-left: 3px; vertical-align: top; } } } } } } </style>
使用
注册组件
在你的 Vue 项目中,你需要在你的组件或应用程序中注册 GSplitPanel 组件。你可以通过局部注册或全局注册来实现。
局部注册
在你的父组件中导入并注册 GSplitPanel 组件:
<template> <div> <GSplitPanel mode="horizontal" :value="0.5"> <template v-slot:left> <div>左侧面板内容</div> </template> <template v-slot:right> <div>右侧面板内容</div> </template> </GSplitPanel> </div> </template> <script setup> import GSplitPanel from '@/components/Split/index.vue'; </script>
全局注册
如果你希望在整个项目中使用 GSplitPanel 组件,可以在你的主入口文件(如 main.js 或 main.ts)中全局注册:
import { createApp } from 'vue'; import App from './App.vue'; import GSplitPanel from '@/components/Split/index.vue'; const app = createApp(App); app.component('GSplitPanel', GSplitPanel); app.mount('#app');
使用组件
在你的项目中,你现在可以使用 标签来使用这个组件。你可以通过 mode 属性来控制是水平分割还是垂直分割,并通过插槽来填充面板的内容。
vue <GSplitPanel mode="horizontal" :value="0.5"> <template v-slot:left> <div>左侧面板内容</div> </template> <template v-slot:right> <div>右侧面板内容</div> </template> </GSplitPanel>