| | |
| | | <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-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'}" |
| | | > |
| | | <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" |
| | | :cell-width="cellWidth" |
| | | :title-height="titleHeight" |
| | | :scale="scale" |
| | | /> |
| | | <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:`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" |
| | | > |
| | | <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" |
| | |
| | | :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" |
| | | :data-key="dataKey" |
| | | :scroll-top="scrollTop" |
| | | :height-of-render-aera="heightOfRenderAera" |
| | | :width-of-render-aera="widthOfRenderAera" |
| | | :cell-height="cellHeight" |
| | | :style="{height:(totalHeight+scrollXBarHeight)+'px'}" |
| | | > |
| | | <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 name="left" |
| | | :data="data"> |
| | | </slot> |
| | | </template> |
| | | </LeftBar> |
| | | </div> |
| | | <div |
| | | ref="blocksWrapper" |
| | | class="gantt-blocks-wrapper" |
| | | > |
| | | <blocks |
| | | :scroll-top="scrollTop" |
| | | :scroll-left="scrollLeft" |
| | | :height-of-render-aera="heightOfRenderAera" |
| | | :width-of-render-aera="widthOfRenderAera" |
| | | :array-keys="arrayKeys" |
| | | :item-key="itemKey" |
| | | :data-key="dataKey" |
| | | :datas="datas" |
| | | :cell-width="cellWidth" |
| | | :cell-height="cellHeight" |
| | | :scale="scale" |
| | | :get-positon-offset="getPositonOffset" |
| | | :get-width-about2times="getWidthAbout2Times" |
| | | :custom-generate-blocks="customGenerateBlocks" |
| | | :start-time-of-render-area="startTimeOfRenderArea" |
| | | :end-time-of-render-area="endTimeOfRenderArea" |
| | | :style="{width:totalWidth+'px'}" |
| | | > |
| | | <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" |
| | |
| | | </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-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 |
| | | 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 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 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 dayjs from "dayjs"; |
| | | import ResizeObserver from "resize-observer-polyfill"; |
| | | import { |
| | | scaleList, |
| | | getBeginTimeOfTimeLine, |
| | | calcScalesAbout2Times |
| | | } from './utils/timeLineUtils.js' |
| | | import { isDef, warn } from './utils/tool.js' |
| | | } 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' |
| | | } 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', |
| | | name: "Gantt", |
| | | |
| | | components: { Timeline, LeftBar, Blocks, MarkLine, CurrentTime }, |
| | | |
| | |
| | | startTime: { |
| | | default: () => dayjs(), |
| | | validator(date) { |
| | | const ok = dayjs(date).isValid() |
| | | if (!ok) warn(`非法的开始时间 ${date}`) |
| | | return ok |
| | | let ok = dayjs(date).isValid(); |
| | | if (!ok) warn(`非法的开始时间 ${date}`); |
| | | return ok; |
| | | } |
| | | }, |
| | | endTime: { |
| | | default: () => dayjs(), |
| | | validator(date) { |
| | | const ok = dayjs(date).isValid() |
| | | if (!ok) warn(`非法的结束时间 ${date}`) |
| | | return ok |
| | | let ok = dayjs(date).isValid(); |
| | | if (!ok) warn(`非法的结束时间 ${date}`); |
| | | return ok; |
| | | } |
| | | }, |
| | | cellWidth: { |
| | |
| | | type: Number, |
| | | default: 60, |
| | | validator(value) { |
| | | return scaleList.includes(value) |
| | | return scaleList.includes(value); |
| | | } |
| | | }, |
| | | datas: { |
| | |
| | | }, |
| | | scrollToTime: { |
| | | validator(date) { |
| | | return dayjs(date).isValid() |
| | | return dayjs(date).isValid(); |
| | | } |
| | | }, |
| | | scrollToPostion: { |
| | | validator(obj) { |
| | | const validX = isDef(obj.x) ? !Number.isNaN(obj.x) : true |
| | | const validY = isDef(obj.y) ? !Number.isNaN(obj.y) : true |
| | | 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或y 有值为非Number类型') |
| | | return false |
| | | warn("scrollToPostion x或y 有值为非Number类型"); |
| | | return false; |
| | | } |
| | | return true |
| | | return true; |
| | | } |
| | | }, |
| | | hideHeader: { |
| | |
| | | |
| | | data() { |
| | | return { |
| | | // 缓存节点 |
| | | //缓存节点 |
| | | selector: { |
| | | gantt_leftbar: {}, |
| | | gantt_table: {}, |
| | |
| | | }, |
| | | scrollTop: 0, |
| | | scrollLeft: 0, |
| | | // block 区域需要渲染的范围 |
| | | // 先渲染出空框架,在mounted后再得到真实的渲染范围,然后在根据范围渲染数据,比之前设置一个默认高度宽度,额外的渲染浪费更少了 |
| | | //block 区域需要渲染的范围 |
| | | //先渲染出空框架,在mounted后再得到真实的渲染范围,然后在根据范围渲染数据,比之前设置一个默认高度宽度,额外的渲染浪费更少了 |
| | | heightOfRenderAera: 0, |
| | | widthOfRenderAera: 0, |
| | | startTimeOfRenderArea: null, |
| | | endTimeOfRenderArea: null, |
| | | scrollBarWitdh: 17 |
| | | } |
| | | }; |
| | | }, |
| | | |
| | | computed: { |
| | | start() { |
| | | return dayjs(this.startTime) |
| | | return dayjs(this.startTime); |
| | | }, |
| | | end() { |
| | | const { start, widthOfRenderAera, scale, cellWidth } = this |
| | | let end = dayjs(this.endTime) |
| | | const totalWidth = calcScalesAbout2Times(start, end, scale) * cellWidth |
| | | 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') |
| | | end = start.add((widthOfRenderAera / cellWidth) * scale, "minute"); |
| | | } |
| | | return end |
| | | return end; |
| | | }, |
| | | totalWidth() { |
| | | const { cellWidth, totalScales } = this |
| | | return cellWidth * totalScales |
| | | let { cellWidth, totalScales } = this; |
| | | return cellWidth * totalScales; |
| | | }, |
| | | totalScales() { |
| | | const { start, end, scale } = this |
| | | return calcScalesAbout2Times(start, end, scale) |
| | | let { start, end, scale } = this; |
| | | return calcScalesAbout2Times(start, end, scale); |
| | | }, |
| | | totalHeight() { |
| | | const { datas, cellHeight } = this |
| | | return datas.length * cellHeight |
| | | let { datas, cellHeight } = this; |
| | | return datas.length * cellHeight; |
| | | }, |
| | | beginTimeOfTimeLine() { |
| | | const value = getBeginTimeOfTimeLine(this.start, this.scale) |
| | | return value |
| | | let value = getBeginTimeOfTimeLine(this.start, this.scale); |
| | | return value; |
| | | }, |
| | | beginTimeOfTimeLineToString() { |
| | | return this.beginTimeOfTimeLine.toString() |
| | | return this.beginTimeOfTimeLine.toString(); |
| | | }, |
| | | avialableScrollLeft() { |
| | | // 不减这个1,滚动到时间轴尽头后继续滚动会慢慢的溢出 |
| | | const { totalWidth, widthOfRenderAera } = this |
| | | return totalWidth - widthOfRenderAera - 1 |
| | | let { totalWidth, widthOfRenderAera } = this; |
| | | return totalWidth - widthOfRenderAera - 1; |
| | | }, |
| | | avialableScrollTop() { |
| | | const { totalHeight, heightOfRenderAera } = this |
| | | return totalHeight - heightOfRenderAera - 1 |
| | | let { totalHeight, heightOfRenderAera } = this; |
| | | return totalHeight - heightOfRenderAera - 1; |
| | | }, |
| | | scrollXBarHeight() { |
| | | return this.hideXScrollBar ? 0 : this.scrollBarWitdh |
| | | return this.hideXScrollBar ? 0 : this.scrollBarWitdh; |
| | | }, |
| | | scrollYBarWidth() { |
| | | return this.hideYScrollBar ? 0 : this.scrollBarWitdh |
| | | return this.hideYScrollBar ? 0 : this.scrollBarWitdh; |
| | | }, |
| | | actualHeaderHeight() { |
| | | return this.hideHeader ? 0 : this.titleHeight |
| | | return this.hideHeader ? 0 : this.titleHeight; |
| | | } |
| | | }, |
| | | |
| | | watch: { |
| | | scrollLeft() { |
| | | this.getTimeRange() |
| | | this.getTimeRange(); |
| | | }, |
| | | widthOfRenderAera() { |
| | | this.getTimeRange() |
| | | this.getTimeRange(); |
| | | }, |
| | | cellWidth() { |
| | | this.getTimeRange() |
| | | this.getTimeRange(); |
| | | }, |
| | | scrollToTime: { |
| | | handler(newV) { |
| | | if (!newV) { |
| | | return |
| | | return; |
| | | } |
| | | const { start, end } = this |
| | | const time = dayjs(newV) |
| | | let { start, end } = this; |
| | | let time = dayjs(newV); |
| | | if (!(time.isAfter(start) && time.isBefore(end))) { |
| | | warn(`当前滚动至${newV}不在${start}和${end}的范围之内`) |
| | | return |
| | | warn(`当前滚动至${newV}不在${start}和${end}的范围之内`); |
| | | return; |
| | | } |
| | | |
| | | const offset = this.getPositonOffset(newV) |
| | | let offset = this.getPositonOffset(newV); |
| | | // immediate 会造成dom 还没有挂载时就进行操作,故需要延迟执行 |
| | | this.$nextTick(() => |
| | | this.syncScrollX( |
| | |
| | | }, |
| | | true |
| | | ) |
| | | ) |
| | | ); |
| | | }, |
| | | immediate: true |
| | | }, |
| | | scrollToPostion: { |
| | | handler(newV) { |
| | | if (!newV) { |
| | | return |
| | | return; |
| | | } |
| | | const x = Number.isNaN(newV.x) ? undefined : newV.x |
| | | const y = Number.isNaN(newV.y) ? undefined : newV.y |
| | | 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) |
| | | this.syncScrollX({ target: { scrollLeft: x } }, true); |
| | | } |
| | | if (isDef(y) && y !== this.scrollTop) { |
| | | this.syncScrollY({ target: { scrollTop: y }}, true) |
| | | this.syncScrollY({ target: { scrollTop: y } }, true); |
| | | } |
| | | }) |
| | | }); |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | this.getSelector() |
| | | 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) |
| | | 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 |
| | | return; |
| | | } |
| | | |
| | | const { |
| | | let { |
| | | beginTimeOfTimeLine, |
| | | scrollLeft, |
| | | cellWidth, |
| | | scale, |
| | | widthOfRenderAera |
| | | } = this |
| | | } = this; |
| | | |
| | | this.startTimeOfRenderArea = beginTimeOfTimeLine |
| | | .add((scrollLeft / cellWidth) * scale, 'minute') |
| | | .add((scrollLeft / cellWidth) * scale, "minute") |
| | | .toDate() |
| | | .getTime() |
| | | .getTime(); |
| | | this.endTimeOfRenderArea = beginTimeOfTimeLine |
| | | .add(((scrollLeft + widthOfRenderAera) / cellWidth) * scale, 'minute') |
| | | .add(((scrollLeft + widthOfRenderAera) / cellWidth) * scale, "minute") |
| | | .toDate() |
| | | .getTime() |
| | | .getTime(); |
| | | }, |
| | | getWidthAbout2Times(start, end) { |
| | | const options = { |
| | | let options = { |
| | | scale: this.scale, |
| | | cellWidth: this.cellWidth |
| | | } |
| | | return _getWidthAbout2Times(start, end, options) |
| | | }; |
| | | return _getWidthAbout2Times(start, end, options); |
| | | }, |
| | | /** |
| | | * 为时间线计算偏移 |
| | | */ |
| | | getPositonOffset(date) { |
| | | const options = { |
| | | let options = { |
| | | scale: this.scale, |
| | | cellWidth: this.cellWidth |
| | | } |
| | | }; |
| | | |
| | | return _getPositonOffset(date, this.beginTimeOfTimeLineToString, options) |
| | | 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 |
| | | 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) { |
| | | const { deltaX, deltaY } = event |
| | | let { deltaX, deltaY } = event; |
| | | this.$nextTick(() => { |
| | | const { |
| | | let { |
| | | scrollTop, |
| | | scrollLeft, |
| | | avialableScrollLeft, |
| | | avialableScrollTop |
| | | } = this |
| | | } = this; |
| | | |
| | | if (deltaY !== 0) { |
| | | if ( |
| | |
| | | scrollTop !== avialableScrollTop |
| | | ) { |
| | | this.syncScrollY( |
| | | { target: { scrollTop: avialableScrollTop }}, |
| | | { target: { scrollTop: avialableScrollTop } }, |
| | | true |
| | | ) |
| | | ); |
| | | } else if ( |
| | | scrollTop + deltaY < 0 && |
| | | scrollTop !== 0 /* 滚动为0限制*/ |
| | | scrollTop !== 0 /*滚动为0限制*/ |
| | | ) { |
| | | this.syncScrollY({ target: { scrollTop: 0 }}, true) |
| | | this.syncScrollY({ target: { scrollTop: 0 } }, true); |
| | | } else { |
| | | this.syncScrollY( |
| | | { target: { scrollTop: scrollTop + deltaY }}, |
| | | { target: { scrollTop: scrollTop + deltaY } }, |
| | | true |
| | | ) |
| | | ); |
| | | } |
| | | } |
| | | if (deltaX !== 0) { |
| | |
| | | scrollLeft !== avialableScrollLeft |
| | | ) { |
| | | this.syncScrollX( |
| | | { target: { scrollLeft: avialableScrollLeft }}, |
| | | { target: { scrollLeft: avialableScrollLeft } }, |
| | | true |
| | | ) |
| | | ); |
| | | } else if ( |
| | | scrollLeft + deltaX < 0 && |
| | | scrollLeft !== 0 /* 滚动为0限制*/ |
| | | scrollLeft !== 0 /*滚动为0限制*/ |
| | | ) { |
| | | this.syncScrollX({ target: { scrollLeft: 0 }}, true) |
| | | this.syncScrollX({ target: { scrollLeft: 0 } }, true); |
| | | } else { |
| | | this.syncScrollX( |
| | | { target: { scrollLeft: scrollLeft + deltaX }}, |
| | | { target: { scrollLeft: scrollLeft + deltaX } }, |
| | | true |
| | | ) |
| | | ); |
| | | } |
| | | } |
| | | }) |
| | | }); |
| | | }, |
| | | // 同步fixleft和block的滚动 |
| | | //同步fixleft和block的滚动 |
| | | syncScrollY(event, fake = false) { |
| | | const { gantt_leftbar, gantt_table, gantt_scroll_y } = this.selector |
| | | const topValue = event.target.scrollTop |
| | | let { gantt_leftbar, gantt_table, gantt_scroll_y } = this.selector; |
| | | let topValue = event.target.scrollTop; |
| | | if (fake) { |
| | | // 会触发一次真的滚动事件event, 后面的代码会在第二个事件中执行 |
| | | gantt_scroll_y.scrollTop = topValue |
| | | return |
| | | //会触发一次真的滚动事件event, 后面的代码会在第二个事件中执行 |
| | | gantt_scroll_y.scrollTop = topValue; |
| | | return; |
| | | } |
| | | gantt_leftbar.scrollTop = topValue |
| | | gantt_table.scrollTop = topValue |
| | | this.scrollTop = topValue |
| | | this.$emit('scrollTop', topValue) |
| | | gantt_leftbar.scrollTop = topValue; |
| | | gantt_table.scrollTop = topValue; |
| | | this.scrollTop = topValue; |
| | | this.$emit("scrollTop", topValue); |
| | | }, |
| | | syncScrollX(event, fake = false) { |
| | | const { |
| | | let { |
| | | gantt_table, |
| | | gantt_timeline, |
| | | gantt_markArea, |
| | | gantt_scroll_x |
| | | } = this.selector |
| | | const leftValue = event.target.scrollLeft |
| | | } = this.selector; |
| | | let leftValue = event.target.scrollLeft; |
| | | if (fake) { |
| | | // 会触发一次真的滚动事件event, 后面的代码会在第二个事件中执行 |
| | | gantt_scroll_x.scrollLeft = leftValue |
| | | return |
| | | //会触发一次真的滚动事件event, 后面的代码会在第二个事件中执行 |
| | | 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) |
| | | 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"> |