ry-gantt-chart
v1.2.0
Published
Vue3甘特图组件 An useful gantt chart component for Vue3.(work with element plus)
Downloads
22
Maintainers
Readme
⭐️一款基于Vue3结合Element Plus开发的实用性较强和支持大数据量操作的甘特图组件。
😇支持:
- 查看项目和生产计划
- 拖动或手动编辑计划任务
- 拖动或手动调整计划进度
- 拖动或手动移除计划任务
- 拖动或手动新增计划任务
- 聚焦到目标任务
- 一键返回顶部
- 可视区域伸展或折叠
- 当前可视区域行数范围标记
- 组件提供内置各种钩子函数以满足日常业务需求
- 数据懒加载,支持大数据量(>1w条)任务展示或操作
- ...
🙂安装
npm i ry-gantt-chart
😉引入
import { createApp } from "vue";
import App from "./App.vue";
import "./common/css/common.css";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import ryGanttChart from "ry-gantt-chart";
import 'ry-gantt-chart/dist/style.css'
const app = createApp(App)
.use(ElementPlus)
.use(ryGanttChart)
.mount("#app");
😁使用
<script setup>
import { ref, onMounted, getCurrentInstance, computed } from "vue";
import mockGanttList from "../../mock/ganttList.js";
import unArrangeTableData from "../../mock/unArrangeTableData.js";
import dayjs from "dayjs";
const ctx = getCurrentInstance();
// 甘特图视图起始日期
const startDate = ref(
dayjs()
.subtract(2, "day")
.format("YYYY-MM-DD") + " 00:00:00"
);
// 甘特图结束日期
const endDate = ref(
dayjs()
.add(3, "month")
.format("YYYY-MM-DD") + " 00:00:00"
);
// 甘特图配置
const config = ref({
type: "month", // year | month | date
scaleWidth: 150, // px 下刻度每刻度长度
divideBy: 2, // 上刻度
rowHeight: 30, // 每条泳道高度
viewHeight: 300, // 甘特图初始高度
viewMaxHeight: 400, // 甘特图最大高度
showCurrentTimeScaler: true, // 是否显示当前时间轴
showRowsRange: true, // 是否显示当前可视区域显示行数范围
showGoToTop: true, // 是否显示滚回视图顶部按钮
});
// 数据集合
const list = ref(mockGanttList(20).list);
// 甘特图左侧表格配置
const ganttTableConfig = ref({
tableColumn: [
{
label: "项目",
prop: "project",
},
{
label: "生产线",
prop: "prodLine",
},
{
label: "区域",
prop: "zone",
"show-overflow-tooltip": true,
},
],
});
// 下方待排区表格配置
const unArrangeConfig = ref({
list: unArrangeTableData,
tableColumn: [
{
label: "流水号",
prop: "pkid",
},
{
label: "工作任务",
prop: "content",
},
],
// 待排区列表行按钮配置
rowOperations: [
{
name: "查看",
type: "success",
click: (scope) => {
console.log(scope);
},
},
],
// 待排区列表操作列配置
operationsConfig: {
width: 100,
},
});
const dateRange = ref([]);
const unitText = computed(() => {
const units = {
year: "年",
month: "月",
date: "日",
};
return units[config.value.type];
});
// 插入新任务
const insertNewTask = () => {
const rowIndex = 2;
ctx.refs.ryGanttChart.insertNewTask(rowIndex, {
pkid: "8021",
startTime: dayjs()
.add("1", "day")
.format("YYYY-MM-DD HH:mm:ss"),
endTime: dayjs()
.add("3", "day")
.format("YYYY-MM-DD HH:mm:ss"),
content: "检查燃油滤芯No7.",
background: "rgb(223,102,2)",
});
};
// 根据id删除某条任务
const handleDeleteTask = () => {
ctx.refs.ryGanttChart.removeTaskById("23007");
};
// 点击了某条任务
const handleClickTask = (rowIndex, task) => {
console.log("rowIndex, task: ", rowIndex, task);
};
// 获取操作过的任务集合
const handleGetModifiedData = () => {
ctx.refs.ryGanttChart.getModifiedData((v) => console.log("v", v));
};
const toggleResults = (val, list) => {
const idx = list.findIndex((v) => v === val);
return idx >= list.length - 1 ? list[0] : list[idx + 1];
};
const handleChangeScaleUnit = () => {
config.value.type = toggleResults(config.value.type, ["year", "month", "date"]);
};
const handleChangeRowHeight = () => {
config.value.rowHeight = toggleResults(config.value.rowHeight, [30, 40, 55]);
};
const handleChangeTopScale = () => {
config.value.divideBy = toggleResults(config.value.divideBy, [1, 2, 3, 4]);
};
const handleChangeDownScale = () => {
config.value.scaleWidth = toggleResults(config.value.scaleWidth, [100, 150, 300]);
};
const handleChangeQty = () => {
const qty = toggleResults(list.value.length, [20, 200, 500]);
list.value = mockGanttList(qty)["list"];
};
const handleDateChange = () => {
[startDate.value, endDate.value] = dateRange.value;
};
// 根据id聚焦到某条任务
const handleFouce = () => {
ctx.refs.ryGanttChart.scrollToTaskById("23022");
};
</script>
<template>
<h1 class="title">RY-GANTT-CHART</h1>
<div class="ctrl-wrapper">
<div style="width: 300px;overflow: hidden;padding-right:20px;">
<el-date-picker
style="width:95%"
type="daterange"
v-model="dateRange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="YYYY-MM-DD HH:mm:ss"
:clearable="false"
@change="handleDateChange"
></el-date-picker>
</div>
<el-button type="success" @click="handleChangeQty">数据量</el-button>
<el-button type="success" @click="handleChangeTopScale">上刻度宽度</el-button>
<el-button type="success" @click="handleChangeDownScale">下刻度宽度</el-button>
<el-button type="success" @click="handleChangeRowHeight">行高</el-button>
<el-button type="success" @click="handleDeleteTask">删除某条任务</el-button>
<el-button type="success" @click="handleFouce">聚焦到某一任务</el-button>
<el-button type="success" @click="insertNewTask">插入新增任务</el-button>
<el-button type="success" @click="handleGetModifiedData">获取操作项</el-button>
<el-button type="success" @click="handleChangeScaleUnit">{{ unitText }}</el-button>
</div>
<ryGanttChart
ref="ryGanttChart"
:config="config"
:list="list"
:startDate="startDate"
:endDate="endDate"
:ganttTableConfig="ganttTableConfig"
:unArrangeConfig="unArrangeConfig"
>
<!-- 任务条内容插槽 -->
<template v-slot:taskContent="slotProps">
<div class="text" @click="handleClickTask(slotProps.rowIndex, slotProps.task)">
{{ `${slotProps.task.content} (id:${slotProps.task.pkid})` }}
</div>
</template>
</ryGanttChart>
</template>
<style scoped lang="scss">
.title {
margin: 0;
text-align: center;
font-family: "Times New Roman", Times, serif;
}
.text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ctrl-wrapper {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 10px 0;
h1 {
margin: 0;
width: 500px;
font-size: 30px;
flex: 1;
}
}
</style>
🧐数据结构
import { dayjs } from "../common/utils";
export default (qty) => {
let taskCount = 23000;
let count = 0;
return {
list: Array.from({ length: qty }, (_, index) => {
count += 3;
return {
pkid: index.toString(), // pkid ->当前行父任务主键
project:
"ROCKET" +
dayjs()
.add(count, "day")
.format("MMDD"),
prodLine: "生产线-" + index,
zone: "月球",
/* tasks -> 子任务对象属性 */
tasks: [
{
pkid: (taskCount++).toString(), // 子任务主键
startTime:
dayjs()
.subtract(1, "day")
.format("YYYY-MM-DD") + " 00:00:00", // startTime -> 任务开始日期
endTime:
dayjs()
.add(1, "day")
.format("YYYY-MM-DD") + " 00:00:00", // endTime ->任务结束日期
content: "Daily check", // 任务内容
background: "rgb(182,220,22)", // background ->任务条背景色
color: "black", // color ->任务条字体颜色
disabled: false, // disabled ->当前任务是否无法被拖动或编辑
},
// ...
],
};
}),
};
};
export default [
{
pkid: "1055",
content: "安装车门",
rangeDays: 3,
},
{
pkid: "1056",
content: "安装倒后镜",
rangeDays: 3,
},
{
pkid: "1067",
content: "全车检查",
rangeDays: 3,
},
{
pkid: "1058",
content: "更换ECU",
rangeDays: 3,
},
];
RY-GANTT-TABLE 属性
| 属性名 | **参数 ** | **说明 ** | | --- | --- | --- | | list:Array | 数据结构见demo示例 | 甘特图源数据 | | startDate:String | "YYYY-MM-DD HH:mm:ss" | 刻度尺起始日期 | | endDate:String | "YYYY-MM-DD HH:mm:ss" | 刻度尺结束日期 | | disabeld:Boolean | default:false | 甘特图是否只读 | | showSideExpander | default:false | 是否显示“展开收起”侧栏按钮 | | showBottomExpander | default:true | 是否显示“展开更多”甘特图视图按钮 | | showUnArrangeTable | default:true | 是否显示待排区,默认true | | config:Object | | 甘特图视图区配置 | | | type:String | 'year' ,'month' , 'date' | 刻度尺单位范围 | | | scaleWidth:Number | 刻度尺下部每刻度间隔宽度(px) | | | divideBy:Number | 刻度尺上部刻度根据每间隔下部刻度多少个进行划分 | | | rowHeight:Number | 泳道行高(px) | | | viewHeight:Number | 甘特图出初始高度 | | | viewMaxHeight:Number | 甘特图最大高度 | | | showCurrentTimeScaler:Boolean | 是否显示当前时间节点标线 | | | showGoToTop:Boolean | 是否显示返回顶部按钮 | | | showRowsRange:Boolean | 是否显示当前可视区域行数范围 | | | topScaleFormatter(obj) | obj:Object | 刻度尺上部每刻度内容格式化处理函数 | 需返回字符串 | | | downScaleFormatter(obj) | obj:Object | 刻度尺下部每刻度内容格式化处理函数 | 需返回字符串 | | unArrangeConfig:Object | | 待排区域表格配置 | | | tableColumn:Array | 表头配置 | | | data:Array | 待排区域表格源数据 | | | rowOperations:Array | 行操作配置 | | | operationsConfig:Object | | | ganttTableConfig:Object | | 甘特图左侧表格配置 | | | tableColumn:Array | 表头配置 | | beforeMountData:Function | (data)=>data | 挂载甘特图视图层数据前调用,需返回数据对象。 | | afterMountedData:Function | (data) =>{} | 挂载甘特图视图层数据完成后调用 | | beforeRemove:Function | (task,next)=>{} | 完成移除任务前调用 | task:Object 当前任务对象 | next:Function 调用next执行后续逻辑 | | beforeHorizontalMove:Function | (task,next)=>{} | 完成水平移动任务前调用 | task:Object 当前任务对象 | next:Function 调用next执行后续逻辑 | | beforeVerticalMove:Function | (task,next)=>{} | 完成垂直移动任务前调用 | task:Object 当前任务对象 | next:Function 调用next执行后续逻辑 | | beforeInsertToGantt:Function | (task,next)=>{} | 完成插入新数据前调用 | task:Object 当前任务对象 | next:Function 调用next执行后续逻辑 | | beforeDropToUnArrange | (task,next)=>{} | 完成放置到待排区前校验 | task:Object 当前任务对象 | next:Function 调用next执行后续逻辑 | | afterDropToUnArrange | (task)=>{} | 完成放置到待排区后调用 | task:Object 当前任务对象 |
RY-GANTT-CHART 方法
| 名称 | 参数 | 返回值 | | --- | --- | --- | | insertNewTask(rowIndex,task) | 插入任务task到某行row | rowIndex : Number | 行索引 task:Object 任务内容 (必要属性参考甘特图数据结构) | | getModifiedData(res) | (res)=>{} | res:变化过的任务数据集合 | Promise | 变化过的任务数据集合 | | getUnArrangeTableData() | | Array | 待排区表格数据 | | scrollToTaskById(id) | id:string | 通过任务主键让视图滚动到该任务所在位置,并且会有高亮闪烁一次的圆点出现 | | removeTaskById(id) | id:string | 根据任务id,从甘特图移除某条任务 | | editTask({targetRowIndex,task}) | targetRowIndex:number , task:Object | 手动编辑某条任务 | targetRowIndex :要把该任务移动到某行的索引 | task :任务对象,任务的pkid ,起始日期,结束日期字段不能为空。 |
RY-GANTT-CHART 事件
| 名称 | 说明 | 回调参数 | | --- | --- | --- | | expander-change | 收起展开按钮change事件 | type:String,isExpand:Boolean | type : side、bottom | | scaler-click | 刻度尺区域点击事件 | |
RY-GANTT-CHART SLOTS 插槽
| 名称 | 说明 | 类型 | | --- | --- | --- | | leftHeader | | - | | taskContent | 任务内容 | 作用域参数为 { task } ,该任务对象 |
**Demo:
- npm run dev 启动这个项目,查看/examples/pages/demo 或 /examples/pages/demo1 的页面
- 或 https://gitee.com/RYANLLL/ry-gantt-chart-demo