Browse Source

perf: remove useless code

vben 4 năm trước cách đây
mục cha
commit
be3a3ed699

+ 1 - 1
.env.production

@@ -7,7 +7,7 @@ VITE_PUBLIC_PATH = /
 # Delete console
 VITE_DROP_CONSOLE = true
 
-# Whether to enable gizp or brotli compression
+# Whether to enable gzip or brotli compression
 # Optional: gzip | brotli | none
 # If you need multiple forms, you can use `,` to separate
 VITE_BUILD_COMPRESS = 'none'

+ 2 - 0
src/App.vue

@@ -22,10 +22,12 @@
     setup() {
       // support Multi-language
       const { antConfigLocale, setLocale } = useLocale();
+
       setLocale();
 
       // Initialize vuex internal system configuration
       initAppConfigStore();
+
       // Create a lock screen monitor
       const lockEvent = useLockPage();
 

+ 3 - 3
src/components/Form/src/hooks/useFormEvents.ts

@@ -5,10 +5,10 @@ import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import { unref, toRaw } from 'vue';
 
 import { isArray, isFunction, isObject, isString } from '/@/utils/is';
-import { deepMerge, unique } from '/@/utils';
+import { deepMerge } from '/@/utils';
 import { dateItemType, handleInputNumberValue } from '../helper';
 import { dateUtil } from '/@/utils/dateUtil';
-import { cloneDeep } from 'lodash-es';
+import { cloneDeep, uniqBy } from 'lodash-es';
 import { error } from '/@/utils/log';
 
 interface UseFormActionContext {
@@ -160,7 +160,7 @@ export function useFormEvents({
         }
       });
     });
-    schemaRef.value = unique(schema, 'field');
+    schemaRef.value = uniqBy(schema, 'field');
   }
 
   function getFieldsValue(): Recordable {

+ 2 - 5
src/components/Menu/src/useOpenKeys.ts

@@ -5,7 +5,7 @@ import type { MenuState } from './types';
 import { computed, Ref, toRaw } from 'vue';
 
 import { unref } from 'vue';
-import { es6Unique } from '/@/utils';
+import { uniq } from 'lodash-es';
 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 import { getAllParentPath } from '/@/router/helper/menuHelper';
 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
@@ -31,10 +31,7 @@ export function useOpenKeys(
           return;
         }
         if (!unref(accordion)) {
-          menuState.openKeys = es6Unique([
-            ...menuState.openKeys,
-            ...getAllParentPath(menuList, path),
-          ]);
+          menuState.openKeys = uniq([...menuState.openKeys, ...getAllParentPath(menuList, path)]);
         } else {
           menuState.openKeys = getAllParentPath(menuList, path);
         }

+ 3 - 2
src/components/SimpleMenu/src/useOpenKeys.ts

@@ -4,8 +4,9 @@ import type { MenuState } from './types';
 import { computed, Ref, toRaw } from 'vue';
 
 import { unref } from 'vue';
-import { es6Unique } from '/@/utils';
+import { uniq } from 'lodash-es';
 import { getAllParentPath } from '/@/router/helper/menuHelper';
+
 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
 
 export function useOpenKeys(
@@ -31,7 +32,7 @@ export function useOpenKeys(
         }
         const keys = getAllParentPath(menuList, path);
         if (!unref(accordion)) {
-          menuState.openNames = es6Unique([...menuState.openNames, ...keys]);
+          menuState.openNames = uniq([...menuState.openNames, ...keys]);
         } else {
           menuState.openNames = keys;
         }

+ 11 - 1
src/components/VirtualScroll/src/index.tsx

@@ -11,12 +11,22 @@ import {
 } from 'vue';
 import { useEventListener } from '/@/hooks/event/useEventListener';
 
-import { convertToUnit } from '/@/components/util';
 import { props as basicProps } from './props';
 import { getSlot } from '/@/utils/helper/tsxHelper';
 import './index.less';
 
 const prefixCls = 'virtual-scroll';
+
+function convertToUnit(str: string | number | null | undefined, unit = 'px'): string | undefined {
+  if (str == null || str === '') {
+    return undefined;
+  } else if (isNaN(+str!)) {
+    return String(str);
+  } else {
+    return `${Number(str)}${unit}`;
+  }
+}
+
 export default defineComponent({
   name: 'VirtualScroll',
   props: basicProps,

+ 1 - 76
src/components/registerGlobComp.ts

@@ -3,42 +3,6 @@ import { Button } from './Button';
 import {
   // Need
   Button as AntButton,
-
-  // Optional
-  // Select,
-  // Alert,
-  // Checkbox,
-  // DatePicker,
-  // Radio,
-  // Switch,
-  // Card,
-  // List,
-  // Tabs,
-  // Descriptions,
-  // Tree,
-  // Table,
-  // Divider,
-  // Modal,
-  // Drawer,
-  // Dropdown,
-  // Tag,
-  // Tooltip,
-  // Badge,
-  // Popover,
-  // Upload,
-  // Transfer,
-  // Steps,
-  // PageHeader,
-  // Result,
-  // Empty,
-  // Avatar,
-  // Menu,
-  // Breadcrumb,
-  // Form,
-  // Input,
-  // Row,
-  // Col,
-  // Spin,
 } from 'ant-design-vue';
 
 import { App } from 'vue';
@@ -47,45 +11,6 @@ const compList = [Icon, Button, AntButton.Group];
 
 export function registerGlobComp(app: App) {
   compList.forEach((comp: any) => {
-    app.component(comp.name, comp);
+    app.component(comp.name || comp.displayName, comp);
   });
-
-  // Optional
-  // If you need to customize global components, you can write here
-  // If you don’t need it, you can delete it
-  // app
-  //   .use(Select)
-  //   .use(Alert)
-  //   .use(Breadcrumb)
-  //   .use(Checkbox)
-  //   .use(DatePicker)
-  //   .use(Radio)
-  //   .use(Switch)
-  //   .use(Card)
-  //   .use(List)
-  //   .use(Descriptions)
-  //   .use(Tree)
-  //   .use(Table)
-  //   .use(Divider)
-  //   .use(Modal)
-  //   .use(Drawer)
-  //   .use(Dropdown)
-  //   .use(Tag)
-  //   .use(Tooltip)
-  //   .use(Badge)
-  //   .use(Popover)
-  //   .use(Upload)
-  //   .use(Transfer)
-  //   .use(Steps)
-  //   .use(PageHeader)
-  //   .use(Result)
-  //   .use(Empty)
-  //   .use(Avatar)
-  //   .use(Menu)
-  //   .use(Tabs)
-  //   .use(Form)
-  //   .use(Input)
-  //   .use(Row)
-  //   .use(Col)
-  //   .use(Spin);
 }

+ 0 - 6
src/components/types.ts

@@ -1,6 +0,0 @@
-import { defineComponent } from 'vue';
-
-export type Component<T extends any = any> =
-  | ReturnType<typeof defineComponent>
-  | (() => Promise<typeof import('*.vue')>)
-  | (() => Promise<T>);

+ 0 - 176
src/components/util.tsx

@@ -1,176 +0,0 @@
-import type { VNodeChild } from 'vue';
-
-export function convertToUnit(
-  str: string | number | null | undefined,
-  unit = 'px'
-): string | undefined {
-  if (str == null || str === '') {
-    return undefined;
-  } else if (isNaN(+str!)) {
-    return String(str);
-  } else {
-    return `${Number(str)}${unit}`;
-  }
-}
-
-/**
- * Camelize a hyphen-delimited string.
- */
-const camelizeRE = /-(\w)/g;
-export const camelize = (str: string): string => {
-  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
-};
-
-export function wrapInArray<T>(v: T | T[] | null | undefined): T[] {
-  return v != null ? (Array.isArray(v) ? v : [v]) : [];
-}
-
-const pattern = {
-  styleList: /;(?![^(]*\))/g,
-  styleProp: /:(.*)/,
-} as const;
-
-function parseStyle(style: string) {
-  const styleMap: Recordable = {};
-
-  for (const s of style.split(pattern.styleList)) {
-    let [key, val] = s.split(pattern.styleProp);
-    key = key.trim();
-    if (!key) {
-      continue;
-    }
-    // May be undefined if the `key: value` pair is incomplete.
-    if (typeof val === 'string') {
-      val = val.trim();
-    }
-    styleMap[camelize(key)] = val;
-  }
-
-  return styleMap;
-}
-
-/**
- * Intelligently merges data for createElement.
- * Merges arguments left to right, preferring the right argument.
- * Returns new VNodeData object.
- */
-export function mergeData(...vNodeData: VNodeChild[]): VNodeChild;
-export function mergeData(...args: any[]): VNodeChild {
-  const mergeTarget: any = {};
-  let i: number = args.length;
-  let prop: string;
-
-  // Allow for variadic argument length.
-  while (i--) {
-    // Iterate through the data properties and execute merge strategies
-    // Object.keys eliminates need for hasOwnProperty call
-    for (prop of Object.keys(args[i])) {
-      switch (prop) {
-        // Array merge strategy (array concatenation)
-        case 'class':
-        case 'directives':
-          if (args[i][prop]) {
-            mergeTarget[prop] = mergeClasses(mergeTarget[prop], args[i][prop]);
-          }
-          break;
-        case 'style':
-          if (args[i][prop]) {
-            mergeTarget[prop] = mergeStyles(mergeTarget[prop], args[i][prop]);
-          }
-          break;
-        // Space delimited string concatenation strategy
-        case 'staticClass':
-          if (!args[i][prop]) {
-            break;
-          }
-          if (mergeTarget[prop] === undefined) {
-            mergeTarget[prop] = '';
-          }
-          if (mergeTarget[prop]) {
-            // Not an empty string, so concatenate
-            mergeTarget[prop] += ' ';
-          }
-          mergeTarget[prop] += args[i][prop].trim();
-          break;
-        // Object, the properties of which to merge via array merge strategy (array concatenation).
-        // Callback merge strategy merges callbacks to the beginning of the array,
-        // so that the last defined callback will be invoked first.
-        // This is done since to mimic how Object.assign merging
-        // uses the last given value to assign.
-        case 'on':
-        case 'nativeOn':
-          if (args[i][prop]) {
-            mergeTarget[prop] = mergeListeners(mergeTarget[prop], args[i][prop]);
-          }
-          break;
-        // Object merge strategy
-        case 'attrs':
-        case 'props':
-        case 'domProps':
-        case 'scopedSlots':
-        case 'staticStyle':
-        case 'hook':
-        case 'transition':
-          if (!args[i][prop]) {
-            break;
-          }
-          if (!mergeTarget[prop]) {
-            mergeTarget[prop] = {};
-          }
-          mergeTarget[prop] = { ...args[i][prop], ...mergeTarget[prop] };
-          break;
-        // Reassignment strategy (no merge)
-        default:
-          // slot, key, ref, tag, show, keepAlive
-          if (!mergeTarget[prop]) {
-            mergeTarget[prop] = args[i][prop];
-          }
-      }
-    }
-  }
-
-  return mergeTarget;
-}
-
-export function mergeStyles(
-  target: undefined | string | object[] | object,
-  source: undefined | string | object[] | object
-) {
-  if (!target) return source;
-  if (!source) return target;
-
-  target = wrapInArray(typeof target === 'string' ? parseStyle(target) : target);
-
-  return (target as object[]).concat(typeof source === 'string' ? parseStyle(source) : source);
-}
-
-export function mergeClasses(target: any, source: any) {
-  if (!source) return target;
-  if (!target) return source;
-
-  return target ? wrapInArray(target).concat(source) : source;
-}
-
-export function mergeListeners(
-  target: Indexable<Function | Function[]> | undefined,
-  source: Indexable<Function | Function[]> | undefined
-) {
-  if (!target) return source;
-  if (!source) return target;
-
-  let event: string;
-
-  for (event of Object.keys(source)) {
-    // Concat function to array of functions if callback present.
-    if (target[event]) {
-      // Insert current iteration data in beginning of merged array.
-      target[event] = wrapInArray(target[event]);
-      (target[event] as Function[]).push(...wrapInArray(source[event]));
-    } else {
-      // Straight assign.
-      target[event] = source[event];
-    }
-  }
-
-  return target;
-}

+ 0 - 20
src/hooks/core/useDebouncedRef.ts

@@ -1,20 +0,0 @@
-import { customRef } from 'vue';
-
-export function useDebouncedRef<T = any>(value: T, delay = 100) {
-  let timeout: TimeoutHandle;
-  return customRef((track, trigger) => {
-    return {
-      get() {
-        track();
-        return value;
-      },
-      set(newValue: T) {
-        clearTimeout(timeout);
-        timeout = setTimeout(() => {
-          value = newValue;
-          trigger();
-        }, delay);
-      },
-    };
-  });
-}

+ 0 - 18
src/hooks/core/useEffect.ts

@@ -1,18 +0,0 @@
-import { watch } from 'vue';
-import { isFunction } from '/@/utils/is';
-
-export function useEffect<T extends any = any>(
-  effectHandler: (deps: T[], prevDeps?: T[]) => () => void,
-  dependencies: T[]
-) {
-  return watch(
-    dependencies,
-    (changedDependencies, prevDependencies, onCleanUp) => {
-      const effectCleaner = effectHandler(changedDependencies, prevDependencies);
-      if (isFunction(effectCleaner)) {
-        onCleanUp(effectCleaner);
-      }
-    },
-    { immediate: true, deep: true }
-  );
-}

+ 0 - 58
src/hooks/core/useState.ts

@@ -1,58 +0,0 @@
-import { isObject } from '@vue/shared';
-import { reactive, Ref, ref, readonly } from 'vue';
-import { isFunction } from '/@/utils/is';
-
-type State<T> = ((s: T) => T) | T;
-type Dispatch<T> = (t: T) => void;
-
-type DispatchState<T> = Dispatch<State<T>>;
-
-type ResultState<T> = Readonly<Ref<T>>;
-
-export function useState<T extends undefined>(
-  initialState: (() => T) | T
-): [ResultState<T>, DispatchState<T>];
-
-export function useState<T extends null>(
-  initialState: (() => T) | T
-): [ResultState<T>, DispatchState<T>];
-
-export function useState<T extends boolean>(
-  initialState: (() => T) | T
-): [ResultState<boolean>, DispatchState<boolean>];
-
-export function useState<T extends string>(
-  initialState: (() => T) | T
-): [ResultState<string>, DispatchState<string>];
-
-export function useState<T extends number>(
-  initialState: (() => T) | T
-): [ResultState<number>, DispatchState<number>];
-
-export function useState<T extends object>(
-  initialState: (() => T) | T
-): [Readonly<T>, DispatchState<T>];
-
-export function useState<T extends any>(
-  initialState: (() => T) | T
-): [Readonly<T>, DispatchState<T>];
-
-export function useState<T>(initialState: (() => T) | T): [ResultState<T> | T, DispatchState<T>] {
-  if (isFunction(initialState)) {
-    initialState = (initialState as Fn)();
-  }
-
-  if (isObject(initialState)) {
-    const state = reactive({ data: initialState }) as any;
-    const setState = (newState: T) => {
-      state.data = newState;
-    };
-    return [readonly(state), setState];
-  } else {
-    const state = ref(initialState) as any;
-    const setState = (newState: T) => {
-      state.value = newState;
-    };
-    return [readonly(state), setState];
-  }
-}

+ 1 - 3
src/layouts/iframe/index.vue

@@ -21,9 +21,7 @@
     setup() {
       const { getFramePages, hasRenderFrame, showIframe } = useFrameKeepAlive();
 
-      const showFrame = computed(() => {
-        return unref(getFramePages).length > 0;
-      });
+      const showFrame = computed(() => unref(getFramePages).length > 0);
 
       return { getFramePages, hasRenderFrame, showIframe, showFrame };
     },

+ 2 - 2
src/layouts/iframe/useFrameKeepAlive.ts

@@ -4,7 +4,7 @@ import { computed, toRaw, unref } from 'vue';
 
 import { tabStore } from '/@/store/modules/tab';
 
-import { unique } from '/@/utils';
+import { uniqBy } from 'lodash-es';
 
 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
 
@@ -40,7 +40,7 @@ export function useFrameKeepAlive() {
         res.push(...getAllFramePages(children));
       }
     }
-    res = unique(res, 'name');
+    res = uniqBy(res, 'name');
     return res;
   }
 

+ 2 - 2
src/router/guard/permissionGuard.ts

@@ -3,7 +3,7 @@ import type { Router, RouteRecordRaw } from 'vue-router';
 import { permissionStore } from '/@/store/modules/permission';
 
 import { PageEnum } from '/@/enums/pageEnum';
-import { getToken } from '/@/utils/auth';
+import { userStore } from '/@/store/modules/user';
 
 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
 
@@ -25,7 +25,7 @@ export function createPermissionGuard(router: Router) {
       return;
     }
 
-    const token = getToken();
+    const token = userStore.getTokenState;
 
     // token does not exist
     if (!token) {

+ 6 - 1
src/router/types.ts

@@ -1,7 +1,12 @@
 import type { RouteRecordRaw } from 'vue-router';
 import { RoleEnum } from '/@/enums/roleEnum';
 
-import type { Component } from '/@/components/types';
+import { defineComponent } from 'vue';
+
+export type Component<T extends any = any> =
+  | ReturnType<typeof defineComponent>
+  | (() => Promise<typeof import('*.vue')>)
+  | (() => Promise<T>);
 
 export interface RouteMeta {
   // title

+ 0 - 14
src/types/shims-volar.d.ts

@@ -1,14 +0,0 @@
-import { RouterLink, RouterView } from 'vue-router';
-
-import { Button } from '/@/components/Button';
-import { Col, Row } from 'ant-design-vue';
-
-declare global {
-  interface __VLS_GlobalComponents {
-    RouterLink: typeof RouterLink;
-    RouterView: typeof RouterView;
-    'a-button': typeof Button;
-    'a-col': typeof Col;
-    'a-row': typeof Row;
-  }
-}

+ 0 - 9
src/utils/auth/index.ts

@@ -1,9 +0,0 @@
-import { userStore } from '/@/store/modules/user';
-
-/**
- * @description:  Get token
- * @return jwt token
- */
-export function getToken(): string {
-  return userStore.getTokenState;
-}

+ 0 - 78
src/utils/cache/cookie.ts

@@ -1,78 +0,0 @@
-import { DEFAULT_CACHE_TIME } from '../../settings/encryptionSetting';
-import { getStorageShortName } from '/@/utils/helper/envHelper';
-import { cacheCipher } from '/@/settings/encryptionSetting';
-import Encryption from '/@/utils/encryption/aesEncryption';
-
-export default class WebCookie {
-  private encryption: Encryption;
-  private hasEncrypt: boolean;
-
-  constructor(hasEncrypt = true, key = cacheCipher.key, iv = cacheCipher.iv) {
-    const encryption = new Encryption({ key, iv });
-    this.encryption = encryption;
-    this.hasEncrypt = hasEncrypt;
-  }
-
-  private getKey(key: string) {
-    return `${getStorageShortName()}${key}`.toUpperCase();
-  }
-
-  /**
-   * Add cookie
-   * @param name cookie key
-   * @param value cookie value
-   * @param expire
-   * If the expiration time is not set, the default management browser will automatically delete
-   * e.g:
-   *  cookieData.set('name','value',)
-   */
-  setCookie(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
-    value = this.hasEncrypt ? this.encryption.encryptByAES(JSON.stringify(value)) : value;
-    document.cookie = this.getKey(key) + '=' + value + '; Max-Age=' + expire;
-  }
-
-  /**
-   * Get the cook value according to the key
-   * @param key cookie key
-   */
-  getCookie(key: string) {
-    const arr = document.cookie.split('; ');
-    for (let i = 0; i < arr.length; i++) {
-      const arr2 = arr[i].split('=');
-      if (arr2[0] === this.getKey(key)) {
-        let message: any = null;
-        const str = arr2[1];
-        if (this.hasEncrypt && str) {
-          message = this.encryption.decryptByAES(str);
-          try {
-            return JSON.parse(message);
-          } catch (e) {
-            return str;
-          }
-        }
-        return str;
-      }
-    }
-    return '';
-  }
-
-  /**
-   * Delete cookie based on cookie key
-   * @param key cookie key
-   */
-  removeCookie(key: string) {
-    this.setCookie(key, 1, -1);
-  }
-
-  /**
-   * clear cookie
-   */
-  clearCookie(): void {
-    const keys = document.cookie.match(/[^ =;]+(?==)/g);
-    if (keys) {
-      for (let i = keys.length; i--; ) {
-        document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();
-      }
-    }
-  }
-}

+ 7 - 7
src/utils/domUtils.ts

@@ -73,13 +73,13 @@ export function removeClass(el: Element, cls: string) {
   }
 }
 /**
- * 获取当前元素的left、top偏移
- *   left:元素最左侧距离文档左侧的距离
- *   top:元素最顶端距离文档顶端的距离
- *   right:元素最右侧距离文档右侧的距离
- *   bottom:元素最底端距离文档底端的距离
- *   rightIncludeBody:元素最左侧距离文档右侧的距离
- *   bottomIncludeBody:元素最底端距离文档最底部的距离
+ * Get the left and top offset of the current element
+ * left: the distance between the leftmost element and the left side of the document
+ * top: the distance from the top of the element to the top of the document
+ * right: the distance from the far right of the element to the right of the document
+ * bottom: the distance from the bottom of the element to the bottom of the document
+ * rightIncludeBody: the distance between the leftmost element and the right side of the document
+ * bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
  *
  * @description:
  */

+ 3 - 5
src/utils/encryption/aesEncryption.ts

@@ -14,7 +14,7 @@ export class Encryption {
     this.iv = CryptoES.enc.Utf8.parse(iv);
   }
 
-  get getOpt(): CryptoES.lib.CipherCfg {
+  get getOptions(): CryptoES.lib.CipherCfg {
     return {
       mode: CryptoES.mode.CBC as any,
       padding: CryptoES.pad.Pkcs7,
@@ -23,13 +23,11 @@ export class Encryption {
   }
 
   encryptByAES(str: string) {
-    const encrypted = CryptoES.AES.encrypt(str, this.key, this.getOpt);
-    return encrypted.toString();
+    return CryptoES.AES.encrypt(str, this.key, this.getOptions).toString();
   }
 
   decryptByAES(str: string) {
-    const decrypted = CryptoES.AES.decrypt(str, this.key, this.getOpt);
-    return decrypted.toString(CryptoES.enc.Utf8);
+    return CryptoES.AES.decrypt(str, this.key, this.getOptions).toString(CryptoES.enc.Utf8);
   }
 }
 export default Encryption;

+ 2 - 2
src/utils/http/axios/index.ts

@@ -4,7 +4,6 @@
 import type { AxiosResponse } from 'axios';
 import type { CreateAxiosOptions, RequestOptions, Result } from './types';
 import { VAxios } from './Axios';
-import { getToken } from '/@/utils/auth';
 import { AxiosTransform } from './axiosTransform';
 
 import { checkStatus } from './checkStatus';
@@ -20,6 +19,7 @@ import { errorStore } from '/@/store/modules/error';
 import { errorResult } from './const';
 import { useI18n } from '/@/hooks/web/useI18n';
 import { createNow, formatRequestDate } from './helper';
+import { userStore } from '/@/store/modules/user';
 
 const globSetting = useGlobSetting();
 const prefix = globSetting.urlPrefix;
@@ -137,7 +137,7 @@ const transform: AxiosTransform = {
    */
   requestInterceptors: (config) => {
     // 请求之前处理config
-    const token = getToken();
+    const token = userStore.getTokenState;
     if (token) {
       // jwt token
       config.headers.Authorization = token;

+ 0 - 32
src/utils/index.ts

@@ -38,24 +38,6 @@ export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
   return src;
 }
 
-/**
- * @description: Deduplication according to the value of an object in the array
- */
-export function unique<T = any>(arr: T[], key: string): T[] {
-  const map = new Map();
-  return arr.filter((item) => {
-    const _item = item as any;
-    return !map.has(_item[key]) && map.set(_item[key], 1);
-  });
-}
-
-/**
- * @description: es6 array to repeat
- */
-export function es6Unique<T>(arr: T[]): T[] {
-  return Array.from(new Set(arr));
-}
-
 export function openWindow(
   url: string,
   opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }
@@ -80,20 +62,6 @@ export function getDynamicProps<T, U>(props: T): Partial<U> {
   return ret as Partial<U>;
 }
 
-export function getLastItem<T extends any>(list: T) {
-  if (Array.isArray(list)) {
-    return list.slice(-1)[0];
-  }
-
-  if (list instanceof Set) {
-    return Array.from(list).slice(-1)[0];
-  }
-
-  if (list instanceof Map) {
-    return Array.from(list.values()).slice(-1)[0];
-  }
-}
-
 /**
  * set page Title
  * @param {*} title  :page Title