Commit 6e03e05032474c858151b3835eb5318486a56729
1 parent
41d79008
perf: perf context menu
Showing
16 changed files
with
205 additions
and
176 deletions
src/components/ContextMenu/index.ts
1 | -import contextMenuVue from './src/index'; | |
2 | -import { isClient } from '/@/utils/is'; | |
3 | -import { Options, Props } from './src/types'; | |
4 | -import { createVNode, render } from 'vue'; | |
5 | -const menuManager: { | |
6 | - domList: Element[]; | |
7 | - resolve: Fn; | |
8 | -} = { | |
9 | - domList: [], | |
10 | - resolve: () => {}, | |
11 | -}; | |
12 | -export const createContextMenu = function (options: Options) { | |
13 | - const { event } = options || {}; | |
14 | - try { | |
15 | - event.preventDefault(); | |
16 | - } catch (e) { | |
17 | - console.log(e); | |
18 | - } | |
19 | - | |
20 | - if (!isClient) return; | |
21 | - return new Promise((resolve) => { | |
22 | - const container = document.createElement('div'); | |
23 | - const propsData: Partial<Props> = {}; | |
24 | - if (options.styles !== undefined) propsData.styles = options.styles; | |
25 | - if (options.items !== undefined) propsData.items = options.items; | |
26 | - if (options.event !== undefined) { | |
27 | - propsData.customEvent = event; | |
28 | - propsData.axis = { x: event.clientX, y: event.clientY }; | |
29 | - } | |
30 | - const vm = createVNode(contextMenuVue, propsData); | |
31 | - render(vm, container); | |
32 | - const bodyClick = function () { | |
33 | - menuManager.resolve(''); | |
34 | - }; | |
35 | - menuManager.domList.push(container); | |
36 | - const remove = function () { | |
37 | - menuManager.domList.forEach((dom: Element) => { | |
38 | - try { | |
39 | - document.body.removeChild(dom); | |
40 | - } catch (error) {} | |
41 | - }); | |
42 | - document.body.removeEventListener('click', bodyClick); | |
43 | - document.body.removeEventListener('scroll', bodyClick); | |
44 | - }; | |
45 | - menuManager.resolve = function (...arg: any) { | |
46 | - resolve(arg[0]); | |
47 | - remove(); | |
48 | - }; | |
49 | - remove(); | |
50 | - document.body.appendChild(container); | |
51 | - document.body.addEventListener('click', bodyClick); | |
52 | - document.body.addEventListener('scroll', bodyClick); | |
53 | - }); | |
54 | -}; | |
55 | -export const unMountedContextMenu = function () { | |
56 | - if (menuManager) { | |
57 | - menuManager.resolve(''); | |
58 | - menuManager.domList = []; | |
59 | - } | |
60 | -}; | |
61 | - | |
1 | +export { createContextMenu, destroyContextMenu } from './src/createContextMenu'; | |
62 | 2 | export * from './src/types'; | ... | ... |
src/components/ContextMenu/src/createContextMenu.ts
0 → 100644
1 | +import contextMenuVue from './index'; | |
2 | +import { isClient } from '/@/utils/is'; | |
3 | +import { CreateContextOptions, ContextMenuProps } from './types'; | |
4 | +import { createVNode, render } from 'vue'; | |
5 | + | |
6 | +const menuManager: { | |
7 | + domList: Element[]; | |
8 | + resolve: Fn; | |
9 | +} = { | |
10 | + domList: [], | |
11 | + resolve: () => {}, | |
12 | +}; | |
13 | + | |
14 | +export const createContextMenu = function (options: CreateContextOptions) { | |
15 | + const { event } = options || {}; | |
16 | + | |
17 | + event && event?.preventDefault(); | |
18 | + | |
19 | + if (!isClient) return; | |
20 | + return new Promise((resolve) => { | |
21 | + const body = document.body; | |
22 | + | |
23 | + const container = document.createElement('div'); | |
24 | + const propsData: Partial<ContextMenuProps> = {}; | |
25 | + if (options.styles) { | |
26 | + propsData.styles = options.styles; | |
27 | + } | |
28 | + | |
29 | + if (options.items) { | |
30 | + propsData.items = options.items; | |
31 | + } | |
32 | + | |
33 | + if (options.event) { | |
34 | + propsData.customEvent = event; | |
35 | + propsData.axis = { x: event.clientX, y: event.clientY }; | |
36 | + } | |
37 | + | |
38 | + const vm = createVNode(contextMenuVue, propsData); | |
39 | + render(vm, container); | |
40 | + | |
41 | + const handleClick = function () { | |
42 | + menuManager.resolve(''); | |
43 | + }; | |
44 | + | |
45 | + menuManager.domList.push(container); | |
46 | + | |
47 | + const remove = function () { | |
48 | + menuManager.domList.forEach((dom: Element) => { | |
49 | + try { | |
50 | + dom && body.removeChild(dom); | |
51 | + } catch (error) {} | |
52 | + }); | |
53 | + body.removeEventListener('click', handleClick); | |
54 | + body.removeEventListener('scroll', handleClick); | |
55 | + }; | |
56 | + | |
57 | + menuManager.resolve = function (...arg: any) { | |
58 | + remove(); | |
59 | + resolve(arg[0]); | |
60 | + }; | |
61 | + remove(); | |
62 | + body.appendChild(container); | |
63 | + body.addEventListener('click', handleClick); | |
64 | + body.addEventListener('scroll', handleClick); | |
65 | + }); | |
66 | +}; | |
67 | + | |
68 | +export const destroyContextMenu = function () { | |
69 | + if (menuManager) { | |
70 | + menuManager.resolve(''); | |
71 | + menuManager.domList = []; | |
72 | + } | |
73 | +}; | ... | ... |
src/components/ContextMenu/src/index.less
1 | 1 | @import (reference) '../../../design/index.less'; |
2 | 2 | |
3 | +@default-height: 42px !important; | |
4 | + | |
5 | +@small-height: 36px !important; | |
6 | + | |
7 | +@large-height: 36px !important; | |
8 | + | |
3 | 9 | .item-style() { |
4 | 10 | li { |
5 | 11 | display: inline-block; |
6 | 12 | width: 100%; |
7 | - height: 46px !important; | |
13 | + height: @default-height; | |
8 | 14 | margin: 0 !important; |
9 | - line-height: 46px; | |
15 | + line-height: @default-height; | |
10 | 16 | |
11 | 17 | span { |
12 | - line-height: 46px; | |
18 | + line-height: @default-height; | |
13 | 19 | } |
14 | 20 | |
15 | 21 | > div { |
16 | 22 | margin: 0 !important; |
17 | 23 | } |
18 | 24 | |
19 | - &:hover { | |
25 | + &:not(.ant-menu-item-disabled):hover { | |
20 | 26 | color: @text-color-base; |
21 | 27 | background: #eee; |
22 | 28 | } |
... | ... | @@ -27,10 +33,9 @@ |
27 | 33 | position: fixed; |
28 | 34 | top: 0; |
29 | 35 | left: 0; |
30 | - z-index: 1500; | |
36 | + z-index: 200; | |
31 | 37 | display: block; |
32 | 38 | width: 156px; |
33 | - min-width: 10rem; | |
34 | 39 | margin: 0; |
35 | 40 | list-style: none; |
36 | 41 | background-color: #fff; | ... | ... |
src/components/ContextMenu/src/index.tsx
1 | -import { | |
2 | - defineComponent, | |
3 | - nextTick, | |
4 | - onMounted, | |
5 | - reactive, | |
6 | - computed, | |
7 | - ref, | |
8 | - unref, | |
9 | - onUnmounted, | |
10 | -} from 'vue'; | |
1 | +import './index.less'; | |
2 | + | |
3 | +import type { ContextMenuItem, ItemContentProps } from './types'; | |
4 | +import type { FunctionalComponent, CSSProperties } from 'vue'; | |
5 | + | |
6 | +import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue'; | |
11 | 7 | |
12 | -import { props } from './props'; | |
13 | 8 | import Icon from '/@/components/Icon'; |
14 | 9 | import { Menu, Divider } from 'ant-design-vue'; |
15 | 10 | |
16 | -import type { ContextMenuItem } from './types'; | |
11 | +import { props } from './props'; | |
17 | 12 | |
18 | -import './index.less'; | |
19 | 13 | const prefixCls = 'context-menu'; |
14 | + | |
15 | +const ItemContent: FunctionalComponent<ItemContentProps> = (props) => { | |
16 | + const { item } = props; | |
17 | + return ( | |
18 | + <span style="display: inline-block; width: 100%;" onClick={props.handler.bind(null, item)}> | |
19 | + {props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />} | |
20 | + <span>{item.label}</span> | |
21 | + </span> | |
22 | + ); | |
23 | +}; | |
24 | + | |
20 | 25 | export default defineComponent({ |
21 | 26 | name: 'ContextMenu', |
22 | 27 | props, |
23 | 28 | setup(props) { |
24 | - const wrapRef = ref<Nullable<HTMLDivElement>>(null); | |
25 | - const state = reactive({ | |
26 | - show: false, | |
27 | - }); | |
29 | + const wrapRef = ref<ElRef>(null); | |
30 | + const showRef = ref(false); | |
31 | + | |
32 | + const getStyle = computed( | |
33 | + (): CSSProperties => { | |
34 | + const { axis, items, styles, width } = props; | |
35 | + const { x, y } = axis || { x: 0, y: 0 }; | |
36 | + const menuHeight = (items || []).length * 40; | |
37 | + const menuWidth = width; | |
38 | + const body = document.body; | |
39 | + | |
40 | + const left = body.clientWidth < x + menuWidth ? x - menuWidth : x; | |
41 | + const top = body.clientHeight < y + menuHeight ? y - menuHeight : y; | |
42 | + return { | |
43 | + ...styles, | |
44 | + width: `${width}px`, | |
45 | + left: `${left + 1}px`, | |
46 | + top: `${top + 1}px`, | |
47 | + }; | |
48 | + } | |
49 | + ); | |
28 | 50 | |
29 | 51 | onMounted(() => { |
30 | - nextTick(() => { | |
31 | - state.show = true; | |
32 | - }); | |
52 | + nextTick(() => (showRef.value = true)); | |
33 | 53 | }); |
34 | 54 | |
35 | 55 | onUnmounted(() => { |
36 | 56 | const el = unref(wrapRef); |
37 | 57 | el && document.body.removeChild(el); |
38 | 58 | }); |
39 | - const getStyle = computed(() => { | |
40 | - const { axis, items, styles, width } = props; | |
41 | - const { x, y } = axis || { x: 0, y: 0 }; | |
42 | - const menuHeight = (items || []).length * 40; | |
43 | - const menuWidth = width; | |
44 | - const body = document.body; | |
45 | - return { | |
46 | - ...(styles as any), | |
47 | - width: `${width}px`, | |
48 | - left: (body.clientWidth < x + menuWidth ? x - menuWidth : x) + 'px', | |
49 | - top: (body.clientHeight < y + menuHeight ? y - menuHeight : y) + 'px', | |
50 | - }; | |
51 | - }); | |
52 | 59 | |
53 | 60 | function handleAction(item: ContextMenuItem, e: MouseEvent) { |
54 | - state.show = false; | |
55 | 61 | const { handler, disabled } = item; |
56 | - if (disabled) { | |
57 | - return; | |
58 | - } | |
59 | - if (e) { | |
60 | - e.stopPropagation(); | |
61 | - e.preventDefault(); | |
62 | - } | |
62 | + if (disabled) return; | |
63 | + showRef.value = false; | |
63 | 64 | |
64 | - handler && handler(); | |
65 | - } | |
66 | - | |
67 | - function renderContent(item: ContextMenuItem) { | |
68 | - const { icon, label } = item; | |
69 | - | |
70 | - const { showIcon } = props; | |
71 | - return ( | |
72 | - <span style="display: inline-block; width: 100%;" onClick={handleAction.bind(null, item)}> | |
73 | - {showIcon && icon && <Icon class="mr-2" icon={icon} />} | |
74 | - <span>{label}</span> | |
75 | - </span> | |
76 | - ); | |
65 | + e?.stopPropagation(); | |
66 | + e?.preventDefault(); | |
67 | + handler?.(); | |
77 | 68 | } |
78 | 69 | |
79 | 70 | function renderMenuItem(items: ContextMenuItem[]) { |
80 | - return items.map((item, index) => { | |
71 | + return items.map((item) => { | |
81 | 72 | const { disabled, label, children, divider = false } = item; |
82 | 73 | |
83 | - const DividerComp = divider ? <Divider key={`d-${index}`} /> : null; | |
74 | + const DividerComp = divider ? <Divider key={`d-${label}`} /> : null; | |
84 | 75 | if (!children || children.length === 0) { |
85 | - return [ | |
86 | - <Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}> | |
87 | - {() => [renderContent(item)]} | |
88 | - </Menu.Item>, | |
89 | - DividerComp, | |
90 | - ]; | |
76 | + return ( | |
77 | + <> | |
78 | + <Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}> | |
79 | + {() => [ | |
80 | + <ItemContent showIcon={props.showIcon} item={item} handler={handleAction} />, | |
81 | + ]} | |
82 | + </Menu.Item> | |
83 | + {DividerComp} | |
84 | + </> | |
85 | + ); | |
91 | 86 | } |
92 | - return !state.show ? null : ( | |
93 | - <Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup `}> | |
87 | + if (!unref(showRef)) return null; | |
88 | + | |
89 | + return ( | |
90 | + <Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}> | |
94 | 91 | {{ |
95 | - title: () => renderContent(item), | |
96 | - default: () => [renderMenuItem(children)], | |
92 | + title: () => ( | |
93 | + <ItemContent showIcon={props.showIcon} item={item} handler={handleAction} /> | |
94 | + ), | |
95 | + default: () => renderMenuItem(children), | |
97 | 96 | }} |
98 | 97 | </Menu.SubMenu> |
99 | 98 | ); |
... | ... | @@ -101,11 +100,12 @@ export default defineComponent({ |
101 | 100 | } |
102 | 101 | return () => { |
103 | 102 | const { items } = props; |
104 | - return !state.show ? null : ( | |
103 | + if (!unref(showRef)) return null; | |
104 | + return ( | |
105 | 105 | <Menu |
106 | 106 | inlineIndent={12} |
107 | 107 | mode="vertical" |
108 | - class={[prefixCls]} | |
108 | + class={prefixCls} | |
109 | 109 | ref={wrapRef} |
110 | 110 | style={unref(getStyle)} |
111 | 111 | > | ... | ... |
src/components/ContextMenu/src/props.ts
1 | -import type { PropType } from 'vue'; | |
1 | +import type { PropType, CSSProperties } from 'vue'; | |
2 | 2 | import type { Axis, ContextMenuItem } from './types'; |
3 | 3 | export const props = { |
4 | 4 | width: { |
5 | 5 | type: Number as PropType<number>, |
6 | - default: 180, | |
6 | + default: 156, | |
7 | 7 | }, |
8 | 8 | customEvent: { |
9 | 9 | type: Object as PropType<Event>, |
10 | 10 | default: null, |
11 | 11 | }, |
12 | 12 | styles: { |
13 | - type: Object as PropType<any>, | |
13 | + type: Object as PropType<CSSProperties>, | |
14 | 14 | default: null, |
15 | 15 | }, |
16 | 16 | showIcon: { |
... | ... | @@ -31,8 +31,4 @@ export const props = { |
31 | 31 | return []; |
32 | 32 | }, |
33 | 33 | }, |
34 | - resolve: { | |
35 | - type: Function as PropType<any>, | |
36 | - default: null, | |
37 | - }, | |
38 | 34 | }; | ... | ... |
src/components/ContextMenu/src/types.ts
... | ... | @@ -11,15 +11,14 @@ export interface ContextMenuItem { |
11 | 11 | divider?: boolean; |
12 | 12 | children?: ContextMenuItem[]; |
13 | 13 | } |
14 | -export interface Options { | |
14 | +export interface CreateContextOptions { | |
15 | 15 | event: MouseEvent; |
16 | 16 | icon?: string; |
17 | 17 | styles?: any; |
18 | 18 | items?: ContextMenuItem[]; |
19 | 19 | } |
20 | 20 | |
21 | -export type Props = { | |
22 | - resolve?: (...arg: any) => void; | |
21 | +export interface ContextMenuProps { | |
23 | 22 | event?: MouseEvent; |
24 | 23 | styles?: any; |
25 | 24 | items: ContextMenuItem[]; |
... | ... | @@ -27,4 +26,10 @@ export type Props = { |
27 | 26 | axis?: Axis; |
28 | 27 | width?: number; |
29 | 28 | showIcon?: boolean; |
30 | -}; | |
29 | +} | |
30 | + | |
31 | +export interface ItemContentProps { | |
32 | + showIcon: boolean; | |
33 | + item: ContextMenuItem; | |
34 | + handler: Fn; | |
35 | +} | ... | ... |
src/components/Drawer/src/BasicDrawer.tsx
... | ... | @@ -22,7 +22,7 @@ export default defineComponent({ |
22 | 22 | props: basicProps, |
23 | 23 | emits: ['visible-change', 'ok', 'close', 'register'], |
24 | 24 | setup(props, { slots, emit, attrs }) { |
25 | - const scrollRef = ref<any>(null); | |
25 | + const scrollRef = ref<ElRef>(null); | |
26 | 26 | |
27 | 27 | const visibleRef = ref(false); |
28 | 28 | const propsRef = ref<Partial<DrawerProps> | null>(null); | ... | ... |
src/components/Modal/src/BasicModal.tsx
... | ... | @@ -22,7 +22,7 @@ export default defineComponent({ |
22 | 22 | setup(props, { slots, emit, attrs }) { |
23 | 23 | const visibleRef = ref(false); |
24 | 24 | const propsRef = ref<Partial<ModalProps> | null>(null); |
25 | - const modalWrapperRef = ref<any>(null); | |
25 | + const modalWrapperRef = ref<ComponentRef>(null); | |
26 | 26 | // modal Bottom and top height |
27 | 27 | const extHeightRef = ref(0); |
28 | 28 | // Unexpanded height of the popup | ... | ... |
src/components/Modal/src/ModalWrapper.tsx
... | ... | @@ -55,7 +55,7 @@ export default defineComponent({ |
55 | 55 | emits: ['heightChange', 'getExtHeight'], |
56 | 56 | setup(props: ModalWrapperProps, { slots, emit }) { |
57 | 57 | const wrapperRef = ref<HTMLElement | null>(null); |
58 | - const spinRef = ref<any>(null); | |
58 | + const spinRef = ref<ComponentRef>(null); | |
59 | 59 | const realHeightRef = ref(0); |
60 | 60 | // 重试次数 |
61 | 61 | // let tryCount = 0; |
... | ... | @@ -126,6 +126,8 @@ export default defineComponent({ |
126 | 126 | await nextTick(); |
127 | 127 | const spinEl = unref(spinRef); |
128 | 128 | |
129 | + if (!spinEl) return; | |
130 | + | |
129 | 131 | const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement; |
130 | 132 | if (!spinContainerEl) return; |
131 | 133 | ... | ... |
src/components/Table/src/BasicTable.vue
... | ... | @@ -74,7 +74,7 @@ |
74 | 74 | components: { Table, BasicForm }, |
75 | 75 | emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'], |
76 | 76 | setup(props, { attrs, emit, slots }) { |
77 | - const tableElRef = ref<any>(null); | |
77 | + const tableElRef = ref<ComponentRef>(null); | |
78 | 78 | const wrapRef = ref<Nullable<HTMLDivElement>>(null); |
79 | 79 | const innerPropsRef = ref<Partial<BasicTableProps>>(); |
80 | 80 | const [registerForm, { getFieldsValue }] = useForm(); |
... | ... | @@ -241,10 +241,8 @@ |
241 | 241 | if (unref(getMergeProps).showSummary) { |
242 | 242 | nextTick(() => { |
243 | 243 | const tableEl = unref(tableElRef); |
244 | - if (!tableEl) { | |
245 | - return; | |
246 | - } | |
247 | - const bodyDomList = tableEl.$el.querySelectorAll('.ant-table-body') as HTMLDivElement[]; | |
244 | + if (!tableEl) return; | |
245 | + const bodyDomList = tableEl.$el.querySelectorAll('.ant-table-body'); | |
248 | 246 | const bodyDom = bodyDomList[0]; |
249 | 247 | useEventListener({ |
250 | 248 | el: bodyDom, | ... | ... |
src/hooks/web/useContextMenu.ts
1 | 1 | import { onUnmounted, getCurrentInstance } from 'vue'; |
2 | -import { createContextMenu, unMountedContextMenu } from '/@/components/ContextMenu'; | |
2 | +import { createContextMenu, destroyContextMenu } from '/@/components/ContextMenu'; | |
3 | 3 | import type { ContextMenuItem } from '/@/components/ContextMenu'; |
4 | 4 | export type { ContextMenuItem }; |
5 | 5 | export function useContextMenu(authRemove = true) { |
6 | 6 | if (getCurrentInstance() && authRemove) { |
7 | 7 | onUnmounted(() => { |
8 | - unMountedContextMenu(); | |
8 | + destroyContextMenu(); | |
9 | 9 | }); |
10 | 10 | } |
11 | - return [createContextMenu, unMountedContextMenu]; | |
11 | + return [createContextMenu, destroyContextMenu]; | |
12 | 12 | } | ... | ... |
src/layouts/default/content/index.less
src/layouts/default/header/LayoutHeader.tsx
... | ... | @@ -60,10 +60,10 @@ export default defineComponent({ |
60 | 60 | }, |
61 | 61 | }, |
62 | 62 | setup(props) { |
63 | - let logoEl: Element | null; | |
63 | + let logoEl: Element | null | undefined; | |
64 | 64 | |
65 | 65 | const logoWidthRef = ref(200); |
66 | - const logoRef = ref<any>(null); | |
66 | + const logoRef = ref<ComponentRef>(null); | |
67 | 67 | const { refreshPage } = useTabs(); |
68 | 68 | |
69 | 69 | const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting(); |
... | ... | @@ -91,7 +91,7 @@ export default defineComponent({ |
91 | 91 | if (!unref(getShowTopMenu)) return; |
92 | 92 | let width = 0; |
93 | 93 | if (!logoEl) { |
94 | - logoEl = logoRef.value.$el; | |
94 | + logoEl = unref(logoRef)?.$el; | |
95 | 95 | } else { |
96 | 96 | width += logoEl.clientWidth; |
97 | 97 | } | ... | ... |
src/layouts/default/header/LayoutMultipleHeader.tsx
... | ... | @@ -39,26 +39,30 @@ export default defineComponent({ |
39 | 39 | return unref(getShowMultipleTab) && !unref(getFullContent); |
40 | 40 | }); |
41 | 41 | |
42 | - const getPlaceholderDomStyle = computed(() => { | |
43 | - return { | |
44 | - height: `${unref(placeholderHeightRef)}px`, | |
45 | - }; | |
46 | - }); | |
42 | + const getPlaceholderDomStyle = computed( | |
43 | + (): CSSProperties => { | |
44 | + return { | |
45 | + height: `${unref(placeholderHeightRef)}px`, | |
46 | + }; | |
47 | + } | |
48 | + ); | |
47 | 49 | |
48 | 50 | const getIsShowPlaceholderDom = computed(() => { |
49 | 51 | return unref(getFixed) || unref(getShowFullHeaderRef); |
50 | 52 | }); |
51 | 53 | |
52 | - const getWrapStyle = computed(() => { | |
53 | - const style: CSSProperties = {}; | |
54 | - if (unref(getFixed)) { | |
55 | - style.width = unref(getCalcContentWidth); | |
54 | + const getWrapStyle = computed( | |
55 | + (): CSSProperties => { | |
56 | + const style: CSSProperties = {}; | |
57 | + if (unref(getFixed)) { | |
58 | + style.width = unref(getCalcContentWidth); | |
59 | + } | |
60 | + if (unref(getShowFullHeaderRef)) { | |
61 | + style.top = `${unref(fullHeaderHeightRef)}px`; | |
62 | + } | |
63 | + return style; | |
56 | 64 | } |
57 | - if (unref(getShowFullHeaderRef)) { | |
58 | - style.top = `${unref(fullHeaderHeightRef)}px`; | |
59 | - } | |
60 | - return style; | |
61 | - }); | |
65 | + ); | |
62 | 66 | |
63 | 67 | const getIsFixed = computed(() => { |
64 | 68 | return unref(getFixed) || unref(getShowFullHeaderRef); | ... | ... |
src/layouts/default/header/index.less
src/layouts/default/multitabs/index.less
... | ... | @@ -40,9 +40,12 @@ |
40 | 40 | height: 12px; |
41 | 41 | font-size: 12px; |
42 | 42 | color: inherit; |
43 | + visibility: hidden; | |
43 | 44 | transition: none; |
44 | 45 | |
45 | 46 | &:hover { |
47 | + visibility: visible; | |
48 | + | |
46 | 49 | svg { |
47 | 50 | width: 0.8em; |
48 | 51 | } |
... | ... | @@ -69,6 +72,10 @@ |
69 | 72 | display: none; |
70 | 73 | } |
71 | 74 | |
75 | + .ant-tabs-close-x { | |
76 | + visibility: visible; | |
77 | + } | |
78 | + | |
72 | 79 | svg { |
73 | 80 | width: 0.7em; |
74 | 81 | fill: @white; | ... | ... |