|
@@ -6,8 +6,15 @@
|
|
|
@search-change="searchChange"
|
|
|
@search-reset="resetChange"
|
|
|
/>
|
|
|
- <div class="my-gantt">
|
|
|
- <div id="gantt_here" class="gantt-container"></div>
|
|
|
+ <div class="btns">
|
|
|
+ <el-button type="params" @click="exportToPNG">导出PNG图片</el-button>
|
|
|
+ <el-button type="params" @click="exportToPDF">导出PDF文件</el-button>
|
|
|
+ <!-- <el-button type="params" v-print="printObj" @click="toPrint"
|
|
|
+ >打印</el-button
|
|
|
+ > -->
|
|
|
+ </div>
|
|
|
+ <div class="main-content">
|
|
|
+ <div ref="ganttRef" id="gantt_here" class="gantt-container"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -15,12 +22,42 @@
|
|
|
<script setup>
|
|
|
import { gantt } from "dhtmlx-gantt";
|
|
|
import "dhtmlx-gantt/codebase/dhtmlxgantt.css";
|
|
|
+import html2canvas from "html2canvas";
|
|
|
+import { htmlPdf } from "@/utils/htmlPDF.js";
|
|
|
import { useCrud } from "@/hooks/userCrud";
|
|
|
+const ganttRef = ref(null);
|
|
|
const { form, data, option, search, page, toDeleteIds, Methords, Utils } =
|
|
|
useCrud({
|
|
|
- src: "/api/v1/process/census/queryApsResultGant",
|
|
|
+ src: "/api/v1/process/census/productLineApsResultGant",
|
|
|
pageStr: "no",
|
|
|
});
|
|
|
+const exportToPDF = () => {
|
|
|
+ var fileName = "甘特图pdf";
|
|
|
+ const fileList = document.getElementsByClassName("gantt_here");
|
|
|
+ htmlPdf(fileName, document.querySelector("#gantt_here"), fileList, 1111);
|
|
|
+};
|
|
|
+const exportToPNG = async () => {
|
|
|
+ const chartContainer = ganttRef.value;
|
|
|
+ const canvas = await html2canvas(chartContainer);
|
|
|
+ const img = canvas.toDataURL("image/png");
|
|
|
+ const a = document.createElement("a");
|
|
|
+ a.href = img;
|
|
|
+ a.download = "甘特图.png";
|
|
|
+ a.click();
|
|
|
+};
|
|
|
+// const printUrl = ref("");
|
|
|
+// const toPrint = async () => {
|
|
|
+// const chartContainer = ganttRef.value;
|
|
|
+// const canvas = await html2canvas(chartContainer);
|
|
|
+// const img = canvas.toDataURL("image/png");
|
|
|
+// printUrl.value = img;
|
|
|
+// };
|
|
|
+// const printObj = ref({
|
|
|
+// id: "gantt_here",
|
|
|
+// preview: true,
|
|
|
+// popTitle: "甘特图",
|
|
|
+// url: printUrl.value,
|
|
|
+// });
|
|
|
const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } =
|
|
|
Methords;
|
|
|
option.value = Object.assign(option.value, {
|
|
@@ -58,7 +95,7 @@ option.value = Object.assign(option.value, {
|
|
|
prop: "timeType",
|
|
|
search: true,
|
|
|
type: "select",
|
|
|
- searchValue: "0",
|
|
|
+ searchValue: "1",
|
|
|
dicData: [
|
|
|
{
|
|
|
dictLabel: "小时",
|
|
@@ -134,99 +171,105 @@ const demoData = {
|
|
|
{
|
|
|
id: 11,
|
|
|
text: "Project #1",
|
|
|
- start_date: "28-07-2024",
|
|
|
+ start_date: "2024-07-01 08:00:00",
|
|
|
+ endTime: "2024-09-30 09:00:00",
|
|
|
+ startTime: "2024-07-30 08:00:00",
|
|
|
duration: "11",
|
|
|
- progress: 1,
|
|
|
- open: true,
|
|
|
- },
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- text: "Project #2",
|
|
|
- start_date: "01-07-2024",
|
|
|
- duration: "18",
|
|
|
- progress: 0.4,
|
|
|
- open: true,
|
|
|
- },
|
|
|
-
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- text: "Task #1",
|
|
|
- start_date: "02-07-2024",
|
|
|
- duration: "8",
|
|
|
- parent: "1",
|
|
|
- progress: 0.5,
|
|
|
- open: true,
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- text: "Task #2",
|
|
|
- start_date: "11-07-2024",
|
|
|
- duration: "8",
|
|
|
- parent: "1",
|
|
|
- progress: 0.6,
|
|
|
+ progress: 0.8,
|
|
|
open: true,
|
|
|
},
|
|
|
+ ],
|
|
|
+};
|
|
|
+//初始化甘特图
|
|
|
+const initGantt = (type) => {
|
|
|
+ gantt.plugins({
|
|
|
+ tooltip: true,
|
|
|
+ marker: true,
|
|
|
+ critical_path: true,
|
|
|
+ export_api: true,
|
|
|
+ });
|
|
|
+ gantt.templates.task_text = function (start, end, task) {
|
|
|
+ return (
|
|
|
+ "<span style='padding:0 5px;color: #000; display: inline-block;width: 100%; white-space: nowrap;overflow: hidden; text-overflow: ellipsis; '>" +
|
|
|
+ task.text +
|
|
|
+ "</span>"
|
|
|
+ );
|
|
|
+ };
|
|
|
+ gantt.templates.progress_text = function (start, end, task) {
|
|
|
+ return (
|
|
|
+ "<span style='text-align:left;'>" +
|
|
|
+ Math.round(task.progress * 100) +
|
|
|
+ "% </span>"
|
|
|
+ );
|
|
|
+ };
|
|
|
+ gantt.templates.task_class = function (start, end, task) {
|
|
|
+ return "taskClass";
|
|
|
+ };
|
|
|
+ if (type) {
|
|
|
+ gantt.config.duration_unit = type;
|
|
|
+ }
|
|
|
+ gantt.config.show_today = true;
|
|
|
+ gantt.config.grid_width = 350;
|
|
|
+ gantt.config.add_column = false;
|
|
|
+ gantt.config.duration_step = 1;
|
|
|
+ gantt.config.drag_resize = true;
|
|
|
+ gantt.config.columns = [
|
|
|
{
|
|
|
- id: 4,
|
|
|
- text: "Task #3",
|
|
|
- start_date: "13-07-2024",
|
|
|
- duration: "6",
|
|
|
- parent: "1",
|
|
|
- progress: 0.5,
|
|
|
- open: true,
|
|
|
+ tree: true,
|
|
|
+ name: "text",
|
|
|
+ label: "订单名称",
|
|
|
+ width: "240",
|
|
|
},
|
|
|
{
|
|
|
- id: 5,
|
|
|
- text: "Task #1.1",
|
|
|
- start_date: "02-07-2024",
|
|
|
- duration: "7",
|
|
|
- parent: "2",
|
|
|
- color: "red",
|
|
|
- progress: 0.6,
|
|
|
- open: true,
|
|
|
+ name: "startTime",
|
|
|
+ label: "开始时间",
|
|
|
+ align: "center",
|
|
|
+ width: "160",
|
|
|
+ template(task) {
|
|
|
+ return task.startTime;
|
|
|
+ },
|
|
|
},
|
|
|
{
|
|
|
- id: 6,
|
|
|
- text: "Task #1.2",
|
|
|
- start_date: "03-07-2024",
|
|
|
- duration: "7",
|
|
|
- parent: "2",
|
|
|
- progress: 0.6,
|
|
|
- open: true,
|
|
|
+ name: "endTime",
|
|
|
+ label: "结束时间",
|
|
|
+ align: "center",
|
|
|
+ width: "160",
|
|
|
+ template(task) {
|
|
|
+ return task.endTime;
|
|
|
+ },
|
|
|
},
|
|
|
- ],
|
|
|
-};
|
|
|
-const event = ref([]);
|
|
|
-//初始化甘特图
|
|
|
-const initGantt = () => {
|
|
|
- gantt.config.grid_width = 350;
|
|
|
- gantt.config.add_column = false; //添加符号
|
|
|
-
|
|
|
- //时间轴图表中,如果不设置,只有行边框,区分上下的任务,设置之后带有列的边框,整个时间轴变成格子状。
|
|
|
- gantt.config.autofit = false;
|
|
|
- gantt.config.row_height = 60;
|
|
|
- gantt.config.bar_height = 34;
|
|
|
- // gantt.config.fit_tasks = true //自动延长时间刻度,以适应所有显示的任务
|
|
|
- gantt.config.auto_types = true; //将包含子任务的任务转换为项目,将没有子任务的项目转换回任务
|
|
|
- gantt.config.xml_date = '%d-%m-%Y' //甘特图时间格式
|
|
|
+ ];
|
|
|
+ gantt.config.autofit = true;
|
|
|
+ gantt.config.resize_rows = true;
|
|
|
+ gantt.config.row_height = 40;
|
|
|
+ gantt.config.bar_height = 30;
|
|
|
+ gantt.config.min_column_width = 60;
|
|
|
+ gantt.config.xml_date = "%Y-%m-%d %H:%i:%s"; //甘特图时间格式
|
|
|
gantt.config.readonly = true; //是否只读
|
|
|
gantt.i18n.setLocale("cn"); //设置语言
|
|
|
- gantt.plugins({ tooltip: true });
|
|
|
- gantt.init("gantt_here"); //初始化
|
|
|
gantt.templates.tooltip_text = function (start, end, task) {
|
|
|
const text = `名称: ${task.text}<br/>总数: ${task.num}<br/>完成进度: ${task.progress * 100}%`;
|
|
|
- return task?.id?.startsWith?.(NODE_PREFIX)
|
|
|
- ? `${text}<br/>交期时间: ${this.tooltip_date_format(end)}`
|
|
|
- : `${text}<br/>计划开始时间: ${this.tooltip_date_format(start)}<br/>计划结束时间: ${this.tooltip_date_format(end)}`; // eslint-disable-line
|
|
|
+ return !task.parent
|
|
|
+ ? `${text}<br/>交期时间: ${task.deliverWhen}`
|
|
|
+ : `${text}<br/>计划开始时间: ${task.startTime}<br/>计划结束时间: ${task.endTime}`; // eslint-disable-line
|
|
|
};
|
|
|
- gantt.parse(demoData); //填充数据
|
|
|
+ const dateToStr = gantt.date.date_to_str(gantt.config.task_date);
|
|
|
+ const today = new Date(new Date().setHours(0, 0, 0, 0));
|
|
|
+ gantt.addMarker({
|
|
|
+ start_date: today,
|
|
|
+ css: "today",
|
|
|
+ text: "今日",
|
|
|
+ title: `Today: ${dateToStr(today)}`,
|
|
|
+ });
|
|
|
+ gantt.init("gantt_here"); //初始化
|
|
|
};
|
|
|
const setTime = () => {
|
|
|
search.value = {
|
|
|
searchTime: getCurrentMonthStartAndEndDates(),
|
|
|
- timeType: "0",
|
|
|
+ timeType: "1",
|
|
|
};
|
|
|
};
|
|
|
+
|
|
|
function convertDateString1(str) {
|
|
|
// 将输入的日期字符串转换为 Date 对象
|
|
|
let date = new Date(str);
|
|
@@ -252,86 +295,108 @@ function convertDateString2(str) {
|
|
|
|
|
|
return formattedDate;
|
|
|
}
|
|
|
-onBeforeMount(() => {});
|
|
|
+// watchEffect(() => {
|
|
|
+// //此api后续弃用
|
|
|
+// // gantt.parse({ data: data.value });
|
|
|
+// gantt.parse(demoData);
|
|
|
+// });
|
|
|
+const setGantt = () => {
|
|
|
+ if (search?.value.searchTime) {
|
|
|
+ gantt.config.start_date = convertDateString1(search?.value.searchTime[0]);
|
|
|
+ gantt.config.end_date = convertDateString2(search?.value.searchTime[1]);
|
|
|
+ gantt.config.show_tasks_outside_timescale = true;
|
|
|
+ }
|
|
|
+ switch (search.value.timeType) {
|
|
|
+ case "0":
|
|
|
+ gantt.config.duration_unit = "hour";
|
|
|
+ gantt.config.scale_height = 28 * 4;
|
|
|
+ gantt.config.scales = [
|
|
|
+ { unit: "year", step: 1, format: "%Y年" },
|
|
|
+ { unit: "month", step: 1, format: "%m月" },
|
|
|
+ {
|
|
|
+ unit: "day",
|
|
|
+ step: 1,
|
|
|
+ format: "%d日",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ unit: "hour",
|
|
|
+ step: 1,
|
|
|
+ format: "%H时",
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ gantt.init("gantt_here");
|
|
|
+ gantt.render();
|
|
|
+ gantt.parse({ data: useData.value });
|
|
|
+ break;
|
|
|
+ case "1":
|
|
|
+ gantt.config.scale_height = 28 * 4;
|
|
|
+ gantt.config.scales = [
|
|
|
+ { unit: "year", step: 1, format: "%Y年" },
|
|
|
+ { unit: "month", step: 1, format: "%m月" },
|
|
|
+ {
|
|
|
+ unit: "day",
|
|
|
+ step: 1,
|
|
|
+ format: "%d日",
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ gantt.config.duration_unit = "day";
|
|
|
+
|
|
|
+ gantt.init("gantt_here");
|
|
|
+ gantt.render();
|
|
|
+ gantt.parse({ data: useData.value });
|
|
|
+ break;
|
|
|
+ case "2":
|
|
|
+ // initGantt("month");
|
|
|
+ gantt.config.scale_height = 28 * 4;
|
|
|
+ gantt.config.scales = [
|
|
|
+ { unit: "year", step: 1, format: "%Y年" },
|
|
|
+ { unit: "month", step: 1, format: "%m月" },
|
|
|
+ ];
|
|
|
+ gantt.config.duration_unit = "month";
|
|
|
+ gantt.init("gantt_here");
|
|
|
+ gantt.parse({ data: useData.value });
|
|
|
+ gantt.render();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+};
|
|
|
+const useData = ref([]);
|
|
|
+const setUseData = (data) => {
|
|
|
+ const data1 = [...data];
|
|
|
+ data1.forEach((element) => {
|
|
|
+ element.start_date = element.startTime;
|
|
|
+ if (element.progress != 1) {
|
|
|
+ element.color = "orange";
|
|
|
+ } else {
|
|
|
+ element.color = "green";
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const data2 = JSON.parse(JSON.stringify(data1));
|
|
|
+ useData.value = data2;
|
|
|
+};
|
|
|
onMounted(() => {
|
|
|
setTime();
|
|
|
- initGantt();
|
|
|
dataList();
|
|
|
+ initGantt();
|
|
|
});
|
|
|
watch(
|
|
|
- search,
|
|
|
- (val) => {
|
|
|
- switch (val.timeType) {
|
|
|
- case "0":
|
|
|
- gantt.config.scale_height = 28 * 4;
|
|
|
- gantt.config.subscales = [
|
|
|
- {
|
|
|
- unit: "year",
|
|
|
- step: 1,
|
|
|
- date: "%Y年",
|
|
|
- },
|
|
|
- {
|
|
|
- unit: "month",
|
|
|
- step: 1,
|
|
|
- date: "%F",
|
|
|
- },
|
|
|
- {
|
|
|
- unit: "day",
|
|
|
- step: 1,
|
|
|
- date: "%d日",
|
|
|
- },
|
|
|
- ];
|
|
|
- gantt.config.scale_unit = "hour";
|
|
|
- gantt.config.date_scale = "%H时";
|
|
|
- gantt.render();
|
|
|
- break;
|
|
|
- case "1":
|
|
|
- gantt.config.scale_height = 28 * 4;
|
|
|
- gantt.config.subscales = [
|
|
|
- {
|
|
|
- unit: "year",
|
|
|
- step: 1,
|
|
|
- date: "%Y年",
|
|
|
- },
|
|
|
- {
|
|
|
- unit: "month",
|
|
|
- step: 1,
|
|
|
- date: "%F",
|
|
|
- },
|
|
|
- ];
|
|
|
- gantt.config.scale_unit = "day";
|
|
|
- gantt.config.date_scale = "周%D,%d日";
|
|
|
- gantt.render();
|
|
|
- break;
|
|
|
- case "2":
|
|
|
- gantt.config.subscales = [
|
|
|
- {
|
|
|
- unit: "year",
|
|
|
- step: 1,
|
|
|
- date: "%Y年",
|
|
|
- },
|
|
|
- ];
|
|
|
- gantt.config.scale_unit = "month";
|
|
|
- gantt.config.date_scale = "%F";
|
|
|
- gantt.config.scale_height = 28 * 2;
|
|
|
- gantt.render();
|
|
|
- break;
|
|
|
- }
|
|
|
- if (search?.value.searchTime) {
|
|
|
- gantt.config.start_date = convertDateString1(search?.value.searchTime[0]);
|
|
|
- gantt.config.end_date = convertDateString2(search?.value.searchTime[1]);
|
|
|
- gantt.config.show_tasks_outside_timescale = true;
|
|
|
+ () => data.value,
|
|
|
+ () => {
|
|
|
+ if (data.value) {
|
|
|
+ setUseData(data.value);
|
|
|
+ setGantt();
|
|
|
}
|
|
|
},
|
|
|
-
|
|
|
{ immediate: true, deep: true }
|
|
|
);
|
|
|
</script>
|
|
|
<style scoped lang="scss">
|
|
|
+.btns {
|
|
|
+ margin: 7px 0;
|
|
|
+}
|
|
|
:deep(.avue-crud__body) {
|
|
|
display: none;
|
|
|
}
|
|
|
-.my-gantt {
|
|
|
+.main-content {
|
|
|
width: 100%;
|
|
|
height: 600px;
|
|
|
.gantt-container {
|
|
@@ -339,4 +404,7 @@ watch(
|
|
|
height: 100%;
|
|
|
}
|
|
|
}
|
|
|
+.ganttStyle {
|
|
|
+ border-radius: 50%;
|
|
|
+}
|
|
|
</style>
|