emis-frontend/src/components/GanttChart/index.vue

1206 lines
37 KiB
Vue
Raw Normal View History

2023-11-27 08:29:42 +00:00
<template>
<el-table
ref="wl-gantt"
class="wl-gantt"
:fit="fit"
:size="size"
:border="border"
:data="selfData"
:stripe="stripe"
:height="height"
:row-key="rowKey"
:row-style="rowStyle"
:class="dateTypeClass"
:cell-style="cellStyle"
:max-height="maxHeight"
:tree-props="selfProps"
:current-row-key="rowKey"
:row-class-name="rowClassName"
:cell-class-name="cellClassName"
:expand-row-keys="expandRowKeys"
:header-row-style="headerRowStyle"
:header-cell-style="headerCellStyle"
:default-expand-all="defaultExpandAll"
:header-row-class-name="headerRowClassName"
:highlight-current-row="highlightCurrentRow"
:header-cell-class-name="headerCellClassName"
@header-contextmenu="handleHeaderContextMenu"
@selection-change="handleSelectionChange"
@row-contextmenu="handleRowContextMenu"
@current-change="handleCurrentChange"
@cell-mouse-enter="handleMouseEnter"
@cell-mouse-leave="handleMouseLeave"
@expand-change="handleExpandChange"
@filter-change="handleFilterChange"
@cell-dblclick="handleCellDbClick"
@header-click="handleHeaderClick"
@row-dblclick="handleRowDbClick"
@sort-change="handleSortChange"
@cell-click="handleCellClick"
@select-all="handleSelectAll"
@row-click="handleRowClick"
@select="handleSelect"
>
<slot name="prv"></slot>
<el-table-column
fixed
label="名称"
min-width="200"
show-overflow-tooltip
:prop="selfProps.name"
:formatter="nameFormatter"
></el-table-column>
<el-table-column
:resizable="false"
fixed
width="160"
align="center"
:prop="selfProps.startDate"
label="开始日期"
>
<template slot-scope="scope">
<el-date-picker
v-if="scope.row._start_edit"
v-model="scope.row[selfProps.startDate]"
@blur="scope.row._start_edit = false"
@change="startDateChange(scope.row)"
type="date"
size="medium"
class="u-full"
:clearable="false"
ref="wl-start-date"
value-format="yyyy-MM-dd"
placeholder="请选择开始日期"
></el-date-picker>
<span
v-else
@click="cellEdit(scope.row, '_start_edit', 'wl-start-date')"
>{{timeFormat(scope.row[selfProps.startDate])}}</span>
</template>
</el-table-column>
<el-table-column
fixed
:resizable="false"
width="160"
align="center"
:prop="selfProps.endDate"
label="结束日期"
>
<template slot-scope="scope">
<el-date-picker
v-if="scope.row._end_edit"
v-model="scope.row[selfProps.endDate]"
@blur="scope.row._end_edit = false"
@change="endDateChange(scope.row)"
type="date"
size="medium"
class="u-full"
:clearable="false"
ref="wl-end-date"
value-format="yyyy-MM-dd"
placeholder="请选择结束日期"
></el-date-picker>
<span
v-else
@click="cellEdit(scope.row, '_end_edit', 'wl-end-date')"
>{{timeFormat(scope.row[selfProps.endDate])}}</span>
</template>
</el-table-column>
<slot></slot>
<!-- year and mouth gantt -->
<template v-if="self_date_type === 'yearAndMonth'">
<el-table-column
:resizable="false"
v-for="year in ganttTitleDate"
:label="year.name"
:key="year.id"
>
<el-table-column
class-name="wl-gantt-item"
v-for="month in year.children"
:resizable="false"
:key="month.id"
:label="month.name"
>
<template slot-scope="scope">
<div :class="dayGanttType(scope.row, month.full_date, 'months')"></div>
</template>
</el-table-column>
</el-table-column>
</template>
<!-- year and week gantt -->
<template v-else-if="self_date_type === 'yearAndWeek'">
<el-table-column
:resizable="false"
v-for="i in ganttTitleDate"
:label="i.full_date"
:key="i.id"
>
<el-table-column
class-name="wl-gantt-item"
v-for="t in i.children"
:resizable="false"
:key="t.id"
:label="t.name"
>
<template slot-scope="scope">
<div :class="dayGanttType(scope.row, t.full_date, 'week')"></div>
</template>
</el-table-column>
</el-table-column>
</template>
<!-- mouth and day gantt -->
<template v-else>
<el-table-column
:resizable="false"
v-for="i in ganttTitleDate"
:label="i.full_date"
:key="i.id"
>
<el-table-column
class-name="wl-gantt-item"
v-for="t in i.children"
:resizable="false"
:key="t.id"
:label="t.name"
>
<template slot-scope="scope">
<div :class="dayGanttType(scope.row, t.full_date)"></div>
</template>
</el-table-column>
</el-table-column>
</template>
</el-table>
</template>
<script>
import dayjs from "dayjs"; // 导入日期js
const uuidv4 = require("uuid/v4"); // 导入uuid生成插件
import isBetween from "dayjs/plugin/isBetween";
dayjs.extend(isBetween);
import { deepClone, flattenDeep, getMin, getMax } from "@/utils/array.js";
export default {
name: "GanttChart",
data() {
return {
self_start_date: "", // 项目开始时间
self_end_date: "", // 项目结束时间
self_data_list: [], // 一维化后的gantt数据
self_date_type: "", // 自身日期类型
self_id: 1, // 自增id
multipleSelection: [], // 多选数据
currentRow: null // 单选数据
};
},
props: {
// gantt数据
data: {
type: Array,
default: () => {
return [];
}
},
// 日期类型
dateType: {
type: String,
default: "yearAndMonth" // monthAndDay,yearAndMonth,yearAndWeek
},
// 树表配置项
props: Object,
// 开始日期
startDate: {
type: [String, Object],
required: true
},
// 结束时间
endDate: {
type: [String, Object],
required: true
},
// 是否检查源数据符合规则,默认开启,如果源数据已遵循project规则,可设置为false以提高性能
checkSource: {
type: Boolean,
default: true
},
// 废弃:反而会因为频繁的判断而影响性能
// 是否生成自增id并组成parents来满足树链的查询条件,如果数据本身已有自增id,可设置为false以提高性能
// 如果设置为false,则数据内必须包含自增id字段:identityId,parents字段必须以`,`分割。
// 字段名可通过props配置,自增id必须唯一并尽可能的短,如1,2,3...,parents应为祖先自增id通过`,`拼接直至父级
recordParents: {
type: Boolean,
default: true
},
// 是否使用id来作为自增id,如果是请保证id本来就简短的数字型而不是较长的字符串或guid
treatIdAsIdentityId: {
type: Boolean,
default: false
},
// 自动变化gantt标题日期模式
autoGanttDateType: {
type: Boolean,
default: true
},
nameFormatter: Function, // 名称列的格式化内容函数
// ---------------------------------------------以下为el-table Attributes--------------------------------------------
defaultExpandAll: {
type: Boolean,
default: false
}, // 是否全部展开
rowKey: {
type: String,
default: "id"
}, // 必须指定key来渲染树形数据
height: [String, Number], // 列表高度
maxHeight: [String, Number], // 列表最大高度
stripe: {
type: Boolean,
default: false
}, // 是否为斑马纹
highlightCurrentRow: {
type: Boolean,
default: false
}, // 是否要高亮当前行
border: {
type: Boolean,
default: true
}, // 是否带有纵向边框
fit: {
type: Boolean,
default: true
}, // 列的宽度是否自撑开
size: String, // Table 的尺寸
rowClassName: Function, // 行的 className 的回调方法
rowStyle: Function, // 行的 style 的回调方法
cellClassName: Function, // 单元格的 className 的回调方法
cellStyle: Function, // 单元格的 style 的回调方法
headerRowClassName: {
type: [Function, String],
default: "wl-gantt-header"
}, // 表头行的 className 的回调方法
headerRowStyle: [Function, Object], // 表头行的 style 的回调方法
headerCellClassName: [Function, String], // 表头单元格的 className 的回调方法
headerCellStyle: [Function, Object], // 表头单元格的 style 的回调方法
expandRowKeys: Array // 可以通过该属性设置 Table 目前的展开行
// 是否使用一维数据组成树
/* arrayToTree: {
type: Boolean,
default: false
} */
},
computed: {
// 甘特图标题日期分配
ganttTitleDate() {
// 分解开始和结束日期
let start_date_spilt = dayjs(this.self_start_date)
.format("YYYY-M-D")
.split("-");
let end_date_spilt = dayjs(this.self_end_date)
.format("YYYY-M-D")
.split("-");
let start_year = Number(start_date_spilt[0]);
let start_mouth = Number(start_date_spilt[1]);
let end_year = Number(end_date_spilt[0]);
let end_mouth = Number(end_date_spilt[1]);
// 自动更新日期类型以适应任务时间范围跨度
if (this.autoGanttDateType) {
// 计算日期跨度
let mouth_diff = this.timeDiffTime(
this.self_start_date,
this.self_end_date,
"months"
);
if (mouth_diff > 12) {
// 12个月以上的分到yearAndMouth
this.self_date_type = "yearAndMonth";
} else if (mouth_diff > 2) {
// 2个月以上的分到yearAndWeek
this.self_date_type = "yearAndWeek";
} else {
this.self_date_type = "monthAndDay";
}
}
// 不自动更新日期类型,以dateType固定展示
if (this.self_date_type === "yearAndWeek") {
return this.yearAndWeekTitleDate(
start_year,
start_mouth,
end_year,
end_mouth
);
} else if (this.self_date_type === "monthAndDay") {
return this.mouthAndDayTitleDate(
start_year,
start_mouth,
end_year,
end_mouth
);
} else {
return this.yearAndMouthTitleDate(
start_year,
start_mouth,
end_year,
end_mouth
);
}
},
// 数据
selfData() {
let _data = this.data || [];
this.self_data_list = flattenDeep(_data, this.selfProps.children);
this.handleData(_data);
return _data;
},
// 树表配置项
selfProps() {
return {
hasChildren: "hasChildren", // 字段来指定哪些行是包含子节点
children: "children", // children字段来表示有子节点
name: "name", // 任务名称字段
id: "id", // id字段
pid: "pid", // pid字段
startDate: "startDate", // 开始时间字段
endDate: "endDate", // 结束时间字段
identityId: "identityId",
parents: "parents",
pre: "pre", // 前置任务字段【注意:如果使用recordParents,则pre值应是目标对象的identityId字段(可配置)】
...this.props
};
},
// 根据日期类型改样式
dateTypeClass() {
if (this.self_date_type === "yearAndMonth") {
return "year-and-month";
} else if (this.self_date_type === "monthAndDay") {
return "month-and-day";
} else if (this.self_date_type === "yearAndWeek") {
return "year-and-week";
}
}
},
methods: {
/**
* 开始时间改变
* row: object 当前行数据
*/
startDateChange(row) {
// 如果将开始时间后移,结束时间也应后移
let _delay = this.timeIsBefore(
row._oldStartDate,
row[this.selfProps.startDate]
);
if (_delay) {
row[this.selfProps.endDate] = this.timeAdd(
row[this.selfProps.endDate],
row._cycle
);
}
// 如果开始早于项目开始,则把项目开始提前
let _early_project_start = this.timeIsBefore(
row[this.selfProps.startDate],
this.self_start_date
);
if (_early_project_start) {
this.self_start_date = row[this.selfProps.startDate];
}
this.emitTimeChange(row);
},
/**
* 结束时间改变
* row: object 当前行数据
*/
endDateChange(row) {
this.emitTimeChange(row);
// 如果开始晚于结束,提示
/* if (
this.timeIsBefore(
row[this.selfProps.endDate],
row[this.selfProps.startDate]
)
) {
row[this.selfProps.startDate] = row._oldStartDate;
this.$message({
showClose: true,
message: "开始时间不可以晚于结束时间",
type: "error"
});
return;
} */
},
/**
* 单元格编辑
* row: object 当前行数据
* key: string 需要操作的字段
* ref:object 需要获取焦点的dom
*/
cellEdit(row, key, ref) {
row[key] = true;
this.$nextTick(() => {
this.$refs[ref].focus();
});
},
/**
* 查询目标是否在父级链或者全部子集中
* item 当前节点
* pre 前置节点
*/
targetInParentsOrChildren(item, pre) {
let _parents = item._parents.split(",");
let _children = item._all_children.map(i => i._identityId);
return _children.concat(_parents).some(i => i == pre._identityId);
/* let _parents = item[this.selfProps.parents].split(",").filter(i => !!i);
let _children = item._all_children.map(i => i[this.selfProps.identityId]);
return _children.push(..._parents).some(i => i == item[this.selfProps.pre]); */
},
// 以下是表格-日期-gantt生成函数----------------------------------------生成gantt表格-------------------------------------
/**
* 年-月模式gantt标题
* start_year: 起始年
* start_mouth:起始月
* end_year:结束年
* end_mouth:结束月
*/
yearAndMouthTitleDate(start_year, start_mouth, end_year, end_mouth) {
// 日期数据盒子
let dates = [
{
name: `${start_year}年`,
date: start_year,
id: uuidv4(),
children: []
}
];
// 处理年份
let year_diff = end_year - start_year;
// 年间隔为同一年
if (year_diff === 0) {
let isLeap = this.isLeap(start_year); // 是否闰年
let mouths = this.generationMonths(
start_year,
start_mouth,
end_mouth + 1,
isLeap,
false
); // 处理月份
dates[0].children = mouths;
return dates;
}
// 处理开始月份
let startIsLeap = this.isLeap(start_year);
let start_mouths = this.generationMonths(
start_year,
start_mouth,
13,
startIsLeap,
false
);
// 处理结束月份
let endIsLeap = this.isLeap(end_year);
let end_mouths = this.generationMonths(
end_year,
1,
end_mouth + 1,
endIsLeap,
false
);
// 年间隔等于一年
if (year_diff === 1) {
dates[0].children = start_mouths;
dates.push({
name: `${end_year}年`,
date: end_year,
children: end_mouths,
id: uuidv4()
});
return dates;
}
// 年间隔大于1年
if (year_diff > 1) {
for (let i = 1; i < year_diff; i++) {
let item_year = start_year + i;
let isLeap = this.isLeap(item_year);
let month_and_day = this.generationMonths(
item_year,
1,
13,
isLeap,
false
);
dates.push({
name: `${item_year}年`,
date: item_year,
id: uuidv4(),
children: month_and_day
});
}
dates.push({
name: `${end_year}年`,
date: item_year,
children: end_mouths,
id: uuidv4()
});
return dates;
}
},
/**
* 年-周模式gantt标题
* start_year: 起始年
* start_mouth:起始月
* end_year:结束年
* end_mouth:结束月
*/
yearAndWeekTitleDate(start_year, start_mouth, end_year, end_mouth) {
// 处理年份
let year_diff = end_year - start_year;
// 只存在同年或前后年的情况
if (year_diff === 0) {
// 年间隔为同一年
let isLeap = this.isLeap(start_year); // 是否闰年
let mouths = this.generationMonths(
start_year,
start_mouth,
end_mouth + 1,
isLeap,
true,
true
); // 处理月份
return mouths;
}
// 处理开始月份
let startIsLeap = this.isLeap(start_year);
let start_mouths = this.generationMonths(
start_year,
start_mouth,
13,
startIsLeap,
true,
true
);
// 处理结束月份
let endIsLeap = this.isLeap(end_year);
let end_mouths = this.generationMonths(
end_year,
1,
end_mouth + 1,
endIsLeap,
true,
true
);
return start_mouths.concat(end_mouths);
},
/**
* 月-日模式gantt标题
* start_year: 起始年
* start_mouth:起始月
* end_year:结束年
* end_mouth:结束月
*/
mouthAndDayTitleDate(start_year, start_mouth, end_year, end_mouth) {
// 处理年份
let year_diff = end_year - start_year;
// 只存在同年或前后年的情况
if (year_diff === 0) {
// 年间隔为同一年
let isLeap = this.isLeap(start_year); // 是否闰年
let mouths = this.generationMonths(
start_year,
start_mouth,
end_mouth + 1,
isLeap
); // 处理月份
return mouths;
}
// 处理开始月份
let startIsLeap = this.isLeap(start_year);
let start_mouths = this.generationMonths(
start_year,
start_mouth,
13,
startIsLeap
);
// 处理结束月份
let endIsLeap = this.isLeap(end_year);
let end_mouths = this.generationMonths(
end_year,
1,
end_mouth + 1,
endIsLeap
);
return start_mouths.concat(end_mouths);
},
/**
* 生成月份函数
* year: Number 当前年份
* start_num: Number 开始月分
* end_num:Number 结束月份
* isLeap: Boolean 是否闰年
* insert_days: Boolean 是否需要插入 日
* week: 是否以周的间隔
*/
generationMonths(
year,
start_num = 1,
end_num = 13,
isLeap = false,
insert_days = true,
week = false
) {
let months = [];
if (insert_days) {
// 无需 日 的模式
for (let i = start_num; i < end_num; i++) {
// 需要 日 的模式
let days = this.generationDays(year, i, isLeap, week);
months.push({
name: `${i}月`,
date: i,
full_date: `${year}-${i}`,
children: days,
id: uuidv4()
});
}
return months;
}
for (let i = start_num; i < end_num; i++) {
// 需要 日 的模式
months.push({
name: `${i}月`,
date: i,
full_date: `${year}-${i}`,
id: uuidv4()
});
}
return months;
},
/**
* 生成日期函数
* year: Number 当前年份
* month: Number 当前月份
* isLeap: Boolean 是否闰年
* week: Boolean 是否间隔一周
*/
generationDays(year, month, isLeap = false, week = false) {
let big_month = [1, 3, 5, 7, 8, 10, 12].includes(month);
let small_month = [4, 6, 9, 11].includes(month);
let dates_num = big_month ? 32 : small_month ? 31 : isLeap ? 30 : 29;
let days = [];
if (week) {
let _day = 1; // 从周日开始
let _start_day_inweek = this.timeInWeek(`${year}-${month}-1`);
if (_start_day_inweek !== 0) {
_day = 8 - _start_day_inweek;
}
for (let i = _day; i < dates_num; i += 7) {
days.push({
date: i,
name: `${i}日`,
id: uuidv4(),
full_date: `${year}-${month}-${i}`
});
}
} else {
for (let i = 1; i < dates_num; i++) {
days.push({
date: i,
name: `${i}日`,
id: uuidv4(),
full_date: `${year}-${month}-${i}`
});
}
}
return days;
},
/**
* 是否闰年函数
* year: Number 当前年份
*/
isLeap(year) {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
},
/**
* 当前日期gantt状态
* row: object 当前行信息
* date: string 当前格子日期
* unit: string 时间单位,以天、月、年计算
*/
dayGanttType(row, date, unit = "days") {
let start_date = row[this.selfProps.startDate];
let end_date = row[this.selfProps.endDate];
let between = dayjs(date).isBetween(start_date, end_date, unit);
if (between) {
return "wl-item-on";
}
let start = dayjs(start_date).isSame(date, unit);
let end = dayjs(end_date).isSame(date, unit);
if (start && end) {
return "wl-item-on wl-item-full";
}
if (start) {
return "wl-item-on wl-item-start";
}
if (end) {
return "wl-item-on wl-item-end";
}
},
// 以下是时间计算类函数 ------------------------------------------------------时间计算---------------------------------------
/**
* 计算时差
* startDate:开始时间
* endDate:结束时间
* unit:单位 days、months、yesrs
*/
timeDiffTime(startDate, endDate, unit = "days") {
return dayjs(endDate).diff(startDate, unit);
},
/**
* 比较时间,是否之前
* startDate:开始时间
* endDate:结束时间
* unit:单位 days、months、yesrs
*/
timeIsBefore(startDate, endDate, unit = "days") {
return dayjs(startDate).isBefore(endDate, unit);
},
/**
* 时间加计算函数
* date:原时间
* num:需要增加的时间数量
* nuit:增加时间的单位 day year
*/
timeAdd(date, num = 1, nuit = "day", format = "YYYY-MM-DD") {
return dayjs(date)
.add(num, nuit)
.format(format);
},
/**
* 时间格式化函数
* date 需要格式化的数据
* format 格式化的格式
*/
timeFormat(date, format = "YYYY-MM-DD") {
return dayjs(date).format(format);
},
/**
* 查询时间是周几
*/
timeInWeek(date) {
return dayjs(date).day();
},
// 以下为输出数据函数 --------------------------------------------------------------输出数据------------------------------------
emitTimeChange(item) {
this.$emit("timeChange", item);
this.$nextTick(() => {
this.$set(item, "_oldStartDate", item[this.selfProps.startDate]);
this.$set(item, "_oldEndDate", item[this.selfProps.endDate]);
});
},
// 处理外部数据 ---------------------------------------------------------------原始数据处理-------------------------------------
handleData(data, parent = null, level = 0) {
level++;
data.forEach(i => {
this.$set(i, "_parent", parent); // 添加父级字段
this.$set(i, "_level", level); // 添加层级字段
if (!i._oldStartDate) {
this.$set(i, "_oldStartDate", i[this.selfProps.startDate]);
}
if (!i._oldEndDate) {
this.$set(i, "_oldEndDate", i[this.selfProps.endDate]);
}
// 当结束时间早于开始时间时,自动处理结束时间为开始时间延后一天
let _end_early_start = this.timeIsBefore(
i[this.selfProps.endDate],
i[this.selfProps.startDate]
);
if (_end_early_start) {
this.$set(i, this.selfProps.endDate, i[this.selfProps.startDate]);
this.$set(i, "_cycle", 1); // 添加工期字段
this.emitTimeChange(i); // 将发生时间更新的数据输出
} else {
let _time_diff = this.timeDiffTime(
i[this.selfProps.startDate],
i[this.selfProps.endDate]
);
this.$set(i, "_cycle", _time_diff + 1); // 添加工期字段
} // 添加工期字段
// 如果当前节点的开始时间早于父节点的开始时间,则将开始时间与父节点相同
this.parentStartDateToChild(i);
// 校验结束时间是否晚于子节点,如不则将节点结束时间改为最晚子节点
this.childEndDateToParent(i);
// 添加自增id字段及树链组成的parents字段
this.recordIdentityIdAndParents(i);
if (Array.isArray(i[this.selfProps.children])) {
this.$set(i, "_isLeaf", false); // 添加是否叶子节点字段
let _all_children = flattenDeep(
i[this.selfProps.children],
this.selfProps.children
);
this.$set(i, "_all_children", _all_children); // 添加全部子节点字段
this.handleData(i[this.selfProps.children], i, level);
} else {
this.$set(i, "_isLeaf", true); // 添加是否叶子节点字段
this.$set(i, "_all_children", []); // 添加全部子节点字段
}
// 处理前置任务
this.handlePreTask(i);
if (!i.hasOwnProperty("_start_edit")) {
// 添加编辑字段管理可编辑项的显示
this.$set(i, "_start_edit", false);
this.$set(i, "_end_edit", false);
}
});
},
// 取父节点开始时间给早于父节点开始时间的子节点
parentStartDateToChild(item) {
if (!item._parent) return;
// 如果子节点时间早于父节点,则将子节点开始时间后移至父节点开始时间,并将结束时间平移【即工期不变】
let _child_early_parent = this.timeIsBefore(
item[this.selfProps.startDate],
item._parent[this.selfProps.startDate]
);
if (_child_early_parent) {
// 修正子节点开始时间
this.$set(
item,
this.selfProps.startDate,
item._parent[this.selfProps.startDate]
);
// 修正子节点结束时间
let _to_endDate = this.timeAdd(
item[this.selfProps.startDate],
item._cycle
);
this.$set(item, this.selfProps.endDate, _to_endDate);
this.emitTimeChange(item); // 将发生时间更新的数据输出
}
},
// 取数组结束时间最大值,如果最大值比父级结束时间大,更新父级结束时间
childEndDateToParent(item) {
if (!Array.isArray(item[this.selfProps.children])) return;
let _child_max = getMax(
item[this.selfProps.children],
this.selfProps.endDate,
true
); // 取子节点中最晚的结束时间
let _parent_end = dayjs(item[this.selfProps.endDate]).valueOf();
if (_child_max > _parent_end) {
// 如果子节点结束时间比父节点晚,则将父节点结束时间退后
this.$set(item, this.selfProps.endDate, this.timeFormat(_child_max));
this.emitTimeChange(item); // 将发生时间更新的数据输出
}
},
// 处理前置任务节点
handlePreTask(item) {
if (!item[this.selfProps.pre]) return;
// 找到前置目标节点
let _pre_target = this.self_data_list.find(
i => i[this.selfProps.id] == item[this.selfProps.pre]
);
if (!_pre_target) return;
let is_pre_standard = this.targetInParentsOrChildren(item, _pre_target);
if (is_pre_standard) return;
// 查看是否需要根据前置时间,如果不符合规则,更新后置时间
let _start_early_prvend = this.timeIsBefore(
item[this.selfProps.startDate],
_pre_target[this.selfProps.endDate]
);
if (_start_early_prvend) {
let _cycle = item._cycle - 1;
let _to_startDate = this.timeAdd(
_pre_target[this.selfProps.endDate],
1
);
let _to_endDate = this.timeAdd(_to_startDate, _cycle);
this.$set(item, this.selfProps.startDate, _to_startDate);
this.$set(item, this.selfProps.endDate, _to_endDate);
}
},
// 处理数据生成自增id和树链parents
recordIdentityIdAndParents(item) {
// if (!this.recordParents) return;
if (this.treatIdAsIdentityId) {
let _parents = item._parent
? item._parent._parents + "," + item._parent[this.selfProps.id]
: "";
this.$set(item, "_parents", _parents);
this.$set(item, "_identityId", item[this.selfProps.id]);
return;
}
// 添加自增id
this.$set(item, "_identityId", this.self_id);
this.self_id++;
// 添加parents字段
let _parents = item._parent
? item._parent._parents + "," + item._parent._identityId
: "";
this.$set(item, "_parents", _parents);
},
// 简洁处理数据
terseHandleData(data, parent = null, level = 0) {
level++;
data.forEach(i => {
this.$set(i, "_parent", parent); // 添加父级字段
this.$set(i, "_level", level); // 添加层级字段
let _time_diff = this.timeDiffTime(
i[this.selfProps.startDate],
i[this.selfProps.endDate]
);
i._cycle = _time_diff + 1;
if (!i._oldStartDate) {
// 添加开始时间字段
this.$set(i, "_oldStartDate", i[this.selfProps.startDate]);
}
if (!i._oldEndDate) {
// 添加结束字段时间
this.$set(i, "_oldEndDate", i[this.selfProps.endDate]);
}
// 添加自增id字段及树链组成的parents字段
this.recordIdentityIdAndParents(i);
if (Array.isArray(i[this.selfProps.children])) {
this.$set(i, "_isLeaf", false); // 添加是否叶子节点字段
let _all_children = flattenDeep(
i[this.selfProps.children],
this.selfProps.children
);
this.$set(i, "_all_children", _all_children); // 添加全部子节点字段
this.terseHandleData(i[this.selfProps.children], i, level);
} else {
this.$set(i, "_isLeaf", true); // 添加是否叶子节点字段
}
// 处理前置任务
this.handlePreTask(i);
if (!i.hasOwnProperty("_start_edit")) {
// 添加编辑字段管理可编辑项的显示
this.$set(i, "_start_edit", false);
this.$set(i, "_end_edit", false);
}
});
},
// el-table事件----------------------------------------------以下为原el-table事件输出------------------------------------------------
handleSelectionChange(val) {
this.$emit("selection-change", val);
this.multipleSelection = val;
}, // 当选择项发生变化时会触发该事件
handleCurrentChange(val, oldVal) {
this.$emit("current-change", val, oldVal);
this.currentRow = val;
}, // 当表格的当前行发生变化的时候会触发该事件
handleSelectAll(val) {
this.$emit("select-all", val);
}, // 当用户手动勾选全选 Checkbox 时触发的事件
handleSelect(selection, row) {
this.$emit("select", selection, row);
}, // 当用户手动勾选全选 Checkbox 时触发的事件
handleMouseEnter(row, column, cell, event) {
this.$emit("cell-mouse-enter", row, column, cell, event);
}, // 当单元格 hover 进入时会触发该事件
handleMouseLeave(row, column, cell, event) {
this.$emit("cell-mouse-leave", row, column, cell, event);
}, // 当单元格 hover 退出时会触发该事件
handleCellClick(row, column, cell, event) {
this.$emit("cell-click", row, column, cell, event);
}, // 当某个单元格被点击时会触发该事件
handleCellDbClick(row, column, cell, event) {
this.$emit("cell-dblclick", row, column, cell, event);
}, // 当某个单元格被双击击时会触发该事件
handleRowClick(row, column, event) {
this.$emit("row-click", row, column, event);
}, // 当某一行被点击时会触发该事件
handleRowContextMenu(row, column, event) {
this.$emit("row-contextmenu", row, column, event);
}, // 当某一行被鼠标右键点击时会触发该事件
handleRowDbClick(row, column, event) {
this.$emit("row-dblclick", row, column, event);
}, // 当某一行被双击时会触发该事件
handleHeaderClick(column, event) {
this.$emit("header-click", column, event);
}, // 当某一列的表头被点击时会触发该事件
handleHeaderContextMenu(column, event) {
this.$emit("header-contextmenu", column, event);
}, // 当某一列的表头被鼠标右键点击时触发该事件
handleSortChange(e) {
this.$emit("sort-change", e);
}, // 当表格的排序条件发生变化的时候会触发该事件
handleFilterChange(filters) {
this.$emit("filter-change", filters);
}, // 当表格的筛选条件发生变化的时候会触发该事件
handleExpandChange(row, expanded) {
this.$emit("expand-change", row, expanded);
} // 当表格的筛选条件发生变化的时候会触发该事件
},
watch: {
dateType(val) {
this.self_date_type = val;
},
startDate(val) {
this.self_start_date = val;
},
endDate(val) {
this.self_end_date = val;
}
},
created() {
this.self_date_type = this.dateType;
this.self_start_date = this.startDate;
this.self_end_date = this.endDate;
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss">
$gantt_item: 16px;
$gantt_item_half: 8px;
.wl-gantt {
.wl-gantt-header > th {
text-align: center;
}
.wl-gantt-item {
position: relative;
transition: all 0.3s;
> .cell {
padding: 0;
}
}
.u-full.el-input {
width: 100%;
}
.wl-item-on {
position: absolute;
top: 50%;
left: 0;
right: -1px;
margin-top: -$gantt_item_half;
height: $gantt_item;
background: #409eff;
transition: all 0.4s;
}
.wl-item-start {
left: 50%;
&:after {
position: absolute;
top: $gantt_item;
left: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: #409eff transparent transparent;
border-width: 6px 6px 6px 0;
border-style: solid;
}
}
.wl-item-end {
right: 50%;
&:after {
position: absolute;
top: $gantt_item;
right: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: transparent #409eff;
border-width: 0 6px 6px 0;
border-style: solid;
}
}
.wl-item-full {
left: 0;
right: 0;
&:before {
position: absolute;
top: $gantt_item;
left: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: #409eff transparent transparent;
border-width: 6px 6px 6px 0;
border-style: solid;
}
&:after {
position: absolute;
top: $gantt_item;
right: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: transparent #409eff;
border-width: 0 6px 6px 0;
border-style: solid;
}
}
}
.year-and-month {
.wl-item-start {
left: 5%;
&:after {
position: absolute;
top: $gantt_item;
left: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: #409eff transparent transparent;
border-width: 6px 6px 6px 0;
border-style: solid;
}
}
.wl-item-end {
right: 5%;
&:after {
position: absolute;
top: $gantt_item;
right: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: transparent #409eff;
border-width: 0 6px 6px 0;
border-style: solid;
}
}
.wl-item-full {
left: 5%;
right: 5%;
&:before {
position: absolute;
top: $gantt_item;
left: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: #409eff transparent transparent;
border-width: 6px 6px 6px 0;
border-style: solid;
}
&:after {
position: absolute;
top: $gantt_item;
right: 0;
z-index: 1;
content: "";
width: 0;
height: 0;
border-color: transparent #409eff;
border-width: 0 6px 6px 0;
border-style: solid;
}
}
}
</style>