v1.4.2 - enhancements
This commit is contained in:
@@ -5,6 +5,42 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.4.2] - 2025-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- TaskBar增加头像显示
|
||||||
|
- TaskBar展示信息显示可配置
|
||||||
|
- TaskBar长按聚焦高亮
|
||||||
|
- Timeline Range自动优化计算, 提升显示效果
|
||||||
|
- TaskListConfig支持像素和百分比设置方式
|
||||||
|
- 支持拖拽和拉伸TaskBar/Milestone接近边界自动扩充时间轴范围
|
||||||
|
- 支持通过属性配置限制TaskBar/Milestone拖拽和拉伸
|
||||||
|
- 暴露更多外部可以使用属性和事件
|
||||||
|
- 增加NPM包应用示例
|
||||||
|
- Added avatar display on TaskBar
|
||||||
|
- Configurable display information on TaskBar
|
||||||
|
- Long-press to focus and highlight TaskBar
|
||||||
|
- Automatically optimized Timeline Range calculation for better visual performance
|
||||||
|
- TaskListConfig now supports both pixel and percentage-based sizing
|
||||||
|
- Support dragging and stretching TaskBar/Milestone to automatically expand timeline range near boundaries
|
||||||
|
- Support property-based configuration to limit dragging and stretching of TaskBar/Milestone
|
||||||
|
- Expose more external properties and events
|
||||||
|
- Added NPM package application examples
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- 客制化多语言扩展支持
|
||||||
|
- 说明文档更新,可读性更强
|
||||||
|
- 增强Timeline时间轴的拖拽体验,增加防抖保护
|
||||||
|
- Custom multi-language extension support
|
||||||
|
- Documentation updated for better readability
|
||||||
|
- Enhance dragging experience on Timeline, adding debounce protection
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- SonarQube代码质量检查问题修改
|
||||||
|
- 缺陷修复
|
||||||
|
- Modification of SonarQube code quality inspection issues
|
||||||
|
- Bugfix
|
||||||
|
|
||||||
## [1.4.1] - 2025-10-26
|
## [1.4.1] - 2025-10-26
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+1980
-609
File diff suppressed because it is too large
Load Diff
+931
-431
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,7 @@ onMounted(async () => {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 540px; /* 增加Drawer宽度,适中大气 */
|
width: 50%; /* 增加Drawer宽度,适中大气 */
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: var(--gantt-bg-primary, #fff);
|
background: var(--gantt-bg-primary, #fff);
|
||||||
box-shadow: 2px 0 24px rgba(0, 0, 0, 0.1);
|
box-shadow: 2px 0 24px rgba(0, 0, 0, 0.1);
|
||||||
|
|||||||
+9
-3
@@ -31,6 +31,7 @@
|
|||||||
"id": 1101,
|
"id": 1101,
|
||||||
"name": "试验方案设计与<span style='font-weight: bold;color:red;'>伦理审查</span>",
|
"name": "试验方案设计与<span style='font-weight: bold;color:red;'>伦理审查</span>",
|
||||||
"assignee": "方案设计师 李明",
|
"assignee": "方案设计师 李明",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=1",
|
||||||
"startDate": "2025-01-01",
|
"startDate": "2025-01-01",
|
||||||
"endDate": "2025-02-28",
|
"endDate": "2025-02-28",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
"id": 1102,
|
"id": 1102,
|
||||||
"name": "受试者招募与筛选",
|
"name": "受试者招募与筛选",
|
||||||
"assignee": "招募专员 张丽",
|
"assignee": "招募专员 张丽",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=5",
|
||||||
"startDate": "2025-03-01",
|
"startDate": "2025-03-01",
|
||||||
"endDate": "2025-04-30",
|
"endDate": "2025-04-30",
|
||||||
"progress": 90,
|
"progress": 90,
|
||||||
@@ -60,6 +62,7 @@
|
|||||||
"id": 1103,
|
"id": 1103,
|
||||||
"name": "药物给药与安全性监测",
|
"name": "药物给药与安全性监测",
|
||||||
"assignee": "临床医生 Dr. Liu",
|
"assignee": "临床医生 Dr. Liu",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=8",
|
||||||
"startDate": "2025-05-01",
|
"startDate": "2025-05-01",
|
||||||
"endDate": "2025-08-31",
|
"endDate": "2025-08-31",
|
||||||
"progress": 40,
|
"progress": 40,
|
||||||
@@ -92,6 +95,7 @@
|
|||||||
"id": 1201,
|
"id": 1201,
|
||||||
"name": "多中心试验<span style='font-weight: bold; color: blue;'>启动</span>",
|
"name": "多中心试验<span style='font-weight: bold; color: blue;'>启动</span>",
|
||||||
"assignee": "项目经理 王芳",
|
"assignee": "项目经理 王芳",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=10",
|
||||||
"startDate": "2025-09-01",
|
"startDate": "2025-09-01",
|
||||||
"endDate": "2025-11-30",
|
"endDate": "2025-11-30",
|
||||||
"progress": 25,
|
"progress": 25,
|
||||||
@@ -107,6 +111,7 @@
|
|||||||
"id": 1202,
|
"id": 1202,
|
||||||
"name": "患者入组与随机化",
|
"name": "患者入组与随机化",
|
||||||
"assignee": "数据管理员 陈静",
|
"assignee": "数据管理员 陈静",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=20",
|
||||||
"startDate": "2025-12-01",
|
"startDate": "2025-12-01",
|
||||||
"endDate": "2026-03-31",
|
"endDate": "2026-03-31",
|
||||||
"progress": 0,
|
"progress": 0,
|
||||||
@@ -122,6 +127,7 @@
|
|||||||
"id": 1203,
|
"id": 1203,
|
||||||
"name": "疗效评估与数据收集",
|
"name": "疗效评估与数据收集",
|
||||||
"assignee": "统计师 赵磊",
|
"assignee": "统计师 赵磊",
|
||||||
|
"avatar": "https://i.pravatar.cc/150?img=15",
|
||||||
"startDate": "2026-01-01",
|
"startDate": "2026-01-01",
|
||||||
"endDate": "2026-08-31",
|
"endDate": "2026-08-31",
|
||||||
"progress": 0,
|
"progress": 0,
|
||||||
@@ -421,7 +427,7 @@
|
|||||||
"startDate": "2025-08-31",
|
"startDate": "2025-08-31",
|
||||||
"endDate": "2025-08-31",
|
"endDate": "2025-08-31",
|
||||||
"type": "milestone",
|
"type": "milestone",
|
||||||
"icon": "shield-check",
|
"icon": "diamond",
|
||||||
"description": "完成I期临床试验,确认ADX-2024在人体的安全性和耐受性,为II期试验奠定基础。"
|
"description": "完成I期临床试验,确认ADX-2024在人体的安全性和耐受性,为II期试验奠定基础。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -431,7 +437,7 @@
|
|||||||
"startDate": "2026-04-30",
|
"startDate": "2026-04-30",
|
||||||
"endDate": "2026-04-30",
|
"endDate": "2026-04-30",
|
||||||
"type": "milestone",
|
"type": "milestone",
|
||||||
"icon": "bar-chart",
|
"icon": "diamond",
|
||||||
"description": "完成II期临床试验中期疗效分析,评估是否继续进行III期试验。"
|
"description": "完成II期临床试验中期疗效分析,评估是否继续进行III期试验。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -441,7 +447,7 @@
|
|||||||
"startDate": "2027-12-31",
|
"startDate": "2027-12-31",
|
||||||
"endDate": "2027-12-31",
|
"endDate": "2027-12-31",
|
||||||
"type": "milestone",
|
"type": "milestone",
|
||||||
"icon": "trophy",
|
"icon": "diamond",
|
||||||
"description": "完成III期临床试验最终分析,向监管部门提交新药上市申请(NDA)。"
|
"description": "完成III期临床试验最终分析,向监管部门提交新药上市申请(NDA)。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -230,5 +230,43 @@
|
|||||||
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 @Guduxwa</span>",
|
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 @Guduxwa</span>",
|
||||||
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @Guduxwa</span>"
|
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @Guduxwa</span>"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.2",
|
||||||
|
"date": "2025-11-01",
|
||||||
|
"notes": [
|
||||||
|
"TaskBar增加头像显示",
|
||||||
|
"TaskBar展示信息显示可配置",
|
||||||
|
"TaskBar长按聚焦高亮",
|
||||||
|
"Timeline Range自动优化计算, 提升显示效果",
|
||||||
|
"TaskListConfig支持像素和百分比设置方式",
|
||||||
|
"客制化多语言扩展支持",
|
||||||
|
"暴露更多外部可以使用属性和事件",
|
||||||
|
"说明文档更新,可读性更强",
|
||||||
|
"增加NPM包应用示例",
|
||||||
|
"支持拖拽和拉伸TaskBar/Milestone接近边界自动扩充时间轴范围",
|
||||||
|
"支持通过属性配置限制TaskBar/Milestone拖拽和拉伸",
|
||||||
|
"增强Timeline时间轴的拖拽体验,增加防抖保护",
|
||||||
|
"SonarQube代码质量检查问题修改",
|
||||||
|
"缺陷修复",
|
||||||
|
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 @SHENGLONG749/@xu-yiqi/@yfwork的使用及反馈的宝贵意见</span>",
|
||||||
|
|
||||||
|
"Added avatar display on TaskBar",
|
||||||
|
"Configurable display information on TaskBar",
|
||||||
|
"Long-press to focus and highlight TaskBar",
|
||||||
|
"Automatically optimized Timeline Range calculation for better visual performance",
|
||||||
|
"TaskListConfig now supports both pixel and percentage-based sizing",
|
||||||
|
"Custom multi-language extension support",
|
||||||
|
"Expose more external properties and events",
|
||||||
|
"Documentation updated for better readability",
|
||||||
|
"Added NPM package application examples",
|
||||||
|
"Support dragging and stretching TaskBar/Milestone to automatically expand timeline range near boundaries",
|
||||||
|
"Support property-based configuration to limit dragging and stretching of TaskBar/Milestone",
|
||||||
|
"Enhance dragging experience on Timeline, adding debounce protection",
|
||||||
|
"SonarQube code quality inspection issue modification",
|
||||||
|
"Bugfix",
|
||||||
|
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @SHENGLONG749/@xu-yiqi/@yfwork for their valuable use and feedback</span>",
|
||||||
|
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @Guduxwa</span>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules/
|
||||||
|
.dscode/
|
||||||
|
.vscode/
|
||||||
|
dist/
|
||||||
|
.idea/
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Gantt Demo App
|
||||||
|
|
||||||
|
基于 Vite + Vue3 的示例项目,用于本地调试 `jordium-gantt-vue3` 组件包。
|
||||||
|
|
||||||
|
## 调试方式
|
||||||
|
支持两种:
|
||||||
|
1. npm link (推荐开发阶段)
|
||||||
|
2. 打包 tgz 后 file: 引用(已改为 link 模式,可回退)
|
||||||
|
|
||||||
|
## 使用 npm link 调试流程
|
||||||
|
在本地库目录(例如:`D:/02 Project/01 久元鼎晟/300 框架集/02 Plugins/01 Gantt/npm-package`)执行:
|
||||||
|
```powershell
|
||||||
|
# 进入库源码根目录
|
||||||
|
npm install
|
||||||
|
npm run build:lib # 若需要生成 dist
|
||||||
|
npm link # 全局注册该包
|
||||||
|
```
|
||||||
|
回到示例项目目录执行:
|
||||||
|
```powershell
|
||||||
|
npm uninstall jordium-gantt-vue3 -D -S 2>$null | Out-Null # 忽略不存在情形
|
||||||
|
npm install
|
||||||
|
npm link jordium-gantt-vue3
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
如果已经 link 过,只需:
|
||||||
|
```powershell
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
修改库源码后,可在库目录重新 `npm run build:lib`,示例项目无需重新安装,只要刷新浏览器(若是构建产物路径不变)。
|
||||||
|
|
||||||
|
## 回退为 tgz 引用
|
||||||
|
将 `package.json` 中:
|
||||||
|
```json
|
||||||
|
"jordium-gantt-vue3": "1.4.0"
|
||||||
|
```
|
||||||
|
改回:
|
||||||
|
```json
|
||||||
|
"jordium-gantt-vue3": "file:绝对路径/jordium-gantt-vue3-1.4.0.tgz"
|
||||||
|
```
|
||||||
|
然后:
|
||||||
|
```powershell
|
||||||
|
npm install --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
```powershell
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
访问: http://localhost:5173/
|
||||||
|
|
||||||
|
## 占位组件
|
||||||
|
当真实库无法加载时,显示 `StubGantt.vue` 占位以避免报错。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
| 现象 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| Failed to resolve entry | 确认 dist 产物与 main/module 指向正确,重新 build:lib |
|
||||||
|
| 样式缺失 | 确认是否需要单独引入 css,如 `import 'jordium-gantt-vue3/dist/style.css'` |
|
||||||
|
| 代码未热更新 | 重新 build:lib 或使用源码 symlink + Vite optimizeDeps.exclude |
|
||||||
|
|
||||||
|
## License
|
||||||
|
仅内部开发调试使用。
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Gantt Demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Generated
+1302
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "gantt-demo-app",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"element-plus": "^2.11.5",
|
||||||
|
"vue": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.0.0",
|
||||||
|
"vite": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup>
|
||||||
|
import GanttTest from './components/GanttTest.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="app-wrapper">
|
||||||
|
<h1>Gantt 组件演示</h1>
|
||||||
|
<GanttTest />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.app-wrapper { padding: 16px; font-family: system-ui, sans-serif; }
|
||||||
|
h1 { font-size: 20px; margin-bottom: 12px; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,345 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { GanttChart } from 'jordium-gantt-vue3'
|
||||||
|
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
||||||
|
|
||||||
|
const tasks = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '项目启动',
|
||||||
|
startDate: '2025-10-30',
|
||||||
|
endDate: '2025-11-5',
|
||||||
|
progress: 100,
|
||||||
|
department: '管理部',
|
||||||
|
departmentCode: 'D001',
|
||||||
|
type: 'task',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const milestones = ref([
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
name: '项目立项',
|
||||||
|
startDate: '2025-10-29',
|
||||||
|
type: 'milestone',
|
||||||
|
icon: 'diamond',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const customMessages = {
|
||||||
|
'zh-CN': {
|
||||||
|
department: '部门',
|
||||||
|
departmentCode: '部门编号',
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
department: 'Department',
|
||||||
|
departmentCode: 'Department Code',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// const tasks = ref([])
|
||||||
|
|
||||||
|
// const milestones = ref([])
|
||||||
|
const showAddTaskDrawer = ref(false)
|
||||||
|
const showAddMilestoneDialog = ref(false)
|
||||||
|
|
||||||
|
// 定义可动态配置的列
|
||||||
|
const availableColumns = ref<TaskListColumnConfig[]>([
|
||||||
|
{ key: 'startDate', label: '开始日期', visible: true },
|
||||||
|
{ key: 'endDate', label: '结束日期', visible: true },
|
||||||
|
{ key: 'progress', label: '进度', visible: true },
|
||||||
|
{ key: 'department', label: '部门', visible: true, width: 120 },
|
||||||
|
{ key: 'departmentCode', label: '部门编号', visible: true },
|
||||||
|
])
|
||||||
|
|
||||||
|
// TaskList宽度配置示例
|
||||||
|
const taskListConfig = {
|
||||||
|
defaultWidth: 400, // 默认展开宽度400px(默认320px)
|
||||||
|
minWidth: 300, // 最小宽度300px(默认280px)
|
||||||
|
maxWidth: 1200, // 最大宽度1200px(默认1160px)
|
||||||
|
columns: availableColumns.value,
|
||||||
|
}
|
||||||
|
|
||||||
|
// toolbar配置示例
|
||||||
|
const toolbarConfig: ToolbarConfig = {
|
||||||
|
showAddTask: true, // 显示添加任务按钮
|
||||||
|
showAddMilestone: true, // 显示添加里程碑按钮
|
||||||
|
showTodayLocate: true, // 显示定位到今天按钮
|
||||||
|
showExportCsv: true, // 显示导出CSV按钮
|
||||||
|
showExportPdf: true, // 显示导出PDF按钮
|
||||||
|
showLanguage: true, // 显示语言切换按钮
|
||||||
|
showTheme: true, // 显示主题切换按钮
|
||||||
|
showFullscreen: true, // 显示全屏按钮
|
||||||
|
showTimeScale: true, // 显示时间刻度按钮组
|
||||||
|
timeScaleDimensions: [ // 显示所有时间刻度维度
|
||||||
|
'hour', 'day', 'week', 'month', 'quarter', 'year',
|
||||||
|
],
|
||||||
|
defaultTimeScale: 'week', // 默认选中周视图
|
||||||
|
showExpandCollapse: false, // 显示展开/折叠按钮
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTask = ref({
|
||||||
|
name: '',
|
||||||
|
startDate: '',
|
||||||
|
endDate: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const addTask = () => {
|
||||||
|
tasks.value.push({
|
||||||
|
id: tasks.value.length + 1,
|
||||||
|
name: newTask.value.name,
|
||||||
|
startDate: newTask.value.startDate,
|
||||||
|
endDate: newTask.value.endDate,
|
||||||
|
progress: 0,
|
||||||
|
})
|
||||||
|
newTask.value = { name: '', startDate: '', endDate: '' }
|
||||||
|
showAddTaskDrawer.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const addMilestone = () => {
|
||||||
|
milestones.value.push({
|
||||||
|
id: milestones.value.length + 1,
|
||||||
|
name: newTask.value.name,
|
||||||
|
startDate: newTask.value.startDate,
|
||||||
|
progress: 0,
|
||||||
|
type: 'milestone',
|
||||||
|
icon: 'diamond',
|
||||||
|
})
|
||||||
|
console.log('milestones: ', milestones.value)
|
||||||
|
newTask.value = { name: '', startDate: '', endDate: '' }
|
||||||
|
showAddMilestoneDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTaskDblclick = (task) => {
|
||||||
|
alert(`双击任务: ${task.name}`)
|
||||||
|
}
|
||||||
|
const onMilestoneDblclick = (milestone) => {
|
||||||
|
alert(`双击里程碑: ${milestone.name}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div style="height: 600px;">
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
:milestones="milestones"
|
||||||
|
:task-list-config="taskListConfig"
|
||||||
|
:toolbar-config="toolbarConfig"
|
||||||
|
:use-default-drawer="false"
|
||||||
|
:use-default-milestone-dialog="false"
|
||||||
|
:locale-messages="customMessages"
|
||||||
|
@add-task="showAddTaskDrawer = true"
|
||||||
|
@add-milestone="showAddMilestoneDialog = true"
|
||||||
|
@task-double-click="onTaskDblclick"
|
||||||
|
@milestone-double-click="onMilestoneDblclick"
|
||||||
|
>
|
||||||
|
</GanttChart>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义抽屉组件 (原生HTML替代 el-drawer) -->
|
||||||
|
<div v-if="showAddTaskDrawer" class="drawer-overlay" @click="showAddTaskDrawer = false">
|
||||||
|
<div class="drawer-container" @click.stop>
|
||||||
|
<div class="drawer-header">
|
||||||
|
<h3>自定义添加任务组件</h3>
|
||||||
|
<button class="close-btn" @click="showAddTaskDrawer = false">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-body">
|
||||||
|
<div class="form-item">
|
||||||
|
<label>任务名称:</label>
|
||||||
|
<input v-model="newTask.name" type="text" placeholder="请输入任务名称" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-item">
|
||||||
|
<label>开始日期:</label>
|
||||||
|
<input v-model="newTask.startDate" type="date" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-item">
|
||||||
|
<label>结束日期:</label>
|
||||||
|
<input v-model="newTask.endDate" type="date" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-footer">
|
||||||
|
<button class="btn btn-primary" @click="addTask">确定</button>
|
||||||
|
<button class="btn btn-default" @click="showAddTaskDrawer = false">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义Dialog组件基于element plus -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="showAddMilestoneDialog"
|
||||||
|
title="自定义添加里程碑组件 - Element Plus"
|
||||||
|
width="400px"
|
||||||
|
@close="newTask = { name: '', startDate: '', endDate: '' }"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<div class="form-item">
|
||||||
|
<label>任务名称:</label>
|
||||||
|
<el-input v-model="newTask.name" placeholder="请输入任务名称" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-item">
|
||||||
|
<label>日期:</label>
|
||||||
|
<el-date-picker v-model="newTask.startDate" type="date" value-format="YYYY-MM-DD" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="addMilestone">确定</el-button>
|
||||||
|
<el-button @click="showAddMilestoneDialog = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 抽屉遮罩层 */
|
||||||
|
.drawer-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
animation: fadeIn 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉容器 */
|
||||||
|
.drawer-container {
|
||||||
|
width: 400px;
|
||||||
|
max-width: 90%;
|
||||||
|
background: white;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
animation: slideIn 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉头部 */
|
||||||
|
.drawer-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
padding: 0;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉主体 */
|
||||||
|
.drawer-body {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单项 */
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉底部 */
|
||||||
|
.drawer-footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: #409eff;
|
||||||
|
color: white;
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #66b1ff;
|
||||||
|
border-color: #66b1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default {
|
||||||
|
background: white;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from { transform: translateX(100%); }
|
||||||
|
to { transform: translateX(0); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
tasks: { type: Array, default: () => [] },
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="stub-gantt">
|
||||||
|
<p style="margin:0 0 8px;font-weight:600;">(占位) jordium-gantt-vue3 未正确打包,显示 Stub 组件。</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>任务ID</th><th>名称</th><th>开始</th><th>结束</th><th>进度</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="t in tasks" :key="t.id">
|
||||||
|
<td>{{ t.id }}</td>
|
||||||
|
<td>{{ t.text }}</td>
|
||||||
|
<td>{{ t.start }}</td>
|
||||||
|
<td>{{ t.end }}</td>
|
||||||
|
<td>{{ (t.progress*100).toFixed(0) }}%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.stub-gantt { font-size:12px; background:#fffbe6; padding:8px; border:1px dashed #faad14; }
|
||||||
|
.stub-gantt table { width:100%; border-collapse: collapse; }
|
||||||
|
.stub-gantt th, .stub-gantt td { border:1px solid #ddd; padding:4px; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
task: any
|
||||||
|
type: string // 'task-row' | 'task-bar'
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
|
// console.error('props', props)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="html-content-card">
|
||||||
|
<div v-if="props.type === 'task-row'" class="task-row" v-html="props.task.thumbnail" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.html-content-card {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-row {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 4px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-bar {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
height: 20px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(ElementPlus)
|
||||||
|
app.mount('#app')
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
});
|
||||||
Generated
+122
-105
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "jordium-gantt-vue3",
|
"name": "jordium-gantt-vue3",
|
||||||
"version": "1.3.0",
|
"version": "1.4.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "jordium-gantt-vue3",
|
"name": "jordium-gantt-vue3",
|
||||||
"version": "1.3.0",
|
"version": "1.4.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
@@ -80,9 +80,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
|
||||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
"integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -96,9 +96,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
|
||||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
"integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -112,9 +112,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
"integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -128,9 +128,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
"integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -144,9 +144,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
"integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -160,9 +160,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
"integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -176,9 +176,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
"integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -192,9 +192,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
"integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -208,9 +208,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
|
||||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
"integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -224,9 +224,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
"integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -240,9 +240,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
|
||||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
"integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -256,9 +256,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
|
||||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
"integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -272,9 +272,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
|
||||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
"integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@@ -288,9 +288,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
|
||||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
"integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -304,9 +304,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
|
||||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
"integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -320,9 +320,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
|
||||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
"integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -336,9 +336,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
"integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -352,9 +352,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
"integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -368,9 +368,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
"integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -384,9 +384,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
"integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -400,9 +400,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
"integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -415,10 +415,26 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.25.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
|
||||||
|
"integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
"integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -432,9 +448,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
|
||||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
"integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -448,9 +464,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
|
||||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
"integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -464,9 +480,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
|
||||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
"integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1736,9 +1752,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.11",
|
||||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
|
||||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
"integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1748,31 +1764,32 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.25.5",
|
"@esbuild/aix-ppc64": "0.25.11",
|
||||||
"@esbuild/android-arm": "0.25.5",
|
"@esbuild/android-arm": "0.25.11",
|
||||||
"@esbuild/android-arm64": "0.25.5",
|
"@esbuild/android-arm64": "0.25.11",
|
||||||
"@esbuild/android-x64": "0.25.5",
|
"@esbuild/android-x64": "0.25.11",
|
||||||
"@esbuild/darwin-arm64": "0.25.5",
|
"@esbuild/darwin-arm64": "0.25.11",
|
||||||
"@esbuild/darwin-x64": "0.25.5",
|
"@esbuild/darwin-x64": "0.25.11",
|
||||||
"@esbuild/freebsd-arm64": "0.25.5",
|
"@esbuild/freebsd-arm64": "0.25.11",
|
||||||
"@esbuild/freebsd-x64": "0.25.5",
|
"@esbuild/freebsd-x64": "0.25.11",
|
||||||
"@esbuild/linux-arm": "0.25.5",
|
"@esbuild/linux-arm": "0.25.11",
|
||||||
"@esbuild/linux-arm64": "0.25.5",
|
"@esbuild/linux-arm64": "0.25.11",
|
||||||
"@esbuild/linux-ia32": "0.25.5",
|
"@esbuild/linux-ia32": "0.25.11",
|
||||||
"@esbuild/linux-loong64": "0.25.5",
|
"@esbuild/linux-loong64": "0.25.11",
|
||||||
"@esbuild/linux-mips64el": "0.25.5",
|
"@esbuild/linux-mips64el": "0.25.11",
|
||||||
"@esbuild/linux-ppc64": "0.25.5",
|
"@esbuild/linux-ppc64": "0.25.11",
|
||||||
"@esbuild/linux-riscv64": "0.25.5",
|
"@esbuild/linux-riscv64": "0.25.11",
|
||||||
"@esbuild/linux-s390x": "0.25.5",
|
"@esbuild/linux-s390x": "0.25.11",
|
||||||
"@esbuild/linux-x64": "0.25.5",
|
"@esbuild/linux-x64": "0.25.11",
|
||||||
"@esbuild/netbsd-arm64": "0.25.5",
|
"@esbuild/netbsd-arm64": "0.25.11",
|
||||||
"@esbuild/netbsd-x64": "0.25.5",
|
"@esbuild/netbsd-x64": "0.25.11",
|
||||||
"@esbuild/openbsd-arm64": "0.25.5",
|
"@esbuild/openbsd-arm64": "0.25.11",
|
||||||
"@esbuild/openbsd-x64": "0.25.5",
|
"@esbuild/openbsd-x64": "0.25.11",
|
||||||
"@esbuild/sunos-x64": "0.25.5",
|
"@esbuild/openharmony-arm64": "0.25.11",
|
||||||
"@esbuild/win32-arm64": "0.25.5",
|
"@esbuild/sunos-x64": "0.25.11",
|
||||||
"@esbuild/win32-ia32": "0.25.5",
|
"@esbuild/win32-arm64": "0.25.11",
|
||||||
"@esbuild/win32-x64": "0.25.5"
|
"@esbuild/win32-ia32": "0.25.11",
|
||||||
|
"@esbuild/win32-x64": "0.25.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jordium-gantt-vue3",
|
"name": "jordium-gantt-vue3",
|
||||||
"version": "1.4.1",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/jordium-gantt-vue3.cjs.js",
|
"main": "dist/jordium-gantt-vue3.cjs.js",
|
||||||
"module": "dist/jordium-gantt-vue3.es.js",
|
"module": "dist/jordium-gantt-vue3.es.js",
|
||||||
|
|||||||
@@ -9,12 +9,48 @@ const __dirname = path.dirname(__filename)
|
|||||||
|
|
||||||
// 构建后生成CSS导出文件
|
// 构建后生成CSS导出文件
|
||||||
function generateCssExport() {
|
function generateCssExport() {
|
||||||
const distDir = path.resolve(__dirname, '../npm-package/dist')
|
const distDir = path.resolve(__dirname, '../../npm-package/dist')
|
||||||
const cssFile = path.join(distDir, 'assets/jordium-gantt-vue3.css')
|
const assetsDir = path.join(distDir, 'assets')
|
||||||
|
const expectedCssFile = path.join(assetsDir, 'jordium-gantt-vue3.css')
|
||||||
const outputFile = path.join(distDir, 'jordium-gantt-vue3-styles.js')
|
const outputFile = path.join(distDir, 'jordium-gantt-vue3-styles.js')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(cssFile)) {
|
// 首先尝试查找固定名称的CSS文件
|
||||||
|
if (fs.existsSync(expectedCssFile)) {
|
||||||
|
const cssContent = fs.readFileSync(expectedCssFile, 'utf8')
|
||||||
|
|
||||||
|
// 生成JS导出文件
|
||||||
|
const jsContent = `// CSS styles for jordium-gantt-vue3
|
||||||
|
// Auto-generated file - do not edit manually
|
||||||
|
export const ganttStyles = ${JSON.stringify(cssContent)}
|
||||||
|
export default ganttStyles
|
||||||
|
`
|
||||||
|
|
||||||
|
fs.writeFileSync(outputFile, jsContent, 'utf8')
|
||||||
|
console.log('✅ Generated CSS export file:', outputFile)
|
||||||
|
console.log('📄 Source CSS file:', expectedCssFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果固定名称文件不存在,检查assets目录
|
||||||
|
if (!fs.existsSync(assetsDir)) {
|
||||||
|
console.warn('⚠️ Assets directory not found:', assetsDir)
|
||||||
|
console.warn('ℹ️ This is normal for library builds - CSS is typically inlined in JS files')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找任何CSS文件
|
||||||
|
const files = fs.readdirSync(assetsDir)
|
||||||
|
const cssFiles = files.filter(file => file.endsWith('.css'))
|
||||||
|
|
||||||
|
if (cssFiles.length === 0) {
|
||||||
|
console.warn('⚠️ No CSS files found in:', assetsDir)
|
||||||
|
console.warn('ℹ️ This is normal for library builds - CSS is typically inlined in JS files')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用第一个找到的CSS文件
|
||||||
|
const cssFile = path.join(assetsDir, cssFiles[0])
|
||||||
const cssContent = fs.readFileSync(cssFile, 'utf8')
|
const cssContent = fs.readFileSync(cssFile, 'utf8')
|
||||||
|
|
||||||
// 生成JS导出文件
|
// 生成JS导出文件
|
||||||
@@ -26,9 +62,7 @@ export default ganttStyles
|
|||||||
|
|
||||||
fs.writeFileSync(outputFile, jsContent, 'utf8')
|
fs.writeFileSync(outputFile, jsContent, 'utf8')
|
||||||
console.log('✅ Generated CSS export file:', outputFile)
|
console.log('✅ Generated CSS export file:', outputFile)
|
||||||
} else {
|
console.log('📄 Source CSS file:', cssFile)
|
||||||
console.warn('⚠️ CSS file not found:', cssFile)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to generate CSS export:', error)
|
console.error('❌ Failed to generate CSS export:', error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
|
|||||||
@@ -1,6 +1,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { useI18n } from '../composables/useI18n'
|
import { useI18n } from '../composables/useI18n'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue?: string | [string, string]
|
||||||
|
type?: 'date' | 'daterange' | 'datetime'
|
||||||
|
placeholder?: string
|
||||||
|
startPlaceholder?: string
|
||||||
|
endPlaceholder?: string
|
||||||
|
disabled?: boolean
|
||||||
|
clearable?: boolean
|
||||||
|
size?: 'small' | 'default' | 'large'
|
||||||
|
format?: string
|
||||||
|
valueFormat?: string
|
||||||
|
rangeSeparator?: string
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
modelValue: '',
|
modelValue: '',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
@@ -24,20 +39,6 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
interface Props {
|
|
||||||
modelValue?: string | [string, string]
|
|
||||||
type?: 'date' | 'daterange'
|
|
||||||
placeholder?: string
|
|
||||||
startPlaceholder?: string
|
|
||||||
endPlaceholder?: string
|
|
||||||
disabled?: boolean
|
|
||||||
clearable?: boolean
|
|
||||||
size?: 'small' | 'default' | 'large'
|
|
||||||
format?: string
|
|
||||||
valueFormat?: string
|
|
||||||
rangeSeparator?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// 内部状态
|
// 内部状态
|
||||||
const isFocused = ref(false)
|
const isFocused = ref(false)
|
||||||
const isHovered = ref(false)
|
const isHovered = ref(false)
|
||||||
@@ -54,6 +55,12 @@ const minuteListRef = ref<HTMLElement>()
|
|||||||
const blurTimer = ref<number | null>(null)
|
const blurTimer = ref<number | null>(null)
|
||||||
const positionUpdateKey = ref(0) // 用于强制重新计算位置
|
const positionUpdateKey = ref(0) // 用于强制重新计算位置
|
||||||
|
|
||||||
|
// 保存打开选择器时的原始值(用于取消时恢复)
|
||||||
|
const originalSingleValue = ref('')
|
||||||
|
const originalSelectedTime = ref('12:00')
|
||||||
|
const originalStartValue = ref('')
|
||||||
|
const originalEndValue = ref('')
|
||||||
|
|
||||||
// 当前显示的年月
|
// 当前显示的年月
|
||||||
const currentYear = ref(new Date().getFullYear())
|
const currentYear = ref(new Date().getFullYear())
|
||||||
const currentMonth = ref(new Date().getMonth())
|
const currentMonth = ref(new Date().getMonth())
|
||||||
@@ -80,10 +87,12 @@ const formatDisplayDateTime = (dateStr: string, timeStr: string) => {
|
|||||||
const date = new Date(dateStr)
|
const date = new Date(dateStr)
|
||||||
if (isNaN(date.getTime())) return ''
|
if (isNaN(date.getTime())) return ''
|
||||||
|
|
||||||
const dateFormat = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`
|
// 使用 format 属性来决定日期格式
|
||||||
|
const separator = props.format.includes('-') ? '-' : '/'
|
||||||
|
const dateFormat = `${date.getFullYear()}${separator}${String(date.getMonth() + 1).padStart(2, '0')}${separator}${String(date.getDate()).padStart(2, '0')}`
|
||||||
|
|
||||||
// 总是显示时间部分
|
// 仅在 datetime 模式下显示时间部分
|
||||||
if (timeStr) {
|
if (props.type === 'datetime' && timeStr) {
|
||||||
return `${dateFormat} ${timeStr}`
|
return `${dateFormat} ${timeStr}`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +111,7 @@ const displayValue = computed(() => {
|
|||||||
}
|
}
|
||||||
return start || end || ''
|
return start || end || ''
|
||||||
}
|
}
|
||||||
|
// 对于单日期/日期时间,使用内部状态来显示(即使未确认也显示)
|
||||||
return singleValue.value ? formatDisplayDateTime(singleValue.value, selectedTime.value) : ''
|
return singleValue.value ? formatDisplayDateTime(singleValue.value, selectedTime.value) : ''
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -217,6 +227,15 @@ const togglePicker = () => {
|
|||||||
if (props.disabled) return
|
if (props.disabled) return
|
||||||
showPicker.value = !showPicker.value
|
showPicker.value = !showPicker.value
|
||||||
if (showPicker.value) {
|
if (showPicker.value) {
|
||||||
|
// 保存打开时的原始值
|
||||||
|
if (props.type === 'daterange') {
|
||||||
|
originalStartValue.value = startValue.value
|
||||||
|
originalEndValue.value = endValue.value
|
||||||
|
} else {
|
||||||
|
originalSingleValue.value = singleValue.value
|
||||||
|
originalSelectedTime.value = selectedTime.value
|
||||||
|
}
|
||||||
|
|
||||||
// 记录当前滚动位置
|
// 记录当前滚动位置
|
||||||
lastScrollPosition.x = window.scrollX
|
lastScrollPosition.x = window.scrollX
|
||||||
lastScrollPosition.y = window.scrollY
|
lastScrollPosition.y = window.scrollY
|
||||||
@@ -246,8 +265,17 @@ const togglePicker = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭日期选择器
|
// 关闭日期选择器(未确认时恢复原始值)
|
||||||
const closePicker = () => {
|
const closePicker = () => {
|
||||||
|
// 如果是 datetime 模式,恢复原始值(因为可能没有点击确认按钮)
|
||||||
|
if (props.type === 'datetime' && showPicker.value) {
|
||||||
|
singleValue.value = originalSingleValue.value
|
||||||
|
selectedTime.value = originalSelectedTime.value
|
||||||
|
} else if (props.type === 'daterange' && showPicker.value) {
|
||||||
|
startValue.value = originalStartValue.value
|
||||||
|
endValue.value = originalEndValue.value
|
||||||
|
}
|
||||||
|
|
||||||
showPicker.value = false
|
showPicker.value = false
|
||||||
showYearPicker.value = false
|
showYearPicker.value = false
|
||||||
showMonthPicker.value = false
|
showMonthPicker.value = false
|
||||||
@@ -354,7 +382,9 @@ const confirmDate = () => {
|
|||||||
// 格式化日期和时间
|
// 格式化日期和时间
|
||||||
const formatDateTime = (dateStr: string) => {
|
const formatDateTime = (dateStr: string) => {
|
||||||
const date = new Date(dateStr)
|
const date = new Date(dateStr)
|
||||||
const dateFormat = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`
|
// 使用 valueFormat 来格式化日期
|
||||||
|
const separator = props.valueFormat.includes('-') ? '-' : '/'
|
||||||
|
const dateFormat = `${date.getFullYear()}${separator}${String(date.getMonth() + 1).padStart(2, '0')}${separator}${String(date.getDate()).padStart(2, '0')}`
|
||||||
return selectedTime.value ? `${dateFormat} ${selectedTime.value}` : dateFormat
|
return selectedTime.value ? `${dateFormat} ${selectedTime.value}` : dateFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,17 +394,27 @@ const confirmDate = () => {
|
|||||||
|
|
||||||
emit('update:modelValue', newValue)
|
emit('update:modelValue', newValue)
|
||||||
emit('change', newValue)
|
emit('change', newValue)
|
||||||
|
|
||||||
|
// 更新原始值,这样关闭时不会恢复
|
||||||
|
originalStartValue.value = startValue.value
|
||||||
|
originalEndValue.value = endValue.value
|
||||||
}
|
}
|
||||||
} else if (singleValue.value) {
|
} else if (singleValue.value) {
|
||||||
// 格式化日期和时间
|
// 格式化日期和时间
|
||||||
const date = new Date(singleValue.value)
|
const date = new Date(singleValue.value)
|
||||||
const dateFormat = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`
|
// 使用 valueFormat 来格式化日期
|
||||||
|
const separator = props.valueFormat.includes('-') ? '-' : '/'
|
||||||
|
const dateFormat = `${date.getFullYear()}${separator}${String(date.getMonth() + 1).padStart(2, '0')}${separator}${String(date.getDate()).padStart(2, '0')}`
|
||||||
const formattedDateTime = selectedTime.value
|
const formattedDateTime = selectedTime.value
|
||||||
? `${dateFormat} ${selectedTime.value}`
|
? `${dateFormat} ${selectedTime.value}`
|
||||||
: dateFormat
|
: dateFormat
|
||||||
|
|
||||||
emit('update:modelValue', formattedDateTime)
|
emit('update:modelValue', formattedDateTime)
|
||||||
emit('change', formattedDateTime)
|
emit('change', formattedDateTime)
|
||||||
|
|
||||||
|
// 更新原始值,这样关闭时不会恢复
|
||||||
|
originalSingleValue.value = singleValue.value
|
||||||
|
originalSelectedTime.value = selectedTime.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认后关闭面板
|
// 确认后关闭面板
|
||||||
@@ -494,7 +534,27 @@ const selectDate = (dateStr: string) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
singleValue.value = dateStr
|
singleValue.value = dateStr
|
||||||
// 不再自动提交,等待用户点击确认按钮
|
|
||||||
|
// 如果 type='date'(纯日期模式),立即提交并关闭
|
||||||
|
if (props.type === 'date') {
|
||||||
|
// 格式化日期
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
const separator = props.valueFormat.includes('-') ? '-' : '/'
|
||||||
|
const formattedDate = `${date.getFullYear()}${separator}${String(date.getMonth() + 1).padStart(2, '0')}${separator}${String(date.getDate()).padStart(2, '0')}`
|
||||||
|
|
||||||
|
// 发送事件
|
||||||
|
emit('update:modelValue', formattedDate)
|
||||||
|
emit('change', formattedDate)
|
||||||
|
|
||||||
|
// 更新原始值,这样关闭时不会恢复
|
||||||
|
originalSingleValue.value = singleValue.value
|
||||||
|
|
||||||
|
// 延迟关闭,让用户看到选中效果
|
||||||
|
setTimeout(() => {
|
||||||
|
closePicker()
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
// 如果 type='datetime',等待用户选择时间并点击确认按钮
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1145,8 +1205,8 @@ const timePickerStyle = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 时间选择器输入框 -->
|
<!-- 时间选择器输入框(仅在 datetime 模式下显示) -->
|
||||||
<div class="el-time-picker-input">
|
<div v-if="type === 'datetime'" class="el-time-picker-input">
|
||||||
<label class="el-time-picker-label" for="time-input">{{ t.time }}:</label>
|
<label class="el-time-picker-label" for="time-input">{{ t.time }}:</label>
|
||||||
<input
|
<input
|
||||||
id="time-input"
|
id="time-input"
|
||||||
@@ -1161,8 +1221,8 @@ const timePickerStyle = computed(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 日期选择器确认按钮 -->
|
<!-- 日期选择器确认按钮(仅在 datetime 模式下显示) -->
|
||||||
<div class="el-date-picker-footer">
|
<div v-if="type === 'datetime'" class="el-date-picker-footer">
|
||||||
<button
|
<button
|
||||||
class="el-date-picker-btn el-date-picker-btn--confirm"
|
class="el-date-picker-btn el-date-picker-btn--confirm"
|
||||||
@click.stop="confirmDate"
|
@click.stop="confirmDate"
|
||||||
|
|||||||
+761
-206
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import { useI18n } from '../composables/useI18n'
|
|||||||
import DatePicker from './DatePicker.vue'
|
import DatePicker from './DatePicker.vue'
|
||||||
import GanttConfirmDialog from './GanttConfirmDialog.vue'
|
import GanttConfirmDialog from './GanttConfirmDialog.vue'
|
||||||
import type { Milestone } from '../models/classes/Milestone'
|
import type { Milestone } from '../models/classes/Milestone'
|
||||||
|
import type { Task } from '../models/classes/Task'
|
||||||
import '../styles/app.css'
|
import '../styles/app.css'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -16,12 +17,13 @@ const props = defineProps<Props>()
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:visible': [visible: boolean]
|
'update:visible': [visible: boolean]
|
||||||
close: []
|
close: []
|
||||||
save: [milestone: Milestone]
|
save: [milestone: Task]
|
||||||
delete: [milestoneId: number]
|
delete: [milestoneId: number]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = reactive<Milestone>({
|
const formData = reactive<Milestone>({
|
||||||
|
id: undefined,
|
||||||
name: '',
|
name: '',
|
||||||
startDate: '',
|
startDate: '',
|
||||||
assignee: '',
|
assignee: '',
|
||||||
@@ -87,6 +89,28 @@ watch(
|
|||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 监听对话框打开,确保新建时重置表单
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(newVisible) => {
|
||||||
|
if (newVisible && !props.milestone) {
|
||||||
|
// 打开对话框且没有传入里程碑(新建模式),重置表单
|
||||||
|
Object.assign(formData, {
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
startDate: '',
|
||||||
|
assignee: '',
|
||||||
|
type: 'milestone',
|
||||||
|
icon: 'diamond',
|
||||||
|
description: '',
|
||||||
|
})
|
||||||
|
// 清空错误
|
||||||
|
errors.name = ''
|
||||||
|
errors.startDate = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// 表单验证
|
// 表单验证
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
errors.name = ''
|
errors.name = ''
|
||||||
@@ -121,7 +145,13 @@ const selectIcon = (icon: string) => {
|
|||||||
// 保存处理
|
// 保存处理
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
emit('save', { ...formData })
|
// 里程碑的 endDate 必须与 startDate 相同
|
||||||
|
const milestoneData = {
|
||||||
|
...formData,
|
||||||
|
endDate: formData.startDate,
|
||||||
|
id: formData.id!, // 确保id不为undefined
|
||||||
|
} as Task
|
||||||
|
emit('save', milestoneData)
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +275,7 @@ const t = (key: string) => {
|
|||||||
id="milestone-date"
|
id="milestone-date"
|
||||||
v-model="formData.startDate"
|
v-model="formData.startDate"
|
||||||
type="date"
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择里程碑日期"
|
placeholder="请选择里程碑日期"
|
||||||
:class="{ error: errors.startDate }"
|
:class="{ error: errors.startDate }"
|
||||||
/>
|
/>
|
||||||
|
|||||||
+262
-147
@@ -3,6 +3,7 @@
|
|||||||
import { computed, ref, onUnmounted } from 'vue'
|
import { computed, ref, onUnmounted } from 'vue'
|
||||||
import type { Milestone } from '../models/classes/Milestone'
|
import type { Milestone } from '../models/classes/Milestone'
|
||||||
import { TimelineScale } from '../models/types/TimelineScale'
|
import { TimelineScale } from '../models/types/TimelineScale'
|
||||||
|
import type { TimelineMonth, TimelineYear, TimelineDay } from '../models/types/TimelineDataTypes'
|
||||||
import { useI18n } from '../composables/useI18n'
|
import { useI18n } from '../composables/useI18n'
|
||||||
import { createLocalDate } from '../utils/predecessorUtils'
|
import { createLocalDate } from '../utils/predecessorUtils'
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -21,29 +22,30 @@ const t = (key: string): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
date: string // 里程碑日期
|
date: string
|
||||||
|
milestone: Milestone
|
||||||
|
name?: string
|
||||||
rowHeight: number
|
rowHeight: number
|
||||||
dayWidth: number
|
dayWidth: number
|
||||||
startDate: Date
|
startDate: Date
|
||||||
name?: string
|
timelineStart: Date
|
||||||
milestone?: Milestone // 完整的里程碑数据
|
timelineEnd: Date
|
||||||
// 新增:用于边界粘性显示的滚动位置信息
|
|
||||||
scrollLeft?: number
|
scrollLeft?: number
|
||||||
containerWidth?: number
|
containerWidth?: number
|
||||||
// 新增:里程碑推挤效果所需的信息
|
milestoneId?: number
|
||||||
milestoneId?: string | number // 唯一标识符
|
|
||||||
otherMilestones?: Array<{
|
otherMilestones?: Array<{
|
||||||
id: string | number
|
id: number
|
||||||
left: number
|
|
||||||
originalLeft: number // 原始位置(不考虑停靠)
|
|
||||||
isSticky: boolean
|
isSticky: boolean
|
||||||
stickyPosition: 'left' | 'right' | 'none'
|
stickyPosition: string
|
||||||
priority: number // 推挤优先级
|
left: number
|
||||||
}> // 其他里程碑的位置信息
|
originalLeft: number
|
||||||
// 新增:时间线数据,用于精确计算subDays定位
|
priority: number
|
||||||
timelineData?: unknown[]
|
}>
|
||||||
// 新增:当前时间刻度
|
currentTimeScale?: TimelineScale | null
|
||||||
currentTimeScale?: TimelineScale
|
timelineData?: TimelineMonth[] | TimelineYear[] | TimelineDay[]
|
||||||
|
periodWidth: number
|
||||||
|
isInHighlightMode?: boolean
|
||||||
|
allowDragAndResize: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拖拽相关状态
|
// 拖拽相关状态
|
||||||
@@ -102,8 +104,112 @@ const formatDateToLocalString = (date: Date): string => {
|
|||||||
return `${year}-${month}-${day}`
|
return `${year}-${month}-${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据像素位置计算日期(支持月度、季度视图)
|
||||||
|
const calculateDateFromPosition = (
|
||||||
|
pixelPosition: number,
|
||||||
|
timelineData: Array<{
|
||||||
|
year: number
|
||||||
|
month: number
|
||||||
|
startDate: Date
|
||||||
|
endDate: Date
|
||||||
|
days?: Array<{ date: Date; day: number }>
|
||||||
|
monthData?: { dayCount: number }
|
||||||
|
}>,
|
||||||
|
timeScale: TimelineScale,
|
||||||
|
): Date | null => {
|
||||||
|
if (!timelineData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let cumulativePosition = 0
|
||||||
|
|
||||||
|
if (timeScale === TimelineScale.DAY) {
|
||||||
|
// 日视图:基于 days 数组
|
||||||
|
for (const periodData of timelineData) {
|
||||||
|
const days = periodData.days || []
|
||||||
|
const periodWidth = days.length * 30 // 日视图每天30px
|
||||||
|
|
||||||
|
if (pixelPosition >= cumulativePosition && pixelPosition < cumulativePosition + periodWidth) {
|
||||||
|
const relativePosition = pixelPosition - cumulativePosition
|
||||||
|
const dayIndex = Math.floor(relativePosition / 30)
|
||||||
|
|
||||||
|
if (dayIndex >= 0 && dayIndex < days.length) {
|
||||||
|
return new Date(days[dayIndex].date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulativePosition += periodWidth
|
||||||
|
}
|
||||||
|
} else if (timeScale === TimelineScale.MONTH) {
|
||||||
|
// 月视图:每个月60px
|
||||||
|
for (const periodData of timelineData) {
|
||||||
|
const monthWidth = 60
|
||||||
|
|
||||||
|
if (pixelPosition >= cumulativePosition && pixelPosition < cumulativePosition + monthWidth) {
|
||||||
|
const relativePosition = pixelPosition - cumulativePosition
|
||||||
|
const daysInMonth = periodData.monthData?.dayCount || 30
|
||||||
|
const dayWidth = monthWidth / daysInMonth
|
||||||
|
|
||||||
|
const dayIndex = Math.floor(relativePosition / dayWidth)
|
||||||
|
const day = Math.min(dayIndex + 1, daysInMonth)
|
||||||
|
|
||||||
|
return new Date(periodData.year, periodData.month - 1, day)
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulativePosition += monthWidth
|
||||||
|
}
|
||||||
|
} else if (timeScale === TimelineScale.QUARTER) {
|
||||||
|
// 季度视图:每个季度60px
|
||||||
|
for (const periodData of timelineData) {
|
||||||
|
const quarters = (periodData as Record<string, unknown>).quarters as Array<{
|
||||||
|
quarter: number
|
||||||
|
startDate: Date
|
||||||
|
endDate: Date
|
||||||
|
}> || []
|
||||||
|
|
||||||
|
for (const quarter of quarters) {
|
||||||
|
const quarterStart = new Date(quarter.startDate)
|
||||||
|
const quarterEnd = new Date(quarter.endDate)
|
||||||
|
const quarterWidth = 60
|
||||||
|
|
||||||
|
if (
|
||||||
|
pixelPosition >= cumulativePosition &&
|
||||||
|
pixelPosition < cumulativePosition + quarterWidth
|
||||||
|
) {
|
||||||
|
const relativePosition = pixelPosition - cumulativePosition
|
||||||
|
const daysInQuarter = Math.ceil(
|
||||||
|
(quarterEnd.getTime() - quarterStart.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
|
)
|
||||||
|
const dayWidth = quarterWidth / daysInQuarter
|
||||||
|
|
||||||
|
const dayIndex = Math.floor(relativePosition / dayWidth)
|
||||||
|
|
||||||
|
const resultDate = new Date(quarterStart)
|
||||||
|
resultDate.setDate(resultDate.getDate() + dayIndex)
|
||||||
|
|
||||||
|
return resultDate
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulativePosition += quarterWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// 拖拽事件处理 - 使用相对位置拖拽方案
|
// 拖拽事件处理 - 使用相对位置拖拽方案
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
// 如果禁用了拖拽和拉伸,直接返回
|
||||||
|
if (props.allowDragAndResize === false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 年度视图禁止拖拽(与TaskBar保持一致)
|
||||||
|
if (props.currentTimeScale === TimelineScale.YEAR) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是停靠状态或被推出边界,禁止拖拽
|
// 如果是停靠状态或被推出边界,禁止拖拽
|
||||||
if (
|
if (
|
||||||
milestoneVisibility.value.isSticky ||
|
milestoneVisibility.value.isSticky ||
|
||||||
@@ -162,7 +268,42 @@ const handleMouseMove = (e: MouseEvent) => {
|
|||||||
isDragging.value = true
|
isDragging.value = true
|
||||||
|
|
||||||
const newLeft = Math.max(0, dragStartLeft.value + deltaX)
|
const newLeft = Math.max(0, dragStartLeft.value + deltaX)
|
||||||
const newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)
|
let newStartDate: Date
|
||||||
|
|
||||||
|
// 根据当前时间刻度使用不同的日期计算方法
|
||||||
|
if (
|
||||||
|
props.currentTimeScale === TimelineScale.MONTH ||
|
||||||
|
props.currentTimeScale === TimelineScale.QUARTER ||
|
||||||
|
props.currentTimeScale === TimelineScale.DAY
|
||||||
|
) {
|
||||||
|
// 月度、季度、日视图:使用 timelineData 精确计算
|
||||||
|
if (props.timelineData && props.currentTimeScale) {
|
||||||
|
const calculatedDate = calculateDateFromPosition(
|
||||||
|
newLeft,
|
||||||
|
props.timelineData as Array<{
|
||||||
|
year: number
|
||||||
|
month: number
|
||||||
|
startDate: Date
|
||||||
|
endDate: Date
|
||||||
|
days?: Array<{ date: Date; day: number }>
|
||||||
|
monthData?: { dayCount: number }
|
||||||
|
}>,
|
||||||
|
props.currentTimeScale,
|
||||||
|
)
|
||||||
|
if (calculatedDate) {
|
||||||
|
newStartDate = calculatedDate
|
||||||
|
} else {
|
||||||
|
// 如果计算失败,回退到简单计算
|
||||||
|
newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有 timelineData,回退到简单计算
|
||||||
|
newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他视图(周视图、小时视图):使用原有的简单计算
|
||||||
|
newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)
|
||||||
|
}
|
||||||
|
|
||||||
// 只更新临时数据,不触发事件
|
// 只更新临时数据,不触发事件
|
||||||
tempMilestoneData.value = {
|
tempMilestoneData.value = {
|
||||||
@@ -246,6 +387,11 @@ const handleMilestoneClick = (e: MouseEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断是否应该显示为暗淡(处于高亮模式)
|
||||||
|
const isDimmed = computed(() => {
|
||||||
|
return props.isInHighlightMode === true
|
||||||
|
})
|
||||||
|
|
||||||
// 计算菱形位置 - 考虑拖拽临时数据
|
// 计算菱形位置 - 考虑拖拽临时数据
|
||||||
const milestoneStyle = computed(() => {
|
const milestoneStyle = computed(() => {
|
||||||
const currentMilestoneDate = tempMilestoneData.value?.startDate || props.date
|
const currentMilestoneDate = tempMilestoneData.value?.startDate || props.date
|
||||||
@@ -282,15 +428,8 @@ const milestoneStyle = computed(() => {
|
|||||||
size = Math.min(props.rowHeight, props.dayWidth * 1.2, 24)
|
size = Math.min(props.rowHeight, props.dayWidth * 1.2, 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 年度视图:使用专门的年度位置计算
|
// 小时视图:使用专门的小时位置计算
|
||||||
if (props.currentTimeScale === TimelineScale.YEAR) {
|
if (props.currentTimeScale === TimelineScale.HOUR) {
|
||||||
const centerPosition = calculateYearViewMilestonePosition(milestoneDate, props.startDate)
|
|
||||||
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
|
||||||
} else if (props.currentTimeScale === TimelineScale.QUARTER) {
|
|
||||||
// 季度视图:使用类似年度视图的简单计算方法
|
|
||||||
const centerPosition = calculateQuarterViewMilestonePosition(milestoneDate, props.startDate)
|
|
||||||
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
|
||||||
} else if (props.currentTimeScale === TimelineScale.HOUR) {
|
|
||||||
// 小时视图:精确到小时和分钟的定位
|
// 小时视图:精确到小时和分钟的定位
|
||||||
const centerPosition = calculateHourViewMilestonePosition(milestoneDate, props.startDate)
|
const centerPosition = calculateHourViewMilestonePosition(milestoneDate, props.startDate)
|
||||||
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
||||||
@@ -298,9 +437,12 @@ const milestoneStyle = computed(() => {
|
|||||||
props.timelineData &&
|
props.timelineData &&
|
||||||
props.currentTimeScale &&
|
props.currentTimeScale &&
|
||||||
(props.currentTimeScale === TimelineScale.WEEK ||
|
(props.currentTimeScale === TimelineScale.WEEK ||
|
||||||
props.currentTimeScale === TimelineScale.MONTH)
|
props.currentTimeScale === TimelineScale.MONTH ||
|
||||||
|
props.currentTimeScale === TimelineScale.DAY ||
|
||||||
|
props.currentTimeScale === TimelineScale.QUARTER ||
|
||||||
|
props.currentTimeScale === TimelineScale.YEAR)
|
||||||
) {
|
) {
|
||||||
// 优先使用基于timelineData的精确定位(适用于周视图和月视图)
|
// 优先使用基于timelineData的精确定位(适用于周视图、月视图、日视图、季度视图和年度视图)
|
||||||
const centerPosition = calculateMilestonePositionFromTimelineData(
|
const centerPosition = calculateMilestonePositionFromTimelineData(
|
||||||
milestoneDate,
|
milestoneDate,
|
||||||
props.timelineData,
|
props.timelineData,
|
||||||
@@ -309,7 +451,7 @@ const milestoneStyle = computed(() => {
|
|||||||
|
|
||||||
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
left = centerPosition - size / 2 // 从中心位置偏移到图标左上角
|
||||||
} else {
|
} else {
|
||||||
// 日视图:保持原有逻辑
|
// 其他情况(没有 timelineData):保持原有逻辑
|
||||||
const startDiff = Math.floor(
|
const startDiff = Math.floor(
|
||||||
(milestoneDate.getTime() - props.startDate.getTime()) / (1000 * 60 * 60 * 24),
|
(milestoneDate.getTime() - props.startDate.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
)
|
)
|
||||||
@@ -543,58 +685,6 @@ onUnmounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 年度视图里程碑位置计算函数
|
// 年度视图里程碑位置计算函数
|
||||||
const calculateYearViewMilestonePosition = (targetDate: Date, baseStartDate: Date): number => {
|
|
||||||
const targetYear = targetDate.getFullYear()
|
|
||||||
const baseYear = baseStartDate.getFullYear()
|
|
||||||
|
|
||||||
// 每年的宽度是360px,每半年180px
|
|
||||||
const yearWidth = 360
|
|
||||||
const halfYearWidth = 180
|
|
||||||
|
|
||||||
// 计算目标年份相对于基准年份的偏移
|
|
||||||
const yearOffset = targetYear - baseYear
|
|
||||||
let position = yearOffset * yearWidth
|
|
||||||
|
|
||||||
// 判断是上半年还是下半年
|
|
||||||
const month = targetDate.getMonth() + 1 // getMonth()返回0-11,需要+1
|
|
||||||
|
|
||||||
if (month > 6) {
|
|
||||||
// 下半年,添加半年偏移
|
|
||||||
position += halfYearWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在半年内的具体位置计算
|
|
||||||
let dayOffset = 0
|
|
||||||
let startOfHalfYear: Date
|
|
||||||
|
|
||||||
if (month <= 6) {
|
|
||||||
// 上半年:1-6月
|
|
||||||
startOfHalfYear = new Date(targetYear, 0, 1) // 1月1日
|
|
||||||
} else {
|
|
||||||
// 下半年:7-12月
|
|
||||||
startOfHalfYear = new Date(targetYear, 6, 1) // 7月1日
|
|
||||||
}
|
|
||||||
|
|
||||||
dayOffset = Math.floor((targetDate.getTime() - startOfHalfYear.getTime()) / (1000 * 60 * 60 * 24))
|
|
||||||
|
|
||||||
// 半年大约181-184天,将天数映射到180px的宽度
|
|
||||||
const daysInHalfYear =
|
|
||||||
month <= 6
|
|
||||||
? Math.floor(
|
|
||||||
(new Date(targetYear, 6, 1).getTime() - new Date(targetYear, 0, 1).getTime()) /
|
|
||||||
(1000 * 60 * 60 * 24),
|
|
||||||
)
|
|
||||||
: Math.floor(
|
|
||||||
(new Date(targetYear + 1, 0, 1).getTime() - new Date(targetYear, 6, 1).getTime()) /
|
|
||||||
(1000 * 60 * 60 * 24),
|
|
||||||
)
|
|
||||||
|
|
||||||
const dayPositionInHalfYear = (dayOffset / daysInHalfYear) * halfYearWidth
|
|
||||||
position += dayPositionInHalfYear
|
|
||||||
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小时视图里程碑位置计算 - 精确到小时和分钟
|
// 小时视图里程碑位置计算 - 精确到小时和分钟
|
||||||
const calculateHourViewMilestonePosition = (targetDate: Date, baseStartDate: Date): number => {
|
const calculateHourViewMilestonePosition = (targetDate: Date, baseStartDate: Date): number => {
|
||||||
// 计算基础天数差
|
// 计算基础天数差
|
||||||
@@ -628,81 +718,94 @@ const calculateHourViewMilestonePosition = (targetDate: Date, baseStartDate: Dat
|
|||||||
return totalPosition
|
return totalPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
// 季度视图里程碑位置计算 - 参考年度视图的简单算法
|
|
||||||
const calculateQuarterViewMilestonePosition = (targetDate: Date, baseStartDate: Date): number => {
|
|
||||||
const targetYear = targetDate.getFullYear()
|
|
||||||
const baseYear = baseStartDate.getFullYear()
|
|
||||||
|
|
||||||
// 每年的宽度是240px (4季度 * 60px),每季度60px
|
|
||||||
const yearWidth = 240
|
|
||||||
const quarterWidth = 60
|
|
||||||
|
|
||||||
// 计算目标年份相对于基准年份的偏移
|
|
||||||
const yearOffset = targetYear - baseYear
|
|
||||||
let position = yearOffset * yearWidth
|
|
||||||
|
|
||||||
// 判断是哪个季度
|
|
||||||
const month = targetDate.getMonth() + 1 // getMonth()返回0-11,需要+1
|
|
||||||
let quarter = 1
|
|
||||||
if (month >= 1 && month <= 3) {
|
|
||||||
// Q1: 1-3月
|
|
||||||
} else if (month >= 4 && month <= 6) {
|
|
||||||
quarter = 2 // Q2: 4-6月
|
|
||||||
} else if (month >= 7 && month <= 9) {
|
|
||||||
quarter = 3 // Q3: 7-9月
|
|
||||||
} else {
|
|
||||||
quarter = 4 // Q4: 10-12月
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加季度偏移 (Q1=0px, Q2=60px, Q3=120px, Q4=180px)
|
|
||||||
position += (quarter - 1) * quarterWidth
|
|
||||||
|
|
||||||
// 在季度内的具体位置计算
|
|
||||||
let dayOffset = 0
|
|
||||||
let startOfQuarter: Date
|
|
||||||
let endOfQuarter: Date
|
|
||||||
|
|
||||||
if (quarter === 1) {
|
|
||||||
startOfQuarter = new Date(targetYear, 0, 1) // 1月1日
|
|
||||||
endOfQuarter = new Date(targetYear, 2, 31) // 3月31日
|
|
||||||
} else if (quarter === 2) {
|
|
||||||
startOfQuarter = new Date(targetYear, 3, 1) // 4月1日
|
|
||||||
endOfQuarter = new Date(targetYear, 5, 30) // 6月30日
|
|
||||||
} else if (quarter === 3) {
|
|
||||||
startOfQuarter = new Date(targetYear, 6, 1) // 7月1日
|
|
||||||
endOfQuarter = new Date(targetYear, 8, 30) // 9月30日
|
|
||||||
} else {
|
|
||||||
startOfQuarter = new Date(targetYear, 9, 1) // 10月1日
|
|
||||||
endOfQuarter = new Date(targetYear, 11, 31) // 12月31日
|
|
||||||
}
|
|
||||||
|
|
||||||
dayOffset = Math.floor((targetDate.getTime() - startOfQuarter.getTime()) / (1000 * 60 * 60 * 24))
|
|
||||||
|
|
||||||
// 计算季度内的天数,将天数映射到60px的宽度
|
|
||||||
const endTime = endOfQuarter.getTime()
|
|
||||||
const startTime = startOfQuarter.getTime()
|
|
||||||
const daysInQuarter = Math.floor((endTime - startTime) / (1000 * 60 * 60 * 24)) + 1 // 包含结束日期
|
|
||||||
|
|
||||||
const dayPositionInQuarter = (dayOffset / daysInQuarter) * quarterWidth
|
|
||||||
|
|
||||||
position += dayPositionInQuarter
|
|
||||||
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基于timelineData和subDays精确计算里程碑位置的函数
|
// 基于timelineData和subDays精确计算里程碑位置的函数
|
||||||
const calculateMilestonePositionFromTimelineData = (
|
const calculateMilestonePositionFromTimelineData = (
|
||||||
targetDate: Date,
|
targetDate: Date,
|
||||||
timelineData: any,
|
timelineData: TimelineMonth[] | TimelineYear[] | TimelineDay[],
|
||||||
timeScale: TimelineScale,
|
timeScale: TimelineScale,
|
||||||
) => {
|
) => {
|
||||||
// 回退到原来的逻辑用于其他时间刻度
|
// 回退到原来的逻辑用于其他时间刻度
|
||||||
let cumulativePosition = 0
|
let cumulativePosition = 0
|
||||||
|
|
||||||
for (const periodData of timelineData) {
|
for (const periodData of timelineData) {
|
||||||
if (timeScale === TimelineScale.WEEK) {
|
if (timeScale === TimelineScale.DAY) {
|
||||||
|
// 日视图:处理days数组,返回中心位置
|
||||||
|
// 类型保护:确保 periodData 是 TimelineMonth 并有 days 属性
|
||||||
|
const days = ('days' in periodData && periodData.days) ? periodData.days : []
|
||||||
|
|
||||||
|
for (let i = 0; i < days.length; i++) {
|
||||||
|
const dayData = days[i]
|
||||||
|
const dayDate = new Date(dayData.date)
|
||||||
|
|
||||||
|
// 比较日期(忽略时分秒)
|
||||||
|
if (
|
||||||
|
dayDate.getFullYear() === targetDate.getFullYear() &&
|
||||||
|
dayDate.getMonth() === targetDate.getMonth() &&
|
||||||
|
dayDate.getDate() === targetDate.getDate()
|
||||||
|
) {
|
||||||
|
// 找到目标日期,返回累计位置 + 当前天数索引 * 日宽度 + 半个日宽度(中心)
|
||||||
|
return cumulativePosition + i * 30 + 15 // 日视图每天30px,中心+15px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 累加当前月份所有天数的宽度
|
||||||
|
cumulativePosition += days.length * 30
|
||||||
|
} else if (timeScale === TimelineScale.QUARTER) {
|
||||||
|
// 季度视图:处理years数组,每个year包含quarters
|
||||||
|
// 类型保护:确保 periodData 是 TimelineYear 并有 quarters 属性
|
||||||
|
const quarters = ('quarters' in periodData && periodData.quarters) ? periodData.quarters : []
|
||||||
|
|
||||||
|
for (const quarter of quarters) {
|
||||||
|
const quarterStart = new Date(quarter.startDate)
|
||||||
|
const quarterEnd = new Date(quarter.endDate)
|
||||||
|
|
||||||
|
if (targetDate >= quarterStart && targetDate <= quarterEnd) {
|
||||||
|
// 找到目标日期所在的季度
|
||||||
|
const quarterWidth = 60
|
||||||
|
const daysInQuarter = Math.ceil(
|
||||||
|
(quarterEnd.getTime() - quarterStart.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
|
)
|
||||||
|
const dayWidth = quarterWidth / daysInQuarter
|
||||||
|
const dayInQuarter = Math.ceil(
|
||||||
|
(targetDate.getTime() - quarterStart.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
|
)
|
||||||
|
const finalPosition = cumulativePosition + dayInQuarter * dayWidth + dayWidth / 2
|
||||||
|
|
||||||
|
return finalPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// 累加每季度的宽度
|
||||||
|
cumulativePosition += 60
|
||||||
|
}
|
||||||
|
} else if (timeScale === TimelineScale.YEAR) {
|
||||||
|
// 年度视图:处理years数组,每个year包含halfYears
|
||||||
|
// 类型保护:确保 periodData 是 TimelineYear 并有 halfYears 属性
|
||||||
|
const halfYears = ('halfYears' in periodData && periodData.halfYears) ? periodData.halfYears : []
|
||||||
|
|
||||||
|
for (const halfYear of halfYears) {
|
||||||
|
const halfYearStart = new Date(halfYear.startDate)
|
||||||
|
const halfYearEnd = new Date(halfYear.endDate)
|
||||||
|
|
||||||
|
if (targetDate >= halfYearStart && targetDate <= halfYearEnd) {
|
||||||
|
// 找到目标日期所在的半年
|
||||||
|
const halfYearWidth = 180 // 年度视图每半年180px
|
||||||
|
const daysInHalfYear = Math.ceil(
|
||||||
|
(halfYearEnd.getTime() - halfYearStart.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
|
)
|
||||||
|
const dayWidth = halfYearWidth / daysInHalfYear
|
||||||
|
const dayInHalfYear = Math.ceil(
|
||||||
|
(targetDate.getTime() - halfYearStart.getTime()) / (1000 * 60 * 60 * 24),
|
||||||
|
)
|
||||||
|
return cumulativePosition + dayInHalfYear * dayWidth + dayWidth / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 累加每半年的宽度
|
||||||
|
cumulativePosition += 180
|
||||||
|
}
|
||||||
|
} else if (timeScale === TimelineScale.WEEK) {
|
||||||
// 周视图:处理嵌套的weeks结构,返回中心位置
|
// 周视图:处理嵌套的weeks结构,返回中心位置
|
||||||
const weeks = periodData.weeks || []
|
// 类型保护:确保 periodData 是 TimelineMonth 并有 weeks 属性
|
||||||
|
const weeks = ('weeks' in periodData && periodData.weeks) ? periodData.weeks : []
|
||||||
|
|
||||||
for (const week of weeks) {
|
for (const week of weeks) {
|
||||||
const weekStart = new Date(week.weekStart)
|
const weekStart = new Date(week.weekStart)
|
||||||
@@ -741,13 +844,17 @@ const calculateMilestonePositionFromTimelineData = (
|
|||||||
}
|
}
|
||||||
} else if (timeScale === TimelineScale.MONTH) {
|
} else if (timeScale === TimelineScale.MONTH) {
|
||||||
// 月视图:处理扁平化的subDays结构,返回中心位置
|
// 月视图:处理扁平化的subDays结构,返回中心位置
|
||||||
|
// 类型保护:确保 periodData 是 TimelineMonth
|
||||||
|
if (!('startDate' in periodData) || !('endDate' in periodData)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
const periodStart = new Date(periodData.startDate)
|
const periodStart = new Date(periodData.startDate)
|
||||||
const periodEnd = new Date(periodData.endDate)
|
const periodEnd = new Date(periodData.endDate)
|
||||||
|
|
||||||
if (targetDate >= periodStart && targetDate <= periodEnd) {
|
if (targetDate >= periodStart && targetDate <= periodEnd) {
|
||||||
// 找到目标日期所在的时间段
|
// 找到目标日期所在的时间段
|
||||||
const monthWidth = 60
|
const monthWidth = 60
|
||||||
const daysInMonth = periodData.monthData?.dayCount || 30
|
const daysInMonth = ('monthData' in periodData && periodData.monthData?.dayCount) || 30
|
||||||
const dayWidth = monthWidth / daysInMonth
|
const dayWidth = monthWidth / daysInMonth
|
||||||
const dayInMonth = targetDate.getDate()
|
const dayInMonth = targetDate.getDate()
|
||||||
const finalPosition = cumulativePosition + (dayInMonth - 1) * dayWidth + dayWidth / 2
|
const finalPosition = cumulativePosition + (dayInMonth - 1) * dayWidth + dayWidth / 2
|
||||||
@@ -777,6 +884,7 @@ const calculateMilestonePositionFromTimelineData = (
|
|||||||
'milestone-sticky-left': milestoneVisibility.stickyPosition === 'left',
|
'milestone-sticky-left': milestoneVisibility.stickyPosition === 'left',
|
||||||
'milestone-sticky-right': milestoneVisibility.stickyPosition === 'right',
|
'milestone-sticky-right': milestoneVisibility.stickyPosition === 'right',
|
||||||
'milestone-pushed-out': milestoneVisibility.isPushedOut,
|
'milestone-pushed-out': milestoneVisibility.isPushedOut,
|
||||||
|
dimmed: isDimmed,
|
||||||
}"
|
}"
|
||||||
@click.stop="handleMilestoneClick"
|
@click.stop="handleMilestoneClick"
|
||||||
>
|
>
|
||||||
@@ -875,6 +983,13 @@ const calculateMilestonePositionFromTimelineData = (
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 高亮模式下,非高亮的Milestone变暗淡 */
|
||||||
|
.milestone.dimmed {
|
||||||
|
opacity: 0.35 !important;
|
||||||
|
filter: grayscale(0.3) !important;
|
||||||
|
transition: all 0.3s ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* 里程碑SVG发光效果 */
|
/* 里程碑SVG发光效果 */
|
||||||
.milestone svg {
|
.milestone svg {
|
||||||
filter: drop-shadow(0 0 8px var(--gantt-danger, #f56c6c));
|
filter: drop-shadow(0 0 8px var(--gantt-danger, #f56c6c));
|
||||||
|
|||||||
+965
-115
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, watch, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, reactive, watch, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { useI18n } from '../composables/useI18n'
|
import { useI18n } from '../composables/useI18n'
|
||||||
import { useMessage } from '../composables/useMessage'
|
import { useMessage } from '../composables/useMessage'
|
||||||
import DatePicker from './DatePicker.vue'
|
import DatePicker from './DatePicker.vue'
|
||||||
@@ -327,21 +327,29 @@ watch(
|
|||||||
newVal => {
|
newVal => {
|
||||||
isVisible.value = newVal
|
isVisible.value = newVal
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
resetForm()
|
|
||||||
if (props.task && props.isEdit) {
|
if (props.task && props.isEdit) {
|
||||||
// 编辑模式,填充表单数据
|
// 编辑模式,先重置表单再填充表单数据
|
||||||
|
resetForm()
|
||||||
const taskData = { ...props.task }
|
const taskData = { ...props.task }
|
||||||
|
|
||||||
// 处理日期格式:如果是只有日期部分的数据,在小时视图编辑时需要特殊处理
|
// 处理日期格式:如果是只有日期部分的数据,在小时视图编辑时需要特殊处理
|
||||||
taskData.startDate = processDateForHourView(taskData.startDate, 'start')
|
taskData.startDate = processDateForHourView(taskData.startDate, 'start')
|
||||||
taskData.endDate = processDateForHourView(taskData.endDate, 'end')
|
taskData.endDate = processDateForHourView(taskData.endDate, 'end')
|
||||||
|
|
||||||
|
// 使用 nextTick 确保 resetForm 的响应式更新完成后再赋值
|
||||||
|
nextTick(() => {
|
||||||
Object.assign(formData, taskData)
|
Object.assign(formData, taskData)
|
||||||
|
})
|
||||||
} else if (props.task && !props.isEdit) {
|
} else if (props.task && !props.isEdit) {
|
||||||
|
// 新建模式,重置表单
|
||||||
|
resetForm()
|
||||||
// 新建模式,自动绑定上级任务
|
// 新建模式,自动绑定上级任务
|
||||||
formData.parentId = props.task.parentId ?? undefined
|
formData.parentId = props.task.parentId ?? undefined
|
||||||
// 新建模式,自动绑定前置任务
|
// 新建模式,自动绑定前置任务
|
||||||
formData.predecessor = props.task.predecessor ?? []
|
formData.predecessor = props.task.predecessor ?? []
|
||||||
|
} else {
|
||||||
|
// 没有 task,也重置表单
|
||||||
|
resetForm()
|
||||||
}
|
}
|
||||||
// 抽屉显示时重新请求任务数据,确保前置任务列表是最新的
|
// 抽屉显示时重新请求任务数据,确保前置任务列表是最新的
|
||||||
window.dispatchEvent(new CustomEvent('request-task-list'))
|
window.dispatchEvent(new CustomEvent('request-task-list'))
|
||||||
@@ -811,7 +819,8 @@ function confirmTimer(desc: string) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
id="task-start-date"
|
id="task-start-date"
|
||||||
v-model="formData.startDate"
|
v-model="formData.startDate"
|
||||||
type="date"
|
:type="'datetime' as any"
|
||||||
|
value-format="YYYY-MM-DD HH:mm"
|
||||||
:placeholder="t.startDateRequired"
|
:placeholder="t.startDateRequired"
|
||||||
:class="{ error: errors.startDate }"
|
:class="{ error: errors.startDate }"
|
||||||
/>
|
/>
|
||||||
@@ -825,7 +834,8 @@ function confirmTimer(desc: string) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
id="task-end-date"
|
id="task-end-date"
|
||||||
v-model="formData.endDate"
|
v-model="formData.endDate"
|
||||||
type="date"
|
:type="'datetime' as any"
|
||||||
|
value-format="YYYY-MM-DD HH:mm"
|
||||||
:placeholder="t.endDateRequired"
|
:placeholder="t.endDateRequired"
|
||||||
:class="{ error: errors.endDate }"
|
:class="{ error: errors.endDate }"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, watch, useSlots, computed } from 'vue'
|
import { ref, onMounted, onUnmounted, watch, useSlots, computed } from 'vue'
|
||||||
import type { Component } from 'vue'
|
|
||||||
import TaskRow from './TaskRow.vue'
|
import TaskRow from './TaskRow.vue'
|
||||||
import { useI18n } from '../composables/useI18n'
|
import { useI18n } from '../composables/useI18n'
|
||||||
import type { Task } from '../models/classes/Task'
|
import type { Task } from '../models/classes/Task'
|
||||||
@@ -9,8 +8,6 @@ import { DEFAULT_TASK_LIST_COLUMNS } from '../models/configs/TaskListConfig'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tasks?: Task[]
|
tasks?: Task[]
|
||||||
onTaskDoubleClick?: (task: Task) => void
|
|
||||||
editComponent?: Component
|
|
||||||
useDefaultDrawer?: boolean
|
useDefaultDrawer?: boolean
|
||||||
taskListConfig?: TaskListConfig
|
taskListConfig?: TaskListConfig
|
||||||
}
|
}
|
||||||
@@ -91,18 +88,14 @@ const handleTaskRowDoubleClick = (task: Task) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先调用外部传入的双击处理器
|
// 始终发送到Timeline处理(通过全局事件),让Timeline决定是否打开编辑器
|
||||||
if (props.onTaskDoubleClick && typeof props.onTaskDoubleClick === 'function') {
|
// 这样外部监听的 @task-double-click 事件也会被触发
|
||||||
props.onTaskDoubleClick(task)
|
|
||||||
} else if (props.useDefaultDrawer) {
|
|
||||||
// 默认行为:发送到Timeline处理(通过全局事件)
|
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent('task-row-double-click', {
|
new CustomEvent('task-row-double-click', {
|
||||||
detail: task,
|
detail: task,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 计算父级任务的进度和日期范围
|
// 计算父级任务的进度和日期范围
|
||||||
const calculateParentTaskData = (
|
const calculateParentTaskData = (
|
||||||
@@ -417,7 +410,6 @@ onUnmounted(() => {
|
|||||||
:level="0"
|
:level="0"
|
||||||
:is-hovered="hoveredTaskId === task.id"
|
:is-hovered="hoveredTaskId === task.id"
|
||||||
:hovered-task-id="hoveredTaskId"
|
:hovered-task-id="hoveredTaskId"
|
||||||
:on-double-click="props.onTaskDoubleClick"
|
|
||||||
:on-hover="handleTaskRowHover"
|
:on-hover="handleTaskRowHover"
|
||||||
:columns="visibleColumns"
|
:columns="visibleColumns"
|
||||||
@toggle="toggleCollapse"
|
@toggle="toggleCollapse"
|
||||||
@@ -478,6 +470,7 @@ onUnmounted(() => {
|
|||||||
background: var(--gantt-bg-secondary);
|
background: var(--gantt-bg-secondary);
|
||||||
color: var(--gantt-text-header);
|
color: var(--gantt-text-header);
|
||||||
border-right-color: var(--gantt-border-medium);
|
border-right-color: var(--gantt-border-medium);
|
||||||
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-body {
|
.task-list-body {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ interface TaskRowSlotProps {
|
|||||||
interface Props {
|
interface Props {
|
||||||
task: Task
|
task: Task
|
||||||
level: number
|
level: number
|
||||||
onDoubleClick?: (task: Task) => void
|
|
||||||
isHovered?: boolean
|
isHovered?: boolean
|
||||||
hoveredTaskId?: number | null
|
hoveredTaskId?: number | null
|
||||||
onHover?: (taskId: number | null) => void
|
onHover?: (taskId: number | null) => void
|
||||||
@@ -85,14 +84,9 @@ const handleTaskRowDoubleClick = (e: MouseEvent) => {
|
|||||||
// 阻止事件冒泡
|
// 阻止事件冒泡
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
// 优先调用外部传入的双击处理器
|
// 发出双击事件给父组件
|
||||||
if (props.onDoubleClick && typeof props.onDoubleClick === 'function') {
|
|
||||||
props.onDoubleClick(props.task)
|
|
||||||
} else {
|
|
||||||
// 默认行为:发出双击事件给父组件
|
|
||||||
emit('dblclick', props.task)
|
emit('dblclick', props.task)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 处理悬停事件
|
// 处理悬停事件
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
@@ -480,7 +474,6 @@ onUnmounted(() => {
|
|||||||
:level="props.level + 1"
|
:level="props.level + 1"
|
||||||
:is-hovered="props.hoveredTaskId === child.id"
|
:is-hovered="props.hoveredTaskId === child.id"
|
||||||
:hovered-task-id="props.hoveredTaskId"
|
:hovered-task-id="props.hoveredTaskId"
|
||||||
:on-double-click="props.onDoubleClick"
|
|
||||||
:on-hover="props.onHover"
|
:on-hover="props.onHover"
|
||||||
:columns="props.columns"
|
:columns="props.columns"
|
||||||
@toggle="emit('toggle', $event)"
|
@toggle="emit('toggle', $event)"
|
||||||
|
|||||||
+881
-169
File diff suppressed because it is too large
Load Diff
@@ -199,6 +199,8 @@ const messages = {
|
|||||||
timerConfirmPrefix: '即将为任务',
|
timerConfirmPrefix: '即将为任务',
|
||||||
timerConfirmSuffix: '计时,若有特殊说明请完善下面的描述',
|
timerConfirmSuffix: '计时,若有特殊说明请完善下面的描述',
|
||||||
timerConfirmPlaceholder: '请输入计时说明',
|
timerConfirmPlaceholder: '请输入计时说明',
|
||||||
|
// Demo配置面板
|
||||||
|
configDemo: '配置演示',
|
||||||
// TaskList配置
|
// TaskList配置
|
||||||
taskListConfig: {
|
taskListConfig: {
|
||||||
title: 'TaskList 配置',
|
title: 'TaskList 配置',
|
||||||
@@ -210,8 +212,34 @@ const messages = {
|
|||||||
defaultWidth: '默认宽度',
|
defaultWidth: '默认宽度',
|
||||||
minWidth: '最小宽度',
|
minWidth: '最小宽度',
|
||||||
maxWidth: '最大宽度',
|
maxWidth: '最大宽度',
|
||||||
|
pixelsModel: '像素 (px)',
|
||||||
|
percentageModel: '百分比 (%)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// TaskBar配置
|
||||||
|
taskBarConfig: {
|
||||||
|
title: 'TaskBar 配置',
|
||||||
|
display: {
|
||||||
|
title: '显示选项',
|
||||||
|
showAvatar: '显示头像 (Avatar)',
|
||||||
|
showTitle: '显示标题 (Title)',
|
||||||
|
showProgress: '显示进度 (Progress)',
|
||||||
|
},
|
||||||
|
mistouch: {
|
||||||
|
title: '防误触配置',
|
||||||
|
dragThreshold: '拖拽阈值 (px)',
|
||||||
|
dragThresholdHint: '移动超过此距离才触发拖拽',
|
||||||
|
resizeHandleWidth: '拉伸手柄宽度 (px)',
|
||||||
|
resizeHandleWidthHint: '调整手柄的可点击宽度 (5-15px)',
|
||||||
|
enableDragDelay: '启用拖拽延迟',
|
||||||
|
enableDragDelayHint: '按住一段时间后才能拖拽',
|
||||||
|
dragDelayTime: '延迟时间 (ms)',
|
||||||
|
dragDelayTimeHint: '延迟启动拖拽的时间',
|
||||||
|
allowDragOnClick: '允许拖拽和拉伸 TaskBar 和 Milestone',
|
||||||
|
allowDragOnClickHint: '控制是否允许拖拽 TaskBar 和 Milestone,以及拉伸 TaskBar 的长度',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disableTaskbarFocusMode: '关闭聚焦功能',
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
dateNotSet: 'Not set',
|
dateNotSet: 'Not set',
|
||||||
@@ -409,6 +437,8 @@ const messages = {
|
|||||||
timerConfirmPrefix: 'About to start timing for',
|
timerConfirmPrefix: 'About to start timing for',
|
||||||
timerConfirmSuffix: '. If there are special notes, please complete the description below.',
|
timerConfirmSuffix: '. If there are special notes, please complete the description below.',
|
||||||
timerConfirmPlaceholder: 'Please enter timer description',
|
timerConfirmPlaceholder: 'Please enter timer description',
|
||||||
|
// Demo配置面板
|
||||||
|
configDemo: 'Configuration Demo',
|
||||||
// TaskList配置
|
// TaskList配置
|
||||||
taskListConfig: {
|
taskListConfig: {
|
||||||
title: 'TaskList Configuration',
|
title: 'TaskList Configuration',
|
||||||
@@ -420,15 +450,55 @@ const messages = {
|
|||||||
defaultWidth: 'Default Width',
|
defaultWidth: 'Default Width',
|
||||||
minWidth: 'Min Width',
|
minWidth: 'Min Width',
|
||||||
maxWidth: 'Max Width',
|
maxWidth: 'Max Width',
|
||||||
|
pixelsModel: 'pixels (px)',
|
||||||
|
percentageModel: 'percentage (%)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// TaskBar配置
|
||||||
|
taskBarConfig: {
|
||||||
|
title: 'TaskBar Configuration',
|
||||||
|
display: {
|
||||||
|
title: 'Display Options',
|
||||||
|
showAvatar: 'Show Avatar',
|
||||||
|
showTitle: 'Show Title',
|
||||||
|
showProgress: 'Show Progress',
|
||||||
|
},
|
||||||
|
mistouch: {
|
||||||
|
title: 'Mistouch Prevention',
|
||||||
|
dragThreshold: 'Drag Threshold (px)',
|
||||||
|
dragThresholdHint: 'Distance to trigger dragging',
|
||||||
|
resizeHandleWidth: 'Resize Handle Width (px)',
|
||||||
|
resizeHandleWidthHint: 'Clickable width of resize handle (5-15px)',
|
||||||
|
enableDragDelay: 'Enable Drag Delay',
|
||||||
|
enableDragDelayHint: 'Hold to drag after a delay',
|
||||||
|
dragDelayTime: 'Delay Time (ms)',
|
||||||
|
dragDelayTimeHint: 'Delay time before dragging starts',
|
||||||
|
allowDragOnClick: 'Allow dragging and resizing of TaskBars and Milestones',
|
||||||
|
allowDragOnClickHint: 'Controls whether to allow dragging of TaskBars and Milestones, as well as resizing the length of TaskBars',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disableTaskbarFocusMode: 'Disable Focus Mode',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 允许外部合并自定义多语言
|
// 允许外部合并自定义多语言
|
||||||
export function setCustomMessages(locale: Locale, custom: Partial<(typeof messages)['zh-CN']>) {
|
export function setCustomMessages(locale: Locale, custom: Partial<(typeof messages)['zh-CN']>) {
|
||||||
if (!messages[locale]) return
|
if (!messages[locale]) return
|
||||||
Object.assign(messages[locale], custom)
|
|
||||||
|
// 使用深度合并,触发响应式更新
|
||||||
|
messages[locale] = {
|
||||||
|
...messages[locale],
|
||||||
|
...custom,
|
||||||
|
} as (typeof messages)['zh-CN']
|
||||||
|
|
||||||
|
// 触发语言切换事件,强制刷新所有使用翻译的组件
|
||||||
|
if (currentLocale.value === locale) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('locale-changed', {
|
||||||
|
detail: { locale },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalStorage key
|
// LocalStorage key
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export { useMessage } from './composables/useMessage.ts' // 导出useMessage组
|
|||||||
|
|
||||||
// 导出配置类型
|
// 导出配置类型
|
||||||
export type { TaskListConfig, TaskListColumnConfig, TaskListColumnType } from './models/configs/TaskListConfig'
|
export type { TaskListConfig, TaskListColumnConfig, TaskListColumnType } from './models/configs/TaskListConfig'
|
||||||
|
export type { TaskBarConfig } from './models/configs/TaskBarConfig'
|
||||||
export type { ToolbarConfig } from './models/configs/ToolbarConfig'
|
export type { ToolbarConfig } from './models/configs/ToolbarConfig'
|
||||||
|
|
||||||
// 导出样式文件
|
// 导出样式文件
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface Task {
|
|||||||
name: string
|
name: string
|
||||||
predecessor?: number[] // 前置任务ID数组
|
predecessor?: number[] // 前置任务ID数组
|
||||||
assignee?: string
|
assignee?: string
|
||||||
|
avatar?: string // 任务负责人头像URL
|
||||||
startDate?: string
|
startDate?: string
|
||||||
endDate?: string
|
endDate?: string
|
||||||
progress?: number
|
progress?: number
|
||||||
@@ -23,6 +24,8 @@ export interface Task {
|
|||||||
timerEndTime?: number // 结束计时时间
|
timerEndTime?: number // 结束计时时间
|
||||||
timerStartDesc?: string // 计时开始时填写的描述
|
timerStartDesc?: string // 计时开始时填写的描述
|
||||||
timerElapsedTime?: number
|
timerElapsedTime?: number
|
||||||
|
// 权限控制
|
||||||
|
isEditable?: boolean // 是否可编辑(可拖拽、拉伸),默认为true
|
||||||
// 支持自定义属性 - 使用 unknown 允许任意类型
|
// 支持自定义属性 - 使用 unknown 允许任意类型
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// TaskBar 配置类型定义
|
||||||
|
|
||||||
|
export interface TaskBarConfig {
|
||||||
|
showAvatar?: boolean // 是否展示头像,默认 true
|
||||||
|
showTitle?: boolean // 是否展示标题文字,默认 true
|
||||||
|
showProgress?: boolean // 是否展示进度文字,默认 true
|
||||||
|
dragThreshold?: number // 拖拽触发阈值(像素),默认 5px
|
||||||
|
resizeHandleWidth?: number // 拉伸手柄宽度(像素),默认 5px,最大 15px
|
||||||
|
enableDragDelay?: boolean // 是否启用拖拽延迟(防止误触),默认 false
|
||||||
|
dragDelayTime?: number // 拖拽延迟时间(毫秒),默认 150ms
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认配置
|
||||||
|
export const DEFAULT_TASK_BAR_CONFIG: TaskBarConfig = {
|
||||||
|
showAvatar: true,
|
||||||
|
showTitle: true,
|
||||||
|
showProgress: true,
|
||||||
|
dragThreshold: 5,
|
||||||
|
resizeHandleWidth: 5,
|
||||||
|
enableDragDelay: false,
|
||||||
|
dragDelayTime: 150,
|
||||||
|
}
|
||||||
@@ -22,9 +22,9 @@ export interface TaskListColumnConfig {
|
|||||||
export interface TaskListConfig {
|
export interface TaskListConfig {
|
||||||
columns?: TaskListColumnConfig[]
|
columns?: TaskListColumnConfig[]
|
||||||
showAllColumns?: boolean // 是否显示所有列,默认true
|
showAllColumns?: boolean // 是否显示所有列,默认true
|
||||||
defaultWidth?: number // 默认展开宽度,单位像素,默认320px
|
defaultWidth?: number | string // 默认展开宽度,支持像素数字(如 320)或百分比字符串(如 '30%'),默认320px
|
||||||
minWidth?: number // 最小宽度,单位像素,默认280px,不能小于280px
|
minWidth?: number | string // 最小宽度,支持像素数字(如 280)或百分比字符串(如 '20%'),默认280px,不能小于280px
|
||||||
maxWidth?: number // 最大宽度,单位像素,默认1160px
|
maxWidth?: number | string // 最大宽度,支持像素数字(如 1160)或百分比字符串(如 '80%'),默认1160px
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认宽度配置
|
// 默认宽度配置
|
||||||
@@ -32,6 +32,46 @@ export const DEFAULT_TASK_LIST_WIDTH = 320 // 默认展开宽度
|
|||||||
export const DEFAULT_TASK_LIST_MIN_WIDTH = 280 // 最小宽度
|
export const DEFAULT_TASK_LIST_MIN_WIDTH = 280 // 最小宽度
|
||||||
export const DEFAULT_TASK_LIST_MAX_WIDTH = 1160 // 最大宽度
|
export const DEFAULT_TASK_LIST_MAX_WIDTH = 1160 // 最大宽度
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析宽度值(支持像素数字或百分比字符串)
|
||||||
|
* @param value 宽度值,可以是数字(像素)或字符串(百分比,如 '30%')
|
||||||
|
* @param containerWidth 容器宽度(用于计算百分比)
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
* @returns 解析后的像素值
|
||||||
|
*/
|
||||||
|
export function parseWidthValue(
|
||||||
|
value: number | string | undefined,
|
||||||
|
containerWidth: number,
|
||||||
|
defaultValue: number,
|
||||||
|
): number {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是数字,直接返回
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是字符串,检查是否是百分比
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const trimmed = value.trim()
|
||||||
|
if (trimmed.endsWith('%')) {
|
||||||
|
const percentage = parseFloat(trimmed)
|
||||||
|
if (!isNaN(percentage)) {
|
||||||
|
return Math.round((containerWidth * percentage) / 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 尝试解析为数字
|
||||||
|
const parsed = parseFloat(trimmed)
|
||||||
|
if (!isNaN(parsed)) {
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
// 默认列配置
|
// 默认列配置
|
||||||
export const DEFAULT_TASK_LIST_COLUMNS: TaskListColumnConfig[] = [
|
export const DEFAULT_TASK_LIST_COLUMNS: TaskListColumnConfig[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,9 +49,15 @@ export const SCALE_CONFIGS = {
|
|||||||
headerLevels: 2,
|
headerLevels: 2,
|
||||||
formatters: { primary: 'yyyy年', secondary: 'MM月' },
|
formatters: { primary: 'yyyy年', secondary: 'MM月' },
|
||||||
},
|
},
|
||||||
|
quarter: {
|
||||||
|
scale: TimelineScale.QUARTER,
|
||||||
|
cellWidth: 60, // 每个季度的宽度
|
||||||
|
headerLevels: 2,
|
||||||
|
formatters: { primary: 'yyyy年', secondary: 'Q季度' },
|
||||||
|
},
|
||||||
year: {
|
year: {
|
||||||
scale: TimelineScale.YEAR,
|
scale: TimelineScale.YEAR,
|
||||||
cellWidth: 360,
|
cellWidth: 180, // 每半年的宽度
|
||||||
headerLevels: 2,
|
headerLevels: 2,
|
||||||
formatters: { primary: 'yyyy年', secondary: '上半年|下半年' },
|
formatters: { primary: 'yyyy年', secondary: '上半年|下半年' },
|
||||||
},
|
},
|
||||||
|
|||||||
+8
-2
@@ -15,7 +15,7 @@ const generateCssExportPlugin = () => {
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue(), generateCssExportPlugin()],
|
plugins: [vue(), generateCssExportPlugin()],
|
||||||
build: {
|
build: {
|
||||||
outDir: './npm-package/dist',
|
outDir: '../npm-package/dist',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
lib: {
|
lib: {
|
||||||
entry: './src/index.ts',
|
entry: './src/index.ts',
|
||||||
@@ -34,7 +34,13 @@ export default defineConfig({
|
|||||||
// 禁用文件名哈希,生成固定文件名
|
// 禁用文件名哈希,生成固定文件名
|
||||||
entryFileNames: 'jordium-gantt-vue3.[format].js',
|
entryFileNames: 'jordium-gantt-vue3.[format].js',
|
||||||
chunkFileNames: 'chunks/[name].js',
|
chunkFileNames: 'chunks/[name].js',
|
||||||
assetFileNames: 'assets/[name].[ext]',
|
assetFileNames: (assetInfo) => {
|
||||||
|
// 为CSS文件使用固定名称
|
||||||
|
if (assetInfo.name && assetInfo.name.endsWith('.css')) {
|
||||||
|
return 'assets/jordium-gantt-vue3.css'
|
||||||
|
}
|
||||||
|
return 'assets/[name].[ext]'
|
||||||
|
},
|
||||||
// 禁用代码分割,将所有代码打包到一个文件中
|
// 禁用代码分割,将所有代码打包到一个文件中
|
||||||
manualChunks: undefined,
|
manualChunks: undefined,
|
||||||
inlineDynamicImports: true
|
inlineDynamicImports: true
|
||||||
|
|||||||
Reference in New Issue
Block a user