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

update:分割栏组件支持拖拽

上级 76b38363
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
<div class="app-split-pane left-pane" :style="{ right: `${100 - offset}%` }"> <div class="app-split-pane left-pane" :style="{ right: `${100 - offset}%` }">
<slot name="left"/> <slot name="left"/>
</div> </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 class="trigger-icon">
</span> </span>
</div> </div>
...@@ -16,7 +20,10 @@ ...@@ -16,7 +20,10 @@
<div class="app-split-pane top-pane" :style="{ bottom: `${100 - offset}%` }"> <div class="app-split-pane top-pane" :style="{ bottom: `${100 - offset}%` }">
<slot name="top"/> <slot name="top"/>
</div> </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 class="trigger-icon">
</span> </span>
</div> </div>
...@@ -34,27 +41,27 @@ interface IProps { ...@@ -34,27 +41,27 @@ interface IProps {
* horizontal 横向 * horizontal 横向
* vertical 垂直 * vertical 垂直
*/ */
mode?: string, mode?: 'horizontal' | 'vertical',
/** /**
* 位置,0-1表示百分比,也可以是具体数值 * 位置,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指向 // ref指向
...@@ -63,55 +70,74 @@ const split = ref(null); ...@@ -63,55 +70,74 @@ const split = ref(null);
// 参数 // 参数
const props = withDefaults(defineProps < IProps > (), { const props = withDefaults(defineProps < IProps > (), {
mode: 'horizontal', mode: 'horizontal',
value: 0.5, modelValue: 0.5,
triggerSize: 6 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(() => { const isHorizontal: Ref<boolean> = computed(() => {
return props.mode === 'horizontal'; return props.mode === 'horizontal';
}) });
// 偏移量值 // 偏移量值
const offsetSize: Ref<string> = computed(() => { const offsetSize: Ref<string> = computed(() => {
return isHorizontal ? 'offsetWidth' : 'offsetHeight'; return isHorizontal.value ? 'offsetWidth' : 'offsetHeight';
}) });
// px转百分比 // 值是否是PX
const px2Percent = (numerator: any, denominator: any) => { const valueIsPx = computed(() => {
return parseFloat(numerator) / parseFloat(denominator); return typeof props.modelValue === 'string';
} });
// 当前分割值 // 起始偏移量
const currentValue: Ref<string | number> = ref(0.5); const startOffset: Ref<number> = ref(0);
currentValue.value = props.value;
// 当前最小分割阈值 // 保存旧偏移量
const currentMin: Ref<string | number> = ref(40); const oldOffset: Ref<number | string> = ref(0);
currentMin.value = props.min;
// 当前最大分割阈值 // 是否移动状态
const currentMax: Ref<string | number> = ref(40); const isMoving: Ref<boolean> = ref(false);
currentMax.value = props.max;
// 偏移量 // 抛出事件集合
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 => { const getSplitCom = (): any => {
return unref(split); return unref(split);
} }
// 值是否是PX
const valueIsPx = (): boolean => {
return typeof props.value === 'string';
}
// 计算阈值 // 计算阈值
const getComputedThresholdValue = (type: 'min' | 'max'): string | number => { const getComputedThresholdValue = (type: 'min' | 'max'): string | number => {
const splitCom = getSplitCom(); const splitCom = getSplitCom();
const size = splitCom[offsetSize.value]; const size = splitCom[offsetSize.value];
if (valueIsPx()) { if (valueIsPx.value) {
return typeof props[type] === 'string' ? props[type] : size * (props[type] as number); return typeof props[type] === 'string' ? props[type] : size * (props[type] as number);
} else { } else {
return typeof props[type] === 'string' ? px2Percent(props[type], size) : props[type]; return typeof props[type] === 'string' ? px2Percent(props[type], size) : props[type];
...@@ -123,16 +149,101 @@ const computeOffset = () => { ...@@ -123,16 +149,101 @@ const computeOffset = () => {
nextTick(() => { nextTick(() => {
const splitCom = getSplitCom(); const splitCom = getSplitCom();
currentMin.value = getComputedThresholdValue('min'); currentMin.value = getComputedThresholdValue('min');
currentMin.value = getComputedThresholdValue('max'); currentMax.value = getComputedThresholdValue('max');
offset.value = (valueIsPx() ? px2Percent(props.value, splitCom[offsetSize.value]) : props.value as number) * 10000 / 100; 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(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
computeOffset(); 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> </script>
......
...@@ -14,4 +14,11 @@ export interface ExpBarControlState extends MainControlState { ...@@ -14,4 +14,11 @@ export interface ExpBarControlState extends MainControlState {
* @memberof ExpBarControlState * @memberof ExpBarControlState
*/ */
selection: IParam; selection: IParam;
/**
* @description 分割栏绑定值
* @type {number}
* @memberof ExpBarControlState
*/
split: number;
} }
\ No newline at end of file
...@@ -5,6 +5,7 @@ export const ctrlState = { ...@@ -5,6 +5,7 @@ export const ctrlState = {
xDataControlName: '{{ctrl.xDataControlName}}', xDataControlName: '{{ctrl.xDataControlName}}',
selection: {}, selection: {},
showTitleBar: {{#if ctrl.showTitleBar}}true{{else}}false{{/if}}, showTitleBar: {{#if ctrl.showTitleBar}}true{{else}}false{{/if}},
split: 0.2,
title: '{{ctrl.title}}', title: '{{ctrl.title}}',
titleRes: '{{#if ctrl.titlePSLanguageRes}}{{ctrl.titlePSLanguageRes.lanResTag}}{{/if}}', titleRes: '{{#if ctrl.titlePSLanguageRes}}{{ctrl.titlePSLanguageRes.lanResTag}}{{/if}}',
viewRefs: [ viewRefs: [
......
...@@ -44,7 +44,7 @@ defineExpose({ state, name: '{{ctrl.name}}' }); ...@@ -44,7 +44,7 @@ defineExpose({ state, name: '{{ctrl.name}}' });
<template> <template>
<div class="app-tree-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}"> <div class="app-tree-exp-bar{{#if ctrl.psSysCss}} {{ctrl.psSysCss.cssName}}{{/if}}">
<AppSplit :value="0.2"> <AppSplit v-model="state.split">
<template #left> <template #left>
<div v-if="state.showTitleBar" class="tree-exp-bar-title"> <div v-if="state.showTitleBar" class="tree-exp-bar-title">
<span>\{{ state.title }}</span> <span>\{{ state.title }}</span>
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册