Compare commits

..

No commits in common. "cee78d71c9a3de945cf0351606799eb2b4d7fc6e" and "375d3132f6f30887fa2486b20279745bfd140bff" have entirely different histories.

1 changed files with 0 additions and 396 deletions

View File

@ -1,396 +0,0 @@
<template>
<GiPageLayout>
<div class="gantt-container">
<div class="page-header">
<h2 class="page-title">人力甘特图页面</h2>
</div>
<!-- 人员列表区域 -->
<div class="person-container">
<div v-for="(person, index) in personList" :key="person.id" class="person-gantt">
<!-- 人员标题栏 -->
<div class="person-header" @click="togglePerson(index)">
<div class="name-container">
<span class="avatar">{{ person.name.charAt(0) }}</span>
<span>{{ person.name }}</span>
<span class="task-count">({{ person.tasks.length }}个项目)</span>
</div>
<button class="expand-button">
<i :class="person.expanded ? 'collapse-icon' : 'expand-icon'"></i>
</button>
</div>
<!-- 甘特图容器 - 根据展开状态控制显示 -->
<div v-show="person.expanded" class="chart-container">
<div :ref="el => chartRefs[index] = el" class="progress-chart"></div>
</div>
</div>
</div>
</div>
</GiPageLayout>
</template>
<script setup lang='ts'>
import * as echarts from 'echarts'
import { ref, onMounted, onUnmounted, watch } from 'vue'
//
const personList = ref([
{
id: 1,
name: '张三',
expanded: true,
tasks: [
{ name: '项目一', startDate: '2025-08-01', days: 5, color: '#5470C6' },
{ name: '项目二', startDate: '2025-08-10', days: 7, color: '#91CC75' },
{ name: '项目三', startDate: '2025-08-20', days: 4, color: '#FAC858' }
]
},
{
id: 2,
name: '李四',
expanded: true,
tasks: [
{ name: '产品设计', startDate: '2025-08-05', days: 8, color: '#EE6666' },
{ name: '技术评审', startDate: '2025-08-15', days: 3, color: '#73C0DE' },
{ name: '系统测试', startDate: '2025-08-18', days: 6, color: '#3BA272' }
]
},
{
id: 3,
name: '王五',
expanded: true,
tasks: [
{ name: '需求分析', startDate: '2025-08-02', days: 4, color: '#FC8452' },
{ name: '前端开发', startDate: '2025-08-08', days: 7, color: '#9A60B4' },
{ name: '后端对接', startDate: '2025-08-17', days: 5, color: '#EA7CCC' }
]
},
{
id: 4,
name: '赵六',
expanded: false,
tasks: [
{ name: '文档编写', startDate: '2025-08-03', days: 6, color: '#5470C6' },
{ name: '用户培训', startDate: '2025-08-12', days: 4, color: '#91CC75' },
{ name: '上线支持', startDate: '2025-08-22', days: 7, color: '#FAC858' }
]
}
])
//
const chartRefs = ref<HTMLElement[]>([]);
//
const chartInstances = ref<echarts.ECharts[]>([]);
// YYYY-MM-DD
const formatDate = (date: Date): string => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// /
const togglePerson = (index: number) => {
personList.value[index].expanded = !personList.value[index].expanded;
//
if (personList.value[index].expanded) {
setTimeout(() => {
if (chartRefs.value[index] && chartInstances.value[index]) {
chartInstances.value[index].resize();
} else {
initChart(index);
}
}, 10);
}
};
//
const initChart = (personIndex: number) => {
const container = chartRefs.value[personIndex];
if (!container) return;
//
if (chartInstances.value[personIndex]) {
chartInstances.value[personIndex].dispose();
}
const person = personList.value[personIndex];
const tasks = person.tasks;
// -
const today = new Date(2025, 7, 14);
//
const projectNames: string[] = [];
const dataItems: any[] = [];
const colors: string[] = [];
tasks.forEach((task) => {
const startDate = new Date(task.startDate);
const endDate = new Date(startDate);
endDate.setDate(startDate.getDate() + task.days);
projectNames.push(task.name);
colors.push(task.color);
dataItems.push({
name: task.name,
value: [
formatDate(startDate),
formatDate(endDate),
task.days
],
itemStyle: {
color: task.color
}
});
});
//
const chart = echarts.init(container);
//
const option = {
tooltip: {
trigger: 'item',
formatter: (params: any) => {
const task = params.data;
return `
<div class="task-tooltip">
<strong>${task.name}</strong><br>
开始: ${params.value[0]}<br>
结束: ${params.value[1]}<br>
耗时: ${task.value[2]}
</div>
`;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'time',
min: formatDate(new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)),
max: formatDate(new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000)),
axisLabel: {
formatter: function (value: number) {
return echarts.time.format(value, '{MM}/{dd}', false);
}
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
opacity: 0.3
}
}
},
yAxis: {
type: 'category',
data: projectNames,
axisLine: {
show: true
},
axisTick: {
show: false
},
axisLabel: {
margin: 16
}
},
dataZoom: [{
type: 'inside',
start: 20,
end: 100
}],
series: [{
name: '项目进度',
type: 'bar',
data: dataItems,
barCategoryGap: '40%',
label: {
show: true,
position: 'inside',
formatter: '{c[2]}天'
},
barWidth: '60%'
}],
animation: true,
animationDuration: 800
};
chart.setOption(option);
chartInstances.value[personIndex] = chart;
}
//
const handleResize = () => {
chartInstances.value.forEach((chart, index) => {
if (chart && personList.value[index].expanded) {
chart.resize();
}
});
};
//
onMounted(() => {
window.addEventListener('resize', handleResize);
personList.value.forEach((_, index) => {
if (personList.value[index].expanded) {
initChart(index);
}
});
});
//
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
chartInstances.value.forEach(chart => {
if (chart) {
chart.dispose();
}
});
});
</script>
<style lang='scss' scoped>
.gantt-container {
height: 100%;
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.page-header {
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
.page-title {
margin: 0;
font-size: 1.5rem;
color: #2c3e50;
font-weight: 600;
}
}
.person-container {
flex: 1;
overflow-y: auto;
padding-right: 8px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background-color: #c2c6cc;
border-radius: 3px;
}
}
.person-gantt {
margin-bottom: 20px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
background-color: white;
&:last-child {
margin-bottom: 0;
}
}
.person-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background-color: #f8f9fa;
border-bottom: 1px solid #e9ecef;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f1f3f5;
}
.name-container {
display: flex;
align-items: center;
font-size: 1.1rem;
font-weight: 500;
color: #495057;
.avatar {
display: inline-flex;
justify-content: center;
align-items: center;
width: 28px;
height: 28px;
margin-right: 10px;
background-color: #3a7afe;
color: white;
border-radius: 50%;
font-weight: bold;
}
.task-count {
margin-left: 8px;
font-size: 0.85rem;
font-weight: normal;
color: #868e96;
}
}
.expand-button {
background: none;
border: none;
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
&:hover {
background-color: #e9ecef;
}
i {
display: block;
width: 0;
height: 0;
border-style: solid;
}
.collapse-icon {
border-width: 0 8px 10px 8px;
border-color: transparent transparent #495057 transparent;
}
.expand-icon {
border-width: 10px 8px 0 8px;
border-color: #495057 transparent transparent transparent;
}
}
}
.chart-container {
padding: 15px;
background-color: #fff;
}
.progress-chart {
width: 100%;
height: 250px;
}
</style>