Merge tag 'v1.8.1' into develop

v1.8.1

# Conflicts:
#	.gitignore
#	package.json
#	src/components/LinkAnchor.vue
#	src/components/TaskList/taskRow/TaskRow.vue
#	src/components/Timeline.vue
This commit is contained in:
LINING-PC\lining
2026-02-05 16:29:50 +08:00
22 changed files with 390 additions and 476 deletions
+2 -1
View File
@@ -8,6 +8,8 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
@@ -36,4 +38,3 @@ Desktop.ini
yarn.lock
.ai/
dist/
-1
View File
@@ -2211,7 +2211,6 @@ const handleCustomMenuAction = (action: string, task: Task) => {
:view-mode="viewMode"
:resource-list-config="resourceListConfig"
:locale="controlMode === 'props' ? propsLocale : undefined"
:theme="controlMode === 'props' ? propsTheme : undefined"
:time-scale="controlMode === 'props' ? propsTimeScale : undefined"
:fullscreen="controlMode === 'props' ? propsFullscreen : undefined"
:expand-all="controlMode === 'props' ? propsExpandAll : undefined"
+14
View File
@@ -492,5 +492,19 @@
"🎉 Optimized: The assignee and avatar properties of the Task object now accept arrays to mark multiple assignees",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to yunlbs/fhjfhj/jakepan@gitee for their use and suggestions</span>"
]
},
{
"version": "1.8.1",
"date": "2026-02-05",
"notes": [
"🎉 优化:重构组件,Theme作用域从全局调整至局部.gantt-root",
"🎉 优化:重构组件,Theme自动跟随系统设置",
"🎉 优化:重构组件,完美支持Nuxt3、TailwindCSS等框架",
"<span style=\"font-weight: bold; color: #f00;\">特别感谢andrijor@gmail.com的专业反馈</span>",
"🎉 Optimized: Refactored components, Theme scope adjusted from global to local .gantt-root",
"🎉 Optimized: Refactored components, Theme automatically follows system settings",
"🎉 Optimized: Refactored components, Perfectly supports Nuxt3, TailwindCSS and other frameworks",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to andrijor@gmail.com for their professional feedback</span>"
]
}
]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jordium-gantt-vue3",
"version": "1.9.0",
"version": "1.8.1",
"type": "module",
"main": "npm-package/dist/jordium-gantt-vue3.cjs.js",
"module": "npm-package/dist/jordium-gantt-vue3.es.js",
+2 -2
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import { useI18n } from '../composables/useI18n'
const props = defineProps({
@@ -78,7 +78,7 @@ const messageTaskName = computed(() => {
gap: 16px;
}
:global(html[data-theme='dark']) .confirm-timer-dialog {
:global(.gantt-root[data-theme='dark']) .confirm-timer-dialog {
background: var(--gantt-bg-primary, #6b6b6b);
}
.dialog-message {
+43 -43
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { useI18n } from '../composables/useI18n'
@@ -1519,48 +1519,48 @@ const timePickerStyle = computed(() => {
}
/* 暗黑模式适配 */
:global(html[data-theme='dark']) .el-input__wrapper {
:global(.gantt-root[data-theme='dark']) .el-input__wrapper {
background-color: var(--gantt-bg-secondary, #2c2c2c);
box-shadow: 0 0 0 1px var(--gantt-border-dark, #414243) inset;
}
:global(html[data-theme='dark']) .el-input__inner,
:global(html[data-theme='dark']) .el-range-input__start,
:global(html[data-theme='dark']) .el-range-input__end,
:global(html[data-theme='dark']) .el-range-separator {
:global(.gantt-root[data-theme='dark']) .el-input__inner,
:global(.gantt-root[data-theme='dark']) .el-range-input__start,
:global(.gantt-root[data-theme='dark']) .el-range-input__end,
:global(.gantt-root[data-theme='dark']) .el-range-separator {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-input__inner-input::placeholder,
:global(html[data-theme='dark']) .el-range-input__start::placeholder,
:global(html[data-theme='dark']) .el-range-input__end::placeholder {
:global(.gantt-root[data-theme='dark']) .el-input__inner-input::placeholder,
:global(.gantt-root[data-theme='dark']) .el-range-input__start::placeholder,
:global(.gantt-root[data-theme='dark']) .el-range-input__end::placeholder {
color: var(--gantt-text-muted, #9e9e9e);
}
:global(html[data-theme='dark']) .el-date-picker.is-focused .el-input__wrapper {
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-focused .el-input__wrapper {
box-shadow: 0 0 0 1px var(--gantt-primary, #409eff) inset;
}
:global(html[data-theme='dark']) .el-date-picker:hover .el-input__wrapper {
:global(.gantt-root[data-theme='dark']) .el-date-picker:hover .el-input__wrapper {
box-shadow: 0 0 0 1px var(--gantt-border-hover, #606266) inset;
}
:global(html[data-theme='dark']) .el-date-picker.is-focused:hover .el-input__wrapper {
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-focused:hover .el-input__wrapper {
box-shadow: 0 0 0 1px var(--gantt-primary, #409eff) inset;
}
:global(html[data-theme='dark']) .el-date-picker.is-disabled .el-input__wrapper {
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-disabled .el-input__wrapper {
background-color: var(--gantt-bg-disabled, #3c3e40);
box-shadow: 0 0 0 1px var(--gantt-border-disabled, #4c4d4f) inset;
}
:global(html[data-theme='dark']) .el-date-picker.is-disabled .el-input__inner,
:global(html[data-theme='dark']) .el-date-picker.is-disabled .el-range-input__start,
:global(html[data-theme='dark']) .el-date-picker.is-disabled .el-range-input__end {
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-disabled .el-input__inner,
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-disabled .el-range-input__start,
:global(.gantt-root[data-theme='dark']) .el-date-picker.is-disabled .el-range-input__end {
color: var(--gantt-text-disabled, #73767a);
}
:global(html[data-theme='dark']) .el-input__clear:hover {
:global(.gantt-root[data-theme='dark']) .el-input__clear:hover {
color: var(--gantt-text-secondary, #909399);
}
@@ -1894,51 +1894,51 @@ const timePickerStyle = computed(() => {
}
/* 暗黑模式下的日期选择器面板 */
:global(html[data-theme='dark']) .el-picker-panel {
:global(.gantt-root[data-theme='dark']) .el-picker-panel {
background: var(--gantt-bg-secondary, #2c2c2c);
border-color: var(--gantt-border-dark, #414243);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
:global(html[data-theme='dark']) .el-date-picker__header {
:global(.gantt-root[data-theme='dark']) .el-date-picker__header {
border-bottom-color: var(--gantt-border-dark, #414243);
}
:global(html[data-theme='dark']) .el-date-picker__header-label {
:global(.gantt-root[data-theme='dark']) .el-date-picker__header-label {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-picker-panel__icon-btn {
:global(.gantt-root[data-theme='dark']) .el-picker-panel__icon-btn {
color: var(--gantt-text-secondary, #909399);
}
:global(html[data-theme='dark']) .el-picker-panel__icon-btn:hover {
:global(.gantt-root[data-theme='dark']) .el-picker-panel__icon-btn:hover {
background: var(--gantt-bg-hover-dark, #3c3e40);
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-date-table__header-cell {
:global(.gantt-root[data-theme='dark']) .el-date-table__header-cell {
color: var(--gantt-text-secondary, #909399);
}
:global(html[data-theme='dark']) .el-date-table__cell:hover {
:global(.gantt-root[data-theme='dark']) .el-date-table__cell:hover {
background: var(--gantt-bg-hover-dark, #3c3e40);
}
:global(html[data-theme='dark']) .el-date-table__cell-inner {
:global(.gantt-root[data-theme='dark']) .el-date-table__cell-inner {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-date-table__cell.is-other-month .el-date-table__cell-inner {
:global(.gantt-root[data-theme='dark']) .el-date-table__cell.is-other-month .el-date-table__cell-inner {
color: var(--gantt-text-muted, #73767a);
}
:global(html[data-theme='dark']) .el-date-table__cell.is-in-range {
:global(.gantt-root[data-theme='dark']) .el-date-table__cell.is-in-range {
background: rgba(64, 158, 255, 0.2);
}
/* 暗黑模式下的日期选择器确认按钮 */
:global(html[data-theme='dark']) .el-date-picker-footer {
:global(.gantt-root[data-theme='dark']) .el-date-picker-footer {
border-top-color: var(--gantt-border-dark, #414243);
}
@@ -2182,69 +2182,69 @@ const timePickerStyle = computed(() => {
}
/* 暗黑模式下的时间选择器 */
:global(html[data-theme='dark']) .el-time-picker-input {
:global(.gantt-root[data-theme='dark']) .el-time-picker-input {
border-top-color: var(--gantt-border-dark, #414243);
}
:global(html[data-theme='dark']) .el-time-picker-label {
:global(.gantt-root[data-theme='dark']) .el-time-picker-label {
color: var(--gantt-text-secondary, #909399);
}
:global(html[data-theme='dark']) .el-time-input {
:global(.gantt-root[data-theme='dark']) .el-time-input {
background: var(--gantt-bg-secondary, #2c2c2c);
border-color: var(--gantt-border-dark, #414243);
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-time-input:hover {
:global(.gantt-root[data-theme='dark']) .el-time-input:hover {
border-color: var(--gantt-border-hover, #606266);
}
:global(html[data-theme='dark']) .el-time-picker-panel {
:global(.gantt-root[data-theme='dark']) .el-time-picker-panel {
background: var(--gantt-bg-secondary, #2c2c2c);
border-color: var(--gantt-border-dark, #414243);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
:global(html[data-theme='dark']) .el-time-picker-header {
:global(.gantt-root[data-theme='dark']) .el-time-picker-header {
border-bottom-color: var(--gantt-border-dark, #414243);
}
:global(html[data-theme='dark']) .el-time-picker-title {
:global(.gantt-root[data-theme='dark']) .el-time-picker-title {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-time-column-header {
:global(.gantt-root[data-theme='dark']) .el-time-column-header {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-time-column-list {
:global(.gantt-root[data-theme='dark']) .el-time-column-list {
border-color: var(--gantt-border-dark, #414243);
}
:global(html[data-theme='dark']) .el-time-column-list:hover::-webkit-scrollbar-thumb {
:global(.gantt-root[data-theme='dark']) .el-time-column-list:hover::-webkit-scrollbar-thumb {
background: var(--gantt-border-hover, #606266);
}
:global(html[data-theme='dark']) .el-time-item {
:global(.gantt-root[data-theme='dark']) .el-time-item {
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-time-item:hover {
:global(.gantt-root[data-theme='dark']) .el-time-item:hover {
background: var(--gantt-bg-hover-dark, #3c3e40);
}
:global(html[data-theme='dark']) .el-time-picker-footer {
:global(.gantt-root[data-theme='dark']) .el-time-picker-footer {
border-top-color: var(--gantt-border-dark, #414243);
}
:global(html[data-theme='dark']) .el-time-picker-btn--cancel {
:global(.gantt-root[data-theme='dark']) .el-time-picker-btn--cancel {
background: var(--gantt-bg-secondary, #2c2c2c);
border-color: var(--gantt-border-dark, #414243);
color: var(--gantt-text-white, #ffffff);
}
:global(html[data-theme='dark']) .el-time-picker-btn--cancel:hover {
:global(.gantt-root[data-theme='dark']) .el-time-picker-btn--cancel:hover {
background: var(--gantt-bg-hover-dark, #3c3e40);
border-color: var(--gantt-border-hover, #606266);
}
+85 -26
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, onUnmounted, onMounted, computed, watch, nextTick, useSlots, provide } from 'vue'
import type { StyleValue } from 'vue'
import TaskList from './TaskList/TaskList.vue'
@@ -31,6 +31,9 @@ import { TimelineScale, SCALE_CONFIGS } from '../models/types/TimelineScale'
import { detectConflicts } from '../utils/conflictUtils'
import { useMessage } from '../composables/useMessage'
//
const ganttRootRef = ref<HTMLElement>()
const props = withDefaults(defineProps<Props>(), {
tasks: () => [],
milestones: () => [],
@@ -74,7 +77,7 @@ const props = withDefaults(defineProps<Props>(), {
expandAll: true,
locale: 'zh-CN',
timeScale: 'week',
theme: 'light',
theme: undefined, //
})
const emit = defineEmits([
@@ -399,6 +402,20 @@ provide('enable-task-bar-context-menu', computed(() => props.enableTaskBarContex
// LinkAnchor
provide('enable-link-anchor', computed(() => props.enableLinkAnchor))
//
const detectSystemTheme = (): 'light' | 'dark' => {
if (typeof window !== 'undefined' && window.matchMedia) {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
return isDark ? 'dark' : 'light'
}
return 'light'
}
// GanttLinks
// 'light'onMountedprops
const currentThemeMode = ref<'light' | 'dark'>('light')
provide('gantt-theme', currentThemeMode)
// 使 composables
const { hasDeclarativeContextMenu: hasDeclarativeTaskListContextMenu, declarativeContextMenu: declarativeTaskListContextMenu } =
useTaskListContextMenu(slots)
@@ -538,7 +555,6 @@ interface Props {
// : 1160px ( TaskListConfig )
//
const ganttRootRef = ref<HTMLElement | null>(null)
const ganttContainerWidth = ref(1920) // 使
//
@@ -595,6 +611,16 @@ onMounted(() => {
updateContainerWidth()
// 使
window.addEventListener('resize', throttledUpdateContainerWidth)
// DOM
nextTick(() => {
if (ganttRootRef.value) {
// props.theme使
const initialTheme = props.theme ?? detectSystemTheme()
ganttRootRef.value.setAttribute('data-theme', initialTheme)
currentThemeMode.value = initialTheme
}
})
})
onUnmounted(() => {
@@ -1275,8 +1301,7 @@ onUnmounted(() => {
window.removeEventListener('context-menu', handleTaskContextMenu as EventListener)
})
//
const currentThemeMode = ref<'light' | 'dark'>('light')
// provide
/**
* 设置主题模式
@@ -1285,7 +1310,10 @@ const currentThemeMode = ref<'light' | 'dark'>('light')
const setTheme = (mode?: 'light' | 'dark') => {
const targetMode = mode || 'dark'
currentThemeMode.value = targetMode
document.documentElement.setAttribute('data-theme', targetMode)
// data-themedocument.documentElement
if (ganttRootRef.value) {
ganttRootRef.value.setAttribute('data-theme', targetMode)
}
}
/**
@@ -1299,13 +1327,37 @@ const currentTheme = (): string => {
watch(
() => props.theme,
(newTheme) => {
if (newTheme && newTheme !== currentThemeMode.value) {
setTheme(newTheme)
//
if (!ganttRootRef.value) return
// theme使undefined
const targetTheme = newTheme ?? detectSystemTheme()
if (targetTheme !== currentThemeMode.value) {
setTheme(targetTheme)
}
},
{ immediate: true },
)
// theme
if (typeof window !== 'undefined' && window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleSystemThemeChange = (e: MediaQueryListEvent) => {
// theme prop
if (!props.theme) {
const systemTheme = e.matches ? 'dark' : 'light'
setTheme(systemTheme)
}
}
//
mediaQuery.addEventListener('change', handleSystemThemeChange)
//
onUnmounted(() => {
mediaQuery.removeEventListener('change', handleSystemThemeChange)
})
}
//
const isFullscreen = ref(false)
@@ -1934,6 +1986,12 @@ const handleTimelineScaleChanged = (scale: TimelineScale) => {
})
}
//
const handleThemeChange = (isDark: boolean) => {
const newTheme = isDark ? 'dark' : 'light'
setTheme(newTheme)
}
// === ===
//
const TIME_SCALE_ORDER: TimelineScale[] = ['hour', 'day', 'week', 'month', 'quarter', 'year']
@@ -3236,6 +3294,7 @@ defineExpose({
@add-milestone="milestoneAddHandler"
@expand-all="handleExpandAll"
@collapse-all="handleCollapseAll"
@theme-change="handleThemeChange"
/>
<!-- 甘特图主体 -->
@@ -3593,42 +3652,42 @@ defineExpose({
}
/* 暗色主题支持 */
:global(html[data-theme='dark']) .gantt-root {
:global(.gantt-root[data-theme='dark']) .gantt-root {
background: #1e1e1e !important;
color: #e5e5e5 !important;
}
:global(html[data-theme='dark']) .gantt-panel {
:global(.gantt-root[data-theme='dark']) .gantt-panel {
background: #2c2c2c !important;
}
:global(html[data-theme='dark']) .gantt-panel-left {
:global(.gantt-root[data-theme='dark']) .gantt-panel-left {
border-right-color: #4c4c4c !important;
}
:global(html[data-theme='dark']) .focus-close-button {
:global(.gantt-root[data-theme='dark']) .focus-close-button {
background: #d85555;
border-color: #d85555;
box-shadow: 0 4px 12px rgba(216, 85, 85, 0.4);
}
:global(html[data-theme='dark']) .focus-close-button:hover {
:global(.gantt-root[data-theme='dark']) .focus-close-button:hover {
background: #e67676;
border-color: #e67676;
box-shadow: 0 6px 16px rgba(216, 85, 85, 0.5);
}
:global(html[data-theme='dark']) .focus-close-button .close-icon,
:global(html[data-theme='dark']) .focus-close-button .close-text {
:global(.gantt-root[data-theme='dark']) .focus-close-button .close-icon,
:global(.gantt-root[data-theme='dark']) .focus-close-button .close-text {
color: #ffffff;
}
:global(html[data-theme='dark']) .focus-close-button:hover .close-icon,
:global(html[data-theme='dark']) .focus-close-button:hover .close-text {
:global(.gantt-root[data-theme='dark']) .focus-close-button:hover .close-icon,
:global(.gantt-root[data-theme='dark']) .focus-close-button:hover .close-text {
color: #ffffff;
}
:global(html[data-theme='dark']) .gantt-splitter {
:global(.gantt-root[data-theme='dark']) .gantt-splitter {
background: linear-gradient(
to right,
transparent,
@@ -3639,7 +3698,7 @@ defineExpose({
box-shadow: inset 0 0 2px rgba(255, 255, 255, 0.1) !important;
}
:global(html[data-theme='dark']) .gantt-splitter:hover {
:global(.gantt-root[data-theme='dark']) .gantt-splitter:hover {
background: linear-gradient(
to right,
transparent,
@@ -3655,24 +3714,24 @@ defineExpose({
}
/* 暗色主题支持 - TaskList切换按钮 */
:global(html[data-theme='dark']) .task-list-toggle {
:global(.gantt-root[data-theme='dark']) .task-list-toggle {
background: rgba(42, 42, 42, 0.95) !important;
border-color: #555555 !important;
color: #cccccc !important;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4) !important;
}
:global(html[data-theme='dark']) .task-list-toggle.collapsed {
:global(.gantt-root[data-theme='dark']) .task-list-toggle.collapsed {
box-shadow: -1px 1px 4px rgba(0, 0, 0, 0.4) !important;
}
:global(html[data-theme='dark']) .task-list-toggle:hover {
:global(.gantt-root[data-theme='dark']) .task-list-toggle:hover {
background: rgba(42, 42, 42, 1) !important;
color: #569cd6 !important;
box-shadow: 1px 2px 8px rgba(86, 156, 214, 0.3) !important;
}
:global(html[data-theme='dark']) .task-list-toggle.collapsed:hover {
:global(.gantt-root[data-theme='dark']) .task-list-toggle.collapsed:hover {
box-shadow: -1px 2px 8px rgba(86, 156, 214, 0.3) !important;
}
@@ -3727,11 +3786,11 @@ defineExpose({
}
/* 暗色主题下的全屏模式 */
:global(html[data-theme='dark']) .gantt-fullscreen {
:global(.gantt-root[data-theme='dark']) .gantt-fullscreen {
background: #1e1e1e !important;
}
:global(html[data-theme='dark']) .gantt-fullscreen .gantt-toolbar {
:global(.gantt-root[data-theme='dark']) .gantt-fullscreen .gantt-toolbar {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
}
+2 -2
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import '../styles/app.css'
const props = defineProps({
visible: Boolean,
@@ -102,7 +102,7 @@ const onNo = () => emit('no')
gap: 12px;
}
:global(html[data-theme='dark']) .gantt-confirm-dialog {
:global(.gantt-root[data-theme='dark']) .gantt-confirm-dialog {
background: var(--gantt-bg-secondary, #f8f9fa) !important;
border-color: var(--gantt-border-dark, #999999) !important;
}
+8 -30
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { ref, watch, nextTick, onMounted, onUnmounted, computed, inject, type Ref } from 'vue'
import type { Task } from '../models/classes/Task'
import { getPredecessorIds } from '../utils/predecessorUtils'
import { CanvasContextManager } from '../utils/canvasUtils'
@@ -32,6 +32,7 @@ interface Props {
// 线
verticalLines?: VerticalLine[]
showVerticalLines?: boolean
// injectprops
}
const props = withDefaults(defineProps<Props>(), {
@@ -41,6 +42,9 @@ const props = withDefaults(defineProps<Props>(), {
offsetTop: 0,
})
// inject
const injectedTheme = inject<Ref<'light' | 'dark'>>('gantt-theme', ref('light'))
// Canvas
const canvasRef = ref<HTMLCanvasElement | null>(null)
@@ -50,16 +54,9 @@ const canvasManager = new CanvasContextManager()
// requestAnimationFrame
let rafId: number | null = null
let pendingRedraw = false
let themeObserver: MutationObserver | null = null
// 线
const isDarkTheme = ref(document.documentElement.getAttribute('data-theme') === 'dark')
//
const updateTheme = () => {
isDarkTheme.value = document.documentElement.getAttribute('data-theme') === 'dark'
scheduleRedraw()
}
const isDarkTheme = computed(() => injectedTheme.value === 'dark')
/**
* 绘制关系线到 Canvas
@@ -445,6 +442,7 @@ watch(
() => props.showVerticalLines,
() => props.offsetLeft, //
() => props.offsetTop,
injectedTheme, // injectcomputed
],
() => {
// 使 RAF
@@ -455,21 +453,7 @@ watch(
//
onMounted(() => {
//
themeObserver = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.attributeName === 'data-theme') {
updateTheme()
break
}
}
})
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme'],
})
// document.documentElement使props.theme
nextTick(() => {
drawLinks()
})
@@ -477,12 +461,6 @@ onMounted(() => {
//
onUnmounted(() => {
//
if (themeObserver) {
themeObserver.disconnect()
themeObserver = null
}
// RAF
if (rafId !== null) {
cancelAnimationFrame(rafId)
+27 -38
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import { useI18n } from '../composables/useI18n'
import type { ToolbarConfig } from '../models/configs/ToolbarConfig'
@@ -134,7 +134,7 @@ watch(
const newMode = newTheme === 'dark'
if (isDarkMode.value !== newMode) {
isDarkMode.value = newMode
document.documentElement.setAttribute('data-theme', newTheme)
// document.documentElementGanttChart
}
}
},
@@ -332,7 +332,7 @@ const handleThemeToggle = () => {
//
isDarkMode.value = newTheme
document.documentElement.setAttribute('data-theme', newTheme ? 'dark' : 'light')
// document.documentElementGanttChart
//
if (props.onThemeChange && typeof props.onThemeChange === 'function') {
@@ -475,23 +475,12 @@ onMounted(() => {
const currentLocale = locale.value
currentLanguage.value = currentLocale === 'zh-CN' ? 'zh' : 'en'
// DOM
document.documentElement.setAttribute('data-theme', isDarkMode.value ? 'dark' : 'light')
// document.documentElementGanttChart
//
document.addEventListener('click', handleClickOutside)
//
if (
window.matchMedia &&
typeof window !== 'undefined' &&
!localStorage.getItem(THEME_STORAGE_KEY)
) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
isDarkMode.value = e.matches
document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light')
})
}
// GanttChart
})
onUnmounted(() => {
@@ -1138,34 +1127,34 @@ onUnmounted(() => {
}
/* 暗黑模式样式 */
:global(html[data-theme='dark']) .toolbar-icon-btn:hover {
:global(.gantt-root[data-theme='dark']) .toolbar-icon-btn:hover {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.08));
color: var(--gantt-primary, #66b1ff);
}
:global(html[data-theme='dark']) .toolbar-icon-btn:focus {
:global(.gantt-root[data-theme='dark']) .toolbar-icon-btn:focus {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.1));
color: var(--gantt-primary, #66b1ff);
}
:global(html[data-theme='dark']) .toolbar-lang-btn:hover {
:global(.gantt-root[data-theme='dark']) .toolbar-lang-btn:hover {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.06));
border-color: var(--gantt-primary, #66b1ff);
color: var(--gantt-primary, #66b1ff);
}
:global(html[data-theme='dark']) .toolbar-lang-btn:focus {
:global(.gantt-root[data-theme='dark']) .toolbar-lang-btn:focus {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.08));
border-color: var(--gantt-primary, #66b1ff);
color: var(--gantt-primary, #66b1ff);
box-shadow: 0 0 0 2px rgba(102, 177, 255, 0.4);
}
:global(html[data-theme='dark']) .dialog-content {
:global(.gantt-root[data-theme='dark']) .dialog-content {
background: var(--gantt-bg-secondary, #2c2c2c);
}
:global(html[data-theme='dark']) .dialog-message {
:global(.gantt-root[data-theme='dark']) .dialog-message {
color: var(--gantt-text-primary, #e5eaf3);
}
@@ -1468,40 +1457,40 @@ onUnmounted(() => {
}
/* 暗黑模式下的按钮组样式 */
:global(html[data-theme='dark']) .gantt-btn-group {
:global(.gantt-root[data-theme='dark']) .gantt-btn-group {
box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.3),
0 1px 2px -1px rgba(0, 0, 0, 0.3);
}
:global(html[data-theme='dark']) .gantt-btn-group:hover,
:global(html[data-theme='dark']) .gantt-btn-group:focus-within {
:global(.gantt-root[data-theme='dark']) .gantt-btn-group:hover,
:global(.gantt-root[data-theme='dark']) .gantt-btn-group:focus-within {
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.5);
}
:global(html[data-theme='dark']) .add-btn-group .gantt-btn-group-item {
:global(.gantt-root[data-theme='dark']) .add-btn-group .gantt-btn-group-item {
background: #337ecc;
border-color: #337ecc;
color: #ffffff;
}
:global(html[data-theme='dark']) .add-btn-group .gantt-btn-group-item:hover {
:global(.gantt-root[data-theme='dark']) .add-btn-group .gantt-btn-group-item:hover {
background: #4d94d4;
border-color: #4d94d4;
}
:global(html[data-theme='dark']) .add-btn-group .gantt-btn-group-item:focus {
:global(.gantt-root[data-theme='dark']) .add-btn-group .gantt-btn-group-item:focus {
background: #2c5aa0;
border-color: #2c5aa0;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
}
:global(html[data-theme='dark']) .add-btn-group .gantt-btn-group-item:active {
:global(.gantt-root[data-theme='dark']) .add-btn-group .gantt-btn-group-item:active {
background: #1f4872;
border-color: #1f4872;
}
:global(html[data-theme='dark']) .gantt-btn-group:not(.gantt-add-btn-group) .gantt-btn-group-item {
:global(.gantt-root[data-theme='dark']) .gantt-btn-group:not(.gantt-add-btn-group) .gantt-btn-group-item {
background: #2c2c2c;
border-color: #4c4c4c;
color: #e5e5e5;
@@ -1594,41 +1583,41 @@ onUnmounted(() => {
}
/* 暗黑模式下的分段控制器样式 */
:global(html[data-theme='dark']) .segmented-control {
:global(.gantt-root[data-theme='dark']) .segmented-control {
background: var(--gantt-bg-secondary, #4b4b4b);
border-color: var(--gantt-border-color, #808080);
}
:global(html[data-theme='dark']) .segmented-control:hover {
:global(.gantt-root[data-theme='dark']) .segmented-control:hover {
border-color: var(--gantt-primary, #3399ff);
}
:global(html[data-theme='dark']) .segmented-thumb {
:global(.gantt-root[data-theme='dark']) .segmented-thumb {
background: var(--gantt-primary, #3399ff);
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.3),
0 1px 6px -1px rgba(0, 0, 0, 0.3);
}
:global(html[data-theme='dark']) .segmented-item {
:global(.gantt-root[data-theme='dark']) .segmented-item {
color: #ffffff !important; /* 强制使用纯白色,更加突出 */
}
/* 特别针对时间刻度分段控制器的暗黑模式样式 */
:global(html[data-theme='dark']) .time-scale-segmented .segmented-item {
:global(.gantt-root[data-theme='dark']) .time-scale-segmented .segmented-item {
color: #ffffff !important; /* 确保时间刻度按钮也使用纯白色 */
}
:global(html[data-theme='dark']) .segmented-item:hover:not(.active) {
:global(.gantt-root[data-theme='dark']) .segmented-item:hover:not(.active) {
color: var(--gantt-primary, #3399ff); /* 使用主色调,更加鲜艳 */
background: rgba(51, 153, 255, 0.12); /* 调整背景透明度,与主色调匹配 */
}
:global(html[data-theme='dark']) .segmented-item:active:not(.active) {
:global(.gantt-root[data-theme='dark']) .segmented-item:active:not(.active) {
background: rgba(51, 153, 255, 0.2); /* 调整背景透明度,与主色调匹配 */
}
:global(html[data-theme='dark']) .segmented-item.active {
:global(.gantt-root[data-theme='dark']) .segmented-item.active {
color: #ffffff;
}
</style>
+4 -4
View File
@@ -253,16 +253,16 @@ onUnmounted(() => {
}
/* 暗色主题支持 */
:global(html[data-theme='dark']) .link-anchor {
:global(.gantt-root[data-theme='dark']) .link-anchor {
border-color: #1a1a1a;
}
:global(html[data-theme='dark']) .link-anchor.valid-target,
:global(html[data-theme='dark']) .link-anchor.invalid-target {
:global(.gantt-root[data-theme='dark']) .link-anchor.valid-target,
:global(.gantt-root[data-theme='dark']) .link-anchor.invalid-target {
border-color: #1a1a1a;
}
:global(html[data-theme='dark']) .anchor-tooltip {
:global(.gantt-root[data-theme='dark']) .anchor-tooltip {
background: rgba(255, 255, 255, 0.9);
color: #1a1a1a;
}
+3 -3
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue'
import { useI18n } from '../composables/useI18n'
import DatePicker from './DatePicker.vue'
@@ -792,12 +792,12 @@ const t = (key: string) => {
}
/* 暗黑模式下的确认弹窗 */
:global(html[data-theme='dark']) .milestone-confirm-dialog {
:global(.gantt-root[data-theme='dark']) .milestone-confirm-dialog {
background: var(--gantt-bg-dark, #1d1e1f);
border-color: var(--gantt-border-dark, #3c3e40);
}
:global(html[data-theme='dark']) .milestone-confirm-footer {
:global(.gantt-root[data-theme='dark']) .milestone-confirm-footer {
background: var(--gantt-bg-darker, #141414);
border-color: var(--gantt-border-dark, #3c3e40);
}
+11 -11
View File
@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any */
<script setup lang="ts">
import { computed, ref, onUnmounted } from 'vue'
import type { Milestone } from '../models/classes/Milestone'
@@ -1067,23 +1067,23 @@ const calculateMilestonePositionFromTimelineData = (
}
/* 暗黑模式下的适配 */
:global(html[data-theme='dark']) .milestone-label {
:global(.gantt-root[data-theme='dark']) .milestone-label {
color: var(--gantt-text-white, #ffffff) !important;
}
/* 暗黑模式下的粘性标签样式 */
:global(html[data-theme='dark']) .milestone-label[style*='position: absolute'] {
:global(.gantt-root[data-theme='dark']) .milestone-label[style*='position: absolute'] {
background: rgba(30, 30, 30, 0.9) !important;
border-color: rgba(246, 124, 124, 0.3) !important;
color: #ffffff !important;
}
:global(html[data-theme='dark']) .milestone svg {
:global(.gantt-root[data-theme='dark']) .milestone svg {
filter: drop-shadow(0 0 8px var(--gantt-danger, #f67c7c));
animation: milestone-glow-dark 2s ease-in-out infinite alternate;
}
:global(html[data-theme='dark']) .milestone:hover svg {
:global(.gantt-root[data-theme='dark']) .milestone:hover svg {
filter: drop-shadow(0 0 16px var(--gantt-danger, #f67c7c))
drop-shadow(0 0 24px rgba(246, 124, 124, 0.4));
animation: milestone-glow-intense-dark 1.5s ease-in-out infinite alternate;
@@ -1125,7 +1125,7 @@ const calculateMilestonePositionFromTimelineData = (
animation: none;
}
:global(html[data-theme='dark']) .milestone.dragging svg {
:global(.gantt-root[data-theme='dark']) .milestone.dragging svg {
filter: drop-shadow(0 0 20px var(--gantt-danger, #f67c7c))
drop-shadow(0 0 32px rgba(246, 124, 124, 0.6));
}
@@ -1164,14 +1164,14 @@ const calculateMilestonePositionFromTimelineData = (
}
/* 暗黑模式下的停靠状态样式 */
:global(html[data-theme='dark']) .milestone-sticky-left svg,
:global(html[data-theme='dark']) .milestone-sticky-right svg {
:global(.gantt-root[data-theme='dark']) .milestone-sticky-left svg,
:global(.gantt-root[data-theme='dark']) .milestone-sticky-right svg {
animation: milestone-glow-sticky-dark 3s ease-in-out infinite alternate;
}
/* 暗黑模式下半图标显示时取消发光效果 */
:global(html[data-theme='dark']) .milestone-sticky-left svg[style*='clip-path'],
:global(html[data-theme='dark']) .milestone-sticky-right svg[style*='clip-path'] {
:global(.gantt-root[data-theme='dark']) .milestone-sticky-left svg[style*='clip-path'],
:global(.gantt-root[data-theme='dark']) .milestone-sticky-right svg[style*='clip-path'] {
animation: none;
filter: none;
}
@@ -1259,7 +1259,7 @@ const calculateMilestonePositionFromTimelineData = (
}
/* 暗黑模式下的Tooltip样式 */
:global(html[data-theme='dark']) .milestone-tooltip {
:global(.gantt-root[data-theme='dark']) .milestone-tooltip {
background: rgba(30, 30, 30, 0.95) !important;
color: #ffffff !important;
}
+4 -4
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import type { Task } from '../models/classes/Task'
@@ -184,17 +184,17 @@ watch(
}
/* 暗黑模式 */
:global(html[data-theme='dark']) .predecessor-tag {
:global(.gantt-root[data-theme='dark']) .predecessor-tag {
background: var(--gantt-primary-dark, #337ecc);
}
:global(html[data-theme='dark']) .form-select {
:global(.gantt-root[data-theme='dark']) .form-select {
background: var(--gantt-bg-primary, #2b2b2b);
border-color: var(--gantt-border-medium, #4c4d4f);
color: var(--gantt-text-primary, #e5eaf3);
}
:global(html[data-theme='dark']) .form-select:focus {
:global(.gantt-root[data-theme='dark']) .form-select:focus {
border-color: var(--gantt-primary, #409eff);
}
</style>
+24 -24
View File
@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any */
<script setup lang="ts">
import { ref, computed, onUnmounted, onMounted, nextTick, watch, useSlots, inject, type ComputedRef, type Ref } from 'vue'
import type { Task } from '../models/classes/Task'
@@ -4227,7 +4227,7 @@ class="hover-tooltip-arrow" :style="{
}
/* 暗黑主题下的标题颜色 */
:global(html[data-theme='dark']) .actual-task-name {
:global(.gantt-root[data-theme='dark']) .actual-task-name {
color: #ffffff !important;
}
@@ -4883,14 +4883,14 @@ class="hover-tooltip-arrow" :style="{
}
/* 暗色主题支持 */
:global(html[data-theme='dark']) .task-bar {
:global(.gantt-root[data-theme='dark']) .task-bar {
border-color: #111827 !important;
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.7),
0 2px 4px rgba(0, 0, 0, 0.3) !important;
}
:global(html[data-theme='dark']) .task-bar:hover {
:global(.gantt-root[data-theme='dark']) .task-bar:hover {
box-shadow:
0 6px 20px rgba(0, 0, 0, 0.8),
0 4px 8px rgba(0, 0, 0, 0.4) !important;
@@ -4898,58 +4898,58 @@ class="hover-tooltip-arrow" :style="{
transition: all 0.2s ease;
}
:global(html[data-theme='dark']) .task-bar:hover::after {
:global(.gantt-root[data-theme='dark']) .task-bar:hover::after {
background: rgba(7, 10, 15, 0.98) !important;
color: #f9fafb !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6) !important;
}
:global(html[data-theme='dark']) .task-bar.normal {
:global(.gantt-root[data-theme='dark']) .task-bar.normal {
background: linear-gradient(135deg, #1e40af, #1e3a8a) !important;
border-color: #1e3a8a !important;
}
:global(html[data-theme='dark']) .task-bar.milestone {
:global(.gantt-root[data-theme='dark']) .task-bar.milestone {
background: linear-gradient(135deg, #c2410c, #9a3412) !important;
border-color: #9a3412 !important;
}
:global(html[data-theme='dark']) .task-bar.completed {
:global(.gantt-root[data-theme='dark']) .task-bar.completed {
background: linear-gradient(135deg, #14532d, #16a34a) !important;
border-color: #14532d !important;
}
:global(html[data-theme='dark']) .task-bar.delayed {
:global(.gantt-root[data-theme='dark']) .task-bar.delayed {
background: linear-gradient(135deg, #991b1b, #dc2626) !important;
border-color: #991b1b !important;
}
:global(html[data-theme='dark']) .task-bar.parent {
:global(.gantt-root[data-theme='dark']) .task-bar.parent {
background: linear-gradient(135deg, #581c87, #7c3aed) !important;
border-color: #581c87 !important;
}
:global(html[data-theme='dark']) .task-content {
:global(.gantt-root[data-theme='dark']) .task-content {
color: #ffffff !important;
}
:global(html[data-theme='dark']) .task-name {
:global(.gantt-root[data-theme='dark']) .task-name {
color: #ffffff !important;
}
:global(html[data-theme='dark']) .progress-bar {
:global(.gantt-root[data-theme='dark']) .progress-bar {
background: rgba(255, 255, 255, 0.2) !important;
}
:global(html[data-theme='dark']) .progress-fill {
:global(.gantt-root[data-theme='dark']) .progress-fill {
background: rgba(255, 255, 255, 0.8) !important;
}
:global(html[data-theme='dark']) .resize-handle {
:global(.gantt-root[data-theme='dark']) .resize-handle {
background: rgba(255, 255, 255, 0.1) !important;
}
:global(html[data-theme='dark']) .resize-handle:hover {
:global(.gantt-root[data-theme='dark']) .resize-handle:hover {
background: rgba(255, 255, 255, 0.3) !important;
}
@@ -5011,42 +5011,42 @@ class="hover-tooltip-arrow" :style="{
}
/* 暗色主题下的短TaskBar溢出效果 */
:global(html[data-theme='dark']) .task-bar.overflow-effect .resize-handle {
:global(.gantt-root[data-theme='dark']) .task-bar.overflow-effect .resize-handle {
background: rgba(255, 255, 255, 0.15);
}
:global(html[data-theme='dark']) .task-bar.overflow-effect .resize-handle:hover {
:global(.gantt-root[data-theme='dark']) .task-bar.overflow-effect .resize-handle:hover {
background: rgba(255, 255, 255, 0.3);
}
/* 暗色主题下的进度百分比样式 */
:global(html[data-theme='dark']) .task-bar.overflow-effect .task-progress {
:global(.gantt-root[data-theme='dark']) .task-bar.overflow-effect .task-progress {
background: rgba(0, 0, 0, 0.9);
color: white;
}
:global(html[data-theme='dark']) .task-bar.week-view {
:global(.gantt-root[data-theme='dark']) .task-bar.week-view {
border-color: var(--gantt-border-light, #555555);
}
/* 暗色主题下的头像样式 */
:global(html[data-theme='dark']) .task-avatar {
:global(.gantt-root[data-theme='dark']) .task-avatar {
border-color: rgba(255, 255, 255, 0.3);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* 暗色主题下的默认头像 */
:global(html[data-theme='dark']) .task-avatar.avatar-default {
:global(.gantt-root[data-theme='dark']) .task-avatar.avatar-default {
background: var(--gantt-bg-tertiary, #4a5568);
color: var(--gantt-text-tertiary, #718096);
}
/* 暗色主题下的图标颜色 */
:global(html[data-theme='dark']) .task-avatar .avatar-icon {
:global(.gantt-root[data-theme='dark']) .task-avatar .avatar-icon {
color: var(--gantt-text-tertiary, #718096);
}
:global(html[data-theme='dark']) .task-avatar.avatar-outside {
:global(.gantt-root[data-theme='dark']) .task-avatar.avatar-outside {
border-color: rgba(255, 255, 255, 0.4);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
+7 -7
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick, computed } from 'vue'
import { useI18n } from '../composables/useI18n'
import type { Task } from '../models/classes/Task'
@@ -661,23 +661,23 @@ onUnmounted(() => {
}
/* 暗色主题支持 */
:global(html[data-theme='dark']) .task-context-menu {
:global(.gantt-root[data-theme='dark']) .task-context-menu {
background-color: #2c2c2c;
border-color: #444444;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
:global(html[data-theme='dark']) .menu-item {
:global(.gantt-root[data-theme='dark']) .menu-item {
color: #e5e5e5;
}
:global(html[data-theme='dark']) .menu-item:hover {
:global(.gantt-root[data-theme='dark']) .menu-item:hover {
background-color: #3a3a3a;
color: #409eff;
}
/* 暗色主题下箭头颜色 */
:global(html[data-theme='dark']) .menu-arrow {
:global(.gantt-root[data-theme='dark']) .menu-arrow {
border-bottom-color: #2c2c2c; /* 匹配暗色菜单背景色 */
filter: drop-shadow(0 -1px 2px rgba(0, 0, 0, 0.25));
}
@@ -784,13 +784,13 @@ onUnmounted(() => {
}
/* 暗色主题下的子菜单 */
:global(html[data-theme='dark']) .submenu {
:global(.gantt-root[data-theme='dark']) .submenu {
background-color: #2c2c2c;
border-color: #444444;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
:global(html[data-theme='dark']) .submenu-item:hover {
:global(.gantt-root[data-theme='dark']) .submenu-item:hover {
background-color: #3a1f1f;
color: #f56c6c;
}
+15 -15
View File
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
import { ref, reactive, watch, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { useI18n } from '../composables/useI18n'
import { useMessage } from '../composables/useMessage'
@@ -1606,29 +1606,29 @@ const taskStatus = computed(() => {
}
/* 暗黑模式样式 */
:global(html[data-theme='dark']) .progress-input-group {
:global(.gantt-root[data-theme='dark']) .progress-input-group {
border-color: var(--gantt-border-medium, #4c4d4f);
background: var(--gantt-bg-primary, #2b2b2b);
}
:global(html[data-theme='dark']) .progress-input-group:hover {
:global(.gantt-root[data-theme='dark']) .progress-input-group:hover {
border-color: var(--gantt-primary, #409eff);
}
:global(html[data-theme='dark']) .progress-input-group:focus-within {
:global(.gantt-root[data-theme='dark']) .progress-input-group:focus-within {
border-color: var(--gantt-primary, #409eff);
}
:global(html[data-theme='dark']) .progress-input {
:global(.gantt-root[data-theme='dark']) .progress-input {
color: var(--gantt-text-primary, #e5eaf3);
background: transparent;
}
:global(html[data-theme='dark']) .progress-input::placeholder {
:global(.gantt-root[data-theme='dark']) .progress-input::placeholder {
color: var(--gantt-text-placeholder, #8b949e);
}
:global(html[data-theme='dark']) .progress-unit {
:global(.gantt-root[data-theme='dark']) .progress-unit {
color: var(--gantt-text-secondary, #a8a8a8);
}
@@ -1686,27 +1686,27 @@ const taskStatus = computed(() => {
}
/* 暗黑模式样式优化 */
:global(html[data-theme='dark']) .drawer-overlay {
:global(.gantt-root[data-theme='dark']) .drawer-overlay {
background: rgba(0, 0, 0, 0.7) !important;
}
:global(html[data-theme='dark']) .drawer-container {
:global(.gantt-root[data-theme='dark']) .drawer-container {
box-shadow: -4px 0 15px rgba(0, 0, 0, 0.4) !important;
}
:global(html[data-theme='dark']) .drawer-close-btn:hover {
:global(.gantt-root[data-theme='dark']) .drawer-close-btn:hover {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.1)) !important;
border-radius: 4px;
}
:global(html[data-theme='dark']) .form-input:focus,
:global(html[data-theme='dark']) .form-select:focus,
:global(html[data-theme='dark']) .form-textarea:focus {
:global(.gantt-root[data-theme='dark']) .form-input:focus,
:global(.gantt-root[data-theme='dark']) .form-select:focus,
:global(.gantt-root[data-theme='dark']) .form-textarea:focus {
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2) !important;
}
:global(html[data-theme='dark']) .form-input::placeholder,
:global(html[data-theme='dark']) .form-textarea::placeholder {
:global(.gantt-root[data-theme='dark']) .form-input::placeholder,
:global(.gantt-root[data-theme='dark']) .form-textarea::placeholder {
color: var(--gantt-text-muted, #9e9e9e) !important;
}
+65 -185
View File
@@ -1,11 +1,9 @@
<script setup lang="ts">
import { ref, computed, useSlots, toRef, inject, type ComputedRef, type Ref } from 'vue'
<script setup lang="ts">
import { ref, computed, useSlots, toRef, inject, type ComputedRef } from 'vue'
import type { StyleValue } from 'vue'
import { useI18n } from '../../../composables/useI18n'
import { formatPredecessorDisplay } from '../../../utils/predecessorUtils'
import type { Task } from '../../../models/classes/Task'
import type { Resource } from '../../../models/types/Resource'
import { isResourceOverloaded } from '../../../utils/resourceUtils'
import type { TaskListColumnConfig } from '../../../models/configs/TaskListConfig'
import type { DeclarativeColumnConfig } from '../composables/taskList/useTaskListColumns'
import TaskContextMenu from '../../TaskContextMenu.vue'
@@ -23,7 +21,7 @@ interface TaskRowSlotProps {
level: number
indent: string
isHovered: boolean
hoveredTaskId: number | string | null
hoveredTaskId: number | null
isParent: boolean
hasChildren: boolean
collapsed: boolean
@@ -44,8 +42,8 @@ interface Props {
level: number
rowIndex?: number
isHovered?: boolean
hoveredTaskId?: number | string | null
onHover?: (taskId: number | string | null) => void
hoveredTaskId?: number | null
onHover?: (taskId: number | null) => void
columns: TaskListColumnConfig[]
declarativeColumns?: DeclarativeColumnConfig[]
renderMode?: 'default' | 'declarative'
@@ -84,36 +82,6 @@ const daysText = computed(() => t.value?.days ?? '')
const slots = useSlots()
const hasContentSlot = computed(() => Boolean(slots['custom-task-content']))
// v1.9.0 Inject view mode to detect if we're rendering a resource
const viewMode = inject<Ref<'task' | 'resource'>>('gantt-view-mode', ref('task'))
// GanttChart
const resourceTaskLayouts = inject<ComputedRef<Map<string, { taskRowMap: Map<string | number, number>, rowHeights: number[], totalHeight: number }>>>('resourceTaskLayouts', computed(() => new Map()))
// v1.9.0 Detect if current row is a resource
const isResourceRow = computed(() => {
return viewMode.value === 'resource' && 'tasks' in props.task
})
// v1.9.0
const isResourceOverloadedComputed = computed(() => {
if (!isResourceRow.value) return false
// Resource
const resource = props.task as Resource
return isResourceOverloaded(resource)
})
// - resource使
const rowHeight = computed(() => {
if (isResourceRow.value) {
const resourceId = String(props.task.id) // string
const layout = resourceTaskLayouts.value.get(resourceId)
return layout?.totalHeight || 56 // v1.9.1 56px51 + 5pxpadding
}
return 51 // task使
})
//
const enableTaskListContextMenu = inject<ComputedRef<boolean>>('enable-task-list-context-menu', computed(() => true))
const hasTaskListContextMenuSlot = inject<ComputedRef<boolean>>('task-list-context-menu-slot', computed(() => false))
@@ -259,16 +227,9 @@ const slotPayload = computed(() => ({
progressClass: progressClass.value,
}))
// - barColor/color
// - barColor
const leftBorderColor = computed(() => {
// 使resource.color
if (isResourceRow.value) {
const resource = props.task as any
if (resource.color) {
return resource.color
}
}
// 使barColor
// barColor使
if (props.task.barColor) {
return props.task.barColor
}
@@ -277,12 +238,10 @@ const leftBorderColor = computed(() => {
})
//
const customBorderStyle = computed((): StyleValue => {
const customBorderStyle = computed(() => {
if (leftBorderColor.value) {
return {
borderLeftColor: `${leftBorderColor.value} !important` as any,
borderLeftWidth: '3px',
borderLeftStyle: 'solid' as const,
borderLeftColor: leftBorderColor.value
}
}
return {}
@@ -317,7 +276,7 @@ const assigneeDisplayData = computed(() => {
return {
avatars: displayAvatars,
nameText,
hasMultiple: displayAvatars.length > 1,
hasMultiple: displayAvatars.length > 1
}
})
</script>
@@ -330,7 +289,6 @@ const assigneeDisplayData = computed(() => {
:data-task-id="props.task.id"
:class="{
'task-row-hovered': isHovered,
'task-type-resource': isResourceRow, /* v1.9.0 资源视图始终显示左边框 */
'parent-task': isParentTask,
'milestone-group-row': isMilestoneGroup,
'task-type-story': isStoryTask,
@@ -338,7 +296,7 @@ const assigneeDisplayData = computed(() => {
'task-type-milestone': isMilestoneTask,
[customRowClass]: customRowClass,
}"
:style="[customRowStyle, customBorderStyle, { height: `${rowHeight}px` }]"
:style="[customRowStyle, customBorderStyle]"
@click="handleRowClick"
@dblclick="handleTaskRowDoubleClick"
@mousedown="handleMouseDown"
@@ -400,40 +358,11 @@ const assigneeDisplayData = computed(() => {
<!-- 叶子节点的占位空间无折叠按钮且无图标显示时 -->
<span
v-if="!isParentTask && !isMilestoneGroup && showTaskIcon === false && !isResourceRow"
v-if="!isParentTask && !isMilestoneGroup && showTaskIcon === false"
class="leaf-spacer"
></span>
<!-- v1.9.0 资源视图直接显示资源名称 -->
<div v-if="isResourceRow" class="resource-row-name">
<!-- v1.9.0 资源超载警示图标 -->
<svg
v-if="isResourceOverloadedComputed"
class="resource-warning-icon"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 2L2 20h20L12 2z"
fill="var(--gantt-danger, #f56c6c)"
/>
<path
d="M12 8v6M12 16h.01"
stroke="#fff"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
<div v-if="(props.task as any).avatar" class="resource-avatar">
<img :src="(props.task as any).avatar" :alt="(props.task as any).name" />
</div>
<span class="resource-name-text">{{ (props.task as any).name || '-' }}</span>
</div>
<!-- 任务视图正常渲染 -->
<TaskRowNameContent
v-else
:task="props.task"
:is-parent-task="isParentTask"
:has-children="hasChildren"
@@ -488,12 +417,7 @@ const assigneeDisplayData = computed(() => {
{{ column.formatter(props.task, column) }}
</template>
<!-- v1.9.0 资源视图使用formatter或直接显示资源属性 -->
<template v-else-if="isResourceRow">
{{ (props.task as any)[column.key] || '-' }}
</template>
<!-- 优先级3: 内置列类型渲染任务视图 -->
<!-- 优先级3: 内置列类型渲染 -->
<!-- 前置任务列 -->
<template v-else-if="column.key === 'predecessor'">
{{ formatPredecessorDisplay(props.task.predecessor) }}
@@ -610,26 +534,30 @@ const assigneeDisplayData = computed(() => {
.task-row {
display: flex;
border-bottom: 1px solid var(--gantt-border-light);
height: 51px; /* v1.9.1 任务视图51px(包含border-bottom 1px),资源视图动态高度(第一行56px,后续行46px) */
height: 51px; /* 修改为51px,与Timeline中的task-row高度保持一致,包含border-bottom 1px */
box-sizing: border-box; /* 确保border包含在高度计算中 */
background: var(--gantt-bg-primary);
align-items: center;
color: var(--gantt-text-secondary);
cursor: pointer;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
transition: all 0.3s ease;
transform: scale(1);
transform-origin: 5px center; /* 从左侧偏右5px的位置作为放大中心 */
z-index: 1;
position: relative;
}
.task-row:hover {
background-color: var(--gantt-bg-hover);
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15);
transform: scale(1.02);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 10;
}
.task-row-hovered {
background-color: var(--gantt-bg-hover) !important;
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15) !important;
transform: scale(1.02) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
z-index: 10 !important;
}
@@ -640,13 +568,15 @@ const assigneeDisplayData = computed(() => {
.task-row.parent-task:hover {
background: var(--gantt-bg-hover-parent, var(--gantt-bg-hover));
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15);
transform: scale(1.02);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
z-index: 10;
}
.task-row.parent-task.task-row-hovered {
background: var(--gantt-bg-hover-parent, var(--gantt-bg-hover)) !important;
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15) !important;
transform: scale(1.02) !important;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2) !important;
z-index: 10 !important;
}
@@ -656,9 +586,12 @@ const assigneeDisplayData = computed(() => {
background: linear-gradient(90deg, var(--gantt-bg-tertiary) 0%, var(--gantt-bg-primary) 100%);
}
.mbox-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15);
ilestone-group-row:hover {
.milestone-group-row:hover {
background: linear-gradient(90deg, var(--gantt-bg-hover-parent) 0%, var(--gantt-bg-hover) 100%);
transform: scale(1.02);
box-shadow:
0 6px 16px rgba(245, 108, 108, 0.3),
0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 10;
border-left-color: var(--gantt-danger, #f56c6c);
border-left-width: 4px; /* 悬停时边框稍微加粗 */
@@ -677,66 +610,49 @@ const assigneeDisplayData = computed(() => {
border-left: 3px solid var(--gantt-danger, #f56c6c);
}
/* v1.9.0 资源类型左边框颜色 */
.task-type-resource {
border-left: 3px solid var(--gantt-success, #67c23a);
}
/* 任务类型悬停时保持左边框,无需加粗 */
/* 任务类型悬停时保持并增强左边框 */
.task-type-story:hover {
border-left: 3px solid var(--gantt-primary, #409eff);
border-left: 5px solid var(--gantt-primary, #409eff);
}
.task-type-task:hover {
border-left: 3px solid var(--gantt-warning, #e6a23c);
border-left: 5px solid var(--gantt-warning, #e6a23c);
}
.task-type-milestone:hover {
border-left: 3px solid var(--gantt-danger, #f56c6c);
}
.task-type-resource:hover {
border-left: 3px solid var(--gantt-success, #67c23a);
border-left: 5px solid var(--gantt-danger, #f56c6c);
}
/* 悬停状态下的左边框保持 */
.task-row-hovered.task-type-story {
border-left: 3px solid var(--gantt-primary, #409eff) !important;
border-left: 5px solid var(--gantt-primary, #409eff) !important;
}
.task-row-hovered.task-type-task {
border-left: 3px solid var(--gantt-warning, #e6a23c) !important;
border-left: 5px solid var(--gantt-warning, #e6a23c) !important;
}
.task-row-hovered.task-type-milestone {
border-left: 3px solid var(--gantt-danger, #f56c6c) !important;
border-left: 5px solid var(--gantt-danger, #f56c6c) !important;
}
.task-row-hovered.task-type-resource {
border-left: 3px solid var(--gantt-success, #67c23a) !important;
}
:global(html[data-theme='dark']) .milestone-group-row {
:global(.gantt-root[data-theme='dark']) .milestone-group-row {
border-left-color: var(--gantt-danger, #f67c7c);
}
/* 暗黑模式下的任务类型左边框颜色 */
:global(html[data-theme='dark']) .task-type-story {
:global(.gantt-root[data-theme='dark']) .task-type-story {
border-left-color: var(--gantt-primary, #7db4f0);
}
:global(html[data-theme='dark']) .task-type-task {
:global(.gantt-root[data-theme='dark']) .task-type-task {
border-left-color: var(--gantt-warning, #f0b83c);
}
:global(html[data-theme='dark']) .task-type-milestone {
:global(.gantt-root[data-theme='dark']) .task-type-milestone {
border-left-color: var(--gantt-danger, #f67c7c);
}
:global(html[data-theme='dark']) .task-type-resource {
border-left-color: var(--gantt-success, #85ce61);
}
.collapse-btn:hover {
background-color: var(--gantt-primary-light);
}
@@ -859,37 +775,47 @@ const assigneeDisplayData = computed(() => {
}
/* 暗黑模式适配 */
:global(html[data-theme='dark']) .milestone-row-icon {
:global(.gantt-root[data-theme='dark']) .milestone-row-icon {
color: var(--gantt-danger, #f67c7c);
}
:global(html[data-theme='dark']) .milestone-empty-col {
:global(.gantt-root[data-theme='dark']) .milestone-empty-col {
color: var(--gantt-text-disabled, #606266);
/* 确保暗黑模式下边框颜色与普通数据行一致 */
border-right-color: var(--gantt-border-light) !important;
}
/* 暗黑模式的悬停效果 */
:global(html[data-theme='dark']) .task-row:hover {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15);
:global(.gantt-root[data-theme='dark']) .task-row:hover {
box-shadow:
0 4px 12px rgba(255, 255, 255, 0.1),
0 2px 8px rgba(0, 0, 0, 0.3);
}
:global(html[data-theme='dark']) .task-row.task-row-hovered {
:global(.gantt-root[data-theme='dark']) .task-row.task-row-hovered {
background-color: var(--gantt-bg-hover) !important;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15) !important;
box-shadow:
0 4px 12px rgba(255, 255, 255, 0.1),
0 2px 8px rgba(0, 0, 0, 0.3) !important;
}
:global(html[data-theme='dark']) .task-row.parent-task:hover {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15);
:global(.gantt-root[data-theme='dark']) .task-row.parent-task:hover {
box-shadow:
0 6px 16px rgba(255, 255, 255, 0.15),
0 2px 8px rgba(0, 0, 0, 0.4);
}
:global(html[data-theme='dark']) .task-row.parent-task.task-row-hovered {
:global(.gantt-root[data-theme='dark']) .task-row.parent-task.task-row-hovered {
background: var(--gantt-bg-hover-parent) !important;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15) !important;
box-shadow:
0 6px 16px rgba(255, 255, 255, 0.15),
0 2px 8px rgba(0, 0, 0, 0.4) !important;
}
:global(html[data-theme='dark']) .milestone-group-row:hover {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15);
:global(.gantt-root[data-theme='dark']) .milestone-group-row:hover {
box-shadow:
0 6px 16px rgba(246, 124, 124, 0.4),
0 2px 8px rgba(255, 255, 255, 0.1);
}
/* TaskRow拖拽样式 */
@@ -909,57 +835,11 @@ const assigneeDisplayData = computed(() => {
background-color: rgba(64, 158, 255, 0.05) !important;
}
/* v1.9.0 资源视图行样式 */
.resource-row-name {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
color: var(--gantt-text-primary);
}
/* v1.9.0 资源超载警示图标 */
.resource-warning-icon {
width: 18px;
height: 18px;
flex-shrink: 0;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.resource-avatar {
width: 28px;
height: 28px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.resource-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.resource-name-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:global(html[data-theme='dark']) .task-row-drop-target.drop-after {
:global(.gantt-root[data-theme='dark']) .task-row-drop-target.drop-after {
background-color: rgba(125, 180, 240, 0.1) !important;
}
:global(html[data-theme='dark']) .task-row-drop-target.drop-child {
:global(.gantt-root[data-theme='dark']) .task-row-drop-target.drop-child {
background-color: rgba(125, 180, 240, 0.1) !important;
}
@@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts">
interface Props {
isMilestoneGroup: boolean
isParentTask: boolean
@@ -75,7 +75,7 @@ withDefaults(defineProps<Props>(), {
}
/* 暗黑模式下的里程碑图标 */
:global(html[data-theme='dark']) .milestone-group-icon {
:global(.gantt-root[data-theme='dark']) .milestone-group-icon {
color: var(--gantt-danger, #f67c7c);
fill: var(--gantt-danger, #f67c7c);
}
+60 -60
View File
@@ -6011,11 +6011,11 @@ const handleAddSuccessor = (task: Task) => {
}
/* 暗色主题下的旗帜样式 */
:global(html[data-theme='dark']) .flag-pole {
:global(.gantt-root[data-theme='dark']) .flag-pole {
background-color: var(--gantt-primary-light, #66b1ff);
}
:global(html[data-theme='dark']) .flag-content {
:global(.gantt-root[data-theme='dark']) .flag-content {
background-color: var(--gantt-primary-light, #66b1ff);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
@@ -6264,72 +6264,72 @@ const handleAddSuccessor = (task: Task) => {
}
/* 暗色主题支持 */
:global(html[data-theme='dark']) .timeline {
:global(.gantt-root[data-theme='dark']) .timeline {
background: var(--gantt-bg-primary, #3a3a3a) !important;
color: var(--gantt-text-primary, #e5e5e5) !important;
}
:global(html[data-theme='dark']) .timeline-header {
:global(.gantt-root[data-theme='dark']) .timeline-header {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
}
:global(html[data-theme='dark']) .timeline-header-row {
:global(.gantt-root[data-theme='dark']) .timeline-header-row {
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-month {
:global(.gantt-root[data-theme='dark']) .timeline-month {
border-right-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .year-month-label {
:global(.gantt-root[data-theme='dark']) .year-month-label {
color: var(--gantt-text-header, #ffffff) !important;
}
:global(html[data-theme='dark']) .timeline-day {
:global(.gantt-root[data-theme='dark']) .timeline-day {
border-right-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .timeline-day.today {
:global(.gantt-root[data-theme='dark']) .timeline-day.today {
background: #1a365d !important;
color: #e3f2fd !important;
}
:global(html[data-theme='dark']) .timeline-day.weekend {
:global(.gantt-root[data-theme='dark']) .timeline-day.weekend {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
color: var(--gantt-text-muted, #b0b0b0) !important;
}
:global(html[data-theme='dark']) .day-label {
:global(.gantt-root[data-theme='dark']) .day-label {
color: var(--gantt-text-header, #ffffff) !important;
}
:global(html[data-theme='dark']) .timeline-body {
:global(.gantt-root[data-theme='dark']) .timeline-body {
background: var(--gantt-bg-primary, #6b6b6b) !important;
}
:global(html[data-theme='dark']) .timeline-body-content {
:global(.gantt-root[data-theme='dark']) .timeline-body-content {
background: var(--gantt-bg-primary, #6b6b6b) !important;
}
:global(html[data-theme='dark']) .day-columns {
:global(.gantt-root[data-theme='dark']) .day-columns {
border-right-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .month-day-columns {
:global(.gantt-root[data-theme='dark']) .month-day-columns {
border-right-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .day-column {
:global(.gantt-root[data-theme='dark']) .day-column {
border-right-color: var(--gantt-border-light, #555555) !important;
/* 非周末列:透明背景,继承timeline-body的背景色 */
}
:global(html[data-theme='dark']) .day-column.today {
:global(.gantt-root[data-theme='dark']) .day-column.today {
border-left-color: var(--gantt-primary, #409eff) !important;
background-color: var(--gantt-primary, #409eff) !important;
}
:global(html[data-theme='dark']) .day-column.today::before {
:global(.gantt-root[data-theme='dark']) .day-column.today::before {
background: linear-gradient(
to bottom,
rgba(64, 158, 255, 0.15) 0%,
@@ -6338,17 +6338,17 @@ const handleAddSuccessor = (task: Task) => {
) !important;
}
:global(html[data-theme='dark']) .day-column.today.weekend {
:global(.gantt-root[data-theme='dark']) .day-column.today.weekend {
background-color: var(--gantt-primary-color, #409eff) !important;
}
/* 暗色主题下的今日定位高亮效果 */
:global(html[data-theme='dark']) .day-column.today-highlight {
:global(.gantt-root[data-theme='dark']) .day-column.today-highlight {
background-color: var(--gantt-primary-color, #409eff) !important;
box-shadow: 0 0 8px rgba(64, 158, 255, 0.6) !important;
}
:global(html[data-theme='dark']) .day-column.today-highlight::before {
:global(.gantt-root[data-theme='dark']) .day-column.today-highlight::before {
background: linear-gradient(
to bottom,
rgba(64, 158, 255, 0.4) 0%,
@@ -6358,23 +6358,23 @@ const handleAddSuccessor = (task: Task) => {
}
/* 暗黑模式下的任务行悬停效果 */
:global(html[data-theme='dark']) .timeline-body .task-row-hovered {
:global(.gantt-root[data-theme='dark']) .timeline-body .task-row-hovered {
background-color: var(--gantt-bg-hover) !important; /* 与TaskList保持一致,使用透明背景 */
/* 降低层级,避免覆盖任务条等元素 */
z-index: 11 !important;
}
/* 确保暗黑模式下子元素能继续响应事件 */
:global(html[data-theme='dark']) .timeline-body .task-row-hovered > * {
:global(.gantt-root[data-theme='dark']) .timeline-body .task-row-hovered > * {
pointer-events: auto !important;
}
/* 暗黑模式下的非工作时间样式 */
:global(html[data-theme='dark']) .timeline-hour-item.non-working-hour {
:global(.gantt-root[data-theme='dark']) .timeline-hour-item.non-working-hour {
background-color: var(--gantt-bg-secondary, #1a1a1a) !important;
}
:global(html[data-theme='dark']) .timeline-hour-item.non-working-hour .hour-label {
:global(.gantt-root[data-theme='dark']) .timeline-hour-item.non-working-hour .hour-label {
color: var(--gantt-text-muted, #b0b0b0) !important;
}
@@ -6540,98 +6540,98 @@ const handleAddSuccessor = (task: Task) => {
}
/* 月度视图暗色主题样式 */
:global(html[data-theme='dark']) .year-row {
:global(.gantt-root[data-theme='dark']) .year-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-year {
:global(.gantt-root[data-theme='dark']) .timeline-year {
border-right-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .year-label {
:global(.gantt-root[data-theme='dark']) .year-label {
color: var(--gantt-text-header, #ffffff) !important;
}
:global(html[data-theme='dark']) .months-row {
:global(.gantt-root[data-theme='dark']) .months-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-month-item {
:global(.gantt-root[data-theme='dark']) .timeline-month-item {
border-right-color: var(--gantt-border-light, #555555) !important;
border-bottom-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .timeline-month-item.today {
:global(.gantt-root[data-theme='dark']) .timeline-month-item.today {
background-color: var(--gantt-primary);
border-left-color: var(--gantt-primary, #409eff) !important;
}
:global(html[data-theme='dark']) .month-label {
:global(.gantt-root[data-theme='dark']) .month-label {
color: var(--gantt-text-header, #ffffff) !important;
}
/* 季度视图暗色主题样式 */
:global(html[data-theme='dark']) .quarters-row {
:global(.gantt-root[data-theme='dark']) .quarters-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-quarter-item {
:global(.gantt-root[data-theme='dark']) .timeline-quarter-item {
border-right-color: var(--gantt-border-light, #555555) !important;
border-bottom-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .timeline-quarter-item.today {
:global(.gantt-root[data-theme='dark']) .timeline-quarter-item.today {
background-color: var(--gantt-primary);
border-left-color: var(--gantt-primary, #409eff) !important;
}
:global(html[data-theme='dark']) .quarter-label {
:global(.gantt-root[data-theme='dark']) .quarter-label {
color: var(--gantt-text-header, #ffffff) !important;
}
:global(html[data-theme='dark']) .quarter-column {
:global(.gantt-root[data-theme='dark']) .quarter-column {
border-right-color: var(--gantt-border-light, #555555) !important;
background-color: var(--gantt-bg-primary, #6b6b6b) !important;
}
:global(html[data-theme='dark']) .quarter-column.today {
:global(.gantt-root[data-theme='dark']) .quarter-column.today {
background-color: var(--gantt-primary, #409eff) !important;
}
:global(html[data-theme='dark']) .month-column {
:global(.gantt-root[data-theme='dark']) .month-column {
border-right-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .month-column.today {
:global(.gantt-root[data-theme='dark']) .month-column.today {
background-color: var(--gantt-primary-color, #409eff);
border-left-color: var(--gantt-primary-color, #409eff) !important;
}
/* 年度视图暗色主题样式 */
:global(html[data-theme='dark']) .half-years-row {
:global(.gantt-root[data-theme='dark']) .half-years-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-half-year-item {
:global(.gantt-root[data-theme='dark']) .timeline-half-year-item {
border-right-color: var(--gantt-border-light, #555555) !important;
border-bottom-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .half-year-label {
:global(.gantt-root[data-theme='dark']) .half-year-label {
color: var(--gantt-text-header, #ffffff) !important;
}
/* 年度视图背景列暗色主题样式 */
:global(html[data-theme='dark']) .half-year-column {
:global(.gantt-root[data-theme='dark']) .half-year-column {
border-right-color: var(--gantt-border-light, #555555) !important;
background-color: var(--gantt-bg-primary, #6b6b6b) !important;
}
:global(html[data-theme='dark']) .half-year-column:hover {
:global(.gantt-root[data-theme='dark']) .half-year-column:hover {
background-color: var(--gantt-bg-hover, rgba(64, 158, 255, 0.1)) !important;
}
@@ -6647,7 +6647,7 @@ const handleAddSuccessor = (task: Task) => {
}
/* 暗黑模式下的年度视图今日标记线 */
:global(html[data-theme='dark']) .today-line-year-view {
:global(.gantt-root[data-theme='dark']) .today-line-year-view {
background-color: var(--gantt-primary, #66b1ff);
box-shadow: 0 0 4px rgba(102, 177, 255, 0.4);
}
@@ -6809,65 +6809,65 @@ const handleAddSuccessor = (task: Task) => {
}
/* 小时视图暗色主题样式 */
:global(html[data-theme='dark']) .date-row {
:global(.gantt-root[data-theme='dark']) .date-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-day-item {
:global(.gantt-root[data-theme='dark']) .timeline-day-item {
border-right-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .date-label {
:global(.gantt-root[data-theme='dark']) .date-label {
color: var(--gantt-text-header, #ffffff) !important;
}
:global(html[data-theme='dark']) .hours-row {
:global(.gantt-root[data-theme='dark']) .hours-row {
background: var(--gantt-bg-secondary, #1a1a1a) !important;
border-bottom-color: var(--gantt-border-medium, #333333) !important;
}
:global(html[data-theme='dark']) .timeline-hour-item {
:global(.gantt-root[data-theme='dark']) .timeline-hour-item {
border-right-color: var(--gantt-border-light, #555555) !important;
border-bottom-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .timeline-hour-item.today {
:global(.gantt-root[data-theme='dark']) .timeline-hour-item.today {
background-color: var(--gantt-primary, #1a365d) !important;
color: var(--gantt-text-white, #e3f2fd) !important;
}
:global(html[data-theme='dark']) .hour-label {
:global(.gantt-root[data-theme='dark']) .hour-label {
color: var(--gantt-text-primary, #e5e5e5) !important;
}
:global(html[data-theme='dark']) .timeline-hour-item.today .hour-label {
:global(.gantt-root[data-theme='dark']) .timeline-hour-item.today .hour-label {
color: var(--gantt-text-white, #e3f2fd) !important;
}
:global(html[data-theme='dark']) .hour-column {
:global(.gantt-root[data-theme='dark']) .hour-column {
border-right-color: var(--gantt-border-light, #555555) !important;
}
:global(html[data-theme='dark']) .hour-column.weekend {
:global(.gantt-root[data-theme='dark']) .hour-column.weekend {
background-color: var(--gantt-bg-secondary, #1a1a1a) !important;
}
:global(html[data-theme='dark']) .hour-column.rest-hour {
:global(.gantt-root[data-theme='dark']) .hour-column.rest-hour {
background-color: var(--gantt-bg-secondary, #1a1a1a) !important;
}
:global(html[data-theme='dark']) .hour-column.working-hour {
:global(.gantt-root[data-theme='dark']) .hour-column.working-hour {
background-color: var(--gantt-bg-primary, #6b6b6b) !important;
}
:global(html[data-theme='dark']) .hour-column.today {
:global(.gantt-root[data-theme='dark']) .hour-column.today {
background-color: var(--gantt-primary-color, #409eff) !important;
border-left-color: var(--gantt-primary-color, #409eff) !important;
}
/* 暗色主题:15分钟刻度线样式 */
:global(html[data-theme='dark']) .quarter-line {
:global(.gantt-root[data-theme='dark']) .quarter-line {
background-color: var(--gantt-border-light, #555555) !important;
}
</style>
+10 -16
View File
@@ -36,13 +36,13 @@
}
/* 暗黑主题下 .gantt-btn-default 全局样式 */
:global(html[data-theme='dark']) .gantt-btn-default {
:global(.gantt-root[data-theme='dark']) .gantt-btn-default {
background: var(--gantt-bg-tertiary, #454545) !important;
border-color: var(--gantt-border-color, #dcdfe6) !important;
color: var(--gantt-text-white, #ffffff) !important;
}
:global(html[data-theme='dark']) .gantt-btn-default:hover:not(:disabled) {
:global(.gantt-root[data-theme='dark']) .gantt-btn-default:hover:not(:disabled) {
background: var(--gantt-bg-hover, rgba(255, 255, 255, 0.1)) !important;
border-color: var(--gantt-primary, #409eff) !important;
color: var(--gantt-primary, #409eff) !important;
@@ -82,23 +82,17 @@
}
/* 黑暗模式全局样式 */
html[data-theme='dark'] {
background: #1e1e1e !important;
color-scheme: dark !important;
.gantt-root[data-theme='dark'] {
background: #1e1e1e;
color-scheme: dark;
}
html[data-theme='dark'] body {
background: #1e1e1e !important;
color: #e5e5e5 !important;
}
/* 移除body样式,因为不再全局污染 */
/* 明亮模式全局样式 */
html[data-theme='light'] {
background: #ffffff !important;
color-scheme: light !important;
.gantt-root[data-theme='light'] {
background: #ffffff;
color-scheme: light;
}
html[data-theme='light'] body {
background: #ffffff !important;
color: #333333 !important;
}
/* 移除body样式,因为不再全局污染 */
+1 -1
View File
@@ -38,7 +38,7 @@
}
/* 暗黑主题 */
html[data-theme='dark'] {
.gantt-root[data-theme='dark'] {
/* 背景色 - 大幅提升亮度,接近中性灰 */
--gantt-bg-primary: #6b6b6b; /* Body主体 - 明显更亮,接近中性灰 */
--gantt-bg-secondary: #4b4b4b; /* Header背景 - 中等亮度,保持层次 */