v1.9.0-rc.1 - updates

This commit is contained in:
LINING-PC\lining
2026-01-30 22:43:47 +08:00
parent 867c24186a
commit 961f8fefde
4 changed files with 185 additions and 52 deletions

View File

@@ -22,14 +22,24 @@ const enableLinkAnchor = inject<ComputedRef<boolean>>('enable-link-anchor', comp
const resourcePercent = computed(() => {
// 如果直接传递了占比,使用传递的值
if (props.resourceAllocationPercent !== undefined) {
return Math.max(20, Math.min(100, props.resourceAllocationPercent))
const val = Number(props.resourceAllocationPercent)
if (Number.isFinite(val) && val > 0) {
return Math.max(0, Math.min(100, val))
}
}
// 在资源视图中尝试从task.resources中查找当前资源的占比
if (viewMode.value === 'resource' && props.currentResourceId && props.task.resources) {
const allocation = props.task.resources.find((r: any) => r.id === props.currentResourceId)
const allocation = props.task.resources.find((r: any) => {
// 确保ID类型匹配可能是字符串或数字
return String(r.id) === String(props.currentResourceId)
})
if (allocation && allocation.percent !== undefined) {
return Math.max(20, Math.min(100, allocation.percent))
const val = Number(allocation.percent)
if (Number.isFinite(val) && val >= 0) {
return Math.max(0, Math.min(100, val))
}
}
}
@@ -576,17 +586,15 @@ const taskBarStyle = computed(() => {
}
}
// v1.9.0 计算TaskBar高度保持基础高度一致,资源视图中应用占比缩放
// v1.9.1 计算TaskBar高度资源视图固定41px全高度不再按占比缩放
const baseTaskBarHeight = props.rowHeight - 10 // 基础高度(与任务视图一致)
let taskBarHeight = baseTaskBarHeight
if (viewMode.value === 'resource') {
// 资源视图:在基础高度上应用占比缩放
const percentValue = resourcePercent.value / 100
taskBarHeight = baseTaskBarHeight * percentValue
}
// v1.9.1 资源视图固定全高度不缩放占比通过CSS伪元素实现
// 任务视图:保持原有高度
// 资源视图的占比视觉效果通过CSS的::before和::after伪元素实现
// v1.9.0 计算垂直位置:资源视图中支持换行布局
// v1.9.1 计算垂直位置:资源视图中支持换行布局
let topOffset = (props.rowHeight - taskBarHeight) / 2 // 默认:居中对齐
if (viewMode.value === 'resource' && props.taskSubRow !== undefined && props.rowHeights && props.rowHeights.length > 0) {
@@ -601,15 +609,15 @@ const taskBarStyle = computed(() => {
cumulativeOffset += rowHeights[i] || 51
}
// 在当前子行内底边对齐
// 第一行padding-top(5px) + 可用空间
// 后续行:可用空间无padding-top
// v1.9.1 在当前子行内居中对齐因为TaskBar固定41px高度
// 第一行padding-top(5px) + 居中
// 后续行:居中无padding-top
if (subRow === 0) {
// 第一行顶部5px padding底部对齐
topOffset = cumulativeOffset + (currentRowHeight - 5 - taskBarHeight)
// 第一行顶部5px padding居中对齐
topOffset = cumulativeOffset + 5 + (currentRowHeight - 5 - taskBarHeight) / 2
} else {
// 后续行:底部对齐底部5px padding
topOffset = cumulativeOffset + (currentRowHeight - 5 - taskBarHeight)
// 后续行:居中对齐
topOffset = cumulativeOffset + (currentRowHeight - taskBarHeight) / 2
}
}
@@ -2415,6 +2423,14 @@ const handleBubbleMouseEnter = (event: MouseEvent) => {
// 阻止事件冒泡
event.stopPropagation()
// v1.9.1 隐藏TaskBar的hover tooltip只显示bubble tooltip
showHoverTooltip.value = false
// 清除可能正在等待的tooltip定时器
if (hoverTooltipTimer) {
window.clearTimeout(hoverTooltipTimer)
hoverTooltipTimer = null
}
showTooltip.value = true
// 智能定位右侧气泡在左侧显示tooltip左侧气泡在右侧显示
@@ -3093,6 +3109,9 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
'--row-height': `${rowHeight}px` /* 传递行高给CSS变量 */,
'--handle-width': `${actualHandleWidth}px` /* 传递手柄宽度给CSS变量 */,
'--parent-color': taskStatus.color, /* 传递父级TaskBar颜色给伪元素箭头使用 */
'--allocation-percent': (Number.isFinite(resourcePercent) ? resourcePercent / 100 : 1), /* v1.9.1 传递占比给CSS变量 */
'--task-bar-bg-color': taskStatus.bgColor, /* v1.9.1 传递背景色给伪元素 */
'--task-bar-border-color': taskStatus.borderColor, /* v1.9.1 传递边框色给伪元素 */
boxShadow: isParent
? `0 4px 16px ${taskStatus.color}40, 0 2px 8px ${taskStatus.color}26` /* 父级任务也使用动态颜色阴影 */
: `0 4px 16px ${taskStatus.color}40, 0 2px 8px ${taskStatus.color}26`, /* 使用TaskBar颜色的阴影 - 加强版 */
@@ -3110,6 +3129,8 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
dimmed: isDimmed,
'has-actual': showActualTaskbar && hasActualProgress, /* 只有在showActualTaskbar=true时才标记有实际进度 */
'resource-conflict': props.hasResourceConflict, /* v1.9.0 资源冲突样式 */
'resource-view': viewMode === 'resource', /* v1.9.1 资源视图专属样式 */
'has-bubble': bubbleIndicator.show, /* v1.9.1 有气泡时提升层级确保气泡不被其他TaskBar遮挡 */
}"
@click="handleTaskBarClick"
@contextmenu="handleContextMenu"
@@ -3210,8 +3231,7 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
<div
v-if="barConfig.showTitle && !(showActualTaskbar && hasActualProgress)"
ref="taskBarNameRef"
:style="viewMode === 'resource' ? {} : getNameStyles()"
:class="{ 'task-name-wrapper': viewMode === 'resource' }"
:style="getNameStyles()"
>
<slot v-if="hasContentSlot" name="custom-task-content" v-bind="slotPayload" />
<div v-else class="task-name">
@@ -3227,7 +3247,7 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
<div
v-if="barConfig.showProgress && shouldShowProgress && !(showActualTaskbar && hasActualProgress)"
class="task-progress"
:style="viewMode === 'resource' ? {} : getProgressStyles()"
:style="getProgressStyles()"
>
{{ task.progress || 0 }}%
</div>
@@ -3496,6 +3516,11 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
z-index: 160 !important;
}
/* v1.9.1 有气泡指示器时提升z-index解决DOM渲染顺序导致的遮挡问题 */
.task-bar.has-bubble {
z-index: 200 !important;
}
/* 有实际进度时,计划条使用虚线边框样式 */
.task-bar.has-actual {
/* 不再强制设置半透明背景由内联样式的isTaskBarHovered控制 */
@@ -3538,28 +3563,141 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
cursor: pointer;
}
/* v1.9.0 资源冲突样式根据UI规范*/
.task-bar.resource-conflict {
/* v1.9.1 资源视图TaskBar全高度占比设计 */
.task-bar.resource-view {
display: flex;
align-items: center;
justify-content: center;
background: transparent !important;
border: none !important;
box-shadow: none !important;
}
/* 上半部分:镂空区域(仅上、左、右三边虚线) */
.task-bar.resource-view::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: calc((1 - var(--allocation-percent, 1)) * 100%);
border-top: 1.5px dashed currentColor;
border-left: 1.5px dashed currentColor;
border-right: 1.5px dashed currentColor;
border-bottom: none;
background: transparent;
box-sizing: border-box;
pointer-events: none;
border-radius: 4px 4px 0 0;
opacity: 0.6;
z-index: -1; /* v1.9.1 设置为负值,确保不遮挡 bubble-indicator */
}
/* 占比100%时,隐藏上半部分镂空区域 */
.task-bar.resource-view[style*="--allocation-percent: 1"]::before {
display: none;
}
/* 下半部分:实心填充区域(四边实线边框 + 背景色) */
.task-bar.resource-view::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: calc(var(--allocation-percent, 1) * 100%);
background: var(--task-bar-bg-color, #e3f2fd);
border: 1px solid var(--task-bar-border-color, #90caf9);
border-radius: 0 0 4px 4px;
box-sizing: border-box;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.05);
pointer-events: none;
z-index: -1; /* v1.9.1 设置为负值,确保不遮挡 bubble-indicator */
}
/* 占比100%时整个TaskBar都是实心四个角圆角 */
.task-bar.resource-view[style*="--allocation-percent: 1"]::after {
border-radius: 4px;
}
/* 进度条限制在填充区内 */
.task-bar.resource-view .progress-bar {
position: absolute;
bottom: 0;
left: 0;
top: auto;
height: calc(var(--allocation-percent, 1) * 100%);
z-index: 1;
border-radius: 0 0 4px 4px;
pointer-events: none;
box-shadow: 2px 0 6px rgba(0, 0, 0, 0.8); /* 右侧黑色阴影,增强层次感 */
}
/* 占比100%时,进度条四个角圆角 */
.task-bar.resource-view[style*="--allocation-percent: 1"] .progress-bar {
border-radius: 4px;
}
/* 已完成任务的进度条不显示阴影 */
.task-bar.completed.resource-view .progress-bar {
box-shadow: none;
}
/* 文字层在最上层覆盖整个41px高度 */
.task-bar.resource-view .task-bar-content {
position: relative;
/*z-index: 2;*/
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* 资源视图悬停效果 */
.task-bar.resource-view:hover::after {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* v1.9.0 资源冲突样式根据UI规范- 调整到最顶层 */
.task-bar.resource-conflict::before {
/* 覆盖伪元素样式,显示警告效果 */
border-color: var(--gantt-error-color, #f56c6c) !important;
opacity: 1 !important;
z-index: 10 !important;
}
.task-bar.resource-conflict::after {
/* 左侧3px红色色带 */
border-left: 3px solid var(--gantt-error-color, #f56c6c) !important;
/* 浅红底色 */
background-color: rgba(245, 108, 108, 0.08) !important;
background-color: rgba(245, 108, 108, 0.12) !important;
/* 斑马纹背景(叠加在底色上)*/
background-image:
repeating-linear-gradient(
45deg,
transparent,
transparent 10px,
rgba(245, 108, 108, 0.12) 10px,
rgba(245, 108, 108, 0.12) 20px
rgba(245, 108, 108, 0.1) 10px,
rgba(245, 108, 108, 0.1) 20px
) !important;
z-index: 1 !important;
}
/* 已完成的任务冲突:不显示斜杠蒙版,仅保留左侧红色警示边 */
.task-bar.completed.resource-conflict::after {
background-color: transparent !important;
background-image: none !important;
/* 保留左侧红色边框 */
border-left: 3px solid var(--gantt-error-color, #f56c6c) !important;
}
/* 冲突状态的文字保持清晰可读 */
.task-bar.resource-conflict .task-name,
.task-bar.resource-conflict .task-progress {
color: var(--gantt-text-primary);
/*color: var(--gantt-text-primary);*/
font-weight: 600;
z-index: 11;
}
.task-bar.dragging {
@@ -3961,10 +4099,14 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
padding-left: 8px;
}
/* 资源视图中的标题包裹器,禁用绝对定位 */
/* v1.9.1 资源视图中的标题包裹器,确保在41px全高度内居中 */
.task-name-wrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
@@ -4063,7 +4205,7 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
top: 50%;
width: 8px; /* 半圆宽度 */
height: 16px; /* 半圆高度 */
z-index: 15;
z-index: 9999 !important; /* v1.9.1 使用超高层级和 !important确保不被任何元素遮挡 */
cursor: pointer;
border: 2px solid;
transform: translateY(-50%);
@@ -4598,14 +4740,14 @@ const handleAnchorDragEnd = (anchorEvent: { taskId: number; type: 'predecessor'
z-index: 16;
pointer-events: none;
padding: 1px 3px;
border-radius: 2px;
/*border-radius: 2px;*/
}
/* 周视图下的TaskBar基础样式调整 */
.task-bar.week-view {
min-width: 4px; /* 确保即使很短的任务也有最小可见宽度 */
border-width: 1px;
border-radius: 2px;
/*border-radius: 2px;*/
}
/* 暗色主题下的短TaskBar溢出效果 */

View File

@@ -113,7 +113,7 @@ const rowHeight = computed(() => {
if (isResourceRow.value) {
const resourceId = String(props.task.id) // 转换为string
const layout = resourceTaskLayouts.value.get(resourceId)
return layout?.totalHeight || 51
return layout?.totalHeight || 56 // v1.9.1 默认56px51 + 5px底部padding
}
return 51 // task视图下使用固定高度
})
@@ -614,7 +614,7 @@ const assigneeDisplayData = computed(() => {
.task-row {
display: flex;
border-bottom: 1px solid var(--gantt-border-light);
height: 51px; /* 修改为51px与Timeline中的task-row高度保持一致包含border-bottom 1px */
height: 51px; /* v1.9.1 任务视图51px包含border-bottom 1px资源视图动态高度第一行56px后续行46px */
box-sizing: border-box; /* 确保border包含在高度计算中 */
background: var(--gantt-bg-primary);
align-items: center;

View File

@@ -4895,9 +4895,6 @@ const handleAddSuccessor = (task: Task) => {
:all-tasks="tasks"
:has-resource-conflict="resourceConflicts.get(String(resource.id))?.has(task.id) || false"
:current-resource-id="resource.id"
:style="{
zIndex: taskIndex,
}"
@update:task="updateTask"
@bar-mounted="handleBarMounted"
@click="handleTaskBarClick(task, $event)"
@@ -5273,10 +5270,11 @@ const handleAddSuccessor = (task: Task) => {
position: absolute;
left: 0;
width: 100%;
height: 51px; /** 为了对齐左侧的Task List Row高度同时需要包含List Row的Bottom Border 1px */
min-height: 51px; /** 为了对齐左侧的Task List Row高度同时需要包含List Row的Bottom Border 1px */
pointer-events: auto;
z-index: 11;
transition: background-color 0.2s ease;
padding-bottom: 2px;
}
/* 资源视图行样式 */

View File

@@ -106,24 +106,17 @@ export function assignTaskRows(
}
// 计算每行的高度
const baseTaskBarHeight = baseRowHeight - 10 // 基础TaskBar高度
// v1.9.1: TaskBar固定41px全高度用伪元素实现镂空所以行高不再按占比缩放
// v1.9.1: 每行底部都有5px padding-bottom第一行顶部也有5px padding-top
const baseTaskBarHeight = baseRowHeight - 10 // 基础TaskBar高度 (51 - 10 = 41px)
for (let i = 0; i < rowTasks.length; i++) {
const tasksInRow = rowTasks[i]
// 找到该行中最大的TaskBar高度
let maxTaskBarHeight = 0
for (const task of tasksInRow) {
const percent = getTaskResourcePercent(task, resourceId)
const taskBarHeight = baseTaskBarHeight * (percent / 100)
maxTaskBarHeight = Math.max(maxTaskBarHeight, taskBarHeight)
}
// 计算该行的容器高度
// 第一行padding-top(5px) + maxTaskBarHeight + padding-bottom(5px)
// 后续行maxTaskBarHeight + padding-bottom(5px)
// v1.9.1: 所有TaskBar都是固定41px高度每行都有底部padding
// 第一行padding-top(5px) + TaskBar高度(41px) + padding-bottom(5px) = 51px
// 后续行:TaskBar高度(41px) + padding-bottom(5px) = 46px
const rowHeight = i === 0
? 5 + maxTaskBarHeight + 5
: maxTaskBarHeight + 5
? 5 + baseTaskBarHeight + 5 // 第一行5 + 41 + 5 = 51px
: baseTaskBarHeight + 5 // 后续行41 + 5 = 46px
rowHeights.push(rowHeight)
}