边框调节
This commit is contained in:
@@ -427,40 +427,114 @@ const onDragEnd = (eventData) => {
|
|||||||
// 处理事件总线的area.resize.move事件
|
// 处理事件总线的area.resize.move事件
|
||||||
const onAreaResizeMove = (eventData) => {
|
const onAreaResizeMove = (eventData) => {
|
||||||
console.log(`[Area:${props.id}] 收到AREA_RESIZE_MOVE事件:`, eventData) // 添加调试日志
|
console.log(`[Area:${props.id}] 收到AREA_RESIZE_MOVE事件:`, eventData) // 添加调试日志
|
||||||
const { areaId, size, position, direction } = eventData
|
const { areaId, size, position, direction, timestamp } = eventData
|
||||||
|
|
||||||
if (areaId !== props.id) {
|
if (areaId !== props.id) {
|
||||||
console.log(`[Area:${props.id}] areaId不匹配,期望: ${props.id}, 实际: ${areaId}`) // 添加调试日志
|
console.log(`[Area:${props.id}] areaId不匹配,期望: ${props.id}, 实际: ${areaId}`) // 添加调试日志
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 防御性检查,确保事件数据完整
|
||||||
|
if (!size || !position) {
|
||||||
|
console.error(`[Area:${props.id}] 无效的事件数据,缺少size或position`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`[Area:${props.id}] 更新前originalPosition:`, originalPosition.value) // 添加调试日志
|
console.log(`[Area:${props.id}] 更新前originalPosition:`, originalPosition.value) // 添加调试日志
|
||||||
|
|
||||||
// 应用最小尺寸限制,与CSS保持一致
|
// 应用最小尺寸限制,与CSS保持一致
|
||||||
const minWidth = 200
|
const minWidth = 200
|
||||||
const minHeight = 30
|
const minHeight = 30
|
||||||
|
|
||||||
let newWidth = size.width
|
// 对输入值进行验证和过滤,确保数值有效
|
||||||
let newHeight = size.height
|
const inputWidth = Number(size.width)
|
||||||
let newLeft = position.left
|
const inputHeight = Number(size.height)
|
||||||
let newTop = position.top
|
const inputLeft = Number(position.left)
|
||||||
|
const inputTop = Number(position.top)
|
||||||
|
|
||||||
// 宽度限制
|
if (isNaN(inputWidth) || isNaN(inputHeight) || isNaN(inputLeft) || isNaN(inputTop)) {
|
||||||
if (direction.includes('right') || direction.includes('left')) {
|
console.error(`[Area:${props.id}] 收到无效的数值,跳过更新`)
|
||||||
newWidth = Math.max(minWidth, newWidth)
|
return
|
||||||
originalPosition.value.width = newWidth
|
|
||||||
if (direction.includes('left')) {
|
|
||||||
originalPosition.value.left = newLeft
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高度限制
|
// 直接使用Panel计算好的尺寸和位置,不再重新计算
|
||||||
if (direction.includes('bottom') || direction.includes('top')) {
|
// Panel已经处理了对角线方向的特殊逻辑,Area只需要验证和应用即可
|
||||||
newHeight = Math.max(minHeight, newHeight)
|
let newWidth = Math.max(minWidth, inputWidth)
|
||||||
originalPosition.value.height = newHeight
|
let newHeight = Math.max(minHeight, inputHeight)
|
||||||
if (direction.includes('top')) {
|
let newLeft = inputLeft
|
||||||
originalPosition.value.top = newTop
|
let newTop = inputTop
|
||||||
}
|
|
||||||
|
// 计算边界位置,用于调试
|
||||||
|
const oldBottom = originalPosition.value.top + originalPosition.value.height
|
||||||
|
const newBottom = newTop + newHeight
|
||||||
|
const oldRight = originalPosition.value.left + originalPosition.value.width
|
||||||
|
const newRight = newLeft + newWidth
|
||||||
|
|
||||||
|
// 对对角线方向进行特殊处理和日志记录
|
||||||
|
switch (direction) {
|
||||||
|
case 'top-right':
|
||||||
|
console.log(`[Area:${props.id}] top-right方向处理: 原下边框: ${oldBottom}, 新下边框: ${newBottom}, 差值: ${newBottom - oldBottom}`)
|
||||||
|
// 验证top-right方向下边框位置是否保持不变
|
||||||
|
if (Math.abs(newBottom - oldBottom) > 1) {
|
||||||
|
console.warn(`[Area:${props.id}] top-right方向下边框位置变化较大,可能存在计算误差`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'top-left':
|
||||||
|
console.log(`[Area:${props.id}] top-left方向处理: 原下边框: ${oldBottom}, 新下边框: ${newBottom}, 差值: ${newBottom - oldBottom}`)
|
||||||
|
console.log(`[Area:${props.id}] top-left方向处理: 原右边框: ${oldRight}, 新右边框: ${newRight}, 差值: ${newRight - oldRight}`)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'bottom-left':
|
||||||
|
console.log(`[Area:${props.id}] bottom-left方向处理: 原上边框: ${originalPosition.value.top}, 新上边框: ${newTop}, 差值: ${newTop - originalPosition.value.top}`)
|
||||||
|
console.log(`[Area:${props.id}] bottom-left方向处理: 原右边框: ${oldRight}, 新右边框: ${newRight}, 差值: ${newRight - oldRight}`)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'top':
|
||||||
|
console.log(`[Area:${props.id}] top方向处理: 原下边框: ${oldBottom}, 新下边框: ${newBottom}, 差值: ${newBottom - oldBottom}`)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'left':
|
||||||
|
console.log(`[Area:${props.id}] left方向处理: 原右边框: ${oldRight}, 新右边框: ${newRight}, 差值: ${newRight - oldRight}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用已有的边界位置计算结果,无需重新声明
|
||||||
|
// 确保新的位置和尺寸在合理范围内
|
||||||
|
// 注意:不直接限制newTop和newLeft,而是通过调整尺寸来保持在可视区域内
|
||||||
|
// 这样可以避免拖拽时的突然跳动
|
||||||
|
|
||||||
|
// 获取父容器尺寸,用于边界检查
|
||||||
|
let parentWidth = window.innerWidth
|
||||||
|
let parentHeight = window.innerHeight
|
||||||
|
|
||||||
|
if (parentContainer.value && parentContainer.value !== window) {
|
||||||
|
const parentRect = parentContainer.value.getBoundingClientRect()
|
||||||
|
parentWidth = parentRect.width
|
||||||
|
parentHeight = parentRect.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保整个Area在父容器可视范围内
|
||||||
|
// 如果右侧超出,调整宽度
|
||||||
|
if (newRight > parentWidth) {
|
||||||
|
newWidth = parentWidth - newLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果底部超出,调整高度
|
||||||
|
if (newBottom > parentHeight) {
|
||||||
|
newHeight = parentHeight - newTop
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保不小于最小尺寸
|
||||||
|
newWidth = Math.max(minWidth, newWidth)
|
||||||
|
newHeight = Math.max(minHeight, newHeight)
|
||||||
|
|
||||||
|
// 原子性更新originalPosition,避免中间状态被访问
|
||||||
|
originalPosition.value = {
|
||||||
|
width: newWidth,
|
||||||
|
height: newHeight,
|
||||||
|
left: newLeft,
|
||||||
|
top: newTop
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Area:${props.id}] 更新后originalPosition:`, originalPosition.value) // 添加调试日志
|
console.log(`[Area:${props.id}] 更新后originalPosition:`, originalPosition.value) // 添加调试日志
|
||||||
@@ -471,7 +545,8 @@ const onAreaResizeMove = (eventData) => {
|
|||||||
left: originalPosition.value.left,
|
left: originalPosition.value.left,
|
||||||
top: originalPosition.value.top,
|
top: originalPosition.value.top,
|
||||||
width: originalPosition.value.width,
|
width: originalPosition.value.width,
|
||||||
height: originalPosition.value.height
|
height: originalPosition.value.height,
|
||||||
|
timestamp: timestamp || Date.now()
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Area', areaId: props.id }
|
source: { component: 'Area', areaId: props.id }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -430,6 +430,10 @@ const onResizeStart = (e, direction) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置之前的resize状态,避免状态污染
|
||||||
|
isResizing = false;
|
||||||
|
currentResizeDirection = null;
|
||||||
|
|
||||||
isResizing = true;
|
isResizing = true;
|
||||||
currentResizeDirection = direction;
|
currentResizeDirection = direction;
|
||||||
currentAreaId = getCurrentAreaId();
|
currentAreaId = getCurrentAreaId();
|
||||||
@@ -439,20 +443,72 @@ const onResizeStart = (e, direction) => {
|
|||||||
const startPosition = { x: e.clientX, y: e.clientY };
|
const startPosition = { x: e.clientX, y: e.clientY };
|
||||||
|
|
||||||
const areaHandler = getAreaHandler();
|
const areaHandler = getAreaHandler();
|
||||||
|
if (!areaHandler) {
|
||||||
|
console.error(`[Panel:${props.id}] 无法获取AreaHandler`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新的Area状态,添加防御性检查
|
||||||
const areaState = areaHandler.getAreaState(currentAreaId);
|
const areaState = areaHandler.getAreaState(currentAreaId);
|
||||||
|
if (!areaState) {
|
||||||
|
console.error(`[Panel:${props.id}] 无法获取Area状态,areaId: ${currentAreaId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用当前DOM元素的实际尺寸作为基准,确保获取最新状态
|
||||||
|
let actualWidth = areaState.width || 0;
|
||||||
|
let actualHeight = areaState.height || 0;
|
||||||
|
let actualLeft = areaState.left || 0;
|
||||||
|
let actualTop = areaState.top || 0;
|
||||||
|
|
||||||
|
// 优先使用Area元素的实际尺寸和位置,而不是Panel元素
|
||||||
|
const areaElement = document.querySelector(`[data-area-id="${currentAreaId}"]`);
|
||||||
|
if (areaElement) {
|
||||||
|
const rect = areaElement.getBoundingClientRect();
|
||||||
|
// 只有当获取到的尺寸有效时才使用DOM尺寸
|
||||||
|
if (rect.width > 0 && rect.height > 0) {
|
||||||
|
// 注意:rect.left和rect.top是相对于视口的位置,需要转换为相对于父容器的位置
|
||||||
|
const parentElement = areaElement.parentElement;
|
||||||
|
if (parentElement) {
|
||||||
|
const parentRect = parentElement.getBoundingClientRect();
|
||||||
|
actualLeft = rect.left - parentRect.left;
|
||||||
|
actualTop = rect.top - parentRect.top;
|
||||||
|
}
|
||||||
|
actualWidth = rect.width;
|
||||||
|
actualHeight = rect.height;
|
||||||
|
console.log(`[Panel:${props.id}] 使用Area DOM实际尺寸和位置作为参考: {width: ${actualWidth}, height: ${actualHeight}, left: ${actualLeft}, top: ${actualTop}}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果Area元素获取失败,尝试使用Panel元素作为备选
|
||||||
|
else {
|
||||||
|
const currentElement = document.querySelector(`[data-panel-id="${props.id}"]`);
|
||||||
|
if (currentElement) {
|
||||||
|
const rect = currentElement.getBoundingClientRect();
|
||||||
|
if (rect.width > 0 && rect.height > 0) {
|
||||||
|
actualWidth = rect.width;
|
||||||
|
actualHeight = rect.height;
|
||||||
|
console.log(`[Panel:${props.id}] 使用Panel DOM实际尺寸作为参考: {width: ${actualWidth}, height: ${actualHeight}}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const areaStartState = {
|
const areaStartState = {
|
||||||
width: areaState.width || 0,
|
width: Math.max(200, actualWidth),
|
||||||
height: areaState.height || 0,
|
height: Math.max(30, actualHeight),
|
||||||
left: areaState.left || 0,
|
left: actualLeft,
|
||||||
top: areaState.top || 0
|
top: actualTop
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送resize开始事件
|
console.log(`[Panel:${props.id}] 确定的areaStartState:`, areaStartState);
|
||||||
|
|
||||||
|
// 发送resize开始事件,包含初始状态便于调试
|
||||||
emitEvent(EVENT_TYPES.PANEL_RESIZE_START, {
|
emitEvent(EVENT_TYPES.PANEL_RESIZE_START, {
|
||||||
panelId: props.id,
|
panelId: props.id,
|
||||||
areaId: currentAreaId,
|
areaId: currentAreaId,
|
||||||
direction,
|
direction,
|
||||||
position: startPosition,
|
position: startPosition,
|
||||||
|
initialState: { width: actualWidth, height: actualHeight, left: actualLeft, top: actualTop }, // 添加初始状态
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Panel', panelId: props.id }
|
source: { component: 'Panel', panelId: props.id }
|
||||||
@@ -465,40 +521,107 @@ const onResizeStart = (e, direction) => {
|
|||||||
if (isResizing && currentAreaId) {
|
if (isResizing && currentAreaId) {
|
||||||
const currentPosition = { x: e.clientX, y: e.clientY };
|
const currentPosition = { x: e.clientX, y: e.clientY };
|
||||||
|
|
||||||
const totalDelta = {
|
// 直接计算拖动距离,使用更简洁的变量名
|
||||||
width: currentPosition.x - startPosition.x,
|
const deltaX = currentPosition.x - startPosition.x;
|
||||||
height: currentPosition.y - startPosition.y
|
const deltaY = currentPosition.y - startPosition.y;
|
||||||
};
|
|
||||||
|
|
||||||
// 定义最小尺寸常量
|
// 定义最小尺寸常量
|
||||||
const minWidth = 200;
|
const minWidth = 200;
|
||||||
const minHeight = 30;
|
const minHeight = 30;
|
||||||
|
|
||||||
// 根据方向正确计算新尺寸
|
// 使用初始状态作为基准,确保对角线方向拖拽时保持相对位置不变
|
||||||
let newWidth = areaStartState.width;
|
const initialWidth = areaStartState.width;
|
||||||
let newHeight = areaStartState.height;
|
const initialHeight = areaStartState.height;
|
||||||
|
const initialLeft = areaStartState.left;
|
||||||
|
const initialTop = areaStartState.top;
|
||||||
|
|
||||||
// 宽度计算
|
// 计算初始边界位置
|
||||||
if (currentResizeDirection.includes('left')) {
|
const initialRight = initialLeft + initialWidth;
|
||||||
// 左方向:宽度增加 = 原始宽度 - deltaX(因为deltaX为负)
|
const initialBottom = initialTop + initialHeight;
|
||||||
newWidth = areaStartState.width - totalDelta.width;
|
|
||||||
} else if (currentResizeDirection.includes('right')) {
|
let newWidth = initialWidth;
|
||||||
// 右方向:宽度增加 = 原始宽度 + deltaX
|
let newHeight = initialHeight;
|
||||||
newWidth = areaStartState.width + totalDelta.width;
|
let newLeft = initialLeft;
|
||||||
|
let newTop = initialTop;
|
||||||
|
|
||||||
|
// 处理不同方向的拖拽逻辑
|
||||||
|
switch (currentResizeDirection) {
|
||||||
|
case 'top-right':
|
||||||
|
// 向右上方拖拽:保持下边框位置不变
|
||||||
|
newTop = Math.max(0, initialTop + deltaY);
|
||||||
|
newHeight = initialBottom - newTop;
|
||||||
|
newWidth = initialWidth + deltaX;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'top-left':
|
||||||
|
// 向左上方拖拽:保持下边框和右边框位置不变
|
||||||
|
newLeft = Math.max(0, initialLeft + deltaX);
|
||||||
|
newWidth = initialRight - newLeft;
|
||||||
|
newTop = Math.max(0, initialTop + deltaY);
|
||||||
|
newHeight = initialBottom - newTop;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bottom-right':
|
||||||
|
// 向右下方拖拽:保持上边框位置不变
|
||||||
|
newWidth = initialWidth + deltaX;
|
||||||
|
newHeight = initialHeight + deltaY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bottom-left':
|
||||||
|
// 向左下方拖拽:保持上边框和右边框位置不变
|
||||||
|
newLeft = Math.max(0, initialLeft + deltaX);
|
||||||
|
newWidth = initialRight - newLeft;
|
||||||
|
newHeight = initialHeight + deltaY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'top':
|
||||||
|
// 向上拖拽:保持下边框位置不变
|
||||||
|
// 注意:向上拖拽时deltaY为负,所以newTop会减小,newHeight会增加
|
||||||
|
newTop = Math.max(0, initialTop + deltaY);
|
||||||
|
newHeight = initialBottom - newTop;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'right':
|
||||||
|
// 向右拖拽:保持高度不变
|
||||||
|
newWidth = initialWidth + deltaX;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'bottom':
|
||||||
|
// 向下拖拽:保持高度增加
|
||||||
|
newHeight = initialHeight + deltaY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'left':
|
||||||
|
// 向左拖拽:保持右边框位置不变
|
||||||
|
newLeft = Math.max(0, initialLeft + deltaX);
|
||||||
|
newWidth = initialRight - newLeft;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 其他方向,使用默认逻辑
|
||||||
|
if (currentResizeDirection.includes('left')) {
|
||||||
|
newWidth = initialWidth - deltaX;
|
||||||
|
newLeft = initialLeft + deltaX;
|
||||||
|
} else if (currentResizeDirection.includes('right')) {
|
||||||
|
newWidth = initialWidth + deltaX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentResizeDirection.includes('top')) {
|
||||||
|
// 向上拖拽时,deltaY为负,所以newHeight应该增加
|
||||||
|
newTop = Math.max(0, initialTop + deltaY);
|
||||||
|
newHeight = initialBottom - newTop;
|
||||||
|
} else if (currentResizeDirection.includes('bottom')) {
|
||||||
|
newHeight = initialHeight + deltaY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高度计算
|
// 确保尺寸在合理范围内
|
||||||
if (currentResizeDirection.includes('top')) {
|
newWidth = Math.max(minWidth, Math.min(newWidth, window.innerWidth - 50));
|
||||||
// 上方向:高度增加 = 原始高度 - deltaY(因为deltaY为负)
|
newHeight = Math.max(minHeight, Math.min(newHeight, window.innerHeight - 100));
|
||||||
newHeight = areaStartState.height - totalDelta.height;
|
|
||||||
} else if (currentResizeDirection.includes('bottom')) {
|
|
||||||
// 下方向:高度增加 = 原始高度 + deltaY
|
|
||||||
newHeight = areaStartState.height + totalDelta.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查尺寸是否小于最小值,如果小于则不发送事件
|
// 检查尺寸是否小于最小值,如果小于则不发送事件
|
||||||
if (newWidth < minWidth || newHeight < minHeight) {
|
if (newWidth < minWidth || newHeight < minHeight) {
|
||||||
// 尺寸小于最小值,不发送事件,停止拖拽
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,18 +631,19 @@ const onResizeStart = (e, direction) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const newPosition = {
|
const newPosition = {
|
||||||
left: areaStartState.left,
|
left: newLeft,
|
||||||
top: areaStartState.top
|
top: newTop
|
||||||
};
|
};
|
||||||
|
|
||||||
if (currentResizeDirection.includes('left')) {
|
// 计算实际的下边框位置,用于调试
|
||||||
newPosition.left = areaStartState.left + totalDelta.width;
|
const actualBottom = newTop + newHeight;
|
||||||
}
|
console.log(`[Panel:${props.id}] resize移动,方向: ${currentResizeDirection},初始边界: {top: ${initialTop}, bottom: ${initialBottom}, left: ${initialLeft}, right: ${initialRight}},拖动距离: {deltaX: ${deltaX}, deltaY: ${deltaY}},新位置: {left: ${newLeft}, top: ${newTop}},新尺寸: ${JSON.stringify(newSize)},实际下边框: ${actualBottom},高度差: ${actualBottom - initialBottom}`);
|
||||||
if (currentResizeDirection.includes('top')) {
|
|
||||||
newPosition.top = areaStartState.top + totalDelta.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[Panel:${props.id}] resize移动,方向: ${currentResizeDirection},总量: ${JSON.stringify(newSize)},位置: ${JSON.stringify(newPosition)}`);
|
// 发送事件前,确保所有值都是合理的数字
|
||||||
|
if (isNaN(newWidth) || isNaN(newHeight) || isNaN(newPosition.left) || isNaN(newPosition.top)) {
|
||||||
|
console.error(`[Panel:${props.id}] 计算出无效的resize值,跳过事件发送`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
emitEvent(EVENT_TYPES.PANEL_RESIZE_MOVE, {
|
emitEvent(EVENT_TYPES.PANEL_RESIZE_MOVE, {
|
||||||
panelId: props.id,
|
panelId: props.id,
|
||||||
@@ -527,6 +651,7 @@ const onResizeStart = (e, direction) => {
|
|||||||
direction: currentResizeDirection,
|
direction: currentResizeDirection,
|
||||||
size: newSize,
|
size: newSize,
|
||||||
position: newPosition,
|
position: newPosition,
|
||||||
|
delta: { x: deltaX, y: deltaY }, // 添加拖动距离,便于调试
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Panel', panelId: props.id }
|
source: { component: 'Panel', panelId: props.id }
|
||||||
@@ -560,8 +685,9 @@ const onResizeStart = (e, direction) => {
|
|||||||
document.removeEventListener('mouseup', onResizeEnd);
|
document.removeEventListener('mouseup', onResizeEnd);
|
||||||
document.removeEventListener('mouseleave', onResizeEnd);
|
document.removeEventListener('mouseleave', onResizeEnd);
|
||||||
|
|
||||||
// 重置resize状态
|
// 完全重置resize状态,避免状态污染
|
||||||
currentResizeDirection = null;
|
currentResizeDirection = null;
|
||||||
|
currentAreaId = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user