From 335fc6b504d5648c13e7a408b0be5403f31d3869 Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Fri, 8 Aug 2025 21:03:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E6=97=A5?= =?UTF-8?q?=E5=8E=86=E5=A4=A9=E3=80=81=E5=91=A8=E6=A0=B7=E5=BC=8F=E6=97=B6?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E6=B0=94=E6=B3=A1=E6=98=BE=E7=A4=BA=E4=B8=8D?= =?UTF-8?q?=E5=85=A8=EF=BC=8C=E7=8E=B0=E5=B0=86=E6=B0=94=E6=B3=A1=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=80=BB=E8=BE=91=E8=B0=83=E6=95=B4=E4=B8=BA=E8=B7=9F?= =?UTF-8?q?=E9=9A=8F=E9=BC=A0=E6=A0=87=E7=A7=BB=E5=8A=A8=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar-daily/calendar-daily.scss | 41 ++----- .../calendar-daily/calendar-daily.tsx | 100 +++++++----------- .../calendar-daily/use-calendar-daily.ts | 71 ++++++++++++- .../calendar-week/calendar-week.scss | 41 ++----- .../calendar-week/calendar-week.tsx | 99 ++++++----------- .../calendar-week/use-calendar-week.ts | 71 ++++++++++++- src/control/calendar/components/util/util.ts | 70 +++++++++++- 7 files changed, 290 insertions(+), 203 deletions(-) diff --git a/src/control/calendar/components/calendar-daily/calendar-daily.scss b/src/control/calendar/components/calendar-daily/calendar-daily.scss index 8b13c187..e931213a 100644 --- a/src/control/calendar/components/calendar-daily/calendar-daily.scss +++ b/src/control/calendar/components/calendar-daily/calendar-daily.scss @@ -235,13 +235,17 @@ } // 面板项popper @include e(event-popover) { - position: relative; width: auto; height: auto; padding: 0; - &.el-popover.el-popper { - padding: 8px; + &.#{bem('popover')} { + position: absolute; + z-index: 110; + padding: getCssVar(spacing, tight); + background: getCssVar(color, bg, 1); + border: none; + box-shadow: getCssVar('shadow', 'elevated'); } // 当配置了 showdetail 参数,只有配置了布局面板时才会显示气泡 @@ -272,36 +276,5 @@ } } } - @include m(close) { - position: absolute; - top: 8px; - right: 8px; - display: none; - cursor: pointer; - - &:hover { - .el-icon { - background: getCssVar(color, primary); - border-radius: 50%; - - svg { - fill: getCssVar(color, white); - } - } - } - } - - .el-icon { - display: flex; - align-items: center; - justify-content: center; - min-width: 24px; - min-height: 24px; - font-size: 20px; - - svg { - fill: var(--el-text-color-secondary); - } - } } } \ No newline at end of file diff --git a/src/control/calendar/components/calendar-daily/calendar-daily.tsx b/src/control/calendar/components/calendar-daily/calendar-daily.tsx index 6494e53b..8067c07d 100644 --- a/src/control/calendar/components/calendar-daily/calendar-daily.tsx +++ b/src/control/calendar/components/calendar-daily/calendar-daily.tsx @@ -3,7 +3,7 @@ import { defineComponent, onMounted, onUnmounted, watch } from 'vue'; import { useNamespace } from '@ibiz-template/vue3-util'; import { showTitle } from '@ibiz-template/core'; import { IUIEvent, calendarDailyEmits, calendarDailyProps } from '../interface'; -import { closeIcon, handlePopClose, isToday } from '../util'; +import { isToday } from '../util'; import { useCalendarDaily } from './use-calendar-daily'; import './calendar-daily.scss'; @@ -29,7 +29,10 @@ export const CalendarDaily = defineComponent({ handleCurTime, initDrawData, eventContextmenu, - } = useCalendarDaily(props, emit); + contentMousemove, + eventMouseenter, + eventMouseleave, + } = useCalendarDaily(props, emit, ns); watch( () => props.selectedData, @@ -61,6 +64,28 @@ export const CalendarDaily = defineComponent({ } }); + /** + * 绘制事件项popover内容 + * @param {IUIEvent} _event 事件项数据 + */ + const renderPopoverContent = (_event: IUIEvent) => { + // 适配弹框内详情不要背景色 + const _tempEvent = { ..._event, color: '', bkColor: '' }; + return ( +
+
+ {slots?.event ? ( + slots.event?.({ data: _tempEvent }) + ) : ( +
{`${ + _event.text || '' + } ${_event.timeRange || ''}`}
+ )} +
+
+ ); + }; + /** * 绘制事件项 * @param {string} location 绘制位置 @@ -88,6 +113,8 @@ export const CalendarDaily = defineComponent({ title = showTitle(title) || ''; // 如果配置了显示详情,则内容区不用显示快捷提示,提示由气泡展示 if (props.showDetail) title = ''; + + const component = renderPopoverContent(event); return (
eventContextmenu(event, e)} + onMouseenter={(_e: MouseEvent) => + eventMouseenter(_e, component, location) + } + onMouseleave={eventMouseleave} style={eventContentStyle} title={title} > @@ -141,56 +172,6 @@ export const CalendarDaily = defineComponent({ ); }; - /** - * 绘制事件项popover - * @param {Element} content 内容 - * @param {IUIEvent} event 事件项 - */ - const renderPopover = ( - content: Element, - event: IUIEvent, - placement?: string, - ) => { - if (!props.showDetail) { - return content; - } - // 适配弹框内详情不要背景色 - const _tempEvent = { ...event, color: '', bkColor: '' }; - return ( - - {{ - reference: () => content, - default: () => { - return ( -
-
handlePopClose(el)} - v-html={closeIcon} - >
-
- {slots?.event ? ( - slots.event?.({ data: _tempEvent }) - ) : ( -
{`${ - event.text || '' - } ${event.timeRange || ''}`}
- )} -
-
- ); - }, - }} -
- ); - }; - /** * 绘制头部 */ @@ -214,7 +195,7 @@ export const CalendarDaily = defineComponent({ const eventContentStyle = { background: event.bkColorFade, }; - const tempContent = renderEventItem( + return renderEventItem( 'header', eventBoxStyle, eventContentStyle, @@ -223,11 +204,6 @@ export const CalendarDaily = defineComponent({ 'head-event', 'allday-info', ); - return renderPopover( - tempContent as unknown as Element, - event, - 'bottom', - ); }) ) : ( @@ -252,7 +228,7 @@ export const CalendarDaily = defineComponent({ */ const renderContent = () => { return ( -
+
{drawData.value.map((timescale: string) => ( @@ -295,7 +271,7 @@ export const CalendarDaily = defineComponent({ background: event.bkColorFade, 'border-left': `3px solid ${event.bkColor}`, }; - const tempContent = renderEventItem( + return renderEventItem( 'content', eventBoxStyle, eventContentStyle, @@ -304,10 +280,6 @@ export const CalendarDaily = defineComponent({ '', 'time-pane', ); - return renderPopover( - tempContent as unknown as Element, - event, - ); })}
diff --git a/src/control/calendar/components/calendar-daily/use-calendar-daily.ts b/src/control/calendar/components/calendar-daily/use-calendar-daily.ts index 8c960cfe..46feee58 100644 --- a/src/control/calendar/components/calendar-daily/use-calendar-daily.ts +++ b/src/control/calendar/components/calendar-daily/use-calendar-daily.ts @@ -2,8 +2,10 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable no-use-before-define */ /* eslint-disable no-shadow */ -import { computed, ref } from 'vue'; -import type { SetupContext } from 'vue'; +import { computed, ref, onBeforeUnmount } from 'vue'; +import type { SetupContext, VNode } from 'vue'; +import { IOverlayPopoverContainer } from '@ibiz-template/runtime'; +import { Namespace } from '@ibiz-template/core'; import { CalendarDailyEmits, CalendarDailyProps, @@ -13,16 +15,21 @@ import { } from '../interface'; import { checkDateRangeIncludes, + createFollowElement, fade, + followMouseMove, handleBkColor, handleEmit, handleTimeRange, isToday, + openPopover, + removeFollowElement, } from '../util'; export const useCalendarDaily = ( props: CalendarDailyProps, emit: SetupContext['emit'], + _ns: Namespace, ) => { const drawData = ref([]); const curTimeTimer = ref(); @@ -346,6 +353,63 @@ export const useCalendarDaily = ( emit('eventContextmenu', { evt, data: [item] }); }; + // 处理事件气泡显示与隐藏 + const followElement: HTMLElement = createFollowElement(); + let overlay: IOverlayPopoverContainer | null = null; + let eventLocation: string = ''; + + /** + * 日历内容区鼠标移动 + * + * @param {MouseEvent} _event + */ + const contentMousemove = (_event: MouseEvent) => { + if (eventLocation === 'content' && overlay && followElement) + followMouseMove(_event, followElement); + }; + + /** + * 事件项上鼠标移入 + * + * @param {MouseEvent} _event + * @param {VNode} _component + * @param {string} _location + */ + const eventMouseenter = ( + _event: MouseEvent, + _component: VNode, + _location: string, + ) => { + if (!props.showDetail || overlay) { + return; + } + const isContent = _location === 'content'; + eventLocation = _location; + overlay = openPopover( + _component, + isContent ? followElement : (_event.target as HTMLElement), + { + placement: isContent ? 'right-start' : 'bottom', + modalClass: _ns.e('event-popover'), + }, + ); + }; + + /** + * 事件项上鼠标移出 + * + * @return {*} + */ + const eventMouseleave = (_e: MouseEvent): void => { + overlay?.dismiss(); + overlay = null; + eventLocation = ''; + }; + + onBeforeUnmount(() => { + removeFollowElement(followElement!); + }); + return { drawData, curTimeTimer, @@ -362,5 +426,8 @@ export const useCalendarDaily = ( handleEventDblClick, eventContextmenu, initDrawData, + contentMousemove, + eventMouseenter, + eventMouseleave, }; }; diff --git a/src/control/calendar/components/calendar-week/calendar-week.scss b/src/control/calendar/components/calendar-week/calendar-week.scss index f761aeeb..d0a3a5c4 100644 --- a/src/control/calendar/components/calendar-week/calendar-week.scss +++ b/src/control/calendar/components/calendar-week/calendar-week.scss @@ -260,13 +260,17 @@ // 面板项popper @include e(event-popover) { - position: relative; width: auto; height: auto; padding: 0; - &.el-popover.el-popper { - padding: 8px; + &.#{bem('popover')} { + position: absolute; + z-index: 110; + padding: getCssVar(spacing, tight); + background: getCssVar(color, bg, 1); + border: none; + box-shadow: getCssVar('shadow', 'elevated'); } // 当配置了 showdetail 参数,只有配置了布局面板时才会显示气泡 @@ -297,37 +301,6 @@ } } } - @include m(close) { - position: absolute; - top: 8px; - right: 8px; - display: none; - cursor: pointer; - - &:hover { - .el-icon { - background: getCssVar(color, primary); - border-radius: 50%; - - svg { - fill: getCssVar(color, white); - } - } - } - } - - .el-icon { - display: flex; - align-items: center; - justify-content: center; - min-width: 24px; - min-height: 24px; - font-size: 20px; - - svg { - fill: var(--el-text-color-secondary); - } - } } } diff --git a/src/control/calendar/components/calendar-week/calendar-week.tsx b/src/control/calendar/components/calendar-week/calendar-week.tsx index bb88c346..6fce84d1 100644 --- a/src/control/calendar/components/calendar-week/calendar-week.tsx +++ b/src/control/calendar/components/calendar-week/calendar-week.tsx @@ -5,7 +5,6 @@ import { showTitle } from '@ibiz-template/core'; import { IUIEvent, calendarWeekEmits, calendarWeekProps } from '../interface'; import { useCalendarWeek } from './use-calendar-week'; import './calendar-week.scss'; -import { closeIcon, handlePopClose } from '../util'; export const CalendarWeek = defineComponent({ name: 'CalendarWeek', @@ -40,7 +39,10 @@ export const CalendarWeek = defineComponent({ onHeaderEventScroll, initHeaderEventScroll, eventContextmenu, - } = useCalendarWeek(props, emit); + contentMousemove, + eventMouseenter, + eventMouseleave, + } = useCalendarWeek(props, emit, ns); watch( () => props.selectedData, @@ -105,6 +107,28 @@ export const CalendarWeek = defineComponent({ ); }; + /** + * 绘制事件项popover内容 + * @param {IUIEvent} _event 事件项数据 + */ + const renderPopoverContent = (_event: IUIEvent) => { + // 适配弹框内详情不要背景色 + const _tempEvent = { ..._event, color: '', bkColor: '' }; + return ( +
+
+ {slots?.event ? ( + slots.event?.({ data: _tempEvent }) + ) : ( +
{`${ + _event.text || '' + } ${_event.timeRange || ''}`}
+ )} +
+
+ ); + }; + /** * 绘制事件项 * @param {string} location 绘制位置 @@ -133,6 +157,7 @@ export const CalendarWeek = defineComponent({ // 如果配置了显示详情,则内容区不用显示快捷提示,提示由气泡展示 if (props.showDetail) title = ''; + const component = renderPopoverContent(event); return (
eventContextmenu(event, e)} + onMouseenter={(_e: MouseEvent) => + eventMouseenter(_e, component, location) + } + onMouseleave={eventMouseleave} style={eventContentStyle} title={title} > @@ -186,57 +215,6 @@ export const CalendarWeek = defineComponent({ ); }; - /** - * 绘制事件项popover - * @param {Element} content 内容 - * @param {IUIEvent} event 事件项 - */ - const renderPopover = ( - content: Element, - event: IUIEvent, - placement?: string, - ) => { - if (!props.showDetail) { - return content; - } - - // 适配弹框内详情不要背景色 - const _tempEvent = { ...event, color: '', bkColor: '' }; - return ( - - {{ - reference: () => content, - default: () => { - return ( -
-
handlePopClose(el)} - v-html={closeIcon} - >
-
- {slots?.event ? ( - slots.event?.({ data: _tempEvent }) - ) : ( -
{`${ - event.text || '' - } ${event.timeRange || ''}`}
- )} -
-
- ); - }, - }} -
- ); - }; - /** * 绘制头部 */ @@ -291,7 +269,7 @@ export const CalendarWeek = defineComponent({ }; if (!event.isShow) return ''; - const tempContent = renderEventItem( + return renderEventItem( 'header', eventBoxStyle, eventContentStyle, @@ -300,11 +278,6 @@ export const CalendarWeek = defineComponent({ 'head-event', 'header', ); - return renderPopover( - tempContent as unknown as Element, - event, - 'bottom', - ); })}
))} @@ -330,7 +303,7 @@ export const CalendarWeek = defineComponent({ */ const renderContent = () => { return ( -
+
{drawData.value.map((timescale: string) => ( @@ -377,7 +350,7 @@ export const CalendarWeek = defineComponent({ background: event.bkColorFade, 'border-left': `3px solid ${event.bkColor}`, }; - const tempContent = renderEventItem( + return renderEventItem( 'content', eventBoxStyle, eventContentStyle, @@ -386,10 +359,6 @@ export const CalendarWeek = defineComponent({ '', 'time-pane', ); - return renderPopover( - tempContent as unknown as Element, - event, - ); })}
diff --git a/src/control/calendar/components/calendar-week/use-calendar-week.ts b/src/control/calendar/components/calendar-week/use-calendar-week.ts index ff3d8a97..c4a65ed2 100644 --- a/src/control/calendar/components/calendar-week/use-calendar-week.ts +++ b/src/control/calendar/components/calendar-week/use-calendar-week.ts @@ -2,8 +2,10 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable no-shadow */ /* eslint-disable no-use-before-define */ -import { computed, ref, watch, nextTick } from 'vue'; -import type { SetupContext } from 'vue'; +import { computed, ref, watch, nextTick, onBeforeUnmount } from 'vue'; +import type { SetupContext, VNode } from 'vue'; +import { Namespace } from '@ibiz-template/core'; +import { IOverlayPopoverContainer } from '@ibiz-template/runtime'; import { CalendarWeekEmits, CalendarWeekProps, @@ -13,7 +15,9 @@ import { } from '../interface'; import { checkDateRangeIncludes, + createFollowElement, fade, + followMouseMove, getCurWeekDates, handleBkColor, handleEmit, @@ -21,11 +25,14 @@ import { isDateInCurWeek, isTimeGreaterThan, isToday, + openPopover, + removeFollowElement, } from '../util'; export const useCalendarWeek = ( props: CalendarWeekProps, emit: SetupContext['emit'], + _ns: Namespace, ) => { const drawData = ref([]); const curTimeTimer = ref(); @@ -806,6 +813,63 @@ export const useCalendarWeek = ( }, ); + // 处理事件气泡显示与隐藏 + const followElement: HTMLElement = createFollowElement(); + let overlay: IOverlayPopoverContainer | null = null; + let eventLocation: string = ''; + + /** + * 日历内容区鼠标移动 + * + * @param {MouseEvent} _event + */ + const contentMousemove = (_event: MouseEvent) => { + if (eventLocation === 'content' && overlay && followElement) + followMouseMove(_event, followElement); + }; + + /** + * 事件项上鼠标移入 + * + * @param {MouseEvent} _event + * @param {VNode} _component + * @param {string} _location + */ + const eventMouseenter = ( + _event: MouseEvent, + _component: VNode, + _location: string, + ) => { + if (!props.showDetail || overlay) { + return; + } + const isContent = _location === 'content'; + eventLocation = _location; + overlay = openPopover( + _component, + isContent ? followElement : (_event.target as HTMLElement), + { + placement: isContent ? 'right-start' : 'bottom', + modalClass: _ns.e('event-popover'), + }, + ); + }; + + /** + * 事件项上鼠标移出 + * + * @return {*} + */ + const eventMouseleave = (_e: MouseEvent): void => { + overlay?.dismiss(); + overlay = null; + eventLocation = ''; + }; + + onBeforeUnmount(() => { + removeFollowElement(followElement!); + }); + return { drawData, curTimeTimer, @@ -836,5 +900,8 @@ export const useCalendarWeek = ( onHeaderEventScroll, initHeaderEventScroll, eventContextmenu, + contentMousemove, + eventMouseenter, + eventMouseleave, }; }; diff --git a/src/control/calendar/components/util/util.ts b/src/control/calendar/components/util/util.ts index 9b34ef89..53649758 100644 --- a/src/control/calendar/components/util/util.ts +++ b/src/control/calendar/components/util/util.ts @@ -1,12 +1,13 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { warn } from 'vue'; +import { h, warn } from 'vue'; import dayjs from 'dayjs'; import { clone } from 'ramda'; -import type { PropType } from 'vue'; +import type { PropType, VNode } from 'vue'; import { isDate, isArray, isObject, fromPairs, get, set } from 'lodash-es'; +import { IModal, IOverlayPopoverContainer } from '@ibiz-template/runtime'; import type { EpProp, EpPropFinalized, @@ -522,6 +523,67 @@ const handleProps = < ]), ); +/** + * 打开popover + * + * @param {VNode} _component + * @param {HTMLElement} _evt + * @param {IData} _opts + */ +function openPopover( + _component: VNode, + _targetEvt: HTMLElement, + _opts: IData, +): IOverlayPopoverContainer { + const overlay = ibiz.overlay.createPopover( + (modal: IModal): VNode => { + return h(_component, { modal }); + }, + undefined, + { + width: 'auto', + height: 'auto', + noArrow: true, + ..._opts, + }, + ); + overlay?.present(_targetEvt as HTMLElement); + return overlay; +} + +/** + * _follow、_overlay跟随_event移动 + * + * @param {MouseEvent} _event + * @param {HTMLElement} _follow + */ +function followMouseMove(_event: MouseEvent, _follow: HTMLElement): void { + // 设置元素位置 + _follow.style.left = `${_event.clientX + 20}px`; + _follow.style.top = `${_event.clientY + 20}px`; +} + +/** + * 创建跟随元素 + * + * @return {*} {HTMLElement} + */ +const createFollowElement = (): HTMLElement => { + const followEl = document.createElement('div'); + followEl.style = 'position: fixed; height: 2px; width: 2px; z-index: -1;'; + document.body?.appendChild(followEl); + return followEl; +}; + +/** + * 删除跟随元素 + * + * @param {HTMLElement} followEl + */ +const removeFollowElement = (followEl: HTMLElement): void => { + document.body.removeChild(followEl); +}; + export { epPropKey, closeIcon, @@ -545,4 +607,8 @@ export { isDateInCurWeek, isTimeGreaterThan, checkDateRangeIncludes, + openPopover, + followMouseMove, + createFollowElement, + removeFollowElement, }; -- Gitee From 383a776dd0da85a6a2316225fc947f16d53598ea Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Fri, 8 Aug 2025 21:35:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat=EF=BC=9A=E6=97=A5=E5=8E=86=E5=A4=A9?= =?UTF-8?q?=E3=80=81=E5=91=A8=E6=A0=B7=E5=BC=8F=E7=9A=84=E5=A4=B4=E9=83=A8?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E9=A1=B9=E6=A0=B7=E5=BC=8F=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/components/calendar-daily/calendar-daily.scss | 1 - src/control/calendar/components/calendar-week/calendar-week.scss | 1 - 2 files changed, 2 deletions(-) diff --git a/src/control/calendar/components/calendar-daily/calendar-daily.scss b/src/control/calendar/components/calendar-daily/calendar-daily.scss index e931213a..de950060 100644 --- a/src/control/calendar/components/calendar-daily/calendar-daily.scss +++ b/src/control/calendar/components/calendar-daily/calendar-daily.scss @@ -66,7 +66,6 @@ text-align: left; border-radius: 4px; background: getCssVar(color, tertiary, light, hover); - border-radius: 4px; position: relative; cursor: pointer; &.is-selected-event { diff --git a/src/control/calendar/components/calendar-week/calendar-week.scss b/src/control/calendar/components/calendar-week/calendar-week.scss index d0a3a5c4..ebe32d82 100644 --- a/src/control/calendar/components/calendar-week/calendar-week.scss +++ b/src/control/calendar/components/calendar-week/calendar-week.scss @@ -121,7 +121,6 @@ width: 100%; height: 100%; min-height: 22px; - padding-left: 4px; cursor: pointer; background-color: getCssVar(color, primary); border: none; -- Gitee