feat(ReText): 新增ReText组件,支持自动省略显示Tooltip功能, 支持多行省略, 高可复用性 (#898)

* feat(ReText): 新增ReText组件 - 基于El-Text, 增加自动省略显示Tooltip功能, 高可复用性
This commit is contained in:
苗大
2024-02-19 13:36:07 +08:00
committed by GitHub
parent c62731df5b
commit f6eaa8d6d8
9 changed files with 294 additions and 164 deletions

View File

@@ -3,10 +3,12 @@ import path from "path";
import { getConfig } from "@/config";
import { menuType } from "../../types";
import extraIcon from "./extraIcon.vue";
import { useDark } from "@pureadmin/utils";
import { ReText } from "@/components/ReText";
import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
import { type CSSProperties, type PropType, computed, ref, toRaw } from "vue";
import ArrowUp from "@iconify-icons/ep/arrow-up-bold";
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
@@ -14,6 +16,7 @@ import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
const { isDark } = useDark();
const props = defineProps({
item: {
@@ -29,13 +32,6 @@ const props = defineProps({
}
});
const getSpanStyle = computed((): CSSProperties => {
return {
width: "100%",
textAlign: "center"
};
});
const getNoDropdownStyle = computed((): CSSProperties => {
return {
display: "flex",
@@ -43,15 +39,7 @@ const getNoDropdownStyle = computed((): CSSProperties => {
};
});
const getMenuTextStyle = computed(() => {
return {
overflow: "hidden",
textOverflow: "ellipsis",
outline: "none"
};
});
const getsubMenuIconStyle = computed((): CSSProperties => {
const getSubMenuIconStyle = computed((): CSSProperties => {
return {
display: "flex",
justifyContent: "center",
@@ -65,43 +53,6 @@ const getsubMenuIconStyle = computed((): CSSProperties => {
};
});
const getSubTextStyle = computed((): CSSProperties => {
if (!isCollapse.value) {
return {
width: "210px",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
} else {
return {
width: ""
};
}
});
const getSubMenuDivStyle = computed((): any => {
return item => {
return !isCollapse.value
? {
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
}
: {
width: "100%",
textAlign:
item?.parentId === null
? "center"
: layout.value === "mix" && item?.pathList?.length === 2
? "center"
: ""
};
};
});
const expandCloseIcon = computed(() => {
if (!getConfig()?.MenuArrowIconNoTransition) return "";
return {
@@ -113,41 +64,6 @@ const expandCloseIcon = computed(() => {
});
const onlyOneChild: menuType = ref(null);
// 存放菜单是否存在showTooltip属性标识
const hoverMenuMap = new WeakMap();
// 存储菜单文本dom元素
const menuTextRef = ref(null);
function hoverMenu(key) {
// 如果当前菜单showTooltip属性已存在退出计算
if (hoverMenuMap.get(key)) return;
nextTick(() => {
// 如果文本内容的整体宽度大于其可视宽度,则文本溢出
menuTextRef.value?.scrollWidth > menuTextRef.value?.clientWidth
? Object.assign(key, {
showTooltip: true
})
: Object.assign(key, {
showTooltip: false
});
hoverMenuMap.set(key, true);
});
}
// 左侧菜单折叠后,当菜单没有图标时只显示第一个文字并加上省略号
function overflowSlice(text, item?: any) {
const newText =
(text?.length > 1 ? text.toString().slice(0, 1) : text) + "...";
if (item && !(isCollapse.value && item?.parentId === null)) {
return layout.value === "mix" &&
item?.pathList?.length === 2 &&
isCollapse.value
? newText
: text;
}
return newText;
}
function hasOneShowingChild(children: menuType[] = [], parent: menuType) {
const showingChildren = children.filter((item: any) => {
@@ -194,7 +110,7 @@ function resolvePath(routePath) {
<div
v-if="toRaw(props.item.meta.icon)"
class="sub-menu-icon"
:style="getsubMenuIconStyle"
:style="getSubMenuIconStyle"
>
<component
:is="
@@ -205,51 +121,34 @@ function resolvePath(routePath) {
"
/>
</div>
<span
<el-text
v-if="
!props.item?.meta.icon &&
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1
(!props.item?.meta.icon &&
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1) ||
(!onlyOneChild.meta.icon &&
isCollapse &&
layout === 'mix' &&
props.item?.pathList?.length === 2)
"
:style="getSpanStyle"
truncated
class="!px-4 !text-inherit"
>
{{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }}
</span>
<span
v-if="
!onlyOneChild.meta.icon &&
isCollapse &&
layout === 'mix' &&
props.item?.pathList?.length === 2
"
:style="getSpanStyle"
>
{{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }}
</span>
{{ transformI18n(onlyOneChild.meta.title) }}
</el-text>
<template #title>
<div :style="getDivStyle">
<span v-if="layout === 'horizontal'">
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
<el-tooltip
v-else
placement="top"
:effect="tooltipEffect"
:offset="-10"
:disabled="!onlyOneChild.showTooltip"
<ReText
:tippyProps="{
offset: [0, -10],
theme: !isDark ? tooltipEffect : undefined
}"
class="!text-inherit"
>
<template #content>
{{ transformI18n(onlyOneChild.meta.title) }}
</template>
<span
ref="menuTextRef"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
</el-tooltip>
{{ transformI18n(onlyOneChild.meta.title) }}
</ReText>
<extraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
</div>
</template>
@@ -264,48 +163,38 @@ function resolvePath(routePath) {
<template #title>
<div
v-if="toRaw(props.item.meta.icon)"
:style="getsubMenuIconStyle"
:style="getSubMenuIconStyle"
class="sub-menu-icon"
>
<component
:is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
/>
</div>
<span v-if="layout === 'horizontal'">
{{ transformI18n(props.item.meta.title) }}
</span>
<div
<ReText
v-if="
!(
layout === 'vertical' &&
isCollapse &&
toRaw(props.item.meta.icon) &&
props.item.parentId === null
)
"
:style="getSubMenuDivStyle(props.item)"
:tippyProps="{
offset: [0, -10],
theme: !isDark ? tooltipEffect : undefined
}"
:class="{
'!text-inherit': true,
'!px-4':
layout !== 'horizontal' &&
isCollapse &&
!toRaw(props.item.meta.icon) &&
props.item.parentId === null
}"
>
<el-tooltip
v-if="layout !== 'horizontal'"
placement="top"
:effect="tooltipEffect"
:offset="-10"
:disabled="!props.item.showTooltip"
>
<template #content>
{{ transformI18n(props.item.meta.title) }}
</template>
<span
ref="menuTextRef"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
{{
overflowSlice(transformI18n(props.item.meta.title), props.item)
}}
</span>
</el-tooltip>
<extraIcon v-if="!isCollapse" :extraIcon="props.item.meta.extraIcon" />
</div>
{{ transformI18n(props.item.meta.title) }}
</ReText>
<extraIcon v-if="!isCollapse" :extraIcon="props.item.meta.extraIcon" />
</template>
<sidebar-item