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:
+2
-1
@@ -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/
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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',在onMounted中根据props或系统主题设置
|
||||
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-theme,而不是document.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 主题模式通过inject获取,不再需要props
|
||||
}
|
||||
|
||||
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, // 监听inject的主题变化(computed会自动响应)
|
||||
],
|
||||
() => {
|
||||
// 使用 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)
|
||||
|
||||
@@ -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.documentElement,由GanttChart统一管理
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -332,7 +332,7 @@ const handleThemeToggle = () => {
|
||||
|
||||
// 立即应用主题变化
|
||||
isDarkMode.value = newTheme
|
||||
document.documentElement.setAttribute('data-theme', newTheme ? 'dark' : 'light')
|
||||
// 不再直接设置document.documentElement,由GanttChart统一管理
|
||||
|
||||
// 立即触发主题变化事件
|
||||
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.documentElement,由GanttChart统一管理
|
||||
|
||||
// 添加点击外部关闭下拉菜单的监听
|
||||
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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 默认56px(51 + 5px底部padding)
|
||||
}
|
||||
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
@@ -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
@@ -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样式,因为不再全局污染 */
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
}
|
||||
|
||||
/* 暗黑主题 */
|
||||
html[data-theme='dark'] {
|
||||
.gantt-root[data-theme='dark'] {
|
||||
/* 背景色 - 大幅提升亮度,接近中性灰 */
|
||||
--gantt-bg-primary: #6b6b6b; /* Body主体 - 明显更亮,接近中性灰 */
|
||||
--gantt-bg-secondary: #4b4b4b; /* Header背景 - 中等亮度,保持层次 */
|
||||
|
||||
Reference in New Issue
Block a user