<template>
<div class="container">
<TablePane ref="tablePane" :buttons="this.operations" :search-fields="searchFields"
:query-params.sync="this.queryParams"
:get-list="getListFun"
:table-data="tableData"
v-if="showGantt"
>
</TablePane>
<div v-if="!showGantt">
<div class="search-wrapper">
<el-form class="search-from" :inline="true" label-width="70px" @submit.prevent="initData">
<el-form-item label="名称">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable/>
</el-form-item>
<el-form-item label="责任人">
<el-input v-model="queryParams.respPerson" placeholder="请输入责任人" clearable/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="initData">
<i class="el-icon-search"></i> 搜索
</el-button>
<el-button @click="resetQuery">
<i class="el-icon-refresh"></i> 重置
</el-button>
<el-button
type="primary"
style="margin-left: 10px;"
@click="retractGantt"
>
{{ '收起甘特图' }}
</el-button>
</el-form-item>
</el-form>
</div>
<div ref="gantt" class="gantt-container"></div>
</div>
<!-- 添加或修改治理计划对话框 -->
<Dialog title="查看治理计划" :visible.sync="open" width="850px" height="600px" append-to-body>
<el-form ref="form" :model="form" label-width="100px" disabled>
<el-row>
<el-col :span="12">
<el-form-item label="编号" prop="code">
<el-input v-model="form.code" placeholder="请输入编号"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="名称" prop="text">
<el-input v-model="form.text" placeholder="请输入名称"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="上级计划" prop="parent">
<treeselect v-model="form.parent" :options="planOptions" :normalizer="normalizer"
placeholder="选择上级计划" disabled
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="完成度(%)" prop="progress">
<el-input-number v-model="form.progress" placeholder="请输入完成百分比"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="责任人" prop="respPerson">
<span slot="label">
<span class="content-font">责任人</span>
</span>
<!-- 用户向导 -->
<p style="margin-top:0px; float: left;font-size: 12px;">
{{ form.respPerson }}
<el-button size="mini"
type="primary"
icon="el-icon-user"
class="btn-wizard-trigger"
style="margin-left: 10px"
>选择用户
</el-button>
</p>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="责任部门" prop="respDept">
<el-input v-model="form.respDept" placeholder="请输入责任部门"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="计划开始日期" prop="planStartDate">
<el-date-picker clearable
v-model="form.planStartDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择计划开始日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际开始日期" prop="realStartDate">
<el-date-picker clearable
v-model="form.realStartDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择实际开始日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="计划结束时间" prop="planEndDate">
<el-date-picker clearable
v-model="form.planEndDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择计划结束时间">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际结束日期" prop="realEndDate">
<el-date-picker clearable
v-model="form.realEndDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择实际结束日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="计划工期(天)" prop="planDuration">
<el-input v-model="form.planDuration" placeholder="请选择计划日期"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际工期(天)" prop="realDuration">
<el-input v-model="form.realDuration" placeholder="请选择实际日期"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" placeholder="请输入备注"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="反馈内容" prop="feedback">
<el-input v-model="form.feedback" type="textarea" placeholder="请输入内容"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="buttons" class="dialog-footer">
<el-button @click="open = false">关 闭</el-button>
</div>
</Dialog>
</div>
</template>
<script>
import TablePane from '@/components/TablePane'
import Dialog from '@/components/Dialog'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import {gantt} from "dhtmlx-gantt";
import "dhtmlx-gantt/codebase/dhtmlxgantt.css";
import {listPlan} from "@/api/dw/plan/planview";
export default {
components: {TablePane, Dialog, Treeselect},
name: "gantt",
data() {
return {
getListFun: listPlan,
searchFields: [
{'field': 'name', 'label': '名称', 'type': 'input'},
{'field': 'respPerson', 'label': '责任人', 'type': 'input'},
],
operations: [
{
'label': '展开甘特图',
'click': this.expandGantt,
'hasPermi': ['dw:planview:list']
}],
tableData: {
'idField': 'uid',
//加载数据后进行转换用于特殊处理
'loadDataHook': this.loadDataHook,
'attrs': {
'row-key': 'uid',
'@row-click': this.rowClick,
'tree-props': { children: 'children', hasChildren: 'hasChildren' },
'default-expand-all': true
},
'column': [
{'label': '编号', 'prop': 'code', 'attrs': { 'sortable': false,'align': 'left' } },
{'label': '状态', 'prop': 'status1', 'attrs': { 'sortable': false } ,'width': 80,'columnHook': this.light},
{'label': '名称', 'prop': 'name', 'attrs': { 'sortable': false } },
{'label': '完成百分比', 'prop': 'schedule', 'attrs': { 'sortable': false } },
{'label': '责任人', 'prop': 'respPerson', 'attrs': { 'sortable': false } },
{'label': '责任部门', 'prop': 'respDept' , 'attrs': { 'sortable': false }},
{'label': '计划开始日期', 'prop': 'planStartDate' ,'type':"dateTime" , 'format': (date) => {
return this.parseTime(date, '{y}-{m}-{d}')}},
{'label': '计划结束时间', 'prop': 'planEndDate' ,'type':"dateTime" , 'format': (date) => {
return this.parseTime(date, '{y}-{m}-{d}')}},
{'label': '实际开始日期', 'prop': 'realStartDate' ,'type':"dateTime" , 'format': (date) => {
return this.parseTime(date, '{y}-{m}-{d}')}},
{'label': '实际结束日期', 'prop': 'realEndDate' ,'type':"dateTime" , 'format': (date) => {
return this.parseTime(date, '{y}-{m}-{d}')}},
{'label': '计划工期', 'prop': 'planDuration' },
{'label': '实际工期', 'prop': 'realDuration' },
{'label': '备注', 'prop': 'remarks' },
{
'width': 100,
'label': '操作',
'type': 'button',
'attrs': { 'sortable': false, 'fixed': 'right' },
'buttons': [
{
'icon': 'el-icon-view',
'label': '查看',
'hasPermi': ['dw:planview:query'],
'click': this.handleView
},
]
}
]
},
/***************************************************甘特图 start****************************************************************/
tasks: {
data: [],
},
queryParams: {
name: null,
respPerson: null
},
showGantt: true, // 状态控制甘特图显示
planOptions: [],
open: false, // 控制详情弹窗显示
form: {} // 当前查看的任务
/***************************************************甘特图 end****************************************************************/
};
},
// 把携带的参数放到queryParams查询参数里
created() {
// console.log(this.$route.params.id);
},
methods: {
/** 查询治理计划列表 */
getList() {
this.$refs.tablePane.loadData()
},
loadDataHook(response) {
response.data = this.handleTree(response.data, 'uid','parentUid')
return response
},
light(column, row){
const currentDate = this.getCurrentDate();
if( row.realStartDate != null){//有实际开始时间
// 状态灯:绿色:已完成;
// 红色:计划开始时间小于当前日期并且没有实际开始日期;
// 黄色:有实际开始日期,但是没有实际结束日期,并且计划结束日期小宇当前日期。
// 其他状态都没有颜色(数据体系里面的计划状态灯昨天没写,也按这个规则)
var res = this.compareDate(new Date(row.planEndDate),new Date(currentDate))
if( row.realEndDate == null){
if(res== -1){
column.class='circle-light-yellow'
}
}else{
column.class='circle-light-green'
}
}else{//没用实际开始时间 红色:没有实际开始,但是当前日期大于计划开始日期;
if(row.planStartDate != null){//有计划开始时间
var res = this.compareDate(new Date(row.planStartDate),new Date(currentDate))
if(res == -1){
column.class='circle-light-red'
}
}
}
},
getCurrentDate(){
const dateObj = new Date();
const year = dateObj.getFullYear(); // 获取当前年份
const month = ("0" + (dateObj.getMonth() + 1)).slice(-2); // 获取当前月份,其中需要将月份加1,因为月份是从0开始计数的
const day = ("0" + dateObj.getDate()).slice(-2); // 获取当前日期
const formattedDate = `${year}-${month}-${day}`; // 格式化日期
return formattedDate; // 输出当前时间的年月日
},
compareDate(date1,date2){//date1 > date2 返回1;date1 < date2 返回-1 相等返回0
if (date1.getTime() > date2.getTime()) {
return 1;
} else if (date1.getTime() < date2.getTime()) {
return -1;
} else {
return 0;
}
},
// 上级节点
getTreeselect() {
listPlan().then(response => {
const data = {uid: 0, name: '顶级节点', children: []};
data.children = this.handleTree(response.data, 'uid', 'parentUid')
this.planOptions.push(data)
})
},
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children
}
return {
id: node.uid,
label: node.name,
children: node.children
}
},
rowClick(row, column, event) {
// 行点击事件
},
/***************************************************甘特图 start****************************************************************/
// 查看任务详情
handleView(taskId) {
// 根据任务ID查找任务详情
const task = this.tasks.data.find(item => item.id == taskId);
if (task) {
this.getTreeselect();
this.form = task;
this.open = true;
}
},
expandGantt() {
this.showGantt = false
this.$nextTick(() => {
this.initData(); // 确保DOM更新后再初始化甘特图
});
},
retractGantt() {
this.showGantt = true;
this.$nextTick(() => {
this.initData(); // 确保DOM更新后再初始化甘特图
});
},
//开始时间-结束时间参数
DateDifference: function (strDateStart, strDateEnd) {
var begintime_ms = Date.parse(new Date(strDateStart.replace(/-/g, "/"))); //begintime 为开始时间
var endtime_ms = Date.parse(new Date(strDateEnd.replace(/-/g, "/"))); // endtime 为结束时间
var date3 = endtime_ms - begintime_ms; //时间差的毫秒数
var days = Math.floor(date3 / (24 * 3600 * 1000));
return days;
},
// 重置查询
resetQuery() {
this.queryParams = {
name: null,
respPerson: null
};
this.initData();
},
initData: function () {
gantt.clearAll();
listPlan(this.queryParams).then((res) => {
// 重新初始化甘特图配置
gantt.config.autosize = true;
gantt.config.readonly = true;
gantt.config.show_grid = true;
this.tasks.data = res.data.map((item) => {
let statusColor;
//存在status字段 说明非一级菜单,判断阶段的具体类型 设置不同颜色
if (item.status == '1') {
//冒烟
statusColor = "#84bd54"
} else if (item.status == '2') {
//单元
statusColor = "#fcca02"
} else if (item.status == '3') {
//回归
statusColor = "#dc1626"
} else {
statusColor = "#999999"
}
return {
id: item.uid,
parent: item.parent,
text: item.name,
start_date: item.planStartDate,
duration: item.planDuration,
open: true, //默认打开,
toolTipsTxt: item.name,
progress: item.schedule,
status: item.status,
code: item.code,
respPerson: item.respPerson,
respDept: item.respDept,
planStartDate: item.planStartDate,
planEndDate: item.planEndDate,
realStartDate: item.realStartDate,
realEndDate: item.realEndDate,
planDuration: item.planDuration,
realDuration: item.realDuration,
remarks: item.remarks,
feedback: item.feedback,
color: statusColor,
}
});
//自适应甘特图的尺寸大小, 使得在不出现滚动条的情况下, 显示全部任务
gantt.config.autosize = true;
//只读模式
gantt.config.readonly = true;
//是否显示左侧树表格
gantt.config.show_grid = true;
//表格列设置
gantt.config.columns = [
{
name: "code",
label: "编号",
tree: true,
width: "160",
onrender: function (task, node) {
node.setAttribute(
"class",
"gantt_cell gantt_last_cell gantt_cell_tree " + task.status
);
},
},
{
name: "status",
label: "状态",
align: "center",
width: "80",
template: function (task) {
// 自定义状态列显示为状态灯
return `<div class="status-light" style="background-color: ${task.color}"></div>`;
}
},
{name: "text", label: "名称", align: "center", width: "180", hide: true},
{name: "progress", label: "完成度(%)", align: "center", width: "90", hide: true},
{name: "respPerson", label: "责任人", align: "center", width: "120", hide: true},
{name: "respDept", label: "责任部门", align: "center", width: "140", hide: true},
{name: "planStartDate", label: "计划开始日期", align: "center", width: "130", hide: true},
{name: "planEndDate", label: "计划结束时间", align: "center", width: "130", hide: true},
{name: "realStartDate", label: "实际开始日期", align: "center", width: "130", hide: true},
{name: "realEndDate", label: "实际结束日期", align: "center", width: "130", hide: true},
{name: "planDuration", label: "计划工期", align: "center", width: "90", hide: true},
{name: "realDuration", label: "实际工期", align: "center", width: "90", hide: true},
{name: "remarks", label: "备注", align: "center", width: "220", hide: true},
// {
// name: "operate",
// label: "操作",
// align: "center",
// width: "80",
// template: function (task) {
// return '<el-button size="mini" type="text" onclick=" window.vueInstance.handleView(\'' + task.id + '\')">查看</el-button>';
// }
// }
];
var weekScaleTemplate = function (date) {
var dateToStr = gantt.date.date_to_str("%m %d");
var endDate = gantt.date.add(
gantt.date.add(date, 1, "week"),
-1,
"day"
);
var weekNum = gantt.date.date_to_str("第 %W 周");
return weekNum(date);
};
var daysStyle = function (date) {
var dateToStr = gantt.date.date_to_str("%D");
if (dateToStr(date) == "六" || dateToStr(date) == "日")
return "weekend";
return "";
};
gantt.config.subscales = [
{
unit: "week",
step: 1,
template: weekScaleTemplate,
},
{
unit: "day",
step: 1,
format: "%d",
},
];
gantt.plugins({
tooltip: true,
});
//设置鼠标放置显示事件
gantt.attachEvent("onGanttReady", function() {
var tooltips = gantt.ext.tooltips;
gantt.templates.tooltip_text = function(start, end, task) {
return "编号:" + task.code + "<br/>" +
"名称:" + task.text + "<br/>" +
"计划开始:" + gantt.templates.tooltip_date_format(start) + "<br/>" +
"工期:" + task.duration
};
});
//设置任务条进度内容
gantt.templates.progress_text = function (start, end, task) {
return (
"<div style='text-align:left;color:#fff;padding-left:20px'>" +
task.progress + "% </div>"
);
};
//任务条显示内容
gantt.templates.task_text = function (start, end, task) {
// return task.text + '(' + task.duration + '天)';
return (
"<div style='text-align:center;color:#fff'>" +
task.text +
"(" +
task.duration +
"天)" +
"</div>"
);
};
//任务条上的文字大小 以及取消border自带样式
gantt.templates.task_class = function (start, end, item) {
return item.$level == 0 ? "firstLevelTask" : "secondLevelTask";
};
gantt.config.layout = {
css: "gantt_container",
cols: [
{
width: "100%", // 如果收起甘特图,左侧表格宽度占满
min_width: 300, //表格左侧最小宽度
rows: [
{
view: "grid",
scrollX: "gridScroll",
scrollable: true,
scrollY: "scrollVer",
},
{
view: "scrollbar",
id: "gridScroll",
group: "horizontal",
},
],
},
...(!this.showGantt ? [{
resizer: true,
width: 1,
},
{
rows: [
{
view: "timeline",
scrollX: "scrollHor",
scrollY: "scrollVer",
},
{
view: "scrollbar",
id: "scrollHor",
group: "horizontal",
},
]
}] : [])
],
};
//时间轴图表中,任务条形图的高度
// gantt.config.task_height = 28
//时间轴图表中,甘特图的高度
// gantt.config.row_height = 36
//时间轴图表中,如果不设置,只有行边框,区分上下的任务,设置之后带有列的边框,整个时间轴变成格子状。
gantt.config.show_task_cells = true;
//当task的长度改变时,自动调整图表坐标轴区间用于适配task的长度
gantt.config.fit_tasks = true;
gantt.config.min_column_width = 50;
gantt.config.auto_types = true;
gantt.config.xml_date = "%Y-%m-%d";
gantt.config.scale_unit = "month";
gantt.config.step = 1;
gantt.config.date_scale = "%Y年%M";
gantt.config.start_on_monday = true;
gantt.config.scale_height = 160;
gantt.config.autoscroll = true;
gantt.config.calendar_property = "start_date";
gantt.config.calendar_property = "end_date";
gantt.config.readonly = true;
gantt.i18n.setLocale("cn");
// 初始化
gantt.init(this.$refs.gantt);
// 数据解析
gantt.parse(this.tasks);
// 添加双击行事件监听器
gantt.attachEvent("onTaskDblClick", function(id, e) {
// 调用查看详情方法
window.vueInstance.handleView(id);
return true;
});
});
},
},
mounted() {
// 将当前Vue实例赋值给window.vueInstance,供甘特图中调用
window.vueInstance = this;
this.initData();
},
};
</script>
<style lang="scss" scoped>
.firstLevelTask {
border: none;
.gantt_task_content {
font-size: 13px;
}
}
.secondLevelTask {
border: none;
}
.thirdLevelTask {
border: 2px solid #da645d;
color: #da645d;
background: #da645d;
}
.milestone-default {
border: none;
background: rgba(0, 0, 0, 0.45);
}
.milestone-unfinished {
border: none;
background: #5692f0;
}
.milestone-finished {
border: none;
background: #84bd54;
}
.milestone-canceled {
border: none;
background: #da645d;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
.container {
height: 100%;
width: 100%;
position: relative;
padding: 10px;
.gantt_grid_head_cell {
padding-left: 20px;
text-align: left !important;
font-size: 14px;
color: #333;
}
.left-container {
height: 100%;
}
.green,
.yellow,
.pink,
.popular {
.gantt_tree_icon.gantt_file {
background: none;
position: relative;
&::before {
content: "";
width: 10px;
height: 10px;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
.green {
.gantt_tree_icon.gantt_file {
&::before {
background: #84bd54;
}
}
}
.yellow {
.gantt_tree_icon.gantt_file {
&::before {
background: #fcca02;
}
}
}
.pink {
.gantt_tree_icon.gantt_file {
&::before {
background: #da645d;
}
}
}
.popular {
.gantt_tree_icon.gantt_file {
&::before {
background: #d1a6ff;
}
}
}
}
.left-container {
height: 100%;
}
.gantt_task_content {
text-align: left;
padding-left: 10px;
}
.gantt-container {
height: calc(100% - 65px) !important;
}
// 状态灯样式
::v-deep .gantt_grid_data .gantt_cell div.status-light {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin: 0 auto;
}
// 表格表头居中样式
::v-deep .gantt_grid_head_cell {
text-align: center !important;
}
.search-wrapper {
text-align: left;
padding: 10px 0;
}
.search-from {
.el-form-item--mini.el-form-item,
.el-form-item--small.el-form-item {
margin-bottom: 3px;
}
}
</style>
<style lang="less">
.circle-light-yellow > span::before {
content: '';
display: inline-block;
width: 16px;
height: 16px;
background-color: #e6a23c;
border-radius: 50%;
margin-right: 10px;
}
.circle-light-green > span::before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
background-color: #67c23a;
border-radius: 50%;
margin-right: 10px;
}
.circle-light-red > span::before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
background-color: #f56c6c;
border-radius: 50%;
margin-right: 10px;
}
</style>
点击展开甘特图按钮,甘特图没切换显示,是甘特图加载有问题吗
最新发布