Become a sponsor

说明
项目封装了一系列组合式函数(Hooks),位于 src/hooks/ 和 src/utils/ 目录,提供权限检查、异步 loading、防重复提交、实时时钟、项目配置等可复用逻辑。
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 管理权限检查 Hook,从 user store 获取权限列表进行判断:
// 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 };
}使用示例:
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']);防重复提交 Hook,函数执行期间自动锁定,位于 src/utils/useLockFn.ts:
// src/utils/useLockFn.ts
import { ref } from 'vue';
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
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 中的提交按钮):
// 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);<el-button :loading="subLoading" type="primary" @click="submit">确定</el-button>异步操作 loading 管理,位于 src/hooks/use-async.ts:
// 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<any>, loading: any): Promise<any> => {
setLoading(loading, true);
return await func.finally(() => setLoading(loading, false));
};使用示例:
const loading = ref(false);
const fetchData = async () => {
await useAsync(getPositionList(params), loading);
};实时时钟 Hook,每秒更新当前时间,位于 src/hooks/useTime.ts:
// 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<number | string>(0);
const minute = ref<number | string>(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 };
}项目配置 Hook,读取项目全局设置:
// src/hooks/setting/useProjectSetting.ts
export function useProjectSetting() {
const projectStore = useProjectSettingStore();
return {
getPermissionMode: computed(() => projectStore.permissionMode), // 'BACK' | 'FRONT'
getIsWaterMark: computed(() => projectStore.isWaterMark),
// ... 其他配置
};
}设计配置 Hook,管理主题色和深色模式:
// src/hooks/setting/useDesignSetting.ts
export function useDesignSetting() {
return {
getAppTheme: computed(() => designStore.appTheme),
getDarkTheme: computed(() => designStore.darkTheme),
// ... 其他设计配置
};
}窗口大小变化回调 Hook,用于表格高度自适应:
// src/hooks/event/useWindowSizeFn.ts
export function useWindowSizeFn(callback: Fn, wait?: number) {
// 监听 window.resize 事件,防抖执行回调
}BasicTable 组件中使用此 Hook 自动计算表格高度:
// src/components/Table/src/Table.vue
useWindowSizeFn(computeTableHeight, 280);温馨提示
Hooks 是 Vue3 组合式 API 的核心模式,将可复用逻辑抽离为函数。usePermission 供 v-perm 指令和 v-if 条件使用,useLockFn 防止按钮重复点击,useAsync 统一管理 loading 状态。
项目提供了 usePermission(权限检查)、useLockFn(防重复提交)、useAsync(异步 loading)、useTime(实时时钟)、useProjectSetting(项目配置)、useDesignSetting(设计配置)、useWindowSizeFn(窗口变化)等组合式函数,将常用逻辑封装为可复用的函数,避免 Mixin 的命名冲突问题。