|
|
@ -3,23 +3,32 @@ |
|
|
|
|
|
|
|
|
<!-- 项目时间范围选择 --> |
|
|
<!-- 项目时间范围选择 --> |
|
|
<div class="project-timeline"> |
|
|
<div class="project-timeline"> |
|
|
<span class="timeline-label">项目时间:</span> |
|
|
|
|
|
<el-date-picker |
|
|
|
|
|
v-model="dateRange" |
|
|
|
|
|
type="daterange" |
|
|
|
|
|
range-separator="~" |
|
|
|
|
|
start-placeholder="开始日期" |
|
|
|
|
|
end-placeholder="结束日期" |
|
|
|
|
|
format="yyyy-MM-dd" |
|
|
|
|
|
value-format="yyyy-MM-dd" |
|
|
|
|
|
:picker-options="pickerOptions" |
|
|
|
|
|
@change="handleDateRangeChange" |
|
|
|
|
|
size="small" |
|
|
|
|
|
style="width: 300px; margin-left: 10px;" |
|
|
|
|
|
></el-date-picker> |
|
|
|
|
|
<span class="timeline-range" v-if="projectStartDate && projectEndDate"> |
|
|
|
|
|
{{ projectStartDate }} —— {{ projectEndDate }} |
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
|
<el-form :inline="true" label-position="top" ref="queryForm"> |
|
|
|
|
|
<el-row :gutter="10"> |
|
|
|
|
|
<el-col :span="6"> |
|
|
|
|
|
<el-form-item label="日期范围"> |
|
|
|
|
|
<el-date-picker |
|
|
|
|
|
v-model="dateRange" |
|
|
|
|
|
range-separator="~" |
|
|
|
|
|
start-placeholder="开始日期" |
|
|
|
|
|
type="daterange" |
|
|
|
|
|
end-placeholder="结束日期" |
|
|
|
|
|
format="yyyy-MM-dd" |
|
|
|
|
|
value-format="yyyy-MM-dd" |
|
|
|
|
|
:picker-options="pickerOptions" |
|
|
|
|
|
size="mini" |
|
|
|
|
|
style="width: 100%;" |
|
|
|
|
|
class="date-range-picker"> |
|
|
|
|
|
</el-date-picker> |
|
|
|
|
|
</el-form-item> |
|
|
|
|
|
</el-col> |
|
|
|
|
|
<el-col :span="3"> |
|
|
|
|
|
<el-form-item label=" "> |
|
|
|
|
|
<el-button type="primary" style="height: 26px;" @click="handleDateRangeChange">查 询</el-button> |
|
|
|
|
|
</el-form-item> |
|
|
|
|
|
</el-col> |
|
|
|
|
|
</el-row> |
|
|
|
|
|
</el-form> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 主要内容区域 --> |
|
|
<!-- 主要内容区域 --> |
|
|
@ -28,11 +37,13 @@ |
|
|
<div class="project-info-section"> |
|
|
<div class="project-info-section"> |
|
|
<el-table :data="projectList" border style="width: 100%" size="small"> |
|
|
<el-table :data="projectList" border style="width: 100%" size="small"> |
|
|
<el-table-column prop="serialNo" label="序号" width="60" align="center"></el-table-column> |
|
|
<el-table-column prop="serialNo" label="序号" width="60" align="center"></el-table-column> |
|
|
<el-table-column label="供应商/项目/零件号" width="120"> |
|
|
|
|
|
|
|
|
<el-table-column label="物料号" width="120" align="center"> |
|
|
<template slot-scope="scope"> |
|
|
<template slot-scope="scope"> |
|
|
<div class="part-info"> |
|
|
|
|
|
<div class="part-number" :title="scope.row.partNumber">{{ truncateText(scope.row.partNumber, 10) }}</div> |
|
|
|
|
|
<div class="design-lead" :title="scope.row.designLead">设计: {{ truncateText(scope.row.designLead, 8) }}</div> |
|
|
|
|
|
|
|
|
<div> |
|
|
|
|
|
<div class="part-number" :title="scope.row.partCode">{{ truncateText(scope.row.partCode, 10) }} |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="design-lead" :title="scope.row.designLead">设计: {{ truncateText(scope.row.designLead, 8) }} |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
</el-table-column> |
|
|
</el-table-column> |
|
|
@ -56,61 +67,42 @@ |
|
|
|
|
|
|
|
|
<!-- 甘特图时间轴 --> |
|
|
<!-- 甘特图时间轴 --> |
|
|
<div class="gantt-timeline"> |
|
|
<div class="gantt-timeline"> |
|
|
<!-- 时间轴头部 --> |
|
|
|
|
|
<div class="timeline-header"> |
|
|
|
|
|
<!-- 年月行 --> |
|
|
|
|
|
<div class="timeline-cell header-cell month-header project-header">项目</div> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="(monthGroup, groupIndex) in monthGroups" |
|
|
|
|
|
:key="`month-group-${groupIndex}`" |
|
|
|
|
|
class="timeline-cell header-cell month-header month-group" |
|
|
|
|
|
:style="{ width: monthGroup.width + 'px', minWidth: monthGroup.width + 'px' }" |
|
|
|
|
|
> |
|
|
|
|
|
{{ monthGroup.year }}/{{ monthGroup.month }} |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 时间轴头部 --> |
|
|
|
|
|
<div class="timeline-header"> |
|
|
|
|
|
<!-- 年月行 --> |
|
|
|
|
|
<div class="timeline-cell header-cell month-header project-header">物料</div> |
|
|
|
|
|
<div v-for="(monthGroup, groupIndex) in monthGroups" :key="`month-group-${groupIndex}`" |
|
|
|
|
|
class="timeline-cell header-cell month-header month-group" |
|
|
|
|
|
:style="{ width: monthGroup.width + 'px', minWidth: monthGroup.width + 'px' }"> |
|
|
|
|
|
{{ monthGroup.year }}/{{ monthGroup.month }} |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 日期行 --> |
|
|
<!-- 日期行 --> |
|
|
<div class="timeline-header"> |
|
|
<div class="timeline-header"> |
|
|
<div class="timeline-cell header-cell project-header">项目</div> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="day in timelineDays" |
|
|
|
|
|
:key="day.date" |
|
|
|
|
|
class="timeline-cell header-cell" |
|
|
|
|
|
:class="{ 'weekend': day.isWeekend }" |
|
|
|
|
|
> |
|
|
|
|
|
|
|
|
<div class="timeline-cell header-cell project-header">物料</div> |
|
|
|
|
|
<div v-for="day in timelineDays" :key="day.date" class="timeline-cell header-cell" |
|
|
|
|
|
:class="{ 'weekend': day.isWeekend }"> |
|
|
{{ day.day }} |
|
|
{{ day.day }} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 甘特图内容 --> |
|
|
<!-- 甘特图内容 --> |
|
|
<div class="gantt-content"> |
|
|
<div class="gantt-content"> |
|
|
<div |
|
|
|
|
|
v-for="(project, projectIndex) in projectList" |
|
|
|
|
|
:key="projectIndex" |
|
|
|
|
|
class="gantt-row" |
|
|
|
|
|
> |
|
|
|
|
|
|
|
|
<div v-for="(project, projectIndex) in projectList" :key="projectIndex" class="gantt-row"> |
|
|
<!-- 项目名称 --> |
|
|
<!-- 项目名称 --> |
|
|
<div class="project-name-cell" :title="project.partNumber"> |
|
|
|
|
|
{{ truncateText(project.partNumber, 20) }} |
|
|
|
|
|
|
|
|
<div class="project-name-cell" :title="project.partCode"> |
|
|
|
|
|
{{ truncateText(project.partCode, 20) }} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 时间轴单元格 --> |
|
|
<!-- 时间轴单元格 --> |
|
|
<div class="timeline-cells"> |
|
|
<div class="timeline-cells"> |
|
|
<div |
|
|
|
|
|
v-for="(day, dayIndex) in timelineDays" |
|
|
|
|
|
:key="dayIndex" |
|
|
|
|
|
class="timeline-cell" |
|
|
|
|
|
:class="{ 'weekend': day.isWeekend }" |
|
|
|
|
|
> |
|
|
|
|
|
|
|
|
<div v-for="(day, dayIndex) in timelineDays" :key="dayIndex" class="timeline-cell" |
|
|
|
|
|
:class="{ 'weekend': day.isWeekend }"> |
|
|
<!-- 渲染甘特图条 --> |
|
|
<!-- 渲染甘特图条 --> |
|
|
<div |
|
|
|
|
|
v-for="(task, taskIndex) in getTasksForDay(project.tasks, day.date)" |
|
|
|
|
|
:key="`${day.date}-${taskIndex}`" |
|
|
|
|
|
class="gantt-bar" |
|
|
|
|
|
:style="getTaskStyle(task, day.date, dayIndex)" |
|
|
|
|
|
:title="`${task.name}: ${task.startDate} - ${task.endDate}`" |
|
|
|
|
|
></div> |
|
|
|
|
|
|
|
|
<div v-for="(task, taskIndex) in getTasksForDay(project.tasks, day.date)" |
|
|
|
|
|
:key="`${day.date}-${taskIndex}`" class="gantt-bar" :style="getTaskStyle(task, day.date, dayIndex)" |
|
|
|
|
|
:title="`${task.name}: ${task.startDate} - ${task.endDate}`"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -123,6 +115,7 @@ |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import { getProjectGanttData, getGanttLegend } from '@/api/project/gantt.js' |
|
|
import { getProjectGanttData, getGanttLegend } from '@/api/project/gantt.js' |
|
|
|
|
|
import moment from 'moment' |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'ProjectPartProgress', |
|
|
name: 'ProjectPartProgress', |
|
|
@ -130,31 +123,31 @@ export default { |
|
|
// 支持外部传入数据 |
|
|
// 支持外部传入数据 |
|
|
externalProjectList: { |
|
|
externalProjectList: { |
|
|
type: Array, |
|
|
type: Array, |
|
|
default: () => [] |
|
|
|
|
|
|
|
|
default: () => [], |
|
|
}, |
|
|
}, |
|
|
externalLegendItems: { |
|
|
externalLegendItems: { |
|
|
type: Array, |
|
|
type: Array, |
|
|
default: () => [] |
|
|
|
|
|
|
|
|
default: () => [], |
|
|
}, |
|
|
}, |
|
|
externalUpdateDate: { |
|
|
externalUpdateDate: { |
|
|
type: String, |
|
|
type: String, |
|
|
default: '' |
|
|
|
|
|
|
|
|
default: '', |
|
|
}, |
|
|
}, |
|
|
externalProjectStartDate: { |
|
|
externalProjectStartDate: { |
|
|
type: String, |
|
|
type: String, |
|
|
default: '' |
|
|
|
|
|
|
|
|
default: '', |
|
|
}, |
|
|
}, |
|
|
externalProjectEndDate: { |
|
|
externalProjectEndDate: { |
|
|
type: String, |
|
|
type: String, |
|
|
default: '' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
default: '', |
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
data() { |
|
|
data() { |
|
|
return { |
|
|
return { |
|
|
loading: false, |
|
|
loading: false, |
|
|
updateDate: '2024. 9. 28', |
|
|
updateDate: '2024. 9. 28', |
|
|
projectStartDate: '2024年9月9日', |
|
|
|
|
|
projectEndDate: '2024年11月30日', |
|
|
|
|
|
|
|
|
projectStartDate: '', // 默认开始日期为当前日期前30天 |
|
|
|
|
|
projectEndDate: '', |
|
|
|
|
|
|
|
|
// 时间选择器配置 |
|
|
// 时间选择器配置 |
|
|
dateRange: [], |
|
|
dateRange: [], |
|
|
@ -167,7 +160,7 @@ export default { |
|
|
const start = new Date() |
|
|
const start = new Date() |
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7) |
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7) |
|
|
picker.$emit('pick', [start, end]) |
|
|
picker.$emit('pick', [start, end]) |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
text: '最近一个月', |
|
|
text: '最近一个月', |
|
|
@ -176,7 +169,7 @@ export default { |
|
|
const start = new Date() |
|
|
const start = new Date() |
|
|
start.setMonth(start.getMonth() - 1) |
|
|
start.setMonth(start.getMonth() - 1) |
|
|
picker.$emit('pick', [start, end]) |
|
|
picker.$emit('pick', [start, end]) |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
text: '最近三个月', |
|
|
text: '最近三个月', |
|
|
@ -185,7 +178,7 @@ export default { |
|
|
const start = new Date() |
|
|
const start = new Date() |
|
|
start.setMonth(start.getMonth() - 3) |
|
|
start.setMonth(start.getMonth() - 3) |
|
|
picker.$emit('pick', [start, end]) |
|
|
picker.$emit('pick', [start, end]) |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
text: '最近半年', |
|
|
text: '最近半年', |
|
|
@ -194,7 +187,7 @@ export default { |
|
|
const start = new Date() |
|
|
const start = new Date() |
|
|
start.setMonth(start.getMonth() - 6) |
|
|
start.setMonth(start.getMonth() - 6) |
|
|
picker.$emit('pick', [start, end]) |
|
|
picker.$emit('pick', [start, end]) |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
text: '最近一年', |
|
|
text: '最近一年', |
|
|
@ -203,8 +196,8 @@ export default { |
|
|
const start = new Date() |
|
|
const start = new Date() |
|
|
start.setFullYear(start.getFullYear() - 1) |
|
|
start.setFullYear(start.getFullYear() - 1) |
|
|
picker.$emit('pick', [start, end]) |
|
|
picker.$emit('pick', [start, end]) |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
], |
|
|
], |
|
|
disabledDate(time) { |
|
|
disabledDate(time) { |
|
|
// 限制日期选择范围为1年 |
|
|
// 限制日期选择范围为1年 |
|
|
@ -213,7 +206,7 @@ export default { |
|
|
const oneYearLater = new Date() |
|
|
const oneYearLater = new Date() |
|
|
oneYearLater.setFullYear(oneYearLater.getFullYear() + 1) |
|
|
oneYearLater.setFullYear(oneYearLater.getFullYear() + 1) |
|
|
return oneYearAgo > oneYearLater |
|
|
return oneYearAgo > oneYearLater |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 图例数据 |
|
|
// 图例数据 |
|
|
@ -223,79 +216,254 @@ export default { |
|
|
{ label: '机加工', color: '#0000ff' }, |
|
|
{ label: '机加工', color: '#0000ff' }, |
|
|
{ label: '热处理', color: '#ffa500' }, |
|
|
{ label: '热处理', color: '#ffa500' }, |
|
|
{ label: '线割', color: '#800080' }, |
|
|
{ label: '线割', color: '#800080' }, |
|
|
{ label: '装配', color: '#a0522d' } |
|
|
|
|
|
|
|
|
{ label: '装配', color: '#a0522d' }, |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// 项目数据 |
|
|
// 项目数据 |
|
|
projectList: [ |
|
|
projectList: [ |
|
|
{ |
|
|
{ |
|
|
serialNo: 1, |
|
|
serialNo: 1, |
|
|
partNumber: 'Honda/3424725', |
|
|
|
|
|
|
|
|
partCode: 'Honda/3424725', |
|
|
partName: 'BUCKLE', |
|
|
partName: 'BUCKLE', |
|
|
designLead: '5A 设计:李凤鸣', |
|
|
designLead: '5A 设计:李凤鸣', |
|
|
tasks: [ |
|
|
tasks: [ |
|
|
{ name: '机加工', startDate: '2024-09-01', endDate: '2024-09-06', color: '#0000ff' }, |
|
|
|
|
|
{ name: '材料采购', startDate: '2024-09-23', endDate: '2024-09-26', color: '#00ff00' }, |
|
|
|
|
|
{ name: '热处理', startDate: '2024-09-27', endDate: '2024-09-30', color: '#ffa500' }, |
|
|
|
|
|
{ name: '线割', startDate: '2024-10-01', endDate: '2024-10-04', color: '#800080' }, |
|
|
|
|
|
{ name: '装配', startDate: '2024-10-05', endDate: '2024-10-08', color: '#a0522d' }, |
|
|
|
|
|
{ name: '完成', startDate: '2024-10-09', endDate: '2024-10-12', color: '#ffff00' } |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
name: '机加工', |
|
|
|
|
|
startDate: '2024-09-01', |
|
|
|
|
|
endDate: '2024-09-06', |
|
|
|
|
|
color: '#0000ff', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '材料采购', |
|
|
|
|
|
startDate: '2024-09-23', |
|
|
|
|
|
endDate: '2024-09-26', |
|
|
|
|
|
color: '#00ff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '热处理', |
|
|
|
|
|
startDate: '2024-09-27', |
|
|
|
|
|
endDate: '2024-09-30', |
|
|
|
|
|
color: '#ffa500', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '线割', |
|
|
|
|
|
startDate: '2024-10-01', |
|
|
|
|
|
endDate: '2024-10-04', |
|
|
|
|
|
color: '#800080', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '装配', |
|
|
|
|
|
startDate: '2024-10-05', |
|
|
|
|
|
endDate: '2024-10-08', |
|
|
|
|
|
color: '#a0522d', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '完成', |
|
|
|
|
|
startDate: '2024-10-09', |
|
|
|
|
|
endDate: '2024-10-12', |
|
|
|
|
|
color: '#ffff00', |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
serialNo: 2, |
|
|
serialNo: 2, |
|
|
partNumber: 'SPR6.1 / G68/A022V738A', |
|
|
|
|
|
|
|
|
partCode: 'SPR6.1 / G68/A022V738A', |
|
|
partName: 'BUCKLE', |
|
|
partName: 'BUCKLE', |
|
|
designLead: '设计:陈晓东', |
|
|
designLead: '设计:陈晓东', |
|
|
tasks: [ |
|
|
tasks: [ |
|
|
{ name: '材料采购', startDate: '2024-09-13', endDate: '2024-09-16', color: '#00ff00' }, |
|
|
|
|
|
{ name: '热处理', startDate: '2024-09-17', endDate: '2024-09-20', color: '#ffa500' }, |
|
|
|
|
|
{ name: '线割', startDate: '2024-09-21', endDate: '2024-09-24', color: '#800080' }, |
|
|
|
|
|
{ name: '装配', startDate: '2024-09-25', endDate: '2024-09-28', color: '#a0522d' }, |
|
|
|
|
|
{ name: '完成', startDate: '2024-09-29', endDate: '2024-10-02', color: '#ffff00' } |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
name: '材料采购', |
|
|
|
|
|
startDate: '2024-09-13', |
|
|
|
|
|
endDate: '2024-09-16', |
|
|
|
|
|
color: '#00ff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '热处理', |
|
|
|
|
|
startDate: '2024-09-17', |
|
|
|
|
|
endDate: '2024-09-20', |
|
|
|
|
|
color: '#ffa500', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '线割', |
|
|
|
|
|
startDate: '2024-09-21', |
|
|
|
|
|
endDate: '2024-09-24', |
|
|
|
|
|
color: '#800080', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '装配', |
|
|
|
|
|
startDate: '2024-09-25', |
|
|
|
|
|
endDate: '2024-09-28', |
|
|
|
|
|
color: '#a0522d', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '完成', |
|
|
|
|
|
startDate: '2024-09-29', |
|
|
|
|
|
endDate: '2024-10-02', |
|
|
|
|
|
color: '#ffff00', |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
serialNo: 3, |
|
|
serialNo: 3, |
|
|
partNumber: 'BYD/R002B424A', |
|
|
|
|
|
|
|
|
partCode: 'BYD/R002B424A', |
|
|
partName: 'BUCKLE', |
|
|
partName: 'BUCKLE', |
|
|
designLead: '设计:陈晓东', |
|
|
designLead: '设计:陈晓东', |
|
|
tasks: [ |
|
|
tasks: [ |
|
|
{ name: '设计', startDate: '2024-09-14', endDate: '2024-09-16', color: '#ff0000' }, |
|
|
|
|
|
{ name: '设计', startDate: '2024-09-20', endDate: '2024-09-22', color: '#ff0000' }, |
|
|
|
|
|
{ name: '设计', startDate: '2024-09-26', endDate: '2024-09-30', color: '#ff0000' }, |
|
|
|
|
|
{ name: '机加工', startDate: '2024-10-01', endDate: '2024-10-04', color: '#0000ff' }, |
|
|
|
|
|
{ name: '材料采购', startDate: '2024-10-05', endDate: '2024-10-08', color: '#00ff00' }, |
|
|
|
|
|
{ name: '热处理', startDate: '2024-10-09', endDate: '2024-10-12', color: '#ffa500' }, |
|
|
|
|
|
{ name: '线割', startDate: '2024-10-13', endDate: '2024-10-16', color: '#800080' }, |
|
|
|
|
|
{ name: '装配', startDate: '2024-10-17', endDate: '2024-10-20', color: '#a0522d' }, |
|
|
|
|
|
{ name: '完成', startDate: '2024-10-21', endDate: '2024-10-24', color: '#ffff00' } |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-14', |
|
|
|
|
|
endDate: '2024-09-16', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-20', |
|
|
|
|
|
endDate: '2024-09-22', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-26', |
|
|
|
|
|
endDate: '2024-09-30', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '机加工', |
|
|
|
|
|
startDate: '2024-10-01', |
|
|
|
|
|
endDate: '2024-10-04', |
|
|
|
|
|
color: '#0000ff', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '材料采购', |
|
|
|
|
|
startDate: '2024-10-05', |
|
|
|
|
|
endDate: '2024-10-08', |
|
|
|
|
|
color: '#00ff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '热处理', |
|
|
|
|
|
startDate: '2024-10-09', |
|
|
|
|
|
endDate: '2024-10-12', |
|
|
|
|
|
color: '#ffa500', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '线割', |
|
|
|
|
|
startDate: '2024-10-13', |
|
|
|
|
|
endDate: '2024-10-16', |
|
|
|
|
|
color: '#800080', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '装配', |
|
|
|
|
|
startDate: '2024-10-17', |
|
|
|
|
|
endDate: '2024-10-20', |
|
|
|
|
|
color: '#a0522d', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '完成', |
|
|
|
|
|
startDate: '2024-10-21', |
|
|
|
|
|
endDate: '2024-10-24', |
|
|
|
|
|
color: '#ffff00', |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
serialNo: 4, |
|
|
serialNo: 4, |
|
|
partNumber: 'BYD/R002B361A', |
|
|
|
|
|
|
|
|
partCode: 'BYD/R002B361A', |
|
|
partName: 'BUCKLE', |
|
|
partName: 'BUCKLE', |
|
|
designLead: '设计:李凤鸣', |
|
|
designLead: '设计:李凤鸣', |
|
|
tasks: [ |
|
|
tasks: [ |
|
|
{ name: '设计', startDate: '2024-09-14', endDate: '2024-09-16', color: '#ff0000' }, |
|
|
|
|
|
{ name: '设计', startDate: '2024-09-20', endDate: '2024-09-22', color: '#ff0000' }, |
|
|
|
|
|
{ name: '设计', startDate: '2024-09-26', endDate: '2024-09-30', color: '#ff0000' }, |
|
|
|
|
|
{ name: '机加工', startDate: '2024-10-01', endDate: '2024-10-04', color: '#0000ff' }, |
|
|
|
|
|
{ name: '材料采购', startDate: '2024-10-05', endDate: '2024-10-08', color: '#00ff00' }, |
|
|
|
|
|
{ name: '热处理', startDate: '2024-10-09', endDate: '2024-10-12', color: '#ffa500' }, |
|
|
|
|
|
{ name: '线割', startDate: '2024-10-13', endDate: '2024-10-16', color: '#800080' }, |
|
|
|
|
|
{ name: '装配', startDate: '2024-10-17', endDate: '2024-10-20', color: '#a0522d' }, |
|
|
|
|
|
{ name: '完成', startDate: '2024-10-21', endDate: '2024-10-24', color: '#ffff00' }, |
|
|
|
|
|
{ name: '设计', startDate: '2024-10-25', endDate: '2024-10-27', color: '#ff0000' }, |
|
|
|
|
|
{ name: '材料采购', startDate: '2024-10-28', endDate: '2024-10-31', color: '#00ff00' }, |
|
|
|
|
|
{ name: '热处理', startDate: '2024-11-01', endDate: '2024-11-04', color: '#ffa500' }, |
|
|
|
|
|
{ name: '线割', startDate: '2024-11-05', endDate: '2024-11-08', color: '#800080' }, |
|
|
|
|
|
{ name: '装配', startDate: '2024-11-09', endDate: '2024-11-12', color: '#a0522d' }, |
|
|
|
|
|
{ name: '完成', startDate: '2024-11-13', endDate: '2024-11-16', color: '#ffff00' } |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-14', |
|
|
|
|
|
endDate: '2024-09-16', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-20', |
|
|
|
|
|
endDate: '2024-09-22', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-09-26', |
|
|
|
|
|
endDate: '2024-09-30', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '机加工', |
|
|
|
|
|
startDate: '2024-10-01', |
|
|
|
|
|
endDate: '2024-10-04', |
|
|
|
|
|
color: '#0000ff', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '材料采购', |
|
|
|
|
|
startDate: '2024-10-05', |
|
|
|
|
|
endDate: '2024-10-08', |
|
|
|
|
|
color: '#00ff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '热处理', |
|
|
|
|
|
startDate: '2024-10-09', |
|
|
|
|
|
endDate: '2024-10-12', |
|
|
|
|
|
color: '#ffa500', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '线割', |
|
|
|
|
|
startDate: '2024-10-13', |
|
|
|
|
|
endDate: '2024-10-16', |
|
|
|
|
|
color: '#800080', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '装配', |
|
|
|
|
|
startDate: '2024-10-17', |
|
|
|
|
|
endDate: '2024-10-20', |
|
|
|
|
|
color: '#a0522d', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '完成', |
|
|
|
|
|
startDate: '2024-10-21', |
|
|
|
|
|
endDate: '2024-10-24', |
|
|
|
|
|
color: '#ffff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '设计', |
|
|
|
|
|
startDate: '2024-10-25', |
|
|
|
|
|
endDate: '2024-10-27', |
|
|
|
|
|
color: '#ff0000', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '材料采购', |
|
|
|
|
|
startDate: '2024-10-28', |
|
|
|
|
|
endDate: '2024-10-31', |
|
|
|
|
|
color: '#00ff00', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '热处理', |
|
|
|
|
|
startDate: '2024-11-01', |
|
|
|
|
|
endDate: '2024-11-04', |
|
|
|
|
|
color: '#ffa500', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '线割', |
|
|
|
|
|
startDate: '2024-11-05', |
|
|
|
|
|
endDate: '2024-11-08', |
|
|
|
|
|
color: '#800080', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '装配', |
|
|
|
|
|
startDate: '2024-11-09', |
|
|
|
|
|
endDate: '2024-11-12', |
|
|
|
|
|
color: '#a0522d', |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '完成', |
|
|
|
|
|
startDate: '2024-11-13', |
|
|
|
|
|
endDate: '2024-11-16', |
|
|
|
|
|
color: '#ffff00', |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
computed: { |
|
|
computed: { |
|
|
@ -310,11 +478,15 @@ export default { |
|
|
endDate = new Date(this.dateRange[1]) |
|
|
endDate = new Date(this.dateRange[1]) |
|
|
} else { |
|
|
} else { |
|
|
// 默认时间范围 |
|
|
// 默认时间范围 |
|
|
startDate = new Date('2024-09-01') |
|
|
|
|
|
endDate = new Date('2024-11-30') |
|
|
|
|
|
|
|
|
startDate = new Date('2025-08-01') |
|
|
|
|
|
endDate = new Date('2025-11-30') |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) { |
|
|
|
|
|
|
|
|
for ( |
|
|
|
|
|
let d = new Date(startDate); |
|
|
|
|
|
d <= endDate; |
|
|
|
|
|
d.setDate(d.getDate() + 1) |
|
|
|
|
|
) { |
|
|
const day = new Date(d) |
|
|
const day = new Date(d) |
|
|
const dayOfWeek = day.getDay() |
|
|
const dayOfWeek = day.getDay() |
|
|
days.push({ |
|
|
days.push({ |
|
|
@ -322,7 +494,7 @@ export default { |
|
|
day: day.getDate(), |
|
|
day: day.getDate(), |
|
|
month: day.getMonth() + 1, |
|
|
month: day.getMonth() + 1, |
|
|
year: day.getFullYear(), |
|
|
year: day.getFullYear(), |
|
|
isWeekend: dayOfWeek === 0 || dayOfWeek === 6 |
|
|
|
|
|
|
|
|
isWeekend: dayOfWeek === 0 || dayOfWeek === 6, |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -349,7 +521,7 @@ export default { |
|
|
year: day.year, |
|
|
year: day.year, |
|
|
month: day.month, |
|
|
month: day.month, |
|
|
dayCount: 1, |
|
|
dayCount: 1, |
|
|
width: 0 |
|
|
|
|
|
|
|
|
width: 0, |
|
|
} |
|
|
} |
|
|
groups.push(currentGroup) |
|
|
groups.push(currentGroup) |
|
|
} else { |
|
|
} else { |
|
|
@ -364,7 +536,7 @@ export default { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return groups |
|
|
return groups |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
methods: { |
|
|
methods: { |
|
|
// 处理日期范围变化 |
|
|
// 处理日期范围变化 |
|
|
@ -389,7 +561,7 @@ export default { |
|
|
|
|
|
|
|
|
// 获取指定日期的任务列表 |
|
|
// 获取指定日期的任务列表 |
|
|
getTasksForDay(tasks, date) { |
|
|
getTasksForDay(tasks, date) { |
|
|
return tasks.filter(task => this.isTaskInDay(task, date)) |
|
|
|
|
|
|
|
|
return tasks.filter((task) => this.isTaskInDay(task, date)) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 判断任务是否在指定日期 |
|
|
// 判断任务是否在指定日期 |
|
|
@ -408,15 +580,16 @@ export default { |
|
|
const currentDate = new Date(date) |
|
|
const currentDate = new Date(date) |
|
|
|
|
|
|
|
|
// 计算任务在时间轴中的位置和宽度 |
|
|
// 计算任务在时间轴中的位置和宽度 |
|
|
const startIndex = this.timelineDays.findIndex(day => |
|
|
|
|
|
new Date(day.date).getTime() >= taskStart.getTime() |
|
|
|
|
|
|
|
|
const startIndex = this.timelineDays.findIndex( |
|
|
|
|
|
(day) => new Date(day.date).getTime() >= taskStart.getTime() |
|
|
) |
|
|
) |
|
|
const endIndex = this.timelineDays.findIndex(day => |
|
|
|
|
|
new Date(day.date).getTime() > taskEnd.getTime() |
|
|
|
|
|
|
|
|
const endIndex = this.timelineDays.findIndex( |
|
|
|
|
|
(day) => new Date(day.date).getTime() > taskEnd.getTime() |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// 如果找不到结束位置,使用最后一个日期 |
|
|
// 如果找不到结束位置,使用最后一个日期 |
|
|
const actualEndIndex = endIndex === -1 ? this.timelineDays.length : endIndex |
|
|
|
|
|
|
|
|
const actualEndIndex = |
|
|
|
|
|
endIndex === -1 ? this.timelineDays.length : endIndex |
|
|
const taskDuration = actualEndIndex - startIndex |
|
|
const taskDuration = actualEndIndex - startIndex |
|
|
|
|
|
|
|
|
// 如果是任务的第一天 |
|
|
// 如果是任务的第一天 |
|
|
@ -433,13 +606,13 @@ export default { |
|
|
left: '0px', |
|
|
left: '0px', |
|
|
top: '50%', |
|
|
top: '50%', |
|
|
transform: 'translateY(-50%)', |
|
|
transform: 'translateY(-50%)', |
|
|
zIndex: 1 |
|
|
|
|
|
|
|
|
zIndex: 1, |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 其他天不显示,避免重复 |
|
|
// 其他天不显示,避免重复 |
|
|
return { |
|
|
return { |
|
|
display: 'none' |
|
|
|
|
|
|
|
|
display: 'none', |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -473,9 +646,16 @@ export default { |
|
|
queryParams.endDate = this.dateRange[1] |
|
|
queryParams.endDate = this.dateRange[1] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const [ganttData, legendData] = await Promise.all([ |
|
|
|
|
|
|
|
|
getProjectGanttData(queryParams).then(({ data }) => { |
|
|
|
|
|
if (data && data.code === 0) { |
|
|
|
|
|
console.log(data); |
|
|
|
|
|
this.projectList = data.projectGanttData.projectList |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
/* const [ganttData, legendData] = await Promise.all([ |
|
|
getProjectGanttData(queryParams), |
|
|
getProjectGanttData(queryParams), |
|
|
getGanttLegend() |
|
|
|
|
|
|
|
|
getGanttLegend(), |
|
|
]) |
|
|
]) |
|
|
|
|
|
|
|
|
if (ganttData && ganttData.code === 0) { |
|
|
if (ganttData && ganttData.code === 0) { |
|
|
@ -493,7 +673,7 @@ export default { |
|
|
|
|
|
|
|
|
if (legendData && legendData.code === 0) { |
|
|
if (legendData && legendData.code === 0) { |
|
|
this.legendItems = legendData.data || this.legendItems |
|
|
this.legendItems = legendData.data || this.legendItems |
|
|
} |
|
|
|
|
|
|
|
|
} */ |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('获取甘特图数据失败:', error) |
|
|
console.error('获取甘特图数据失败:', error) |
|
|
this.$message.error('获取数据失败,使用默认数据') |
|
|
this.$message.error('获取数据失败,使用默认数据') |
|
|
@ -505,7 +685,9 @@ export default { |
|
|
// 初始化默认时间范围 |
|
|
// 初始化默认时间范围 |
|
|
initDefaultDateRange() { |
|
|
initDefaultDateRange() { |
|
|
if (!this.dateRange || this.dateRange.length === 0) { |
|
|
if (!this.dateRange || this.dateRange.length === 0) { |
|
|
this.dateRange = ['2024-09-01', '2024-11-30'] |
|
|
|
|
|
|
|
|
let start = moment().subtract(3, 'months').format('YYYY-MM-DD') |
|
|
|
|
|
let end = moment().format('YYYY-MM-DD') |
|
|
|
|
|
this.dateRange = [start,end] |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -514,13 +696,13 @@ export default { |
|
|
if (!text) return '' |
|
|
if (!text) return '' |
|
|
if (text.length <= maxLength) return text |
|
|
if (text.length <= maxLength) return text |
|
|
return text.substring(0, maxLength) + '...' |
|
|
return text.substring(0, maxLength) + '...' |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
mounted() { |
|
|
mounted() { |
|
|
this.initDefaultDateRange() |
|
|
this.initDefaultDateRange() |
|
|
this.fetchGanttData() |
|
|
this.fetchGanttData() |
|
|
} |
|
|
|
|
|
|
|
|
}, |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
@ -538,7 +720,7 @@ export default { |
|
|
padding: 15px; |
|
|
padding: 15px; |
|
|
background-color: white; |
|
|
background-color: white; |
|
|
border-radius: 8px; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
|
|
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.page-header h2 { |
|
|
.page-header h2 { |
|
|
@ -558,7 +740,7 @@ export default { |
|
|
padding: 10px 15px; |
|
|
padding: 10px 15px; |
|
|
background-color: white; |
|
|
background-color: white; |
|
|
border-radius: 8px; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
|
|
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.timeline-label { |
|
|
.timeline-label { |
|
|
@ -581,13 +763,10 @@ export default { |
|
|
flex: 0 0 320px; |
|
|
flex: 0 0 320px; |
|
|
background-color: white; |
|
|
background-color: white; |
|
|
border-radius: 8px; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
|
|
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
overflow: hidden; |
|
|
overflow: hidden; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.part-info { |
|
|
|
|
|
padding: 5px 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.part-number { |
|
|
.part-number { |
|
|
font-weight: bold; |
|
|
font-weight: bold; |
|
|
@ -615,7 +794,7 @@ export default { |
|
|
flex: 1; |
|
|
flex: 1; |
|
|
background-color: white; |
|
|
background-color: white; |
|
|
border-radius: 8px; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
|
|
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
overflow: hidden; |
|
|
overflow: hidden; |
|
|
display: flex; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
flex-direction: column; |
|
|
@ -758,6 +937,31 @@ export default { |
|
|
opacity: 0.8; |
|
|
opacity: 0.8; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 日期范围选择器样式 */ |
|
|
|
|
|
.date-range-picker >>> .el-range-input { |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
color: #606266; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.date-range-picker >>> .el-range-separator { |
|
|
|
|
|
padding: 0 5px; |
|
|
|
|
|
color: #c0c4cc; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.date-range-picker >>> .el-range__icon { |
|
|
|
|
|
margin-left: 5px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.date-range-picker >>> .el-input__inner { |
|
|
|
|
|
padding: 0 8px; |
|
|
|
|
|
height: 28px !important; |
|
|
|
|
|
line-height: 28px !important; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.date-range-picker >>> .el-range-editor { |
|
|
|
|
|
padding: 0 8px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* 响应式设计 */ |
|
|
/* 响应式设计 */ |
|
|
@media (max-width: 1200px) { |
|
|
@media (max-width: 1200px) { |
|
|
.main-content { |
|
|
.main-content { |
|
|
@ -772,5 +976,9 @@ export default { |
|
|
.gantt-section { |
|
|
.gantt-section { |
|
|
height: 500px; |
|
|
height: 500px; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.date-range-picker { |
|
|
|
|
|
width: 280px !important; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</style> |