提交 bce14421 编写于 作者: MosYCo's avatar MosYCo

update:分割栏组件支持拖拽

上级 76b38363
......@@ -4,7 +4,11 @@
<div class="app-split-pane left-pane" :style="{ right: `${100 - offset}%` }">
<slot name="left"/>
</div>
<div class="app-split-trigger app-split-trigger-horizontal" :style="{ left: `${offset}%`, width: `${triggerSize}px` }">
<div
class="app-split-trigger app-split-trigger-horizontal"
:style="{ left: `${offset}%`, width: `${triggerSize}px` }"
@mousedown="handleMouseDown"
>
<span class="trigger-icon">
</span>
</div>
......@@ -16,7 +20,10 @@
<div class="app-split-pane top-pane" :style="{ bottom: `${100 - offset}%` }">
<slot name="top"/>
</div>
<div class="app-split-trigger app-split-trigger-vertical" :style="{ top: `${offset}%`, height: `${triggerSize}px` }">
<div
class="app-split-trigger app-split-trigger-vertical"
:style="{ top: `${offset}%`, height: `${triggerSize}px` }"
@mousedown="handleMouseDown">
<span class="trigger-icon">
</span>
</div>
......@@ -34,27 +41,27 @@ interface IProps {
* horizontal 横向
* vertical 垂直
*/
mode?: string,
mode?: 'horizontal' | 'vertical',
/**
* 位置,0-1表示百分比,也可以是具体数值
*/
value: number | string,
modelValue: number | string,
/**
* 最小阈值
*/
min: number | string,
min?: number | string,
/**
* 最大阈值
*/
max: number | string,
max?: number | string,
/**
* 分割栏大小
*/
triggerSize: number;
triggerSize?: number;
}
// ref指向
......@@ -63,55 +70,74 @@ const split = ref(null);
// 参数
const props = withDefaults(defineProps < IProps > (), {
mode: 'horizontal',
value: 0.5,
triggerSize: 6
})
modelValue: 0.5,
triggerSize: 6,
min: '150px',
max: '150px'
});
// 当前分割值
const currentValue: Ref<string | number> = ref(props.modelValue);
// 当前最小分割阈值
const currentMin: Ref<string | number> = ref(props.min ? props.min : '150px');
// 当前最大分割阈值
const currentMax: Ref<string | number> = ref(props.max ? props.min : '150px');
// 偏移量
const offset: Ref<number> = ref(0);
// 是否横向布局
const isHorizontal: Ref<boolean> = computed(() => {
return props.mode === 'horizontal';
})
});
// 偏移量值
const offsetSize: Ref<string> = computed(() => {
return isHorizontal ? 'offsetWidth' : 'offsetHeight';
})
return isHorizontal.value ? 'offsetWidth' : 'offsetHeight';
});
// px转百分比
const px2Percent = (numerator: any, denominator: any) => {
return parseFloat(numerator) / parseFloat(denominator);
}
// 值是否是PX
const valueIsPx = computed(() => {
return typeof props.modelValue === 'string';
});
// 当前分割值
const currentValue: Ref<string | number> = ref(0.5);
currentValue.value = props.value;
// 起始偏移量
const startOffset: Ref<number> = ref(0);
// 当前最小分割阈值
const currentMin: Ref<string | number> = ref(40);
currentMin.value = props.min;
// 保存旧偏移量
const oldOffset: Ref<number | string> = ref(0);
// 当前最大分割阈值
const currentMax: Ref<string | number> = ref(40);
currentMax.value = props.max;
// 是否移动状态
const isMoving: Ref<boolean> = ref(false);
// 偏移量
const offset: Ref<number> = ref(0);
// 抛出事件集合
interface EmitEvents {
(event: 'move-start'): void;
(event: 'moving', mouseEvent: MouseEvent): void;
(event: 'move-end'): void;
(event: 'update:modelValue', value: string | number): void;
}
// 抛出事件
const emit = defineEmits<EmitEvents>();
// px转百分比
const px2Percent = (numerator: any, denominator: any) => {
return parseFloat(numerator) / parseFloat(denominator);
}
// 获取分割组件
const getSplitCom = (): any => {
return unref(split);
}
// 值是否是PX
const valueIsPx = (): boolean => {
return typeof props.value === 'string';
}
// 计算阈值
const getComputedThresholdValue = (type: 'min' | 'max'): string | number => {
const splitCom = getSplitCom();
const size = splitCom[offsetSize.value];
if (valueIsPx()) {
if (valueIsPx.value) {
return typeof props[type] === 'string' ? props[type] : size * (props[type] as number);
} else {
return typeof props[type] === 'string' ? px2Percent(props[type], size) : props[type];
......@@ -123,16 +149,101 @@ const computeOffset = () => {
nextTick(() => {
const splitCom = getSplitCom();
currentMin.value = getComputedThresholdValue('min');
currentMin.value = getComputedThresholdValue('max');
offset.value = (valueIsPx() ? px2Percent(props.value, splitCom[offsetSize.value]) : props.value as number) * 10000 / 100;
currentMax.value = getComputedThresholdValue('max');
offset.value = (valueIsPx.value ? px2Percent(props.modelValue, splitCom[offsetSize.value]) : props.modelValue as number) * 10000 / 100;
})
}
// 处理鼠标按下
const handleMouseDown = (event: MouseEvent) => {
startOffset.value = isHorizontal.value ? event.pageX : event.pageY;
oldOffset.value = props.modelValue;
isMoving.value = true;
on('mousemove', handleMove);
on('mouseup', handleMouseUp);
emit('move-start');
}
// 处理鼠标松开
const handleMouseUp = (event: MouseEvent) => {
isMoving.value = false;
off('mousemove', handleMove);
off('mouseup', handleMouseUp);
emit('move-end');
}
// 处理鼠标移动
const handleMove = (event: MouseEvent) => {
let pageOffset = isHorizontal.value ? event.pageX : event.pageY;
let _offset = pageOffset - startOffset.value;
const splitCom = getSplitCom();
let outerWidth = splitCom[offsetSize.value];
let value: any = valueIsPx.value ? `${parseFloat(oldOffset.value as string) + _offset}px` : (px2Percent(outerWidth * Number(oldOffset.value) + _offset, outerWidth));
let anotherValue = getAnotherOffset(splitCom, value);
// 计算最小值
if (parseFloat(value) <= parseFloat(currentMin.value as string))
value = getMax(value, currentMin.value);
// 计算最大值
if (parseFloat(anotherValue) <= parseFloat(currentMax.value as string))
value = getAnotherOffset(splitCom, getMax(anotherValue, currentMax.value));
// atMin、atMax分别表示是否已达到最小阈值最大阈值
(event as any).atMin = props.modelValue === currentMin.value;
(event as any).atMax = valueIsPx.value ? getAnotherOffset(splitCom, props.modelValue) === currentMax.value : getAnotherOffset(splitCom, props.modelValue).toFixed(5) === (currentMax.value as any).toFixed(5);
emit('update:modelValue', value);
emit('moving', event);
}
// 获取另一侧偏移量
const getAnotherOffset = (splitCom: any, value: any) => {
let res: any = 0;
if (valueIsPx.value) res = `${splitCom[offsetSize.value] - parseFloat(value)}px`;
else res = 1 - value;
return res;
}
// 获取最大值
const getMax = (value1: any, value2: any) => {
if (valueIsPx.value)
return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
else
return Math.max(value1, value2);
}
// 启用监听事件
const on = (event: string, handler: Function) => {
if (event && handler instanceof Function) {
document.addEventListener(event as any, handler as any);
}
}
// 关闭监听事件
const off = (event: string, handler: Function) => {
if (event && handler instanceof Function) {
document.removeEventListener(event as any, handler as any, false);
}
}
// 组件挂载
onMounted(() => {
nextTick(() => {
computeOffset();
})
});
window.addEventListener('resize', () => {
computeOffset();
});
})
// 监听参数,实现双向绑定
watch(() => props.modelValue, (val: string | number, oldVal: string | number) => {
if (val !== currentValue.value) {
computeOffset();
}
})
onUnmounted(() => {
window.removeEventListener('resize', () => {
computeOffset();
});
})
</script>
......
......@@ -14,4 +14,11 @@ export interface ExpBarControlState extends MainControlState {
* @memberof ExpBarControlState
*/
selection: IParam;
/**
* @description 分割栏绑定值
* @type {number}
* @memberof ExpBarControlState
*/
split: number;
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ export const ctrlState = {
xDataControlName: '{{ctrl.xDataControlName}}',
selection: {},
showTitleBar: {{#if ctrl.showTitleBar}}true{{else}}false{{/if}},
split: 0.2,
title: '{{ctrl.title}}',
titleRes: '{{#if ctrl.titlePSLanguageRes}}{{ctrl.titlePSLanguageRes.lanResTag}}{{/if}}',
viewRefs: [
......
......@@ -44,7 +44,7 @@ defineExpose({ state, name: '{{ctrl.name}}' });
<template>
<div class="app-tree-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<AppSplit :value="0.2">
<AppSplit v-model="state.split">
<template #left>
<div v-if="state.showTitleBar" class="tree-exp-bar-title">
<span>\{{ state.title }}</span>
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册