
sgSpliter.vue
<template><!-- 注意:父组件position必须是relative、absolute或fixed,不建议直接在绑定:data后面用"{属性}",建议单独在script中声明data,避免拖拽过程重复调用 --><div :class="$options.name" :placement="placement" @mousedown="__addWindowEvents"><divv-if="showArrowBtn"class="arrow-btn"@click="clickArrowBtn"@mousedown.stop:styleType="arrowBtnStyleType":collapse="collapse"><!-- 箭头在父组件的最左侧 --><template v-if="placement === `left`"><i class="el-icon-arrow-left" v-if="collapse" /><i class="el-icon-arrow-right" v-else /></template><!-- 箭头在父组件的最右侧 --><template v-if="placement === `right`"><i class="el-icon-arrow-right" v-if="collapse" /><i class="el-icon-arrow-left" v-else /></template><!-- 箭头在父组件的最上侧 --><template v-if="placement === `top`"><i class="el-icon-arrow-up" v-if="collapse" /><i class="el-icon-arrow-down" v-else /></template><!-- 箭头在父组件的最下侧 --><template v-if="placement === `bottom`"><i class="el-icon-arrow-down" v-if="collapse" /><i class="el-icon-arrow-up" v-else /></template></div></div>
</template>
<script>
export default {name: "sgSpliter",components: {},data() {return {form: {},collapse: false,showArrowBtn: true,placement: `right`,parent: null,defaultSize: 200, //当拖拽区域到0,再点击箭头展开的默认宽度nearEdgeSize: 5, //当拖拽宽度小于此宽度,自动折叠到0minSize: null, //可选,指定 最小宽度maxSize: null, //可选,指定 最大宽度size: null,size_bk: null,arrowBtnStyleType: `default`, //按钮风格:白色背景default、蓝色背景bluesplitBarSize: 1, //可选,分隔条大小,默认 2pxresizable: true, //可选,指定 是否可调整大小,会影响相邻};},props: ["data"],computed: {},watch: {data: {handler(newValue, oldValue) {// console.log(`深度监听${this.$options.name}:`, newValue, oldValue);if (Object.keys(newValue || {}).length) {this.form = JSON.parse(JSON.stringify(newValue));this.$g.convertForm2ComponentParam(`showArrowBtn`, this);this.$g.convertForm2ComponentParam(`defaultSize`, this);this.$g.convertForm2ComponentParam(`nearEdgeSize`, this);this.$g.convertForm2ComponentParam(`minSize`, this);this.$g.convertForm2ComponentParam(`maxSize`, this);this.$g.convertForm2ComponentParam(`placement`, this);this.$g.convertForm2ComponentParam(`parent`, this);this.$g.convertForm2ComponentParam(`arrowBtnStyleType`, this);this.$g.convertForm2ComponentParam(`splitBarSize`, this);this.$g.convertForm2ComponentParam(`resizable`, this);this.form.hasOwnProperty("collapse") &&this.collapseSpliter({ collapse: this.form.collapse }); //允许外部控制默认折叠或展开this.$nextTick(() => {this.$el.style.setProperty(`--splitBarSize`, `${this.splitBarSize}px`); //js往css传递局部参数});// 不要在这里初始化size_bk,由于拖拽过程会重复触发这里的代码执行}},deep: true, //深度监听immediate: true, //立即执行},size(size) {size <= this.nearEdgeSize && (size = 0);this.collapse = size === 0;this.$emit(`change`, { size });},},mounted() {this.$nextTick(() => {this.parent || (this.parent = this.$el.parentNode);if (this.parent) {let rect = this.parent.getBoundingClientRect();switch (this.placement) {case `left`: // 竖线在父组件的最左侧case `right`: // 竖线在父组件的最右侧this.size_bk = rect.width;break;case `top`: // 竖线在父组件的最上侧case `bottom`: // 竖线在父组件的最下侧this.size_bk = rect.height;break;default:}}});},beforeDestroy() {this.__removeWindowEvents();},methods: {//size发生变化的时候就做缓动效果changeTransitionSize() {let parent = this.parent;if (parent) {let attr = `${this.$options.name}-transitionSize`;parent.setAttribute(attr, true);setTimeout(() => parent.removeAttribute(attr), 200);}},clickArrowBtn($event) {this.collapseSpliter();this.$emit(`clickArrowBtn`, { $event, collapse: this.collapse });},collapseSpliter({ collapse } = {}) {this.collapse = collapse === undefined ? !this.collapse : collapse;let expandSize = this.size_bk > this.nearEdgeSize ? this.size_bk : this.defaultSize;this.changeTransitionSize();this.size = this.collapse ? 0 : expandSize;this.$emit(`change`, { size: this.size });this.$emit(`collapseSpliter`, { collapse });},bkSize(d) {this.size_bk = this.size;},__addWindowEvents() {this.__removeWindowEvents();addEventListener("mousemove", this.mousemove_window);addEventListener("mouseup", this.mouseup_window);},__removeWindowEvents() {removeEventListener("mousemove", this.mousemove_window);removeEventListener("mouseup", this.mouseup_window);},mousemove_window($event) {if (!this.resizable) return;this.parent || (this.parent = this.$el.parentNode);if (this.parent) {let { x, y } = $event,rect = this.parent.getBoundingClientRect(),size;switch (this.placement) {case `left`: // 竖线在父组件的最左侧size = rect.x + rect.width - x;break;case `right`: // 竖线在父组件的最右侧size = x - rect.x;break;case `top`: // 竖线在父组件的最上侧size = rect.y + rect.height - y;break;case `bottom`: // 竖线在父组件的最下侧size = y - rect.y;break;default:}this.minSize && size < this.minSize && (size = this.minSize);this.maxSize && size > this.maxSize && (size = this.maxSize);this.size = size;this.bkSize();} else {this.$message.error(`没有获取到父组件parent!`);}},mouseup_window($event) {this.__removeWindowEvents();this.$emit(`dragEnd`, { $event });},},
};
</script>
<style lang="scss" scoped>
$splitBarSize: var(--splitBarSize); //css获取js传递的参数
.sgSpliter {z-index: 1;background-color: #efefef;position: absolute;left: 0;top: 0;right: 0;bottom: 0;position: absolute;.arrow-btn {transition: 0.382s;opacity: 0;pointer-events: none;// transform: translateY(50%); //防止托盘最小高度的时候还冒出一小截width: 20px;right: -20px;height: 20px;display: flex;justify-content: center;align-items: center;color: #409eff;background-color: white;font-size: 12px;position: absolute;margin: auto;box-sizing: border-box;cursor: pointer;&:hover {filter: brightness(1.1);}&[styleType="blue"] {color: white;background-color: #4f6bdf;}&[collapse] {opacity: 1;pointer-events: auto;}}// 位置----------------------------------------&[placement="left"],&[placement="right"] {cursor: col-resize;width: $splitBarSize;height: 100%;}&[placement="top"],&[placement="bottom"] {cursor: row-resize;width: 100%;height: $splitBarSize;}&[placement="left"] {left: 0;right: revert;.arrow-btn {left: revert;right: $splitBarSize;top: 0;bottom: 0;border-radius: 8px 0 0 8px;padding: 20px 0;box-shadow: -5px 0px 10px 0 rgba(0, 0, 0, 0.1);}}&[placement="right"] {left: revert;right: 0;.arrow-btn {left: $splitBarSize;right: revert;top: 0;bottom: 0;border-radius: 0 8px 8px 0;padding: 20px 0;box-shadow: 5px 0px 10px 0 rgba(0, 0, 0, 0.1);}}&[placement="top"] {top: 0;bottom: revert;.arrow-btn {left: 0;right: 0;top: revert;bottom: $splitBarSize;border-radius: 8px 8px 0 0;padding: 0 20px;box-shadow: 0px -5px 10px 0 rgba(0, 0, 0, 0.1);}}&[placement="bottom"] {top: revert;bottom: 0;.arrow-btn {left: 0;right: 0;top: $splitBarSize;bottom: revert;border-radius: 0 0 8px 8px;padding: 0 20px;box-shadow: 0px 5px 10px 0 rgba(0, 0, 0, 0.1);}}// ----------------------------------------&:hover {background-color: #b3d8ff;.arrow-btn {opacity: 1;pointer-events: auto;}}// 按下拖拽线条后出现的半透明区域&::after {content: "";transition: 0.382s;position: absolute;background-color: #409eff22;opacity: 0;}$splitOpacityBgExpandSize: 5px; //半透明延伸宽度$splitOpacityBgSize: calc(#{$splitOpacityBgExpandSize} * 2 + #{$splitBarSize});&[placement="left"],&[placement="right"] {&::after {width: $splitOpacityBgSize;height: 100%;left: -#{$splitOpacityBgExpandSize};top: 0;}}&[placement="top"],&[placement="bottom"] {&::after {width: 100%;height: $splitOpacityBgSize;left: 0;top: -#{$splitOpacityBgExpandSize};}}&:active {opacity: 1;background-color: #409eff;&::after {opacity: 1;}}
}
</style><style lang="scss">
[sgSpliter-transitionSize] {transition: 0.2s;
}
</style>
demo
<template><div :class="$options.name"><div class="left" :style="{ width: `${leftWidth}px` }"><sgSpliter :data="{ placement: `right` }" @change="leftWidth = $event.size" /></div><div class="right"><div class="top" :style="{ height: `${topHeight}px` }"><sgSpliter :data="{ placement: `bottom` }" @change="topHeight = $event.size" /></div><div class="bottom"><div class="left"><div class="top"></div><div class="bottom" :style="{ height: `${bottomHeight}px` }"><sgSpliter:data="{ placement: `top` }"@change="bottomHeight = $event.size"/></div></div><div class="right" :style="{ width: `${bottomWidth}px` }"><sgSpliter :data="{ placement: `left` }" @change="bottomWidth = $event.size" /></div></div></div></div>
</template>
<script>
import sgSpliter from "@/vue/components/admin/sgSpliter";
export default {name: `demoSpliter`,components: { sgSpliter },data() {return {leftWidth: 200,topHeight: 200,bottomHeight: 200,bottomWidth: 200,};},
};
</script>
<style lang="scss" scoped>
.demoSpliter {display: flex;& > .left {height: 100%;flex-shrink: 0;position: relative;box-sizing: border-box;border-right: 1px solid #eee;}& > .right {flex-grow: 1;display: flex;flex-direction: column;& > .top {flex-shrink: 0;width: 100%;position: relative;box-sizing: border-box;border-bottom: 1px solid #eee;}& > .bottom {flex-grow: 1;width: 100%;display: flex;& > .left {flex-grow: 1;height: 100%;display: flex;flex-direction: column;& > .top {flex-grow: 1;width: 100%;}& > .bottom {flex-shrink: 0;width: 100%;position: relative;box-sizing: border-box;border-top: 1px solid #eee;}}& > .right {flex-shrink: 0;height: 100%;position: relative;box-sizing: border-box;border-left: 1px solid #eee;}}}
}
</style>