add split-pane

This commit is contained in:
zhigang.li 2018-01-08 11:52:23 +08:00
parent 525e9a2205
commit cdc55dc39b
5 changed files with 323 additions and 7 deletions

View File

@ -152,14 +152,14 @@ export const appRouter = [
name: 'count-to',
title: '数字渐变',
component: resolve => { require(['@/views/my-components/count-to/count-to.vue'], resolve); }
},
{
path: 'split-pane-page',
icon: 'ios-pause',
name: 'split-pane-page',
title: 'split-pane',
component: resolve => { require(['@/views/my-components/split-pane/split-pane-page.vue'], resolve); }
}
// {
// path: 'clipboard-page',
// icon: 'clipboard',
// name: 'clipboard-page',
// title: '一键复制',
// component: resolve => { require(['@/views/my-components/clipboard/clipboard.vue'], resolve); }
// }
]
},
{

View File

@ -0,0 +1,100 @@
<template>
<div>
<Card :padding="0">
<div class="split-pane-con">
<split-pane :style="{height: '100%'}" :min="30" :max="80" @on-trigger-moving="handleMoving" direction="horizontal" v-model="triggerOffset">
<div slot="left" style="height: 100%;">
<split-pane :style="{height: '100%'}" direction="vertical" v-model="triggerOffsetV">
<div class="introduce-left-con" slot="top" style="background: #EDE3A0;height: 100%;padding: 30px;">
<h4>- 该组件可以拖动修改左右尺寸还可以绑定v-model来设置如设置v-model="40"即左侧40%右侧60%</h4>
<h4>- 可设置最小和最大距离:min="80"即向右拖动到80%处就不能再拖动</h4>
<h4>- 可绑定事件@on-trigger-moving回调函数的返回值是鼠标事件对象同时该对象还包括两个我们自定义的变量即atMax和atMin即此时是否是在最大或最小距离处类型是Boolean来拖动右边的trigger看看吧</h4>
<h4 style="margin-bottom: 10px;">- 可使用slot="trigger"自定义拖动触发器但有三个注意点:</h4>
<h5>-- 样式需要设置position: absolute;</h5>
<h5>-- 需要给trigger绑定mousedown事件绑定的方法调用this.$refs.pane.handleMousedow(e)e为mousedown事件的事件对象</h5>
<h5>-- 给trigger添加:style="{width: offset + '%'}"这里的offset是通过v-model给split-pane组件绑定的值</h5>
<h4>- 其他api请看源码</h4>
</div>
<div slot="bottom" style="background: #A2EDB6;height: 100%;">
<split-pane ref="pane" :style="{height: '100%'}" direction="horizontal" v-model="triggerOffsetMin">
<div slot="left" style="background: #EDACE2;height: 100%;"></div>
<div slot="trigger"
:style="{left: triggerOffsetMin + '%'}"
@mousedown="handleMousedown"
class="custom-trigger"></div>
<div slot="right" style="background: #A2EDB6;height: 100%;"></div>
</split-pane>
</div>
</split-pane>
</div>
<div class="split-pane-right-con" slot="right" style="background: #8FB5ED;height: 100%;">
<p>是否是在最小距离处 {{ atMin }}</p>
<p>是否是在最大距离处 {{ atMax }}</p>
</div>
</split-pane>
</div>
</Card>
</div>
</template>
<script>
import splitPane from './split-pane';
export default {
name: 'split-pane-page',
components: {
splitPane
},
data () {
return {
triggerOffset: 50,
triggerOffsetV: 70,
triggerOffsetMin: 40,
atMax: false,
atMin: false
};
},
methods: {
handleMousedown (e) {
this.$refs.pane.handleMousedown(e);
},
handleMoving (e) {
this.atMax = e.atMax;
this.atMin = e.atMin;
}
}
};
</script>
<style lang="less" scoped>
.split-pane-con{
width: 100%;
height: 89vh;
}
.custom-trigger{
position: absolute;
width: 40px;
height: 40px;
box-sizing: border-box;
top: 50%;
transform: translate(-50%, -50%);
background: white;
border-radius: 50%;
box-shadow: 2px 2px 5px 2px rgba(0, 0, 0, .1) , 2px 2px 10px 2px rgba(0, 0, 0, .2) inset;
border: 1px solid #c3c3c3;
cursor: pointer;
}
.introduce-left-con h4{
margin-bottom: 20px;
}
.introduce-left-con h5{
margin-bottom: 10px;
margin-left: 20px;
}
.split-pane-right-con{
padding: 30px;
}
.split-pane-right-con p{
font-size: 26px;
font-weight: 700;
color: white;
}
</style>

View File

@ -0,0 +1,3 @@
import splitPane from './split-pane.vue';
export default splitPane;

View File

@ -0,0 +1,63 @@
@prefix: ~"split-pane";
@container: ~"@{prefix}-container";
@trigger: ~"@{prefix}-trigger";
.@{prefix}{
position: relative;
&-container{
height: 100%;
width: 100%;
}
&-horizontal{
& > div > .@{trigger}{
transform: translateX(-50%);
cursor: col-resize;
width: 2px;
height: 100%;
margin: 0 1px;
}
}
&-vertical{
& > div > .@{trigger}{
transform: translateY(-50%);
cursor: row-resize;
height: 2px;
width: 100%;
margin: 1px 0;
}
}
&-trigger{
position: absolute;
z-index: 3;
background: #BDBDBD;
}
&-left-area{
height: 100%;
float: left;
z-index: 2;
overflow: auto;
}
&-right-area{
height: 100%;
float: left;
z-index: 2;
overflow: auto;
}
&-top-area{
width: 100%;
z-index: 2;
overflow: auto;
}
&-bottom-area{
width: 100%;
z-index: 2;
overflow: auto;
}
}

View File

@ -0,0 +1,150 @@
<style lang="less">
@import './split-pane.less';
</style>
<template>
<div
:class="wraperClasses"
ref="wraper"
@mouseup="handleMouseup"
@mousemove="handleMousemove"
@mouseleave="handleMouseout">
<div v-if="direction === 'horizontal'" :class="`${prefix}-container`">
<div :class="`${prefix}-left-area`" :style="{width: leftSize}">
<slot name="left"></slot>
</div>
<slot name="trigger">
<div
:class="`${prefix}-trigger`"
ref="trigger"
:style="{left: `${triggerOffset}%`}"
@mousedown="handleMousedown"
unselectable="on">
</div>
</slot>
<div :class="`${prefix}-right-area`" :style="{width: rightSize}">
<slot name="right"></slot>
</div>
</div>
<div v-else :class="`${prefix}-container`">
<div :class="`${prefix}-top-area`" :style="{height: leftSize}">
<slot name="top"></slot>
</div>
<slot name="trigger">
<div
:class="`${prefix}-trigger`"
ref="trigger"
:style="{top: `${triggerOffset}%`}"
@mousedown="handleMousedown"
unselectable="on">
</div>
</slot>
<div :class="`${prefix}-bottom-area`" :style="{height: rightSize}">
<slot name="bottom"></slot>
</div>
</div>
</div>
</template>
<script>
const oneOf = function (ele, targetArr) {
if (targetArr.indexOf(ele) >= 0) {
return true;
} else {
return false;
}
};
export default {
name: 'splitPane',
props: {
value: {
type: Number,
default: 50
},
direction: {
type: String,
default: 'horizontal',
validator (val) {
return oneOf(val, ['vertical', 'horizontal']);
}
},
min: {
type: [Number, String],
default: 3
},
max: {
type: [Number, String],
default: 97
}
},
data () {
return {
prefix: 'split-pane',
canMove: false,
triggerOffset: 50,
triggerOldOffset: 50,
offset: {},
atMin: false,
atMax: false
};
},
computed: {
wraperClasses () {
return [
this.prefix,
this.direction === 'vertical' ? `${this.prefix}-vertical` : `${this.prefix}-horizontal`
];
},
leftSize () {
return `${this.triggerOffset}%`;
},
rightSize () {
return `${100 - this.triggerOffset}%`;
}
},
methods: {
handleMouseup () {
this.canMove = false;
},
handleMousedown (e) {
this.canMove = true;
this.triggerOldOffset = this.triggerOffset;
this.offset = {
x: e.pageX,
y: e.pageY
};
e.preventDefault();
},
handleMouseout () {
this.canMove = false;
},
handleMousemove (e) {
if (this.canMove) {
let offset;
if (this.direction === 'horizontal') {
offset = this.triggerOldOffset + Math.floor(((e.clientX - this.offset.x) / this.$refs.wraper.offsetWidth) * 10000) / 100;
} else {
offset = this.triggerOldOffset + Math.floor(((e.clientY - this.offset.y) / this.$refs.wraper.offsetHeight) * 10000) / 100;
}
if (offset <= this.min) {
this.triggerOffset = Math.max(offset, this.min);
} else {
this.triggerOffset = Math.min(offset, this.max);
}
this.atMin = this.triggerOffset === this.min;
this.atMax = this.triggerOffset === this.max;
e.atMin = this.atMin;
e.atMax = this.atMax;
this.$emit('input', offset);
this.$emit('on-trigger-moving', e);
}
}
},
mounted () {
if (this.value !== undefined) {
this.triggerOffset = this.value;
}
}
};
</script>