<template>
<div class="container">
<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="toggleGantt"
>
{{ showGantt ? '收起甘特图' : '展开甘特图' }}
</el-button>
</el-form-item>
</el-form>
</div>
<div ref="gantt" class="gantt-container"></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 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: {Dialog, Treeselect},
name: "gantt",
data() {
return {
tasks: {
data: [],
},
queryParams: {
name: null,
respPerson: null
},
showGantt: true, // 状态控制甘特图显示
planOptions: [],
open: false, // 控制详情弹窗显示
form: {} // 当前查看的任务
};
},
// 把携带的参数放到queryParams查询参数里
created() {
// console.log(this.$route.params.id);
},
methods: {
// 查看任务详情
handleView(taskId) {
// 根据任务ID查找任务详情
const task = this.tasks.data.find(item => item.id == taskId);
if (task) {
this.getTreeselect();
this.form = task;
this.open = true;
}
},
toggleGantt() {
this.showGantt = !this.showGantt;
this.initData(); // 重新初始化甘特图
},
// 上级节点
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
}
},
//开始时间-结束时间参数
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: this.showGantt ? 800 : "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>
实现表格和甘特图的左右拖拽
最新发布