|
@@ -1,490 +0,0 @@
|
|
|
-<script setup lang="ts">
|
|
|
-import type { SplitPanelProps } from './props'
|
|
|
-import { splitPanelEmits } from './props'
|
|
|
-
|
|
|
-const props = withDefaults(defineProps<SplitPanelProps>(), {})
|
|
|
-const emits = defineEmits(splitPanelEmits)
|
|
|
-
|
|
|
-// 根节点
|
|
|
-const rootRef = ref<HTMLElement | null>(null)
|
|
|
-
|
|
|
-// 侧边容器节点
|
|
|
-const wrapRef = ref<HTMLElement | null>(null)
|
|
|
-
|
|
|
-// 侧边节点
|
|
|
-const sideRef = ref<HTMLElement | null>(null)
|
|
|
-
|
|
|
-// 是否折叠
|
|
|
-const isCollapse = ref<boolean>(false)
|
|
|
-
|
|
|
-// 拉伸后尺寸
|
|
|
-const resizedSize = ref<string | null>(null)
|
|
|
-
|
|
|
-// 是否正在拉伸
|
|
|
-const resizing = ref<boolean>(false)
|
|
|
-
|
|
|
-/* 切换折叠状态 */
|
|
|
-const toggleCollapse = (collapse?: boolean) => {
|
|
|
- isCollapse.value = typeof collapse === 'boolean' ? collapse : !isCollapse.value
|
|
|
- emits('update:collapse', isCollapse.value)
|
|
|
-}
|
|
|
-
|
|
|
-/* 获取最大拉伸尺寸 */
|
|
|
-const getMaxSize = (el: HTMLElement) => {
|
|
|
- const size = props.vertical ? el.clientHeight : el.clientWidth
|
|
|
- if (!props.maxSize) {
|
|
|
- return size
|
|
|
- }
|
|
|
- if (props.maxSize < 0) {
|
|
|
- // 负值形式
|
|
|
- return size + props.maxSize
|
|
|
- } else if (props.maxSize < 1) {
|
|
|
- // 百分比形式
|
|
|
- return Math.floor(size * props.maxSize)
|
|
|
- }
|
|
|
- return Math.min(props.maxSize, size)
|
|
|
-}
|
|
|
-
|
|
|
-/* 拉伸 */
|
|
|
-const onResize = (event: MouseEvent) => {
|
|
|
- const rootEl = rootRef.value
|
|
|
- const sideEl = sideRef.value
|
|
|
- if (!rootEl || !sideEl) {
|
|
|
- return
|
|
|
- }
|
|
|
- resizing.value = true
|
|
|
- // 获取原始位置
|
|
|
- const downX = event.clientX
|
|
|
- const downY = event.clientY
|
|
|
- const downW = sideEl.clientWidth
|
|
|
- const downH = sideEl.clientHeight
|
|
|
- const limitMin = props.minSize || 0
|
|
|
- const limitMax = getMaxSize(rootEl)
|
|
|
-
|
|
|
- // 鼠标移动事件
|
|
|
- const mousemoveFn = (e: MouseEvent) => {
|
|
|
- const size = props.vertical
|
|
|
- ? (props.reverse ? downY - e.clientY : e.clientY - downY) + downH
|
|
|
- : (props.reverse ? downX - e.clientX : e.clientX - downX) + downW
|
|
|
- resizedSize.value = (size < limitMin ? limitMin : size > limitMax ? limitMax : size) + 'px'
|
|
|
- }
|
|
|
-
|
|
|
- // 鼠标抬起事件
|
|
|
- const mouseupFn = () => {
|
|
|
- resizing.value = false
|
|
|
- document.removeEventListener('mousemove', mousemoveFn)
|
|
|
- document.removeEventListener('mouseup', mouseupFn)
|
|
|
- }
|
|
|
-
|
|
|
- // 添加鼠标事件监听
|
|
|
- document.addEventListener('mousemove', mousemoveFn)
|
|
|
- document.addEventListener('mouseup', mouseupFn)
|
|
|
-}
|
|
|
-
|
|
|
-watch(
|
|
|
- [() => props.collapse, () => props.allowCollapse],
|
|
|
- () => {
|
|
|
- if (!props.allowCollapse) {
|
|
|
- isCollapse.value = false
|
|
|
- } else {
|
|
|
- isCollapse.value = props.collapse
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
-)
|
|
|
-</script>
|
|
|
-
|
|
|
-<template>
|
|
|
- <div
|
|
|
- ref="rootRef"
|
|
|
- :class="[
|
|
|
- 'split-panel',
|
|
|
- { 'is-reverse': reverse },
|
|
|
- { 'is-vertical': vertical },
|
|
|
- { 'is-collapse': isCollapse },
|
|
|
- { 'is-resizing': resizing }
|
|
|
- ]"
|
|
|
- :style="{
|
|
|
- '--split-size': resizedSize ?? size,
|
|
|
- '--split-space': space
|
|
|
- }"
|
|
|
- >
|
|
|
- <!-- 侧边容器 -->
|
|
|
- <div ref="wrapRef" class="split-panel-wrap">
|
|
|
- <div ref="sideRef" class="split-panel-side" :style="customStyle">
|
|
|
- <slot></slot>
|
|
|
- </div>
|
|
|
- <!-- 间距 -->
|
|
|
- <div class="split-panel-space">
|
|
|
- <div v-if="resizable" class="split-resize-line" @mousedown="onResize"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- 内容 -->
|
|
|
- <div class="split-panel-body" :style="bodyStyle">
|
|
|
- <slot name="body" :collapse="isCollapse"></slot>
|
|
|
- </div>
|
|
|
- <!-- 折叠按钮 -->
|
|
|
- <div v-if="allowCollapse" :style="collapseStyle" class="split-collapse-button" @click="toggleCollapse()">
|
|
|
- <slot name="collapse" :collapse="isCollapse">
|
|
|
- <ElIcon class="split-collapse-icon">
|
|
|
- <ArrowUp v-if="vertical" />
|
|
|
- <ArrowLeft v-else />
|
|
|
- </ElIcon>
|
|
|
- </slot>
|
|
|
- </div>
|
|
|
- <!-- 小屏幕遮罩层 -->
|
|
|
- <div class="split-panel-mask" @click="toggleCollapse()"></div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
-.split-panel {
|
|
|
- display: flex;
|
|
|
- position: relative;
|
|
|
- --split-size: 200px;
|
|
|
- --split-space: 16px;
|
|
|
-
|
|
|
- // 侧边容器
|
|
|
- & > .split-panel-wrap {
|
|
|
- flex-shrink: 0;
|
|
|
- box-sizing: border-box;
|
|
|
- width: calc(var(--split-size) + var(--split-space));
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- opacity: 1;
|
|
|
-
|
|
|
- // 侧边
|
|
|
- & > .split-panel-side {
|
|
|
- flex-shrink: 0;
|
|
|
- width: var(--split-size);
|
|
|
- border: 1px solid var(--el-border-color-light);
|
|
|
- box-sizing: border-box;
|
|
|
- position: relative;
|
|
|
- }
|
|
|
-
|
|
|
- // 间距
|
|
|
- & > .split-panel-space {
|
|
|
- flex-shrink: 0;
|
|
|
- width: var(--split-space);
|
|
|
- box-sizing: border-box;
|
|
|
- position: relative;
|
|
|
-
|
|
|
- // 拉伸线
|
|
|
- .split-resize-line {
|
|
|
- width: 12px;
|
|
|
- height: 100%;
|
|
|
- position: absolute;
|
|
|
- left: -6px;
|
|
|
- z-index: 4;
|
|
|
- cursor: e-resize;
|
|
|
-
|
|
|
- &::after {
|
|
|
- content: '';
|
|
|
- width: 3px;
|
|
|
- height: 100%;
|
|
|
- display: block;
|
|
|
- margin: 0 auto;
|
|
|
- }
|
|
|
-
|
|
|
- &:hover::after {
|
|
|
- background: var(--el-color-primary);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 内容
|
|
|
- & > .split-panel-body {
|
|
|
- flex: 1;
|
|
|
- overflow: auto;
|
|
|
- box-sizing: border-box;
|
|
|
- position: relative;
|
|
|
- }
|
|
|
-
|
|
|
- // 折叠按钮
|
|
|
- & > .split-collapse-button {
|
|
|
- width: 24px;
|
|
|
- height: 24px;
|
|
|
- line-height: 24px;
|
|
|
- text-align: center;
|
|
|
- position: absolute;
|
|
|
- left: var(--split-size);
|
|
|
- top: 50%;
|
|
|
- margin: -12px 0 0 -12px;
|
|
|
- background: var(--el-bg-color-overlay);
|
|
|
- box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.04);
|
|
|
- border: 1px solid var(--el-border-color-light);
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- border-radius: 50%;
|
|
|
- cursor: pointer;
|
|
|
- z-index: 5;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- font-size: 16px;
|
|
|
- vertical-align: -2px;
|
|
|
- color: var(--el-color-info);
|
|
|
- font-weight: bold;
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- transform: scaleX(1);
|
|
|
- }
|
|
|
-
|
|
|
- &:hover .split-collapse-icon {
|
|
|
- color: var(--el-color-primary);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 折叠状态
|
|
|
- &.is-collapse {
|
|
|
- & > .split-panel-wrap {
|
|
|
- width: 0 !important;
|
|
|
- pointer-events: none;
|
|
|
- opacity: 0;
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-collapse-button {
|
|
|
- left: 0;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleX(-1);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 垂直
|
|
|
- &.is-vertical {
|
|
|
- flex-direction: column;
|
|
|
-
|
|
|
- & > .split-panel-wrap {
|
|
|
- flex-direction: column;
|
|
|
- height: calc(var(--split-size) + var(--split-space));
|
|
|
- width: auto;
|
|
|
-
|
|
|
- & > .split-panel-side {
|
|
|
- height: var(--split-size);
|
|
|
- width: auto;
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-space {
|
|
|
- height: var(--split-space);
|
|
|
- width: auto;
|
|
|
-
|
|
|
- .split-resize-line {
|
|
|
- width: 100%;
|
|
|
- height: 12px;
|
|
|
- left: auto;
|
|
|
- top: -6px;
|
|
|
- cursor: n-resize;
|
|
|
-
|
|
|
- &::after {
|
|
|
- width: 100%;
|
|
|
- height: 3px;
|
|
|
- margin: 4px 0 0 0;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-collapse-button {
|
|
|
- top: var(--split-size);
|
|
|
- left: 50%;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleY(1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.is-collapse {
|
|
|
- & > .split-panel-wrap {
|
|
|
- width: auto !important;
|
|
|
- height: 0 !important;
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-collapse-button {
|
|
|
- top: 0;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleY(-1);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 反向
|
|
|
- &.is-reverse {
|
|
|
- flex-direction: row-reverse;
|
|
|
-
|
|
|
- & > .split-panel-wrap {
|
|
|
- flex-direction: row-reverse;
|
|
|
-
|
|
|
- & > .split-panel-space .split-resize-line {
|
|
|
- left: auto;
|
|
|
- right: -6px;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-collapse-button {
|
|
|
- left: auto;
|
|
|
- right: var(--split-size);
|
|
|
- margin: -12px -12px 0 0;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleX(-1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.is-collapse > .split-collapse-button {
|
|
|
- right: 0;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleX(1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.is-vertical {
|
|
|
- flex-direction: column-reverse;
|
|
|
-
|
|
|
- & > .split-panel-wrap {
|
|
|
- flex-direction: column-reverse;
|
|
|
-
|
|
|
- & > .split-panel-space .split-resize-line {
|
|
|
- top: auto;
|
|
|
- right: auto;
|
|
|
- bottom: -6px;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-collapse-button {
|
|
|
- left: 50%;
|
|
|
- top: auto;
|
|
|
- bottom: var(--split-size);
|
|
|
- margin: 0 0 -12px -12px;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleY(-1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.is-collapse > .split-collapse-button {
|
|
|
- bottom: 0;
|
|
|
-
|
|
|
- .split-collapse-icon {
|
|
|
- transform: scaleY(1);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 拉伸状态
|
|
|
- &.is-resizing {
|
|
|
- user-select: none;
|
|
|
-
|
|
|
- & > .split-panel-wrap,
|
|
|
- & > .split-collapse-button {
|
|
|
- transition: none;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 遮罩层
|
|
|
- .split-panel-mask {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- opacity: 0;
|
|
|
- backdrop-filter: blur(6px);
|
|
|
- background: rgba(158, 158, 158, 0.2);
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- pointer-events: none;
|
|
|
- display: none;
|
|
|
- z-index: 2;
|
|
|
- }
|
|
|
-
|
|
|
- // 内部表格弹性布局
|
|
|
- &.is-flex-table {
|
|
|
- flex: 1;
|
|
|
- overflow: auto;
|
|
|
-
|
|
|
- & > .split-panel-body,
|
|
|
- & > .split-panel-wrap > .split-panel-side {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
-
|
|
|
- & > .pro-table {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- overflow: auto;
|
|
|
-
|
|
|
- & > .el-table {
|
|
|
- flex: 1;
|
|
|
- height: 100%;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 小屏幕样式 */
|
|
|
-@media screen and (max-width: 768px) {
|
|
|
- .split-panel.is-responsive:not(.is-vertical) {
|
|
|
- &:not(.is-collapse) {
|
|
|
- overflow: hidden !important;
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-wrap {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- bottom: 0;
|
|
|
-
|
|
|
- & > .split-panel-side {
|
|
|
- background: var(--el-bg-color-overlay);
|
|
|
- border: none;
|
|
|
- z-index: 3;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-body {
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-mask {
|
|
|
- display: block;
|
|
|
- }
|
|
|
-
|
|
|
- &:not(.is-collapse) {
|
|
|
- & > .split-panel-mask {
|
|
|
- left: var(--split-size);
|
|
|
- pointer-events: all;
|
|
|
- opacity: 1;
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-body {
|
|
|
- transform: translateX(calc(var(--split-size) + var(--split-space)));
|
|
|
- z-index: 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 反向
|
|
|
- &.is-reverse {
|
|
|
- & > .split-panel-wrap {
|
|
|
- right: 0;
|
|
|
- left: auto;
|
|
|
- }
|
|
|
-
|
|
|
- &:not(.is-collapse) {
|
|
|
- & > .split-panel-mask {
|
|
|
- left: 0;
|
|
|
- right: var(--split-size);
|
|
|
- }
|
|
|
-
|
|
|
- & > .split-panel-body {
|
|
|
- transform: translateX(calc(0px - var(--split-size) - var(--split-space)));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|