# Split分隔面板

使用Vue3实现的分隔面板,可以将一片区域,分割为可以拖拽调整宽度或高度的两部分区域。

# Split Props

属性 说明 类型 默认值
mode 类型,可选值为 horizontalvertical String horizontal
min 左侧或者上侧的最小阈值 String | Number 40px
max 左侧或者上侧的最小阈值(0表示不设置最大值) String | Number 0
min2 右侧或者下侧的最小阈值 String | Number 40px
modelValue 面板位置,可以是 0~1 (代表百分比),或具体数值的像素(不支持空字符串),可用 v-model 双向绑定;0~1代表百分比,字符串代表像素值 String |Number 0.5

# Split event

事件名 说明 参数
on-move-start 拖拽开始 event
on-moving 拖拽中 event
on-move-end 拖拽结束 event

# Split slot

名称 说明
left mode 为 horizontal 时可用,左边面板
right mode 为 horizontal 时可用,右边面板
top mode 为 vertical 时可用,上边面板
bottom mode 为 vertical 时可用,下边面板
trigger 自定义分隔拖拽节点

# 实现代码

XSplit.vue

<template>
  <div class="x-split-wrapper"
       ref="WrapperEl">
    <div :class="{'x-split--horizontal': horizontalMode,'x-split--vertical': verticalMode,}">
      <div :class="{'x-split-pane': true, 'x-pane--left': horizontalMode, 'x-pane--top': verticalMode}"
           ref="leftTopPaneEl"
           :style="{
                right: horizontalMode ? rightValueStyle : '',
                bottom: verticalMode ? rightValueStyle : '',
           }">
        <slot v-if="horizontalMode"
              name="left"></slot>
        <slot v-if="mode === 'vertical'"
              name="top"></slot>
      </div>
      <div class="x-split-trigger-wrapper"
           ref="triggerWrapperEl"
           :style="{
                left: horizontalMode ? leftTopValueStyle : '',
                top: verticalMode ? leftTopValueStyle : '',
           }"
           @mousedown="onMouoseDown($event)">
        <div :class="{'x-split-trigger': true, 'x-split-trigger--vertical': horizontalMode, 'x-split-trigger--horizontal': verticalMode}">
          <slot name="trigger">
            <div v-for="i in 8"
                 :key="i"
                 class="x-split-trigger__bar"></div>
          </slot>

        </div>
      </div>
      <div :class="{'x-split-pane': true, 'x-pane--right': horizontalMode, 'x-pane--bottom': verticalMode}"
           ref="rightBottomPaneEl"
           :style="{
                left: horizontalMode ? leftTopValueStyle : '',
                top: verticalMode ? leftTopValueStyle : '',
           }">
        <slot v-if="horizontalMode"
              name="right"></slot>
        <slot v-if="verticalMode"
              name="bottom"></slot>
      </div>

    </div>
  </div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue';
defineOptions({ name: 'XSplit' });
const props = defineProps({
  mode: {
    type: String,
    default: 'horizontal',
    validator: (val) => ['horizontal', 'vertical'].includes(val)
  },
  modelValue: {
    type: [Number, String],
    default: 0.5
  },
  min: {
    type: [Number, String],
    default: 40
  },
  max: {
    type: [Number, String],
    default: 0
  },
  min2: {
    type: [Number, String],
    default: 46
  }
});
const emits = defineEmits(['update:modelValue', 'on-move-start', 'on-moving', 'on-move-end']);
// emits: ['update:modelValue', 'change'],

const horizontalMode = computed(() => {
  return props.mode === 'horizontal';
});
const verticalMode = computed(() => {
  return props.mode === 'vertical';
});

const isPercent = ref(false);

let _min = 40;
watchEffect(() => {
  try {
    _min = parseFloat(props.min);
  } catch (error) {
    console.error(error);
  }
});

let _min2 = 46;
watchEffect(() => {
  try {
    _min2 = parseFloat(props.min2);
  } catch (error) {
    console.error(error);
  }
});

let _max = 0;
watchEffect(() => {
  try {
    _max = parseFloat(props.max);
  } catch (error) {
    console.error(error);
  }
});

let _modelValue = parseFloat(props.modelValue);
if (0 <= _modelValue && _modelValue <= 1) {
  // 表示百分比
  isPercent.value = true;
} else {
  // 当做数值型
  isPercent.value = false;
}

const leftTopValue = ref(0);
leftTopValue.value = _modelValue;

const WrapperEl = ref(null);
const leftTopPaneEl = ref(null);
const triggerWrapperEl = ref(null);
const rightBottomPaneEl = ref(null);

const leftTopValueStyle = computed(() => {
  if (isPercent.value) {
    return leftTopValue.value * 100 + '%';
  }
  return leftTopValue.value + 'px';
});
const rightValueStyle = ref('');
const setRightValueStyle = () => {
  if (horizontalMode.value) {
    if (isPercent.value) {
      rightValueStyle.value = (1 - leftTopValue.value) * 100 + '%';
      return;
    }
    if (WrapperEl.value) {
      rightValueStyle.value = WrapperEl.value.offsetWidth - leftTopValue.value + 'px';
    }
  }
  if (verticalMode.value) {
    if (isPercent.value) {
      rightValueStyle.value = (1 - leftTopValue.value) * 100 + '%';
      return;
    }
    if (WrapperEl.value) {
      rightValueStyle.value = WrapperEl.value.offsetHeight - leftTopValue.value + 'px';
    }
  }
};

let isMoving = false;
let startClientX = 0;
let startLeftWidth = 0;
let startLeftValue = 0;

let startClientY = 0;
let startTopHeight = 0;
let startTopValue = 0;

const onMouoseDown = (event) => {
  document.addEventListener('mousemove', onMouseMove);
  document.addEventListener('mouseup', onMouseUp);
  startLeftWidth = leftTopPaneEl.value.offsetWidth;
  startTopHeight = leftTopPaneEl.value.offsetHeight;
  startLeftValue = leftTopValue.value;
  startTopValue = leftTopValue.value;
  startClientX = event.clientX;
  startClientY = event.clientY;
  isMoving = true;
  emits('on-move-start', event);
};

const execHorizontal = (event) => {
  if (isPercent.value) {
    const deltaWidth = event.clientX - startClientX;
    const wrapperWidth = WrapperEl.value.offsetWidth;
    let _leftTopValue = (startLeftWidth + deltaWidth) / wrapperWidth;

    if (_leftTopValue <= 0) {
      _leftTopValue = 0;
    }
    if (_leftTopValue >= 1) {
      _leftTopValue = 1;
    }

    _leftTopValue = _leftTopValue.toFixed(6) - 0;
    const leftWidth = wrapperWidth * _leftTopValue;
    if (leftWidth <= _min) {
      _leftTopValue = (_min / wrapperWidth).toFixed(6) - 0;
    }
    if (_max > 0 && leftWidth >= _max) {
      _leftTopValue = (_max / wrapperWidth).toFixed(6) - 0;
    }

    if (wrapperWidth * (1 - _leftTopValue) <= _min2) {
      _leftTopValue = (wrapperWidth - _min2) / wrapperWidth;
    }

    leftTopValue.value = _leftTopValue;

    setRightValueStyle();
    emits('update:modelValue', leftTopValue.value);
  } else {
    const deltaWidth = event.clientX - startClientX;
    let _leftTopValue = startLeftValue + deltaWidth;
    const wrapperWidth = WrapperEl.value.offsetWidth;
    if (_leftTopValue <= 0) {
      _leftTopValue = 0;
    }
    if (_leftTopValue >= wrapperWidth) {
      _leftTopValue = wrapperWidth;
    }
    if (_leftTopValue <= _min) {
      _leftTopValue = _min;
    }
    if (_max > 0 && _leftTopValue >= _max) {
      _leftTopValue = _max;
    }

    if (wrapperWidth - _leftTopValue <= _min2) {
      _leftTopValue = wrapperWidth - _min2;
    }

    leftTopValue.value = _leftTopValue;
    setRightValueStyle();
    emits('update:modelValue', leftTopValue.value + 'px');
  }
};

const execVertical = (event) => {
  if (isPercent.value) {
    const deltaHeight = event.clientY - startClientY;
    let _leftTopValue = (startTopHeight + deltaHeight) / WrapperEl.value.offsetHeight;
    if (_leftTopValue <= 0) {
      _leftTopValue = 0;
    }
    if (_leftTopValue >= 1) {
      _leftTopValue = 1;
    }

    const wrapperHeight = WrapperEl.value.offsetHeight;
    _leftTopValue = _leftTopValue.toFixed(6) - 0;
    const topHeight = wrapperHeight * _leftTopValue;
    if (topHeight <= _min) {
      _leftTopValue = (_min / wrapperHeight).toFixed(6) - 0;
    }
    if (_max > 0 && topHeight >= _max) {
      _leftTopValue = (_max / wrapperHeight).toFixed(6) - 0;
    }
    if (wrapperHeight * (1 - _leftTopValue) <= _min2) {
      _leftTopValue = (WrapperEl.value.offsetHeight - _min2) / WrapperEl.value.offsetHeight;
    }
    leftTopValue.value = _leftTopValue;
    setRightValueStyle();
    emits('update:modelValue', leftTopValue.value);
  } else {
    const deltaHeight = event.clientY - startClientY;
    let _leftTopValue = startTopValue + deltaHeight;
    const wrapperHeight = WrapperEl.value.offsetHeight;
    if (_leftTopValue <= 0) {
      _leftTopValue = 0;
    }
    if (_leftTopValue >= wrapperHeight) {
      _leftTopValue = wrapperHeight;
    }
    if (_leftTopValue <= _min) {
      _leftTopValue = _min;
    }
    if (_max > 0 && _leftTopValue >= _max) {
      _leftTopValue = _max;
    }
    if (wrapperHeight - _leftTopValue <= _min2) {
      _leftTopValue = wrapperHeight - _min2;
    }
    leftTopValue.value = _leftTopValue;
    setRightValueStyle();
    emits('update:modelValue', leftTopValue.value + 'px');
  }
};

const onMouseMove = (event) => {
  if (isMoving) {
    if (horizontalMode.value) {
      execHorizontal(event);
    }
    if (verticalMode.value) {
      execVertical(event);
    }
    emits('on-moving', event);
  }
};
const onMouseUp = (event) => {
  if (isMoving) {
    if (horizontalMode.value) {
      execHorizontal(event);
    }
    if (verticalMode.value) {
      execVertical(event);
    }
  }
  document.removeEventListener('mousemove', onMouseMove);
  document.removeEventListener('mouseup', onMouseUp);
  isMoving = false;
  startClientX = 0;
  startLeftWidth = 0;
  emits('on-move-end', event);
};

onMounted(() => {
  setRightValueStyle();
});

onUnmounted(() => {
  document.removeEventListener('mousemove', onMouseMove);
  document.removeEventListener('mouseup', onMouseUp);
});
</script>
<style scoped lang="css">
.x-split-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
}
.x-split-pane {
  position: absolute;
  z-index: 1;
}

.x-split-pane.x-pane--left {
  left: 0;
  top: 0;
  bottom: 0;
}
.x-split-pane.x-pane--top {
  left: 0;
  right: 0;
  top: 0;
}
.x-split-pane.x-pane--right {
  right: 0;
  top: 0;
  bottom: 0;
  padding-left: 6px;
}
.x-split-pane.x-pane--bottom {
  right: 0;
  left: 0;
  bottom: 0;
  padding-top: 6px;
}
.x-split-trigger-wrapper {
  position: absolute;
  /* transform: translate(-50%, -50%); */
  z-index: 10;
}
.x-split--horizontal > .x-split-trigger-wrapper {
  height: 100%;
  width: 0;
}
.x-split--vertical > .x-split-trigger-wrapper {
  height: 0;
  width: 100%;
}
.x-split-trigger {
  border: 1px solid #d7dde4;
}
.x-split-trigger--vertical {
  width: 6px;
  box-sizing: border-box;
  height: 100%;
  background: #f8f8f9;
  border-top: none;
  border-bottom: none;
  cursor: col-resize;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.x-split-trigger--vertical .x-split-trigger__bar {
  width: 4px;
  height: 1px;
  margin: 2px;
  background-color: rgba(23, 35, 61, 0.25);
}

.x-split-trigger--horizontal {
  height: 6px;
  box-sizing: border-box;
  width: 100%;
  background: #f8f8f9;
  border-left: none;
  border-right: none;
  cursor: row-resize;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}
.x-split-trigger--horizontal .x-split-trigger__bar {
  width: 1px;
  height: 4px;
  margin: 0 2px;
  background-color: rgba(23, 35, 61, 0.25);
}
</style>

# 基础用法

<XSplit v-model="splitValue">
  <template v-slot:left>
    <div>top box</div>
  </template>
  <template v-slot:right>
    <div>bottom box</div>
  </template>
</XSplit>

# 嵌套使用

<div style="margin: 20px;width: 500px; height: 300px;border: 1px solid #eee;">
  <XSplit :modelValue="200">
    <template v-slot:left>
      <div style="height: 100%">
        <XSplit :modelValue="100"
                mode="vertical"
                @on-moving="onMoving">
          <template v-slot:top>
            <div>top box</div>
          </template>
          <template v-slot:bottom>
            <div>bottom box</div>
          </template>
        </XSplit>
      </div>
    </template>
    <template v-slot:right>
      <div>right box</div>
    </template>
  </XSplit>
</div>

# 图示

# Vue2的版本

<template>
  <div class="x-split-wrapper"
       ref="WrapperEl">
    <div :class="{'x-split--horizontal': horizontalMode,'x-split--vertical': verticalMode}">
      <div :class="{'x-split-pane': true, 'x-pane--left': horizontalMode, 'x-pane--top': verticalMode}"
           ref="leftTopPaneEl"
           :style="{
                right: horizontalMode ? rightValueStyle : '',
                bottom: verticalMode ? rightValueStyle : '',
           }">
        <slot v-if="horizontalMode"
              name="left"></slot>
        <slot v-if="mode === 'vertical'"
              name="top"></slot>
      </div>
      <div class="x-split-trigger-wrapper"
           ref="triggerWrapperEl"
           :style="{
                left: horizontalMode ? leftTopValueStyle : '',
                top: verticalMode ? leftTopValueStyle : '',
           }"
           @mousedown="onMouoseDown($event)">
        <div :class="{'x-split-trigger': true, 'x-split-trigger--vertical': horizontalMode, 'x-split-trigger--horizontal': verticalMode}">
          <slot name="trigger">
            <i v-for="i in 8"
                 :key="i"
                 class="x-split-trigger__bar"></i>
          </slot>
        </div>
      </div>
      <div :class="{'x-split-pane': true, 'x-pane--right': horizontalMode, 'x-pane--bottom': verticalMode}"
           ref="rightBottomPaneEl"
           :style="{
                left: horizontalMode ? leftTopValueStyle : '',
                top: verticalMode ? leftTopValueStyle : '',
           }">
        <slot v-if="horizontalMode"
              name="right"></slot>
        <slot v-if="verticalMode"
              name="bottom"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'XSplit',
  props: {
    mode: {
      type: String,
      default: 'horizontal',
      validator: (val) => ['horizontal', 'vertical'].includes(val)
    },
    value: {
      type: [Number, String],
      default: 0.5
    },
    min: {
      type: [Number, String],
      default: 40
    },
    max: {
      type: [Number, String],
      default: 0
    },
    min2: {
      type: [Number, String],
      default: 46
    }
  },
  data() {
    return {
      leftTopValue: 0,
      rightValueStyle: '',
      isMoving: false,
      startClientX: 0,
      startLeftWidth: 0,
      startLeftValue: 0,
      startClientY: 0,
      startTopHeight: 0,
      startTopValue: 0,
      innerMin: 40,
      innerMin2: 46,
      innerMax: 0,
      isPercent: false
    };
  },
  computed: {
    horizontalMode() {
      return this.mode === 'horizontal';
    },
    verticalMode() {
      return this.mode === 'vertical';
    },
    leftTopValueStyle() {
      if (this.isPercent) {
        return this.leftTopValue * 100 + '%';
      }
      return this.leftTopValue + 'px';
    }
  },
  watch: {
    min: {
      immediate: true,
      handler(val) {
        try {
          this.innerMin = parseFloat(val);
        } catch (error) {
          console.error(error);
        }
      }
    },
    min2: {
      immediate: true,
      handler(val) {
        try {
          this.innerMin2 = parseFloat(val);
        } catch (error) {
          console.error(error);
        }
      }
    },
    max: {
      immediate: true,
      handler(val) {
        try {
          this.innerMax = parseFloat(val);
        } catch (error) {
          console.error(error);
        }
      }
    },
    value: {
      immediate: true,
      handler(val) {
        let _modelValue = parseFloat(val);
        if (0 <= _modelValue && _modelValue <= 1) {
          this.isPercent = true;
        } else {
          this.isPercent = false;
        }
        this.leftTopValue = _modelValue;
      }
    }
  },
  mounted() {
    this.setRightValueStyle();
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('mouseup', this.onMouseUp);
  },
  methods: {
    setRightValueStyle() {
      if (this.horizontalMode) {
        if (this.isPercent) {
          this.rightValueStyle = (1 - this.leftTopValue) * 100 + '%';
          return;
        }
        if (this.$refs.WrapperEl) {
          this.rightValueStyle = this.$refs.WrapperEl.offsetWidth - this.leftTopValue + 'px';
        }
      }
      if (this.verticalMode) {
        if (this.isPercent) {
          this.rightValueStyle = (1 - this.leftTopValue) * 100 + '%';
          return;
        }
        if (this.$refs.WrapperEl) {
          this.rightValueStyle = this.$refs.WrapperEl.offsetHeight - this.leftTopValue + 'px';
        }
      }
    },
    onMouoseDown(event) {
      document.addEventListener('mousemove', this.onMouseMove);
      document.addEventListener('mouseup', this.onMouseUp);
      this.startLeftWidth = this.$refs.leftTopPaneEl.offsetWidth;
      this.startTopHeight = this.$refs.leftTopPaneEl.offsetHeight;
      this.startLeftValue = this.leftTopValue;
      this.startTopValue = this.leftTopValue;
      this.startClientX = event.clientX;
      this.startClientY = event.clientY;
      this.isMoving = true;
      this.$emit('on-move-start', event);
    },
    execHorizontal(event) {
      if (this.isPercent) {
        const deltaWidth = event.clientX - this.startClientX;
        const wrapperWidth = this.$refs.WrapperEl.offsetWidth;
        let _leftTopValue = (this.startLeftWidth + deltaWidth) / wrapperWidth;

        if (_leftTopValue <= 0) {
          _leftTopValue = 0;
        }
        if (_leftTopValue >= 1) {
          _leftTopValue = 1;
        }

        _leftTopValue = _leftTopValue.toFixed(6) - 0;
        const leftWidth = wrapperWidth * _leftTopValue;
        if (leftWidth <= this.innerMin) {
          _leftTopValue = (this.innerMin / wrapperWidth).toFixed(6) - 0;
        }
        if (this.innerMax > 0 && leftWidth >= this.innerMax) {
          _leftTopValue = (this.innerMax / wrapperWidth).toFixed(6) - 0;
        }

        if (wrapperWidth * (1 - _leftTopValue) <= this.innerMin2) {
          _leftTopValue = (wrapperWidth - this.innerMin2) / wrapperWidth;
        }

        this.leftTopValue = _leftTopValue;
        this.setRightValueStyle();
        this.$emit('input', this.leftTopValue);
      } else {
        const deltaWidth = event.clientX - this.startClientX;
        let _leftTopValue = this.startLeftValue + deltaWidth;
        const wrapperWidth = this.$refs.WrapperEl.offsetWidth;
        if (_leftTopValue <= 0) {
          _leftTopValue = 0;
        }
        if (_leftTopValue >= wrapperWidth) {
          _leftTopValue = wrapperWidth;
        }
        if (_leftTopValue <= this.innerMin) {
          _leftTopValue = this.innerMin;
        }
        if (this.innerMax > 0 && _leftTopValue >= this.innerMax) {
          _leftTopValue = this.innerMax;
        }

        if (wrapperWidth - _leftTopValue <= this.innerMin2) {
          _leftTopValue = wrapperWidth - this.innerMin2;
        }

        this.leftTopValue = _leftTopValue;
        this.setRightValueStyle();
        this.$emit('input', this.leftTopValue + 'px');
      }
    },
    execVertical(event) {
      if (this.isPercent) {
        const deltaHeight = event.clientY - this.startClientY;
        let _leftTopValue = (this.startTopHeight + deltaHeight) / this.$refs.WrapperEl.offsetHeight;
        if (_leftTopValue <= 0) {
          _leftTopValue = 0;
        }
        if (_leftTopValue >= 1) {
          _leftTopValue = 1;
        }

        const wrapperHeight = this.$refs.WrapperEl.offsetHeight;
        _leftTopValue = _leftTopValue.toFixed(6) - 0;
        const topHeight = wrapperHeight * _leftTopValue;
        if (topHeight <= this.innerMin) {
          _leftTopValue = (this.innerMin / wrapperHeight).toFixed(6) - 0;
        }
        if (this.innerMax > 0 && topHeight >= this.innerMax) {
          _leftTopValue = (this.innerMax / wrapperHeight).toFixed(6) - 0;
        }
        if (wrapperHeight * (1 - _leftTopValue) <= this.innerMin2) {
          _leftTopValue = (this.$refs.WrapperEl.offsetHeight - this.innerMin2) / this.$refs.WrapperEl.offsetHeight;
        }
        this.leftTopValue = _leftTopValue;
        this.setRightValueStyle();
        this.$emit('input', this.leftTopValue);
      } else {
        const deltaHeight = event.clientY - this.startClientY;
        let _leftTopValue = this.startTopValue + deltaHeight;
        const wrapperHeight = this.$refs.WrapperEl.offsetHeight;
        if (_leftTopValue <= 0) {
          _leftTopValue = 0;
        }
        if (_leftTopValue >= wrapperHeight) {
          _leftTopValue = wrapperHeight;
        }
        if (_leftTopValue <= this.innerMin) {
          _leftTopValue = this.innerMin;
        }
        if (this.innerMax > 0 && _leftTopValue >= this.innerMax) {
          _leftTopValue = this.innerMax;
        }
        if (wrapperHeight - _leftTopValue <= this.innerMin2) {
          _leftTopValue = wrapperHeight - this.innerMin2;
        }
        this.leftTopValue = _leftTopValue;
        this.setRightValueStyle();
        this.$emit('input', this.leftTopValue + 'px');
      }
    },
    onMouseMove(event) {
      if (this.isMoving) {
        if (this.horizontalMode) {
          this.execHorizontal(event);
        }
        if (this.verticalMode) {
          this.execVertical(event);
        }
        this.$emit('on-moving', event);
      }
    },
    onMouseUp(event) {
      if (this.isMoving) {
        if (this.horizontalMode) {
          this.execHorizontal(event);
        }
        if (this.verticalMode) {
          this.execVertical(event);
        }
      }
      document.removeEventListener('mousemove', this.onMouseMove);
      document.removeEventListener('mouseup', this.onMouseUp);
      this.isMoving = false;
      this.startClientX = 0;
      this.startLeftWidth = 0;
      this.$emit('on-move-end', event);
    }
  }
};
</script>

<style scoped>
.x-split-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
}
.x-split-pane {
  position: absolute;
  z-index: 1;
}

.x-split-pane.x-pane--left {
  left: 0;
  top: 0;
  bottom: 0;
}
.x-split-pane.x-pane--top {
  left: 0;
  right: 0;
  top: 0;
}
.x-split-pane.x-pane--right {
  right: 0;
  top: 0;
  bottom: 0;
  padding-left: 6px;
}
.x-split-pane.x-pane--bottom {
  right: 0;
  left: 0;
  bottom: 0;
  padding-top: 6px;
}
.x-split-trigger-wrapper {
  position: absolute;
  z-index: 10;
}
.x-split--horizontal > .x-split-trigger-wrapper {
  height: 100%;
  width: 0;
}
.x-split--vertical > .x-split-trigger-wrapper {
  height: 0;
  width: 100%;
}
.x-split-trigger {
  border: 1px solid #d7dde4;
}
.x-split-trigger--vertical {
  width: 6px;
  box-sizing: border-box;
  height: 100%;
  background: #f8f8f9;
  border-top: none;
  border-bottom: none;
  cursor: col-resize;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.x-split-trigger--vertical .x-split-trigger__bar {
  width: 4px;
  height: 1px;
  margin: 2px;
  background-color: rgba(23, 35, 61, 0.25);
  display: block;
}

.x-split-trigger--horizontal {
  height: 6px;
  box-sizing: border-box;
  width: 100%;
  background: #f8f8f9;
  border-left: none;
  border-right: none;
  cursor: row-resize;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}
.x-split-trigger--horizontal .x-split-trigger__bar {
  width: 1px;
  height: 4px;
  margin: 0 2px;
  background-color: rgba(23, 35, 61, 0.25);
}
</style>

上次更新: 5/7/2026, 2:17:18 AM