From 44f0f9c1ae65f906516f22d4a3c8f61eb5f79a7d Mon Sep 17 00:00:00 2001 From: jeremyjone Date: Tue, 16 May 2023 17:15:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8add=20=20jump=20to=20date=20fu?= =?UTF-8?q?nction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/demo.vue | 26 ++++++--- src/components/root/RootWrap.vue | 17 ++---- src/components/root/index.vue | 3 +- src/composables/useExport.ts | 91 ++++++++++++++++++++++++++++++-- src/composables/useToday.ts | 7 +-- src/models/param/header.ts | 8 +++ 6 files changed, 127 insertions(+), 25 deletions(-) diff --git a/demo/demo.vue b/demo/demo.vue index c822e04..19cb85e 100644 --- a/demo/demo.vue +++ b/demo/demo.vue @@ -1,6 +1,7 @@ @@ -117,22 +120,22 @@ ganttData[0].children = [ { index: ++id, name: 'sub-t' + id, - startDate: new Date(2023, 4, 1), - endDate: new Date(2023, 4, 5), + startDate: new Date(2023, 3, 1), + endDate: new Date(2023, 3, 5), o: { t1: 'a', t2: 'b' } }, { index: ++id, name: 'sub-t' + id, - startDate: new Date(2023, 4, 1), - endDate: new Date(2023, 4, 5), + startDate: new Date(2023, 3, 1), + endDate: new Date(2023, 3, 5), o: { t1: 'a', t2: 'b' }, children: [ { index: ++id, name: 'sub-sub-t' + id, - startDate: new Date(2023, 4, 1), - endDate: new Date(2023, 4, 5), + startDate: new Date(2023, 3, 1), + endDate: new Date(2023, 3, 5), o: { t1: 'a', t2: 'b' } } ] @@ -228,6 +231,17 @@ const onClickLink = (data: any) => { function onMove(data: any) { return true; } + +const onNoDateError = () => { + console.log('no date error'); +}; + +const ganttRef = ref(null) as any; +function jumpToDate() { + console.log('jumpToDate', ganttRef.value); + + ganttRef.value?.jumpToDate(); +} diff --git a/src/components/root/RootWrap.vue b/src/components/root/RootWrap.vue index 8ce89c2..1cfa338 100644 --- a/src/components/root/RootWrap.vue +++ b/src/components/root/RootWrap.vue @@ -3,9 +3,8 @@ diff --git a/src/components/root/index.vue b/src/components/root/index.vue index 87c97b2..fe46912 100644 --- a/src/components/root/index.vue +++ b/src/components/root/index.vue @@ -187,7 +187,8 @@ onResizeTableColumn(midLineRef, { console.log('.....root', getCurrentInstance()); // #region 暴露方法 -defineExpose(useExport()); +const exports = useExport(ganttRef); +defineExpose(exports); // #endregion diff --git a/src/composables/useExport.ts b/src/composables/useExport.ts index 0dbd982..6c9429d 100644 --- a/src/composables/useExport.ts +++ b/src/composables/useExport.ts @@ -1,7 +1,92 @@ -export default () => { +import { XDate } from '@/models/param/date'; +import { isDate, isUndefined } from 'lodash'; +import { type Ref, type DefineComponent } from 'vue'; +import useEvent from './useEvent'; +import useGanttHeader from './useGanttHeader'; +import useGanttWidth from './useGanttWidth'; +import useToday from './useToday'; + +export default (ganttRef: Ref) => { + const { isInArea } = useToday(); + const { EmitNoDateError } = useEvent(); + const { ganttHeader } = useGanttHeader(); + const { ganttColumnWidth, currentMillisecond } = useGanttWidth(); + + /** + * 跳转到某个日期 + */ + function handleJumpTo(_date: Date | undefined) { + if (!ganttRef.value) return; + + let date: XDate; + + // 未定义日期,默认跳转到今天 + if (isUndefined(_date) || !isDate(_date)) { + date = new XDate(); + } else { + date = new XDate(_date); + } + + if (!isInArea(date)) { + EmitNoDateError(); + return; + } + + date.startOf(ganttHeader.unit); + const width = + (date.intervalTo(ganttHeader.start) / currentMillisecond.value) * + ganttColumnWidth.value; + + let left = 0; + let right = 0; + + if (ganttRef.value.$el.scrollLeft < width) { + // 日期在右侧 + right = width - ganttRef.value.$el.clientWidth / 3; + } else { + // 日期在左侧 + left = Math.abs(ganttRef.value.$el.clientWidth / 6 - width); + } + + // 滚动动画,ease-in模式 + if (left && right) return; + + const duration = 1000; + const distance = left || right; + let oldTimestamp: number | null = null; + let scrollX = 0; + let oldLeft: number | undefined; // 初始不定义,保证第一次不会匹配 + + function step(newTimestamp: number) { + if (oldTimestamp !== null && ganttRef.value) { + // if duration is 0 scrollX will be -Infinity + if ( + ganttRef.value.$el.scrollLeft < left || + (right > 0 && ganttRef.value.$el.scrollLeft >= right) || + oldLeft === ganttRef.value.$el.scrollLeft + ) + return; + + const x = (distance * (newTimestamp - oldTimestamp)) / duration; + if (left) { + scrollX -= x; + } else if (right) { + scrollX += x; + } else { + return; + } + oldLeft = ganttRef.value.$el.scrollLeft; + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + ganttRef.value.$el.scrollLeft += scrollX; + } + oldTimestamp = newTimestamp; + window.requestAnimationFrame(step); + } + window.requestAnimationFrame(step); + } + return { setSelected: () => {}, - jumpToDate: () => {}, - setHeaderUnit: () => {} + jumpToDate: handleJumpTo }; }; diff --git a/src/composables/useToday.ts b/src/composables/useToday.ts index 6ccf535..8627f9a 100644 --- a/src/composables/useToday.ts +++ b/src/composables/useToday.ts @@ -28,8 +28,8 @@ export default () => { function isInArea(date: XDate) { if (ganttHeader.dates.length === 0) return false; - const sd = ganttHeader.dates[0]; - const ed = ganttHeader.dates[ganttHeader.dates.length - 1]; + const sd = ganttHeader.start; + const ed = ganttHeader.end; return sd?.compareTo(date) === 'l' && ed?.compareTo(date) === 'r'; } @@ -40,6 +40,7 @@ export default () => { return { todayLeft, - showToday + showToday, + isInArea }; }; diff --git a/src/models/param/header.ts b/src/models/param/header.ts index 827908e..66a91e0 100644 --- a/src/models/param/header.ts +++ b/src/models/param/header.ts @@ -199,7 +199,14 @@ class GanttHeader extends Header { */ dates: XDate[] = []; + /** + * 甘特的起始时间(数据起始时间请使用 data.start) + */ start?: XDate; + + /** + * 甘特的结束时间(数据结束时间请使用 data.end) + */ end?: XDate; unit: HeaderDateUnit = 'day'; minLength: number = 0; @@ -268,6 +275,7 @@ class GanttHeader extends Header { }); this.headers = this.convertToRows(columns, this.getAllColumns(columns)); + this.end = this.dates[this.dates.length - 1]; } /**