# Hooks 组合式函数 :::tip 说明 项目封装了一系列组合式函数(Hooks),位于 `src/hooks/` 和 `src/utils/` 目录,提供权限检查、异步 loading、防重复提交、实时时钟、项目配置等可复用逻辑。 ::: ## Hooks 目录结构 ``` src/hooks/ ├── event/ │ ├── useEventListener.ts # DOM 事件监听 │ ├── useBreakpoint.ts # 响应式断点 │ └── useWindowSizeFn.ts # 窗口大小变化回调 ├── setting/ │ ├── useProjectSetting.ts # 项目配置(权限模式、水印等) │ └── useDesignSetting.ts # 设计配置(主题色、深色模式) ├── web/ │ ├── usePermission.ts # 权限检查(hasPermission、hasSomePermission) │ ├── useECharts.ts # ECharts 封装 │ ├── usePage.ts # 页面跳转 │ ├── useTags.ts # 标签页管理 │ └── useCopyToClipboard.ts # 剪贴板复制 ├── core/ │ └── useTimeout.ts # 定时器封装 ├── useDomWidth.ts # DOM 宽度 ├── useOnline.ts # 网络状态 ├── useBattery.ts # 电量信息 ├── useTime.ts # 实时时钟 └── use-async.ts # 异步 loading 管理 ``` ## usePermission 权限检查 Hook,从 user store 获取权限列表进行判断: ```typescript // src/hooks/web/usePermission.ts import { useUserStore } from '@/store/modules/user'; export function usePermission() { const userStore = useUserStore(); function _somePermissions(accesses: string[]) { const permissionsList = userStore.getPermissions; if (permissionsList[0] == '*:*:*') return true; // 超级管理员 return accesses.some((item) => permissionsList.includes(item)); } // 是否包含其中某个权限(用于 v-perm 指令) function hasSomePermission(accesses?: string | string[]): boolean { const permissionsList = userStore.getPermissions; if (permissionsList[0] === '*:*:*') return true; const list = Array.isArray(accesses) ? accesses : []; return list.some((access) => permissionsList.includes(access)); } // 是否包含所有权限(用于 v-perms 指令) function hasEveryPermission(accesses?: string | string[]): boolean { const list = Array.isArray(accesses) ? accesses : []; const permissionsList = userStore.getPermissions; if (permissionsList[0] === '*:*:*') return true; return list.every((access) => permissionsList.includes(access)); } // 判断是否存在权限(可用于 v-if 显示逻辑) function hasPermission(accesses: string[]): boolean { if (!accesses || !accesses.length) return true; return _somePermissions(accesses); } return { hasPermission, hasEveryPermission, hasSomePermission }; } ``` 使用示例: ```typescript import { usePermission } from '@/hooks/web/usePermission'; const { hasPermission, hasSomePermission } = usePermission(); // v-if 条件渲染 const showAddBtn = computed(() => hasPermission(['sys:position:add'])); // 检查是否有任一权限 const canOperate = hasSomePermission(['sys:position:update', 'sys:position:delete']); ``` ## useLockFn 防重复提交 Hook,函数执行期间自动锁定,位于 `src/utils/useLockFn.ts`: ```typescript // src/utils/useLockFn.ts import { ref } from 'vue'; export function useLockFn(fn: (...args: any[]) => Promise) { const isLock = ref(false); const lockFn = async (...args: any[]) => { if (isLock.value) return; // 锁定期间忽略调用 isLock.value = true; try { await fn(...args); } finally { isLock.value = false; // 无论成功失败都解锁 } }; return { isLock, lockFn }; } ``` 实际用法(edit.vue 中的提交按钮): ```typescript // src/views/system/position/edit.vue import { useLockFn } from '@/utils/useLockFn'; const handleSubmit = async () => { await formRef.value?.validate(); props.positionId ? await positionUpdate(formData) : await positionAdd(formData); message('操作成功'); emit('update:visible', false); emit('success'); }; // isLock 绑定按钮的 loading 状态,lockFn 作为点击处理函数 const { isLock: subLoading, lockFn: submit } = useLockFn(handleSubmit); ``` ```vue 确定 ``` ## useAsync 异步操作 loading 管理,位于 `src/hooks/use-async.ts`: ```typescript // src/hooks/use-async.ts import { isReactive, isRef } from 'vue'; function setLoading(loading, val) { if (loading != undefined && isRef(loading)) { loading.value = val; } else if (loading != undefined && isReactive(loading)) { loading.loading = val; } } export const useAsync = async (func: Promise, loading: any): Promise => { setLoading(loading, true); return await func.finally(() => setLoading(loading, false)); }; ``` 使用示例: ```typescript const loading = ref(false); const fetchData = async () => { await useAsync(getPositionList(params), loading); }; ``` ## useTime 实时时钟 Hook,每秒更新当前时间,位于 `src/hooks/useTime.ts`: ```typescript // src/hooks/useTime.ts import { ref, onMounted, onUnmounted } from 'vue'; export function useTime() { let timer; const year = ref(0); const month = ref(0); const week = ref(''); const day = ref(0); const hour = ref(0); const minute = ref(0); const second = ref(0); const updateTime = () => { const date = new Date(); year.value = date.getFullYear(); month.value = date.getMonth() + 1; week.value = '日一二三四五六'.charAt(date.getDay()); day.value = date.getDate(); hour.value = (date.getHours() + '')?.padStart(2, '0'); minute.value = (date.getMinutes() + '')?.padStart(2, '0'); second.value = date.getSeconds(); }; updateTime(); onMounted(() => { timer = setInterval(() => updateTime(), 1000); }); onUnmounted(() => { clearInterval(timer); }); return { month, day, hour, minute, second, week }; } ``` ## useProjectSetting 项目配置 Hook,读取项目全局设置: ```typescript // src/hooks/setting/useProjectSetting.ts export function useProjectSetting() { const projectStore = useProjectSettingStore(); return { getPermissionMode: computed(() => projectStore.permissionMode), // 'BACK' | 'FRONT' getIsWaterMark: computed(() => projectStore.isWaterMark), // ... 其他配置 }; } ``` ## useDesignSetting 设计配置 Hook,管理主题色和深色模式: ```typescript // src/hooks/setting/useDesignSetting.ts export function useDesignSetting() { return { getAppTheme: computed(() => designStore.appTheme), getDarkTheme: computed(() => designStore.darkTheme), // ... 其他设计配置 }; } ``` ## useWindowSizeFn 窗口大小变化回调 Hook,用于表格高度自适应: ```typescript // src/hooks/event/useWindowSizeFn.ts export function useWindowSizeFn(callback: Fn, wait?: number) { // 监听 window.resize 事件,防抖执行回调 } ``` BasicTable 组件中使用此 Hook 自动计算表格高度: ```typescript // src/components/Table/src/Table.vue useWindowSizeFn(computeTableHeight, 280); ``` :::tip 温馨提示 Hooks 是 Vue3 组合式 API 的核心模式,将可复用逻辑抽离为函数。`usePermission` 供 `v-perm` 指令和 `v-if` 条件使用,`useLockFn` 防止按钮重复点击,`useAsync` 统一管理 loading 状态。 ::: ## 总结 项目提供了 usePermission(权限检查)、useLockFn(防重复提交)、useAsync(异步 loading)、useTime(实时时钟)、useProjectSetting(项目配置)、useDesignSetting(设计配置)、useWindowSizeFn(窗口变化)等组合式函数,将常用逻辑封装为可复用的函数,避免 Mixin 的命名冲突问题。