From f6eaa8d6d89776be97e737e378f11c1e8872438e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=97=E5=A4=A7?= Date: Mon, 19 Feb 2024 13:36:07 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(ReText):=20=E6=96=B0=E5=A2=9E`ReText`?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E7=9C=81=E7=95=A5=E6=98=BE=E7=A4=BA`Tooltip`=E5=8A=9F=E8=83=BD?= =?UTF-8?q?,=20=E6=94=AF=E6=8C=81=E5=A4=9A=E8=A1=8C=E7=9C=81=E7=95=A5,=20?= =?UTF-8?q?=E9=AB=98=E5=8F=AF=E5=A4=8D=E7=94=A8=E6=80=A7=20(#898)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(ReText): 新增ReText组件 - 基于El-Text, 增加自动省略显示Tooltip功能, 高可复用性 --- locales/en.yaml | 1 + locales/zh-CN.yaml | 1 + src/components/ReCropper/src/circled.css | 1 - src/components/ReText/index.ts | 7 + src/components/ReText/src/index.vue | 62 ++++++ src/layout/components/sidebar/sidebarItem.vue | 203 ++++-------------- src/main.ts | 10 +- src/router/modules/components.ts | 9 + src/views/components/text.vue | 164 ++++++++++++++ 9 files changed, 294 insertions(+), 164 deletions(-) create mode 100644 src/components/ReText/index.ts create mode 100644 src/components/ReText/src/index.vue create mode 100644 src/views/components/text.vue diff --git a/locales/en.yaml b/locales/en.yaml index a9e25217d..7d0284641 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -44,6 +44,7 @@ menus: hsmap: Map hsdraggable: Draggable hssplitPane: Split Pane + hsText: Text Ellipsis hsElButton: Button hsbutton: Button Animation hsCheckButton: Check Button diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index 52e7e5ed3..f5c14d08d 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -44,6 +44,7 @@ menus: hsmap: 地图 hsdraggable: 拖拽 hssplitPane: 切割面板 + hsText: 文本省略 hsElButton: 按钮 hsCheckButton: 可选按钮 hsbutton: 按钮动效 diff --git a/src/components/ReCropper/src/circled.css b/src/components/ReCropper/src/circled.css index d9216c4f1..54c77d23f 100644 --- a/src/components/ReCropper/src/circled.css +++ b/src/components/ReCropper/src/circled.css @@ -1,4 +1,3 @@ -@import "tippy.js/themes/light.css"; @import "cropperjs/dist/cropper.css"; .re-circled { diff --git a/src/components/ReText/index.ts b/src/components/ReText/index.ts new file mode 100644 index 000000000..621356605 --- /dev/null +++ b/src/components/ReText/index.ts @@ -0,0 +1,7 @@ +import reText from "./src/index.vue"; +import { withInstall } from "@pureadmin/utils"; + +/** 支持`Tooltip`提示的文本省略组件 */ +export const ReText = withInstall(reText); + +export default ReText; diff --git a/src/components/ReText/src/index.vue b/src/components/ReText/src/index.vue new file mode 100644 index 000000000..1b34178f4 --- /dev/null +++ b/src/components/ReText/src/index.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/layout/components/sidebar/sidebarItem.vue b/src/layout/components/sidebar/sidebarItem.vue index dd40645c1..be0b9c01f 100644 --- a/src/layout/components/sidebar/sidebarItem.vue +++ b/src/layout/components/sidebar/sidebarItem.vue @@ -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) { - - {{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }} - - - {{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }} - + {{ transformI18n(onlyOneChild.meta.title) }} + + @@ -264,48 +163,38 @@ function resolvePath(routePath) { { app.directive(key, (directives as { [key: string]: Directive })[key]); }); -// 全局注册`@iconify/vue`图标库 +// 全局注册@iconify/vue图标库 import { IconifyIconOffline, IconifyIconOnline, @@ -45,13 +45,11 @@ app.component("FontIcon", FontIcon); import { Auth } from "@/components/ReAuth"; app.component("Auth", Auth); -// 全局注册`vue-tippy` +// 全局注册vue-tippy import "tippy.js/dist/tippy.css"; -import "tippy.js/animations/perspective.css"; +import "tippy.js/themes/light.css"; import VueTippy from "vue-tippy"; -app.use(VueTippy, { - defaultProps: { animation: "perspective" } -}); +app.use(VueTippy); getPlatformConfig(app).then(async config => { setupStore(app); diff --git a/src/router/modules/components.ts b/src/router/modules/components.ts index 485d0432a..a73d3517a 100644 --- a/src/router/modules/components.ts +++ b/src/router/modules/components.ts @@ -91,6 +91,15 @@ export default { title: $t("menus.hssegmented") } }, + { + path: "/components/text", + name: "PureText", + component: () => import("@/views/components/text.vue"), + meta: { + title: $t("menus.hsText"), + extraIcon: "IF-pure-iconfont-new svg" + } + }, { path: "/components/el-button", name: "PureButton", diff --git a/src/views/components/text.vue b/src/views/components/text.vue new file mode 100644 index 000000000..f988620ed --- /dev/null +++ b/src/views/components/text.vue @@ -0,0 +1,164 @@ + + + + + From f762587fa778ed81b005002163930c4964d90830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E4=B8=87?= <52823142+Ten-K@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:31:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?perf:=20=E5=A2=9E=E5=BC=BA`ReTypeit`?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E6=8F=92=E6=A7=BD?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E6=89=80=E6=9C=89`typeit`=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9=20(#922)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 增强typeit组件 * fix: update * fix: delete invalid code --- src/components/ReTypeit/index.ts | 50 ++++-------------------- src/components/ReTypeit/src/index.tsx | 56 +++++++++++++++++++++++++++ src/views/able/typeit.vue | 8 +++- src/views/login/index.vue | 4 +- 4 files changed, 72 insertions(+), 46 deletions(-) create mode 100644 src/components/ReTypeit/src/index.tsx diff --git a/src/components/ReTypeit/index.ts b/src/components/ReTypeit/index.ts index 4c34bae62..dd6f2ca49 100644 --- a/src/components/ReTypeit/index.ts +++ b/src/components/ReTypeit/index.ts @@ -1,44 +1,8 @@ -import { h, defineComponent } from "vue"; -import TypeIt from "typeit"; +import typeIt from "./src/index"; +import type { TypeItOptions } from "typeit"; -// 打字机效果组件(只是简单的封装下,更多配置项参考 https://www.typeitjs.com/docs/vanilla/usage#options) -export default defineComponent({ - name: "TypeIt", - props: { - /** 打字速度,以每一步之间的毫秒数为单位,默认`200` */ - speed: { - type: Number, - default: 200 - }, - values: { - type: Array, - defalut: [] - }, - className: { - type: String, - default: "type-it" - }, - cursor: { - type: Boolean, - default: true - } - }, - render() { - return h( - "span", - { - class: this.className - }, - { - default: () => [] - } - ); - }, - mounted() { - new TypeIt(`.${this.className}`, { - strings: this.values, - speed: this.speed, - cursor: this.cursor - }).go(); - } -}); +const TypeIt = typeIt; + +export { TypeIt, TypeItOptions }; + +export default TypeIt; diff --git a/src/components/ReTypeit/src/index.tsx b/src/components/ReTypeit/src/index.tsx new file mode 100644 index 000000000..9e61b85ca --- /dev/null +++ b/src/components/ReTypeit/src/index.tsx @@ -0,0 +1,56 @@ +import type { El } from "typeit/dist/types"; +import TypeIt, { type TypeItOptions } from "typeit"; +import { ref, defineComponent, onMounted, type PropType } from "vue"; + +// 打字机效果组件(配置项详情请查阅 https://www.typeitjs.com/docs/vanilla/usage#options) +export default defineComponent({ + name: "TypeIt", + props: { + options: { + type: Object as PropType, + default: () => ({}) as TypeItOptions + } + }, + setup(props, { slots, expose }) { + /** + * 输出错误信息 + * @param message 错误信息 + */ + function throwError(message: string) { + throw new TypeError(message); + } + + /** + * 获取浏览器默认语言 + */ + function getBrowserLanguage() { + return navigator.language; + } + + const typedItRef = ref(null); + + onMounted(() => { + const $typed = typedItRef.value!.querySelector(".type-it") as El; + + if (!$typed) { + const errorMsg = + getBrowserLanguage() === "zh-CN" + ? "请确保有且只有一个具有class属性为 'type-it' 的元素" + : "Please make sure that there is only one element with a Class attribute with 'type-it'"; + throwError(errorMsg); + } + + const typeIt = new TypeIt($typed, props.options).go(); + + expose({ + typeIt + }); + }); + + return () => ( +
+ {slots.default?.() ?? } +
+ ); + } +}); diff --git a/src/views/able/typeit.vue b/src/views/able/typeit.vue index c79820755..3c0d24f70 100644 --- a/src/views/able/typeit.vue +++ b/src/views/able/typeit.vue @@ -1,9 +1,13 @@ - + diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 0be17a3e5..20ff66fe3 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -166,7 +166,9 @@ watch(loginDay, value => {

- +