v1.4.2-patch.2 - bugfix: tasklist列宽度设置问题

This commit is contained in:
LINING-PC\lining
2025-11-11 12:17:05 +08:00
parent cacaece319
commit 1b17d50bcd
10 changed files with 140 additions and 15 deletions
+6
View File
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.2-patch2] - 2025-11-11
### Fixed
- 缺陷修复:TaskList配置列宽无效的问题修复
- Defect fix: Fixed the issue of invalid column width configuration in TaskList
## [1.4.2-patch1] - 2025-11-03 ## [1.4.2-patch1] - 2025-11-03
### Fixed ### Fixed
+2 -2
View File
@@ -48,13 +48,13 @@ const toolbarConfig = {
// TaskList列配置 // TaskList列配置
const availableColumns = ref<TaskListColumnConfig[]>([ const availableColumns = ref<TaskListColumnConfig[]>([
{ key: 'predecessor', label: '前置任务', visible: true }, { key: 'predecessor', label: '前置任务', visible: true },
{ key: 'assignee', label: '负责人', visible: true }, { key: 'assignee', label: '负责人', visible: true, width: 250 },
{ key: 'startDate', label: '开始日期', visible: true }, { key: 'startDate', label: '开始日期', visible: true },
{ key: 'endDate', label: '结束日期', visible: true }, { key: 'endDate', label: '结束日期', visible: true },
{ key: 'estimatedHours', label: '预估工时', visible: true }, { key: 'estimatedHours', label: '预估工时', visible: true },
{ key: 'actualHours', label: '实际工时', visible: true }, { key: 'actualHours', label: '实际工时', visible: true },
{ key: 'progress', label: '进度', visible: true }, { key: 'progress', label: '进度', visible: true },
{ key: 'custom', label: '自定义列', visible: true, width: 120 }, // 添加默认宽度120px { key: 'custom', label: '自定义列', visible: true, width: '30%' }, // 添加默认宽度120px
]) ])
// TaskList宽度配置 // TaskList宽度配置
+10
View File
@@ -276,5 +276,15 @@
"Defect fix: Fixed the issue of infinite loop calls when updating child tasks after updating the parent task", "Defect fix: Fixed the issue of infinite loop calls when updating child tasks after updating the parent task",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to Ky1in666@github for their valuable use and feedback</span>" "<span style=\"font-weight: bold; color: #f00;\">Special thanks to Ky1in666@github for their valuable use and feedback</span>"
] ]
},
{
"version": "1.4.2-patch2",
"date": "2025-11-11",
"notes": [
"缺陷修复:TaskList配置列宽无效的问题修复",
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 @SHENGLONG749的使用及反馈的宝贵意见</span>",
"Defect fix: Fixed the issue of invalid column width configuration in TaskList",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @SHENGLONG749 for their valuable use and feedback</span>"
]
} }
] ]
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jordium-gantt-vue3", "name": "jordium-gantt-vue3",
"version": "1.4.2-pacth.1", "version": "1.4.2-patch.2",
"type": "module", "type": "module",
"main": "dist/jordium-gantt-vue3.cjs.js", "main": "dist/jordium-gantt-vue3.cjs.js",
"module": "dist/jordium-gantt-vue3.es.js", "module": "dist/jordium-gantt-vue3.es.js",
+39 -4
View File
@@ -299,22 +299,45 @@ function onMouseDown(e: MouseEvent) {
taskListBodyProposedWidth.value = window.innerWidth * 0.8 - 6 // 减去splitter宽度 taskListBodyProposedWidth.value = window.innerWidth * 0.8 - 6 // 减去splitter宽度
// 获取左侧面板的最小宽度 // 获取左侧面板的最小宽度
taskListBodyWidthLimit.value = Math.min(taskListBodyProposedWidth.value, taskListBodyWidth.value) taskListBodyWidthLimit.value = Math.min(taskListBodyProposedWidth.value,
taskListBodyWidthLimit.value)
// 广播拖拽开始事件,通知其他组件暂停悬停效果 // 广播拖拽开始事件,通知其他组件暂停悬停效果
window.dispatchEvent(new CustomEvent('splitter-drag-start')) window.dispatchEvent(new CustomEvent('splitter-drag-start'))
// 在拖拽期间禁用页面选择 // 在拖拽期间禁用页面选择和所有指针事件
document.body.style.userSelect = 'none' document.body.style.userSelect = 'none'
document.body.style.webkitUserSelect = 'none' document.body.style.webkitUserSelect = 'none'
document.body.style.cursor = 'col-resize' document.body.style.cursor = 'col-resize'
document.body.style.pointerEvents = 'none' // 禁止所有指针事件
// 全局事件拦截器:在捕获阶段拦截所有事件(除了 mousemove 和 mouseup
const blockAllEvents = (ev: Event) => {
if (ev.type !== 'mousemove' && ev.type !== 'mouseup') {
ev.preventDefault()
ev.stopPropagation()
ev.stopImmediatePropagation()
}
}
// 在捕获阶段添加事件监听,确保最先拦截
document.addEventListener('mousedown', blockAllEvents, { capture: true })
document.addEventListener('click', blockAllEvents, { capture: true })
document.addEventListener('dblclick', blockAllEvents, { capture: true })
document.addEventListener('mouseover', blockAllEvents, { capture: true })
document.addEventListener('mouseout', blockAllEvents, { capture: true })
document.addEventListener('mouseenter', blockAllEvents, { capture: true })
document.addEventListener('mouseleave', blockAllEvents, { capture: true })
document.addEventListener('wheel', blockAllEvents, { capture: true, passive: false })
document.addEventListener('contextmenu', blockAllEvents, { capture: true })
function onMouseMove(ev: MouseEvent) { function onMouseMove(ev: MouseEvent) {
if (!dragging.value) return if (!dragging.value) return
// 阻止默认行为,防止滚动等 // 强制阻止所有默认行为和事件传播
ev.preventDefault() ev.preventDefault()
ev.stopPropagation() ev.stopPropagation()
ev.stopImmediatePropagation()
const delta = ev.clientX - startX const delta = ev.clientX - startX
const proposedWidth = startWidth + delta const proposedWidth = startWidth + delta
@@ -327,13 +350,25 @@ function onMouseDown(e: MouseEvent) {
function onMouseUp() { function onMouseUp() {
dragging.value = false dragging.value = false
// 移除全局事件拦截器
document.removeEventListener('mousedown', blockAllEvents, { capture: true })
document.removeEventListener('click', blockAllEvents, { capture: true })
document.removeEventListener('dblclick', blockAllEvents, { capture: true })
document.removeEventListener('mouseover', blockAllEvents, { capture: true })
document.removeEventListener('mouseout', blockAllEvents, { capture: true })
document.removeEventListener('mouseenter', blockAllEvents, { capture: true })
document.removeEventListener('mouseleave', blockAllEvents, { capture: true })
document.removeEventListener('wheel', blockAllEvents, { capture: true })
document.removeEventListener('contextmenu', blockAllEvents, { capture: true })
// 广播拖拽结束事件,通知其他组件恢复悬停效果 // 广播拖拽结束事件,通知其他组件恢复悬停效果
window.dispatchEvent(new CustomEvent('splitter-drag-end')) window.dispatchEvent(new CustomEvent('splitter-drag-end'))
// 恢复页面选择光标 // 恢复页面选择光标和指针事件
document.body.style.userSelect = '' document.body.style.userSelect = ''
document.body.style.webkitUserSelect = '' document.body.style.webkitUserSelect = ''
document.body.style.cursor = '' document.body.style.cursor = ''
document.body.style.pointerEvents = ''
taskListBodyWidth.value = getTaskListMaxWidth() // TaskList默认宽度 taskListBodyWidth.value = getTaskListMaxWidth() // TaskList默认宽度
ganttPanelLeftMinWidth.value = getTaskListMinWidth() // 左侧面板最小宽度 ganttPanelLeftMinWidth.value = getTaskListMinWidth() // 左侧面板最小宽度
+8 -1
View File
@@ -1572,7 +1572,6 @@ watch(
const safeNewContainerWidth = newContainerWidth || 0 const safeNewContainerWidth = newContainerWidth || 0
const safeOldScrollLeft = oldScrollLeft || 0 const safeOldScrollLeft = oldScrollLeft || 0
const safeOldContainerWidth = oldContainerWidth || 0 const safeOldContainerWidth = oldContainerWidth || 0
// 如果容器宽度发生变化(包括Splitter拖拽、TaskList展开收起、窗口resize等) // 如果容器宽度发生变化(包括Splitter拖拽、TaskList展开收起、窗口resize等)
if (Math.abs(safeNewContainerWidth - safeOldContainerWidth) > 1 && safeOldContainerWidth > 0) { if (Math.abs(safeNewContainerWidth - safeOldContainerWidth) > 1 && safeOldContainerWidth > 0) {
hasManualResize.value = true hasManualResize.value = true
@@ -1583,6 +1582,13 @@ watch(
// computed会自动重新计算 // computed会自动重新计算
}) })
// 🔥 容器宽度变化时,标记初始化完成(修复 splitter 拖拽后半圆不显示的问题)
if (isInitializing.value) {
setTimeout(() => {
isInitializing.value = false
}, 300)
}
// 延长禁用动画的时间,确保各种resize操作稳定 // 延长禁用动画的时间,确保各种resize操作稳定
setTimeout(() => { setTimeout(() => {
hasManualResize.value = false hasManualResize.value = false
@@ -1622,6 +1628,7 @@ watch(
}, 200) }, 200)
} }
}, },
{ immediate: true },
) )
// 监听外部hideBubbles属性变化,确保Timeline的容器变化能及时反应 // 监听外部hideBubbles属性变化,确保Timeline的容器变化能及时反应
+34 -2
View File
@@ -33,6 +33,37 @@ const hasRowSlot = computed(() => Boolean(slots['custom-task-content']))
// //
const { t } = useI18n() const { t } = useI18n()
// TaskList
const taskListRef = ref<HTMLElement | null>(null)
//
const getColumnWidthStyle = (column: { width?: number | string }) => {
if (!column.width) return {}
let widthPx: string
//
if (typeof column.width === 'string' && column.width.includes('%')) {
const containerWidth = taskListRef.value?.offsetWidth || 0
if (containerWidth > 0) {
const percentage = parseFloat(column.width) / 100
const pixels = Math.floor(containerWidth * percentage)
widthPx = `${pixels}px`
} else {
return {} //
}
} else {
//
widthPx = `${column.width}px`
}
return {
flex: `0 0 ${widthPx}`,
minWidth: widthPx,
maxWidth: widthPx,
}
}
// //
const visibleColumns = computed(() => { const visibleColumns = computed(() => {
const columns = props.taskListConfig?.columns || DEFAULT_TASK_LIST_COLUMNS const columns = props.taskListConfig?.columns || DEFAULT_TASK_LIST_COLUMNS
@@ -385,7 +416,7 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<div class="task-list"> <div ref="taskListRef" class="task-list">
<div class="task-list-header"> <div class="task-list-header">
<!-- 任务名称列始终显示 --> <!-- 任务名称列始终显示 -->
<div class="col col-name"> <div class="col col-name">
@@ -397,7 +428,7 @@ onUnmounted(() => {
:key="column.key" :key="column.key"
class="col" class="col"
:class="column.cssClass || `col-${column.key}`" :class="column.cssClass || `col-${column.key}`"
:style="column.width ? { width: column.width + 'px' } : undefined" :style="getColumnWidthStyle(column)"
> >
{{ (t as any)[column.key] || column.label }} {{ (t as any)[column.key] || column.label }}
</div> </div>
@@ -412,6 +443,7 @@ onUnmounted(() => {
:hovered-task-id="hoveredTaskId" :hovered-task-id="hoveredTaskId"
:on-hover="handleTaskRowHover" :on-hover="handleTaskRowHover"
:columns="visibleColumns" :columns="visibleColumns"
:get-column-width-style="getColumnWidthStyle"
@toggle="toggleCollapse" @toggle="toggleCollapse"
@dblclick="handleTaskRowDoubleClick" @dblclick="handleTaskRowDoubleClick"
@contextmenu="handleTaskRowContextMenu" @contextmenu="handleTaskRowContextMenu"
+3
View File
@@ -35,6 +35,7 @@ interface Props {
hoveredTaskId?: number | null hoveredTaskId?: number | null
onHover?: (taskId: number | null) => void onHover?: (taskId: number | null) => void
columns: TaskListColumnConfig[] columns: TaskListColumnConfig[]
getColumnWidthStyle?: (column: { width?: number | string }) => object
} }
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits([ const emit = defineEmits([
@@ -410,6 +411,7 @@ onUnmounted(() => {
:key="column.key" :key="column.key"
class="col" class="col"
:class="column.cssClass || `col-${column.key}`" :class="column.cssClass || `col-${column.key}`"
:style="getColumnWidthStyle ? getColumnWidthStyle(column) : {}"
> >
<!-- 里程碑分组显示空列 --> <!-- 里程碑分组显示空列 -->
<template v-if="isMilestoneGroup"> <template v-if="isMilestoneGroup">
@@ -476,6 +478,7 @@ onUnmounted(() => {
:hovered-task-id="props.hoveredTaskId" :hovered-task-id="props.hoveredTaskId"
:on-hover="props.onHover" :on-hover="props.onHover"
:columns="props.columns" :columns="props.columns"
:get-column-width-style="props.getColumnWidthStyle"
@toggle="emit('toggle', $event)" @toggle="emit('toggle', $event)"
@dblclick="emit('dblclick', $event)" @dblclick="emit('dblclick', $event)"
@start-timer="emit('start-timer', $event)" @start-timer="emit('start-timer', $event)"
+35 -3
View File
@@ -631,6 +631,7 @@ const timelineContainerWidth = ref(0)
// //
const hideBubbles = ref(true) // const hideBubbles = ref(true) //
const isInitialScrolling = ref(true) // const isInitialScrolling = ref(true) //
let hideBubblesTimeout: number | null = null //
// //
const HOUR_WIDTH = 40 // 40px const HOUR_WIDTH = 40 // 40px
@@ -945,6 +946,15 @@ const handleSplitterDragStart = () => {
const handleSplitterDragEnd = () => { const handleSplitterDragEnd = () => {
isSplitterDragging.value = false isSplitterDragging.value = false
//
const timelineContainer = document.querySelector('.timeline') as HTMLElement
if (timelineContainer) {
const newWidth = timelineContainer.clientWidth
if (Math.abs(newWidth - timelineContainerWidth.value) > 1) {
timelineContainerWidth.value = newWidth
}
}
// Splitter // Splitter
// Timeline // Timeline
hideBubbles.value = true hideBubbles.value = true
@@ -964,9 +974,15 @@ const handleTimelineContainerResized = () => {
taskBarPositions.value = {} taskBarPositions.value = {}
taskBarRenderKey.value++ taskBarRenderKey.value++
//
if (hideBubblesTimeout) {
clearTimeout(hideBubblesTimeout)
}
// //
setTimeout(() => { hideBubblesTimeout = setTimeout(() => {
hideBubbles.value = false hideBubbles.value = false
hideBubblesTimeout = null
// SVG线 // SVG线
updateSvgSize() updateSvgSize()
}, 300) }, 300)
@@ -2053,9 +2069,15 @@ onMounted(() => {
// TaskBar // TaskBar
hideBubbles.value = true hideBubbles.value = true
//
if (hideBubblesTimeout) {
clearTimeout(hideBubblesTimeout)
}
// //
setTimeout(() => { hideBubblesTimeout = setTimeout(() => {
hideBubbles.value = false hideBubbles.value = false
hideBubblesTimeout = null
}, 300) // 300msresize }, 300) // 300msresize
} }
} }
@@ -2546,12 +2568,19 @@ watch(
) )
// //
watch([timelineScrollLeft, timelineContainerWidth], computeAllMilestonesPositions) watch([timelineScrollLeft, timelineContainerWidth], () => {
// splitter
if (isSplitterDragging.value) return
computeAllMilestonesPositions()
})
// 线 // 线
watch( watch(
timelineContainerWidth, timelineContainerWidth,
(newWidth, oldWidth) => { (newWidth, oldWidth) => {
// splitter timelineData
if (isSplitterDragging.value) return
// 0 // 0
if (!oldWidth || oldWidth === 0 || Math.abs(newWidth - oldWidth) > 50) { if (!oldWidth || oldWidth === 0 || Math.abs(newWidth - oldWidth) > 50) {
if (newWidth > 0) { if (newWidth > 0) {
@@ -2593,6 +2622,9 @@ watch(
// timelineDataTaskBar线 // timelineDataTaskBar线
watch([timelineData, timelineContainerWidth], () => { watch([timelineData, timelineContainerWidth], () => {
// splitter TaskBar
if (isSplitterDragging.value) return
// //
taskBarPositions.value = {} taskBarPositions.value = {}
// keyTaskBar // keyTaskBar
+1 -1
View File
@@ -15,7 +15,7 @@ export interface TaskListColumnConfig {
key: string // 用于国际化的key,也可以作为识别符 key: string // 用于国际化的key,也可以作为识别符
label?: string // 显示标签 label?: string // 显示标签
cssClass?: string // CSS类名 cssClass?: string // CSS类名
width?: number // 可选的列宽度 width?: number | string // 列宽度,支持像素(120)或百分比('15%'
visible?: boolean // 是否显示,默认true visible?: boolean // 是否显示,默认true
} }