提交 2037293a 编写于 作者: V Vben

refactor: refactor hooks

上级 215d8bab
## Wip
## (破坏性更新) Breaking changes
- 使用 `pinia` 替换 `vuex`,`vuex-module-decorators`
- 影响,之前如果有自己使用 vuex-module-decorators,需要改造为 pinia。
- 原因:
- pinia 于 vuex5api 基本类似,且简单易懂。
- 后续切换 vuex5 成本非常低,也可以当作第三方状态管理库使用
- 移除 `useKeyPress` 使用`vueuse`-`onKeyStroke`代替
- 移除 `useDebounceFn` 使用`vueuse`-`useDebounceFn`代替
- 移除 `useThrottle` 使用`vueuse`-`useThrottleFn`代替
### ✨ Refactor
- 移除 `useElResize`
### 🐛 Bug Fixes
- 登录页样式修复
......
......@@ -3,15 +3,13 @@ import type { Menu } from '/@/router/types';
import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
import { getMenus } from '/@/router/menus';
import { KeyCodeEnum } from '/@/enums/keyCodeEnum';
import { cloneDeep } from 'lodash-es';
import { filter, forEach } from '/@/utils/helper/treeHelper';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useGo } from '/@/hooks/web/usePage';
import { useScrollTo } from '/@/hooks/event/useScrollTo';
import { useKeyPress } from '/@/hooks/event/useKeyPress';
import { onKeyStroke, useDebounceFn } from '@vueuse/core';
import { useI18n } from '/@/hooks/web/useI18n';
export interface SearchResult {
......@@ -41,7 +39,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
const { t } = useI18n();
const go = useGo();
const [handleSearch] = useDebounce(search, 200);
const handleSearch = useDebounceFn(search, 200);
onBeforeMount(async () => {
const list = await getMenus();
......@@ -146,23 +144,10 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
emit('close');
}
useKeyPress(['enter', 'up', 'down', 'esc'], (events) => {
const keyCode = events.keyCode;
switch (keyCode) {
case KeyCodeEnum.UP:
handleUp();
break;
case KeyCodeEnum.DOWN:
handleDown();
break;
case KeyCodeEnum.ENTER:
handleEnter();
break;
case KeyCodeEnum.ESC:
handleClose();
break;
}
});
onKeyStroke('Enter', handleEnter);
onKeyStroke('ArrowUp', handleUp);
onKeyStroke('ArrowDown', handleDown);
onKeyStroke('Escape', handleClose);
return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter };
}
......@@ -76,7 +76,7 @@
import iconsData from '../data/icons.data';
import { propTypes } from '/@/utils/propTypes';
import { usePagination } from '/@/hooks/web/usePagination';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
import { useI18n } from '/@/hooks/web/useI18n';
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
import { useMessage } from '/@/hooks/web/useMessage';
......@@ -123,7 +123,7 @@
const { t } = useI18n();
const { prefixCls } = useDesign('icon-picker');
const [debounceHandleSearchChange] = useDebounce(handleSearchChange, 100);
const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
const { clipboardRef, isSuccessRef } = useCopyToClipboard(props.value);
const { createMessage } = useMessage();
......
......@@ -24,7 +24,6 @@
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { ScrollContainer } from '/@/components/Container';
// import { useElResize } from '/@/hooks/event/useElResize';
import { propTypes } from '/@/utils/propTypes';
import { createModalContext } from '../hooks/useModalContext';
......
......@@ -8,7 +8,7 @@ import { uniq } from 'lodash-es';
import { getAllParentPath } from '/@/router/helper/menuHelper';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
export function useOpenKeys(
menuState: MenuState,
......@@ -17,7 +17,7 @@ export function useOpenKeys(
mixSider: Ref<boolean>,
collapse: Ref<boolean>
) {
const [debounceSetOpenKeys] = useDebounce(setOpenKeys, 50);
const debounceSetOpenKeys = useDebounceFn(setOpenKeys, 50);
async function setOpenKeys(path: string) {
const native = !mixSider.value;
const menuList = toRaw(menus.value);
......
import type { BasicTableProps, TableRowSelection } from '../types/table';
import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table';
import type { Ref, ComputedRef } from 'vue';
import { computed, unref, ref, nextTick, watch } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils';
......@@ -7,9 +8,8 @@ import { isBoolean } from '/@/utils/is';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { useModalContext } from '/@/components/Modal';
import { useDebounce } from '/@/hooks/core/useDebounce';
import type { BasicColumn } from '/@/components/Table';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useDebounceFn } from '@vueuse/core';
export function useTableScroll(
propsRef: ComputedRef<BasicTableProps>,
......@@ -23,7 +23,7 @@ export function useTableScroll(
const modalFn = useModalContext();
// Greater than animation time 280
const [debounceRedoHeight] = useDebounce(redoHeight, 100);
const debounceRedoHeight = useDebounceFn(redoHeight, 100);
const getCanResize = computed(() => {
const { canResize, scroll } = unref(propsRef);
......
......@@ -41,7 +41,7 @@
import { propTypes } from '/@/utils/propTypes';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
import { ToolbarEnum } from './enum';
......@@ -128,7 +128,7 @@
function emitChange(value?: string): void {
emit('search', value);
}
const [debounceEmitChange] = useDebounce(emitChange, 200);
const debounceEmitChange = useDebounceFn(emitChange, 200);
function handleSearch(e: ChangeEvent): void {
debounceEmitChange(e.target.value);
......
export const enum KeyCodeEnum {
UP = 38,
DOWN = 40,
ENTER = 13,
ESC = 27,
}
......@@ -29,19 +29,7 @@ export function createContext<T>(
const provideData = readonly ? defineReadonly(state) : state;
!createProvider && provide(key, native ? context : provideData);
// const Provider = createProvider
// ? defineComponent({
// name: 'Provider',
// inheritAttrs: false,
// setup(_, { slots }) {
// provide(key, provideData);
// return () => slots.default?.();
// },
// })
// : null;
return {
// Provider,
state,
};
}
......
export interface DebounceAndThrottleOptions {
// 立即执行
immediate?: boolean;
// 是否为debounce
debounce?: boolean;
// 只执行一次
once?: boolean;
}
export type CancelFn = () => void;
export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown;
export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [
DebounceAndThrottleProcedure<T>,
CancelFn
];
import {
// throttle,
useThrottle,
} from './useThrottle';
/**
* @description: Applicable in components
*/
export function useDebounce<T extends unknown[]>(
handle: DebounceAndThrottleProcedure<T>,
wait: number,
options: DebounceAndThrottleOptions = {}
): DebounceAndThrottleProcedureResult<T> {
return useThrottle(
handle,
wait,
Object.assign(options, {
debounce: true,
})
);
}
export interface DebounceAndThrottleOptions {
// 立即执行
immediate?: boolean;
// 是否为debounce
debounce?: boolean;
// 只执行一次
once?: boolean;
}
export type CancelFn = () => void;
export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown;
export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [
DebounceAndThrottleProcedure<T>,
CancelFn
];
import { isFunction } from '/@/utils/is';
export function throttle<T extends unknown[]>(
handle: DebounceAndThrottleProcedure<T>,
wait: number,
options: DebounceAndThrottleOptions = {}
): DebounceAndThrottleProcedureResult<T> {
if (!isFunction(handle)) {
throw new Error('handle is not Function!');
}
let { immediate = false } = options;
const { once = false, debounce = false } = options;
let timeoutId: Nullable<TimeoutHandle>;
// Has it been cancelled
let cancelled: boolean | null = false;
/**
* @description: clear timer
*/
function clearTimer() {
if (timeoutId) {
window.clearTimeout(timeoutId);
timeoutId = null;
}
}
/** cancel exec */
function cancel() {
clearTimer();
cancelled = true;
}
// If once is true, only execute once
function cancelExec(): void {
once && cancel();
}
function fn(this: unknown, ...args: T) {
// If it has been cancelled, it will not be executed
if (cancelled) {
return;
}
const exec = () => {
!debounce && clearTimer();
handle.apply(this, args);
cancelExec();
};
if (immediate) {
immediate = false;
const callNow = !timeoutId;
if (callNow) {
exec();
timeoutId = null;
}
} else {
debounce && clearTimer();
if (!timeoutId || debounce) {
timeoutId = setTimeout(exec, wait);
}
}
}
return [fn, cancel];
}
export function useThrottle<T extends unknown[]>(
handle: DebounceAndThrottleProcedure<T>,
wait: number,
options: DebounceAndThrottleOptions = {}
): DebounceAndThrottleProcedureResult<T> {
return throttle(handle, wait, options);
}
import { useDebounce } from '/@/hooks/core/useDebounce';
import { addResizeListener, removeResizeListener } from '/@/utils/event';
interface WindowSizeOptions {
once?: boolean;
immediate?: boolean;
}
export function useElResize<T>(
el: Element | typeof window,
fn: Fn<T>,
wait = 100,
options?: WindowSizeOptions
) {
let handler = () => {
fn();
};
const [handleSize, cancel] = useDebounce(handler, wait, options);
handler = wait ? handleSize : handler;
function start() {
addResizeListener(el, handler);
}
function stop() {
removeResizeListener(el, handler);
cancel();
}
return [start, stop];
}
import type { Ref } from 'vue';
import { ref, watch, unref } from 'vue';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useThrottle } from '/@/hooks/core/useThrottle';
import { useThrottleFn, useDebounceFn } from '@vueuse/core';
export type RemoveEventFn = () => void;
......@@ -31,7 +30,7 @@ export function useEventListener({
if (el) {
const element: Ref<Element> = ref(el as Element);
const [handler] = isDebounce ? useDebounce(listener, wait) : useThrottle(listener, wait);
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
const realHandler = wait ? handler : listener;
const removeEventListener = (e: Element) => {
isAddRef.value = true;
......
// https://ahooks.js.org/zh-CN/hooks/dom/use-key-press
import type { Ref } from 'vue';
import { onBeforeUnmount, onMounted, unref } from 'vue';
import { noop } from '/@/utils';
import { isFunction, isString, isNumber, isArray } from '/@/utils/is';
export type KeyPredicate = (event: KeyboardEvent) => boolean;
export type keyType = KeyboardEvent['keyCode'] | KeyboardEvent['key'];
export type KeyFilter = keyType | keyType[] | ((event: KeyboardEvent) => boolean);
export type EventHandler = (event: KeyboardEvent) => void;
export type keyEvent = 'keydown' | 'keyup';
export type TargetElement = HTMLElement | Element | Document | Window;
export type Target = Ref<TargetElement>;
export type EventOption = {
events?: keyEvent[];
target?: Target;
};
const defaultEvents: keyEvent[] = ['keydown'];
// 键盘事件 keyCode 别名
const aliasKeyCodeMap: Recordable<number | number[]> = {
esc: 27,
tab: 9,
enter: 13,
space: 32,
up: 38,
left: 37,
right: 39,
down: 40,
delete: [8, 46],
};
// 键盘事件 key 别名
const aliasKeyMap: Recordable<string | string[]> = {
esc: 'Escape',
tab: 'Tab',
enter: 'Enter',
space: ' ',
// IE11 uses key names without `Arrow` prefix for arrow keys.
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete'],
};
// 修饰键
const modifierKey: Recordable<(event: KeyboardEvent) => boolean> = {
ctrl: (event: KeyboardEvent) => event.ctrlKey,
shift: (event: KeyboardEvent) => event.shiftKey,
alt: (event: KeyboardEvent) => event.altKey,
meta: (event: KeyboardEvent) => event.metaKey,
};
/**
* 判断按键是否激活
* @param [event: KeyboardEvent]键盘事件
* @param [keyFilter: any] 当前键
* @returns Boolean
*/
function genFilterKey(event: any, keyFilter: any) {
// 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空
if (!event.key) {
return false;
}
// 数字类型直接匹配事件的 keyCode
if (isNumber(keyFilter)) {
return event.keyCode === keyFilter;
}
// 字符串依次判断是否有组合键
const genArr = keyFilter.split('.');
let genLen = 0;
for (const key of genArr) {
// 组合键
const genModifier = modifierKey[key];
// key 别名
const aliasKey = aliasKeyMap[key];
// keyCode 别名
const aliasKeyCode = aliasKeyCodeMap[key];
/**
* 满足以上规则
* 1. 自定义组合键别名
* 2. 自定义 key 别名
* 3. 自定义 keyCode 别名
* 4. 匹配 key 或 keyCode
*/
if (
(genModifier && genModifier(event)) ||
(aliasKey && isArray(aliasKey) ? aliasKey.includes(event.key) : aliasKey === event.key) ||
(aliasKeyCode && isArray(aliasKeyCode)
? aliasKeyCode.includes(event.keyCode)
: aliasKeyCode === event.keyCode) ||
event.key.toUpperCase() === key.toUpperCase()
) {
genLen++;
}
}
return genLen === genArr.length;
}
/**
* 键盘输入预处理方法
*/
function genKeyFormat(keyFilter: any): KeyPredicate {
if (isFunction(keyFilter)) {
return keyFilter;
}
if (isString(keyFilter) || isNumber(keyFilter)) {
return (event: KeyboardEvent) => genFilterKey(event, keyFilter);
}
if (isArray(keyFilter)) {
return (event: KeyboardEvent) => keyFilter.some((item: any) => genFilterKey(event, item));
}
return keyFilter ? () => true : () => false;
}
export function useKeyPress(
keyFilter: KeyFilter,
eventHandler: EventHandler = noop,
option: EventOption = {}
) {
const { events = defaultEvents, target } = option;
let el: TargetElement | null | undefined;
function handler(event: any) {
const genGuard: KeyPredicate = genKeyFormat(keyFilter);
if (genGuard(event)) {
return eventHandler(event);
}
}
onMounted(() => {
el = getTargetElement(target, window);
if (!el) return;
for (const eventName of events) {
el.addEventListener(eventName, handler);
}
});
onBeforeUnmount(() => {
if (!el) return;
for (const eventName of events) {
el.removeEventListener(eventName, handler);
}
});
}
export function getTargetElement(
target?: Target,
defaultElement?: TargetElement
): TargetElement | undefined | null {
if (!target) {
return defaultElement;
}
return isFunction(target) ? target() : unref(target);
}
import type { Ref } from 'vue';
import { ref, onMounted, watch, onUnmounted } from 'vue';
import { useThrottle } from '/@/hooks/core/useThrottle';
import { isWindow, isObject } from '/@/utils/is';
import { useThrottleFn } from '@vueuse/core';
export function useScroll(
refEl: Ref<Element | Window | null>,
......@@ -31,8 +31,7 @@ export function useScroll(
Reflect.deleteProperty(options, 'wait');
}
const [throttleHandle] = useThrottle(handler, wait);
handler = throttleHandle;
handler = useThrottleFn(handler, wait);
}
let stopWatch: () => void;
......
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
interface WindowSizeOptions {
once?: boolean;
......@@ -11,7 +11,7 @@ export function useWindowSizeFn<T>(fn: Fn<T>, wait = 150, options?: WindowSizeOp
let handler = () => {
fn();
};
const [handleSize, cancel] = useDebounce(handler, wait, options);
const handleSize = useDebounceFn(handler, wait);
handler = handleSize;
const start = () => {
......@@ -23,7 +23,6 @@ export function useWindowSizeFn<T>(fn: Fn<T>, wait = 150, options?: WindowSizeOp
const stop = () => {
window.removeEventListener('resize', handler);
cancel();
};
tryOnMounted(() => {
......
......@@ -4,7 +4,7 @@ import type { Ref } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { tryOnUnmounted } from '@vueuse/core';
import { unref, nextTick, watch, computed, ref } from 'vue';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
......@@ -21,8 +21,7 @@ export function useECharts(
const cacheOptions = ref<EChartsOption>({});
let removeResizeFn: Fn = () => {};
const [debounceResize] = useDebounce(resize, 200);
resizeFn = debounceResize;
resizeFn = useDebounceFn(resize, 200);
const getOptions = computed(
(): EChartsOption => {
......
......@@ -2,13 +2,14 @@ import { computed, unref } from 'vue';
import { useAppStore } from '/@/store/modules/app';
import router from '/@/router';
import { useRouter } from 'vue-router';
/**
* @description: Full screen display content
*/
export const useFullContent = () => {
const appStore = useAppStore();
const router = useRouter();
const { currentRoute } = router;
// Whether to display the content in full screen without displaying the menu
......
import { computed, onUnmounted, unref, watchEffect } from 'vue';
import { useThrottle } from '/@/hooks/core/useThrottle';
import { useThrottleFn } from '@vueuse/core';
import { useAppStore } from '/@/store/modules/app';
import { useLockStore } from '/@/store/modules/lock';
......@@ -59,7 +59,7 @@ export function useLockPage() {
clear();
});
const [keyupFn] = useThrottle(resetCalcLockTimeout, 2000);
const keyupFn = useThrottleFn(resetCalcLockTimeout, 2000);
return computed(() => {
if (unref(getLockTime)) {
......
......@@ -94,6 +94,7 @@ export function usePermission() {
'Please switch PermissionModeEnum to ROLE mode in the configuration to operate!'
);
}
if (!isArray(roles)) {
roles = [roles];
}
......
......@@ -5,7 +5,7 @@ import { watch, unref, ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { useThrottle } from '/@/hooks/core/useThrottle';
import { useThrottleFn } from '@vueuse/core';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
......@@ -20,7 +20,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
const permissionStore = usePermissionStore();
const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
const throttleHandleSplitLeftMenu = useThrottleFn(handleSplitLeftMenu, 50);
const splitNotLeft = computed(
() => unref(splitType) !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontal)
......
......@@ -5,7 +5,7 @@ import { computed, unref, onMounted, nextTick, ref } from 'vue';
import { TriggerEnum } from '/@/enums/menuEnum';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useDebounceFn } from '@vueuse/core';
/**
* Handle related operations of menu events
......@@ -64,7 +64,7 @@ export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>, mix = fals
onMounted(() => {
nextTick(() => {
const [exec] = useDebounce(changeWrapWidth, 80);
const exec = useDebounceFn(changeWrapWidth, 80);
exec();
});
});
......
......@@ -88,8 +88,7 @@
import { useUserStore } from '/@/store/modules/user';
import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
import { useDesign } from '/@/hooks/web/useDesign';
import { useKeyPress } from '/@/hooks/event/useKeyPress';
import { KeyCodeEnum } from '/@/enums/keyCodeEnum';
import { onKeyStroke } from '@vueuse/core';
export default defineComponent({
name: 'LoginForm',
......@@ -129,13 +128,8 @@
});
const { validForm } = useFormValid(formRef);
useKeyPress(['enter'], (events) => {
const keyCode = events.keyCode;
if (keyCode === KeyCodeEnum.ENTER) {
handleLogin();
}
});
onKeyStroke('Enter', handleLogin);
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册