From 6de7c4258f16fe101dcc9e127fbce053fbea5fd0 Mon Sep 17 00:00:00 2001
From: loulijun2021 <1694218219@qq.com>
Date: 星期六, 24 九月 2022 13:29:09 +0800
Subject: [PATCH] 1.研究甘特图,之前调试出来好的甘特图样式没展现出来,现在已将样式调出来
---
src/lib/v-gantt-chart/lib/gantt.vue | 550 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 550 insertions(+), 0 deletions(-)
diff --git a/src/lib/v-gantt-chart/lib/gantt.vue b/src/lib/v-gantt-chart/lib/gantt.vue
new file mode 100644
index 0000000..7334293
--- /dev/null
+++ b/src/lib/v-gantt-chart/lib/gantt.vue
@@ -0,0 +1,550 @@
+<template>
+
+ <div class="gantt-chart"
+ @wheel="wheelHandle">
+ <div class="gantt-container"
+ :style="{height:`calc(100% - ${scrollXBarHeight}px)`,width:`calc(100% - ${scrollYBarWidth}px)`}">
+ <!-- <div class="gantt-container"> -->
+ <div v-show="!hideHeader"
+ class="gantt-header"
+ :style="{width:`calc(100% + ${scrollYBarWidth}px)`}">
+ <div class="gantt-header-title"
+ :style="{'line-height':titleHeight+'px',height:titleHeight+'px','width':titleWidth+'px'}">
+ <slot name="title">welcome v-gantt-chart</slot>
+ </div>
+ <div ref="headerTimeline"
+ class="gantt-header-timeline">
+ <div class="gantt-timeline-wrapper"
+ :style="{width:(totalWidth+scrollYBarWidth)+'px'}">
+ <timeline :start="start"
+ :end="end"
+ :cellWidth="cellWidth"
+ :titleHeight="titleHeight"
+ :scale="scale">
+ </timeline>
+ </div>
+ </div>
+ </div>
+
+ <div class="gantt-body"
+ :style="{height:`calc(100% - ${actualHeaderHeight}px)`}">
+ <!-- <div class="gantt-body" style="height:500px"> -->
+ <div class="gantt-table">
+ <div ref="marklineArea"
+ :style="{marginLeft:(titleWidth+50)+'px'}"
+ class="gantt-markline-area">
+ <!-- <CurrentTime v-if="showCurrentTime"
+ :getPositonOffset="getPositonOffset" /> -->
+ <!-- <mark-line v-for="(times,index) in timeLines"
+ :key="index"
+ :markLineTime="times.time"
+ :getPositonOffset="getPositonOffset"
+ :color="times.color"></mark-line> -->
+ </div>
+ <div ref="leftbarWrapper"
+ class="gantt-leftbar-wrapper"
+ :style="{'width':(titleWidth)+'px',height:`calc(100% + ${scrollXBarHeight}px)`}">
+ <LeftBar :datas="datas"
+ :dataKey="dataKey"
+ :scrollTop="scrollTop"
+ :heightOfRenderAera="heightOfRenderAera"
+ :widthOfRenderAera="widthOfRenderAera"
+ :cellHeight="cellHeight"
+ :style="{height:(totalHeight+scrollXBarHeight)+'px'}">
+ <template slot-scope="{data}">
+ <slot name="left"
+ :data="data">
+ </slot>
+ </template>
+ </LeftBar>
+ </div>
+ <div ref="blocksWrapper"
+ class="gantt-blocks-wrapper">
+ <blocks :scrollTop="scrollTop"
+ :scrollLeft="scrollLeft"
+ :heightOfRenderAera="heightOfRenderAera"
+ :widthOfRenderAera="widthOfRenderAera"
+ :arrayKeys="arrayKeys"
+ :itemKey="itemKey"
+ :dataKey="dataKey"
+ :datas="datas"
+ :cellWidth="cellWidth"
+ :cellHeight="cellHeight"
+ :scale="scale"
+ :getPositonOffset="getPositonOffset"
+ :getWidthAbout2Times="getWidthAbout2Times"
+ :customGenerateBlocks="customGenerateBlocks"
+ :startTimeOfRenderArea="startTimeOfRenderArea"
+ :endTimeOfRenderArea="endTimeOfRenderArea"
+ :style="{width:totalWidth+'px'}">
+
+ <!-- <template slot-scope="{data,item}">
+ <slot name="block"
+ :data="data"
+ :item="item">
+ </slot>
+ </template> -->
+
+ <template
+ slot-scope="{data,item,getPositonOffset,getWidthAbout2Times,isInRenderingTimeRange}">
+ <slot name="block"
+ :data="data"
+ :item="item"
+ :getPositonOffset="getPositonOffset"
+ :getWidthAbout2Times="getWidthAbout2Times"
+ :isInRenderingTimeRange="isInRenderingTimeRange"
+ :startTimeOfRenderArea="startTimeOfRenderArea"
+ :endTimeOfRenderArea="endTimeOfRenderArea"></slot>
+ </template>
+
+ </blocks>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div ref="scrollYBar"
+ class="gantt-scroll-y"
+ :style="{width:`${scrollYBarWidth}px`,
+ height:`calc(100% - ${actualHeaderHeight}px`,marginTop:`${actualHeaderHeight}px`}"
+ @scroll="syncScrollY">
+ <div :style="{height:totalHeight+'px'}"></div>
+ </div>
+
+ <div ref="scrollXBar"
+ class="gantt-scroll-x"
+ :style="{height:`${scrollXBarHeight}px`,
+ width:`calc(100% - ${titleWidth}px )`,marginLeft:titleWidth+'px'}"
+ @scroll="syncScrollX">
+ <div :style="{width:totalWidth+'px'}"></div>
+ </div>
+
+ </div>
+</template>
+
+<script>
+import dayjs from "dayjs";
+import ResizeObserver from "resize-observer-polyfill";
+import {
+ scaleList,
+ getBeginTimeOfTimeLine,
+ calcScalesAbout2Times
+} from "./utils/timeLineUtils.js";
+import { isDef, warn } from "./utils/tool.js";
+import {
+ getPositonOffset as _getPositonOffset,
+ getWidthAbout2Times as _getWidthAbout2Times
+} from "./utils/gtUtils.js";
+import throttle from "./utils/throttle.js";
+import Timeline from "./components/time-line/index.vue";
+import CurrentTime from "./components/mark-line/current-time.vue";
+import LeftBar from "./components/left-bar/index.vue";
+import Blocks from "./components/blocks/index.vue";
+import MarkLine from "./components/mark-line/index.vue";
+
+export default {
+ name: "Gantt",
+
+ components: { Timeline, LeftBar, Blocks, MarkLine, CurrentTime },
+
+ props: {
+ startTime: {
+ default: () => dayjs(),
+ validator(date) {
+ let ok = dayjs(date).isValid();
+ if (!ok) warn(`闈炴硶鐨勫紑濮嬫椂闂� ${date}`);
+ return ok;
+ }
+ },
+ endTime: {
+ default: () => dayjs(),
+ validator(date) {
+ let ok = dayjs(date).isValid();
+ if (!ok) warn(`闈炴硶鐨勭粨鏉熸椂闂� ${date}`);
+ return ok;
+ }
+ },
+ cellWidth: {
+ type: Number,
+ default: 50
+ },
+ cellHeight: {
+ type: Number,
+ default: 20
+ },
+ titleHeight: {
+ type: Number,
+ default: 40
+ },
+ titleWidth: {
+ type: Number,
+ default: 200
+ },
+ scale: {
+ type: Number,
+ default: 60,
+ validator(value) {
+ return scaleList.includes(value);
+ }
+ },
+ datas: {
+ type: Array,
+ default: () => []
+ },
+ dataKey: {
+ type: String,
+ default: undefined
+ },
+ itemKey: {
+ type: String,
+ default: undefined
+ },
+ arrayKeys: {
+ type: Array,
+ default: () => []
+ },
+ showCurrentTime: {
+ type: Boolean,
+ default: false
+ },
+ timeLines: {
+ type: Array
+ },
+ scrollToTime: {
+ validator(date) {
+ return dayjs(date).isValid();
+ }
+ },
+ scrollToPostion: {
+ validator(obj) {
+ let validX = isDef(obj.x) ? !Number.isNaN(obj.x) : true;
+ let validY = isDef(obj.y) ? !Number.isNaN(obj.y) : true;
+ if (!validX && !validY) {
+ warn("scrollToPostion x鎴杫 鏈夊�间负闈濶umber绫诲瀷");
+ return false;
+ }
+ return true;
+ }
+ },
+ hideHeader: {
+ type: Boolean,
+ default: false
+ },
+ hideXScrollBar: {
+ type: Boolean,
+ default: false
+ },
+ hideYScrollBar: {
+ type: Boolean,
+ default: false
+ },
+ customGenerateBlocks: {
+ type: Boolean,
+ default: false
+ }
+ },
+
+ data() {
+ return {
+ //缂撳瓨鑺傜偣
+ selector: {
+ gantt_leftbar: {},
+ gantt_table: {},
+ gantt_scroll_y: {},
+ gantt_timeline: {},
+ gantt_scroll_x: {},
+ gantt_markArea: {}
+ },
+ scrollTop: 0,
+ scrollLeft: 0,
+ //block 鍖哄煙闇�瑕佹覆鏌撶殑鑼冨洿
+ //鍏堟覆鏌撳嚭绌烘鏋讹紝鍦╩ounted鍚庡啀寰楀埌鐪熷疄鐨勬覆鏌撹寖鍥达紝鐒跺悗鍦ㄦ牴鎹寖鍥存覆鏌撴暟鎹紝姣斾箣鍓嶈缃竴涓粯璁ら珮搴﹀搴︼紝棰濆鐨勬覆鏌撴氮璐规洿灏戜簡
+ heightOfRenderAera: 0,
+ widthOfRenderAera: 0,
+ startTimeOfRenderArea: null,
+ endTimeOfRenderArea: null,
+ scrollBarWitdh: 17
+ };
+ },
+
+ computed: {
+ start() {
+ return dayjs(this.startTime);
+ },
+ end() {
+ let { start, widthOfRenderAera, scale, cellWidth } = this;
+ let end = dayjs(this.endTime);
+ let totalWidth = calcScalesAbout2Times(start, end, scale) * cellWidth;
+ if (start.isAfter(end) || totalWidth <= widthOfRenderAera) {
+ end = start.add((widthOfRenderAera / cellWidth) * scale, "minute");
+ }
+ return end;
+ },
+ totalWidth() {
+ let { cellWidth, totalScales } = this;
+ return cellWidth * totalScales;
+ },
+ totalScales() {
+ let { start, end, scale } = this;
+ return calcScalesAbout2Times(start, end, scale);
+ },
+ totalHeight() {
+ let { datas, cellHeight } = this;
+ return datas.length * cellHeight;
+ },
+ beginTimeOfTimeLine() {
+ let value = getBeginTimeOfTimeLine(this.start, this.scale);
+ return value;
+ },
+ beginTimeOfTimeLineToString() {
+ return this.beginTimeOfTimeLine.toString();
+ },
+ avialableScrollLeft() {
+ // 涓嶅噺杩欎釜1锛屾粴鍔ㄥ埌鏃堕棿杞村敖澶村悗缁х画婊氬姩浼氭參鎱㈢殑婧㈠嚭
+ let { totalWidth, widthOfRenderAera } = this;
+ return totalWidth - widthOfRenderAera - 1;
+ },
+ avialableScrollTop() {
+ let { totalHeight, heightOfRenderAera } = this;
+ return totalHeight - heightOfRenderAera - 1;
+ },
+ scrollXBarHeight() {
+ return this.hideXScrollBar ? 0 : this.scrollBarWitdh;
+ },
+ scrollYBarWidth() {
+ return this.hideYScrollBar ? 0 : this.scrollBarWitdh;
+ },
+ actualHeaderHeight() {
+ return this.hideHeader ? 0 : this.titleHeight;
+ }
+ },
+
+ watch: {
+ scrollLeft() {
+ this.getTimeRange();
+ },
+ widthOfRenderAera() {
+ this.getTimeRange();
+ },
+ cellWidth() {
+ this.getTimeRange();
+ },
+ scrollToTime: {
+ handler(newV) {
+ if (!newV) {
+ return;
+ }
+ let { start, end } = this;
+ let time = dayjs(newV);
+ if (!(time.isAfter(start) && time.isBefore(end))) {
+ warn(`褰撳墠婊氬姩鑷�${newV}涓嶅湪${start}鍜�${end}鐨勮寖鍥翠箣鍐卄);
+ return;
+ }
+
+ let offset = this.getPositonOffset(newV);
+ // immediate 浼氶�犳垚dom 杩樻病鏈夋寕杞芥椂灏辫繘琛屾搷浣滐紝鏁呴渶瑕佸欢杩熸墽琛�
+ this.$nextTick(() =>
+ this.syncScrollX(
+ {
+ target: {
+ scrollLeft: offset
+ }
+ },
+ true
+ )
+ );
+ },
+ immediate: true
+ },
+ scrollToPostion: {
+ handler(newV) {
+ if (!newV) {
+ return;
+ }
+ let x = Number.isNaN(newV.x) ? undefined : newV.x;
+ let y = Number.isNaN(newV.y) ? undefined : newV.y;
+ this.$nextTick(() => {
+ if (isDef(x) && x !== this.scrollLeft) {
+ this.syncScrollX({ target: { scrollLeft: x } }, true);
+ }
+ if (isDef(y) && y !== this.scrollTop) {
+ this.syncScrollY({ target: { scrollTop: y } }, true);
+ }
+ });
+ },
+ immediate: true
+ }
+ },
+
+ mounted() {
+ this.getSelector();
+ // 璁$畻鍑嗙‘鐨勬覆鏌撳尯鍩熻寖鍥�
+ const observeContainer = throttle(entries => {
+ entries.forEach(entry => {
+ const cr = entry.contentRect;
+ this.heightOfRenderAera = cr.height;
+ this.widthOfRenderAera = cr.width;
+ });
+ });
+ const observer = new ResizeObserver(observeContainer);
+ observer.observe(this.$refs.blocksWrapper);
+ },
+
+ methods: {
+ /**
+ * 璁$畻闇�瑕佹覆鏌撶殑鏃堕棿鑼冨洿
+ *
+ */
+ getTimeRange() {
+ if (this.heightOfRenderAera === 0) {
+ return;
+ }
+
+ let {
+ beginTimeOfTimeLine,
+ scrollLeft,
+ cellWidth,
+ scale,
+ widthOfRenderAera
+ } = this;
+
+ this.startTimeOfRenderArea = beginTimeOfTimeLine
+ .add((scrollLeft / cellWidth) * scale, "minute")
+ .toDate()
+ .getTime();
+ this.endTimeOfRenderArea = beginTimeOfTimeLine
+ .add(((scrollLeft + widthOfRenderAera) / cellWidth) * scale, "minute")
+ .toDate()
+ .getTime();
+ },
+ getWidthAbout2Times(start, end) {
+ let options = {
+ scale: this.scale,
+ cellWidth: this.cellWidth
+ };
+ return _getWidthAbout2Times(start, end, options);
+ },
+ /**
+ * 涓烘椂闂寸嚎璁$畻鍋忕Щ
+ */
+ getPositonOffset(date) {
+ let options = {
+ scale: this.scale,
+ cellWidth: this.cellWidth
+ };
+
+ return _getPositonOffset(date, this.beginTimeOfTimeLineToString, options);
+ },
+ //缂撳瓨鑺傜偣
+ getSelector() {
+ this.selector.gantt_leftbar = this.$refs.leftbarWrapper;
+ this.selector.gantt_table = this.$refs.blocksWrapper;
+ this.selector.gantt_scroll_y = this.$refs.scrollYBar;
+ this.selector.gantt_timeline = this.$refs.headerTimeline;
+ this.selector.gantt_scroll_x = this.$refs.scrollXBar;
+ this.selector.gantt_markArea = this.$refs.marklineArea;
+ },
+ wheelHandle(event) {
+ let { deltaX, deltaY } = event;
+ this.$nextTick(() => {
+ let {
+ scrollTop,
+ scrollLeft,
+ avialableScrollLeft,
+ avialableScrollTop
+ } = this;
+
+ if (deltaY !== 0) {
+ if (
+ scrollTop + deltaY >= avialableScrollTop &&
+ scrollTop !== avialableScrollTop
+ ) {
+ this.syncScrollY(
+ { target: { scrollTop: avialableScrollTop } },
+ true
+ );
+ } else if (
+ scrollTop + deltaY < 0 &&
+ scrollTop !== 0 /*婊氬姩涓�0闄愬埗*/
+ ) {
+ this.syncScrollY({ target: { scrollTop: 0 } }, true);
+ } else {
+ this.syncScrollY(
+ { target: { scrollTop: scrollTop + deltaY } },
+ true
+ );
+ }
+ }
+ if (deltaX !== 0) {
+ if (
+ scrollLeft + deltaX >= avialableScrollLeft &&
+ scrollLeft !== avialableScrollLeft
+ ) {
+ this.syncScrollX(
+ { target: { scrollLeft: avialableScrollLeft } },
+ true
+ );
+ } else if (
+ scrollLeft + deltaX < 0 &&
+ scrollLeft !== 0 /*婊氬姩涓�0闄愬埗*/
+ ) {
+ this.syncScrollX({ target: { scrollLeft: 0 } }, true);
+ } else {
+ this.syncScrollX(
+ { target: { scrollLeft: scrollLeft + deltaX } },
+ true
+ );
+ }
+ }
+ });
+ },
+ //鍚屾fixleft鍜宐lock鐨勬粴鍔�
+ syncScrollY(event, fake = false) {
+ let { gantt_leftbar, gantt_table, gantt_scroll_y } = this.selector;
+ let topValue = event.target.scrollTop;
+ if (fake) {
+ //浼氳Е鍙戜竴娆$湡鐨勬粴鍔ㄤ簨浠秂vent, 鍚庨潰鐨勪唬鐮佷細鍦ㄧ浜屼釜浜嬩欢涓墽琛�
+ gantt_scroll_y.scrollTop = topValue;
+ return;
+ }
+ gantt_leftbar.scrollTop = topValue;
+ gantt_table.scrollTop = topValue;
+ this.scrollTop = topValue;
+ this.$emit("scrollTop", topValue);
+ },
+ syncScrollX(event, fake = false) {
+ let {
+ gantt_table,
+ gantt_timeline,
+ gantt_markArea,
+ gantt_scroll_x
+ } = this.selector;
+ let leftValue = event.target.scrollLeft;
+ if (fake) {
+ //浼氳Е鍙戜竴娆$湡鐨勬粴鍔ㄤ簨浠秂vent, 鍚庨潰鐨勪唬鐮佷細鍦ㄧ浜屼釜浜嬩欢涓墽琛�
+ gantt_scroll_x.scrollLeft = leftValue;
+ return;
+ }
+ gantt_table.scrollLeft = leftValue;
+ gantt_timeline.scrollLeft = leftValue;
+ gantt_markArea.style.left = "-" + leftValue + "px";
+ this.scrollLeft = leftValue;
+ this.$emit("scrollLeft", leftValue);
+ }
+ }
+};
+</script>
+
+<style lang="scss">
+@import "./gantt.scss";
+.gantt-leftbar-wrapper{
+ // overflow-y: scroll;
+ // overflow: visible;
+}
+.gantt-body{
+ // overflow-y: scroll;
+}
+.gantt-leftbar{
+ // overflow-y: scroll;
+}
+</style>
--
Gitblit v1.9.3