This commit is contained in:
张成
2025-10-08 15:10:33 +08:00
commit 2e1cd65b07
161 changed files with 19936 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
import Split from './split.vue'
export default Split

View File

@@ -0,0 +1,114 @@
@split-prefix-cls: ~"ivu-split";
@box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.4);
@trigger-bar-background: rgba(23, 35, 61, 0.25);
@trigger-background: #F8F8F9;
@trigger-width: 6px;
@trigger-bar-width: 4px;
@trigger-bar-offset: (@trigger-width - @trigger-bar-width) / 2;
@trigger-bar-interval: 3px;
@trigger-bar-weight: 1px;
@trigger-bar-con-height: (@trigger-bar-weight + @trigger-bar-interval) * 8;
.@{split-prefix-cls}{
&-wrapper{
position: relative;
width: 100%;
height: 100%;
}
&-pane{
position: absolute;
&.left-pane, &.right-pane{
top: 0px;
bottom: 0px;
}
&.left-pane{
left: 0px;
}
&.right-pane{
right: 0px;
}
&.top-pane, &.bottom-pane{
left: 0px;
right: 0px;
}
&.top-pane{
top: 0px;
}
&.bottom-pane{
bottom: 0px;
}
}
&-trigger{
&-con{
position: absolute;
transform: translate(-50%, -50%);
z-index: 10;
}
&-bar-con{
position: absolute;
overflow: hidden;
&.vertical{
left: @trigger-bar-offset;
top: 50%;
height: @trigger-bar-con-height;
transform: translate(0, -50%);
}
&.horizontal{
left: 50%;
top: @trigger-bar-offset;
width: @trigger-bar-con-height;
transform: translate(-50%, 0);
}
}
&-vertical{
width: @trigger-width;
height: 100%;
background: @trigger-background;
box-shadow: @box-shadow;
cursor: col-resize;
.@{split-prefix-cls}-trigger-bar{
width: @trigger-bar-width;
height: 1px;
background: @trigger-bar-background;
float: left;
margin-top: @trigger-bar-interval;
}
}
&-horizontal{
height: @trigger-width;
width: 100%;
background: @trigger-background;
box-shadow: @box-shadow;
cursor: row-resize;
.@{split-prefix-cls}-trigger-bar{
height: @trigger-bar-width;
width: 1px;
background: @trigger-bar-background;
float: left;
margin-right: @trigger-bar-interval;
}
}
}
&-horizontal{
.@{split-prefix-cls}-trigger-con{
top: 50%;
height: 100%;
width: 0;
}
}
&-vertical{
.@{split-prefix-cls}-trigger-con{
left: 50%;
height: 0;
width: 100%;
}
}
.no-select{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
}

View File

@@ -0,0 +1,188 @@
<template>
<div ref="outerWrapper" :class="wrapperClasses">
<div v-if="isHorizontal" :class="`${prefix}-horizontal`">
<div :style="{right: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'left-pane']"><slot name="left"/></div>
<div :class="`${prefix}-trigger-con`" :style="{left: `${offset}%`}" @mousedown="handleMousedown">
<slot name="trigger">
<trigger mode="vertical"/>
</slot>
</div>
<div :style="{left: `${offset}%`}" :class="[`${prefix}-pane`, 'right-pane']"><slot name="right"/></div>
</div>
<div v-else :class="`${prefix}-vertical`">
<div :style="{bottom: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'top-pane']"><slot name="top"/></div>
<div :class="`${prefix}-trigger-con`" :style="{top: `${offset}%`}" @mousedown="handleMousedown">
<slot name="trigger">
<trigger mode="horizontal"/>
</slot>
</div>
<div :style="{top: `${offset}%`}" :class="[`${prefix}-pane`, 'bottom-pane']"><slot name="bottom"/></div>
</div>
</div>
</template>
<script>
import { oneOf, on, off } from '@/utils/tools'
import Trigger from './trigger.vue'
export default {
name: 'SplitPane',
components: {
Trigger
},
props: {
value: {
type: [Number, String],
default: 0.5
},
mode: {
validator(value) {
return oneOf(value, ['horizontal', 'vertical'])
},
default: 'horizontal'
},
min: {
type: [Number, String],
default: '40px'
},
max: {
type: [Number, String],
default: '40px'
}
},
/**
* Events
* @on-move-start
* @on-moving 返回值事件对象但是在事件对象中加入了两个参数atMin(当前是否在最小值处), atMax(当前是否在最大值处)
* @on-move-end
*/
data() {
return {
prefix: 'ivu-split',
offset: 0,
oldOffset: 0,
isMoving: false
}
},
computed: {
wrapperClasses() {
return [`${this.prefix}-wrapper`, this.isMoving ? 'no-select' : '']
},
isHorizontal() {
return this.mode === 'horizontal'
},
anotherOffset() {
return 100 - this.offset
},
valueIsPx() {
return typeof this.value === 'string'
},
offsetSize() {
return this.isHorizontal ? 'offsetWidth' : 'offsetHeight'
},
computedMin() {
return this.getComputedThresholdValue('min')
},
computedMax() {
return this.getComputedThresholdValue('max')
}
},
methods: {
px2percent(numerator, denominator) {
return parseFloat(numerator) / parseFloat(denominator)
},
getComputedThresholdValue(type) {
let size = this.$refs.outerWrapper[this.offsetSize]
if (this.valueIsPx)
return typeof this[type] === 'string' ? this[type] : size * this[type]
else
return typeof this[type] === 'string'
? this.px2percent(this[type], size)
: this[type]
},
getMin(value1, value2) {
if (this.valueIsPx)
return `${Math.min(parseFloat(value1), parseFloat(value2))}px`
else return Math.min(value1, value2)
},
getMax(value1, value2) {
if (this.valueIsPx)
return `${Math.max(parseFloat(value1), parseFloat(value2))}px`
else return Math.max(value1, value2)
},
getAnotherOffset(value) {
let res = 0
if (this.valueIsPx)
res = `${this.$refs.outerWrapper[this.offsetSize] -
parseFloat(value)}px`
else res = 1 - value
return res
},
handleMove(e) {
let pageOffset = this.isHorizontal ? e.pageX : e.pageY
let offset = pageOffset - this.initOffset
let outerWidth = this.$refs.outerWrapper[this.offsetSize]
let value = this.valueIsPx
? `${parseFloat(this.oldOffset) + offset}px`
: this.px2percent(outerWidth * this.oldOffset + offset, outerWidth)
let anotherValue = this.getAnotherOffset(value)
if (parseFloat(value) <= parseFloat(this.computedMin))
value = this.getMax(value, this.computedMin)
if (parseFloat(anotherValue) <= parseFloat(this.computedMax))
value = this.getAnotherOffset(
this.getMax(anotherValue, this.computedMax)
)
e.atMin = this.value === this.computedMin
e.atMax = this.valueIsPx
? this.getAnotherOffset(this.value) === this.computedMax
: this.getAnotherOffset(this.value).toFixed(5) ===
this.computedMax.toFixed(5)
this.$emit('input', value)
this.$emit('on-moving', e)
},
handleUp() {
this.isMoving = false
off(document, 'mousemove', this.handleMove)
off(document, 'mouseup', this.handleUp)
this.$emit('on-move-end')
},
handleMousedown(e) {
this.initOffset = this.isHorizontal ? e.pageX : e.pageY
this.oldOffset = this.value
this.isMoving = true
on(document, 'mousemove', this.handleMove)
on(document, 'mouseup', this.handleUp)
this.$emit('on-move-start')
}
},
watch: {
value() {
this.offset =
((this.valueIsPx
? this.px2percent(
this.value,
this.$refs.outerWrapper[this.offsetSize]
)
: this.value) *
10000) /
100
}
},
mounted() {
this.$nextTick(() => {
this.offset =
((this.valueIsPx
? this.px2percent(
this.value,
this.$refs.outerWrapper[this.offsetSize]
)
: this.value) *
10000) /
100
})
}
}
</script>
<style lang="less">
@import './index.less';
</style>

View File

@@ -0,0 +1,45 @@
<template>
<div :class="classes">
<div :class="barConClasses">
<i :class="`${prefix}-bar`" v-once v-for="i in 8" :key="`trigger-${i}`"></i>
</div>
</div>
</template>
<script>
export default {
name: 'Trigger',
props: {
mode: String
},
data() {
return {
prefix: 'ivu-split-trigger',
initOffset: 0
}
},
computed: {
isVertical() {
return this.mode === 'vertical'
},
classes() {
return [
this.prefix,
this.isVertical
? `${this.prefix}-vertical`
: `${this.prefix}-horizontal`
]
},
barConClasses() {
return [
`${this.prefix}-bar-con`,
this.isVertical ? 'vertical' : 'horizontal'
]
}
}
}
</script>
<style lang="less">
@import './index.less';
</style>