21. 工具函数速查
前端手册工具函数大约 7 分钟约 1983 字
一句话定位
src/utils/ 下 34 个文件,本篇按文件列出常用导出与真实用法;写业务前先来这里找一遍,别重复造轮子。
代码位置:Web/src/utils/
速查目录
| 文件 | 关键词 |
|---|---|
axios-utils.ts | getAPI / feature / getToken / clearTokens / decryptJWT / sleep |
storage.ts | Local / Session |
format.ts | formatDateTime / formatBytes / formatFileSize / getCurrentMonthRange |
formatTime.ts | formatDate / formatPast / formatAxis / getWeek |
formRule.ts | useFormRulePresets() |
authFunction.ts | auth / auths / authAll / hAuth* |
useInfo.ts | isAdmin / userId / tenantId / hasPrivilege … |
download.ts | downloadByUrl / downloadStreamFile / downloadByOnlineUrl |
exportExcel.ts | exportExcel |
crypto.ts | signatureByKSort / signatureByMD5 |
mitt.ts | mittBus |
watermark.ts | watermark.set/del |
other.ts | deepClone / isMobile / globalComponentSize / handleOpenLink / useTitle |
loading.ts | NextLoading.start/done |
base64Conver.ts | dataURLtoBlob / fileToBase64 / base64ToFile / blobToFile |
pinyinMatch.ts | matchPinyin |
gpsConvertor.ts | wgs84ToGcj02 / gcj02ToBd09 / wgs84ToBd09 |
arrayOperation.ts | judgementSameArr / isObjectValueEqual / removeDuplicate / clone |
toolsValidate.ts | verify* 系列正则校验 |
commonFunction.ts | 日期/百分比/千分符格式化、copyText |
request.ts | service / request2(非契约接口用) |
| 其它 | theme.ts、sysInfo.ts、idleTimeout.ts、json-utils.ts、setIconfont.ts、getStyleSheets.ts、themeTransition.ts、flowLoading.ts、document*.ts、build.ts、calendar.js |
1. axios-utils.ts
import { getAPI, feature, getToken, getHeader, clearTokens, decryptJWT, getJWTDate, sleep, serveConfig } from '/@/utils/axios-utils';
// 取 API 实例(唯一调用方式)
const res = await getAPI(SysConfigApi).apiSysConfigPagePost(params);
// 把异常变返回值:[err, data]
const [err, res2] = await feature(getAPI(SysConfigApi).apiSysConfigDeletePost({ id }));
if (err) return;
// token
const token = getToken(); // Local 的 access-token
getHeader(); // { authorization: 'Bearer xxx' }
clearTokens(); // 清 Local token + Session.clear()
// JWT
const payload = decryptJWT(token); // 解 payload(含 exp)
const expDate = getJWTDate(payload.exp);
// 延迟
await sleep(300);拦截器、
customCatch、feature()详见「接口层」篇。
2. storage.ts
键名自动加前缀 ${__NEXT_NAME__}:(即 admin.net.pro:)。
import { Local, Session } from '/@/utils/storage';
Local.set('themeConfig', obj); Local.get('themeConfig'); Local.remove('tid'); Local.clear();
Session.set('userInfo', obj); Session.get('userInfo'); Session.remove('k'); Session.clear();| 差异 | Local(localStorage) | Session(sessionStorage) |
|---|---|---|
token 键 | 普通 localStorage | 走 Cookie(js-cookie),跨标签页共享 |
clear() | 保留 tid(租户 ID) | 清 Cookie 的 token/userInfo/constList + sessionStorage |
3. format.ts / formatTime.ts
import { formatDateTime, formatBytes, formatFileSize, getFileTagType, formatDateWithDayEdge, getCurrentMonthRange } from '/@/utils/format';
import { formatDate, formatPast, formatAxis, getWeek } from '/@/utils/formatTime';
formatDateTime(row.createTime); // '2026-10-18 12:00:00'(空值返回 '-')
formatDateTime(row.createTime, 'YYYY-mm-dd'); // '2026-10-18'
formatBytes(1536); // '1.5 KB'
formatBytes(1536, { trimZeros: false }); // '1.50 KB'
formatFileSize(2048); // '2.00 MB'(入参单位是 KB)
getFileTagType(2048); // 'warning'(>=1GB danger / >=1MB warning / 其余 success)
const { startTime, endTime } = getCurrentMonthRange(); // 当月起止,常用于查询条件
formatDate(new Date(), 'YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ');
formatPast('2026-10-01'); // '17天前' / 超过 3 天返回日期
formatAxis(new Date()); // '上午好'
getWeek(new Date()); // 第几周占位符:Y+(年) m+(月) d+(日) H+(时) M+(分) S+(秒) q+(季) W+(星期) Z+(周);长度 >1 自动补零。
4. formRule.ts
import { useFormRulePresets } from '/@/utils/formRule';
const { required, phone, idCard, email, digits, intRange, confirmSame, maxLen, numberPrecision } = useFormRulePresets();
// 模板
// :rules="[required('姓名'), phone()]"
// :rules="[idCard(() => state.ruleForm.cardType === 0)]"完整清单见「表单、校验与弹窗」篇。
5. authFunction.ts / useInfo.ts
import { auth, auths, authAll } from '/@/utils/authFunction';
auth('sysConfig/update'); // 单个权限
auths(['sysConfig/add', 'sysConfig/import']); // 满足其一
authAll(['a', 'b']); // 全部满足
import { isAdmin, isSupperAdmin, isTenantAdmin, isNormalUser, isMember,
userId, userName, userAccount, userEmail, userPhone, tenantId,
orgId, orgName, posId, posName, jobNum, points, roles,
createTime, hasRoleCode, hasPrivilege } from '/@/utils/useInfo';
isAdmin(); // accountType 999(超管)或 888(租户管理员)
hasRoleCode('audit'); // 是否有某角色编码
hasPrivilege('sysConfig/add');⚠️ 这两个文件的函数都每次现取 store,不要在模块顶层缓存返回值当常量用。
6. download.ts
import { downloadByUrl, downloadStreamFile, downloadByOnlineUrl, downloadByBase64, downloadByData, getFileName, openWindow } from '/@/utils/download';
downloadByUrl({ url: row.url!, fileName: row.fileName });
downloadStreamFile(res, fileName); // res 为 blob 响应,文件名可从 content-disposition 解析
downloadByOnlineUrl('https://x/y.png', '图片.png'); // 在线图片 → base64 → 下载
openWindow('https://example.com');详见「上传、下载与文件预览」篇。
7. exportExcel.ts
import { exportExcel } from '/@/utils/exportExcel';
// jsonarr: 数据数组;name: 文件名;header: 表头映射;sheetName: 工作表名
exportExcel(
[{ name: '张三', age: 18 }],
'用户列表',
[{ name: '姓名' }, { age: '年龄' }],
'Sheet1'
);底层 xlsx-js-style,自带边框样式。列表页更推荐直接用 vxe-grid 工具栏的导出。
8. crypto.ts
import { signatureByKSort, signatureByMD5 } from '/@/utils/crypto';
// 按 key 字典序排序后拼接 + MD5,返回含签名的完整参数对象
const params = signatureByKSort(appId, appKey, 'command', { a: 1 });
// → { app_id, app_key, command, data, timestamp, sign }
const md5 = signatureByMD5({ a: 1 });9. mitt.ts
import mittBus from '/@/utils/mitt';
mittBus.on('openSettingsDrawer', handler);
mittBus.emit('openSettingsDrawer');
mittBus.off('openSettingsDrawer', handler); // 同一函数引用事件清单见「状态管理」篇。
10. watermark.ts
import Watermark from '/@/utils/watermark';
Watermark.set('张三 138****0000'); // 添加全屏水印
Watermark.del(); // 移除登录时由 userInfo.setUserInfos() 根据 themeConfig.isWatermark 自动调用,业务一般不用管。
11. other.ts(默认导出 + 命名导出)
import other from '/@/utils/other';
import { deepClone, isMobile, globalComponentSize, handleEmpty, handleOpenLink, useTitle, lazyImg, elSvg, translateDynamic } from '/@/utils/other';
other.deepClone(obj);
other.isMobile(); // 是否移动端
other.globalComponentSize(); // 'small' | 'default' | 'large'(供 :size 使用)
other.handleEmpty(list); // 清理数组里所有属性都为空的对象
other.handleOpenLink(routeItem); // 外链跳转(isLink / isIframe 分支)
other.useTitle(); // 设置浏览器标题(路由变化时自动调用)
other.elSvg(app); // main.ts 中注册 ele-* 图标
other.setTagsViewNameI18n(route);12. loading.ts
import { NextLoading } from '/@/utils/loading';
NextLoading.start(); // 全屏 loading(样式在 theme/loading.scss)
NextLoading.done(); // 移除
NextLoading.done(500); // 延迟 500ms 移除路由初始化时 backEnd.ts 会自动 NextLoading.start(),业务页一般不需要自己调。
13. base64Conver.ts
import { dataURLtoBlob, urlToBase64, fileToBase64, base64ToFile, blobToFile } from '/@/utils/base64Conver';
const blob = dataURLtoBlob(base64); // base64 → Blob
const b64 = await urlToBase64('https://x/y.png'); // 图片 URL → base64(canvas)
const b64 = await fileToBase64(file); // File → base64
const file = base64ToFile(b64, 'a.png'); // base64 → File
const file = blobToFile(blob, 'a.xlsx'); // Blob → File14. pinyinMatch.ts
import { matchPinyin } from '/@/utils/pinyinMatch';
matchPinyin('系统监控', 'xtjk'); // true(首字母)
matchPinyin('系统监控', 'xitong'); // true(全拼)
matchPinyin('系统监控', 'xtjiankong'); // true(混合)用于菜单搜索;非中文标题直接返回 false。
15. gpsConvertor.ts
import { wgs84ToGcj02, gcj02ToBd09, wgs84ToBd09 } from '/@/utils/gpsConvertor';
const { lng, lat } = wgs84ToGcj02(116.4, 39.9); // GPS → 高德/腾讯
const baidu = gcj02ToBd09(lng, lat); // 高德 → 百度
const bd = wgs84ToBd09(116.4, 39.9); // GPS → 百度16. arrayOperation.ts
import { judgementSameArr, isObjectValueEqual, removeDuplicate, clone } from '/@/utils/arrayOperation';
judgementSameArr(['a','b'], ['a','b','c']); // true:newArr 全部存在于 oldArr(v-auth-all 底层)
isObjectValueEqual({ a: 1 }, { a: 1 }); // 深比较
removeDuplicate(list, 'id'); // 按属性去重
const copy = clone(obj);17. toolsValidate.ts(正则校验,返回 boolean)
import { verifyPhone, verifyTelPhone, verifyEmail, verifyIdCard, verifyAccount, verifyPassword,
verifyPasswordPowerful, verifyPasswordStrength, verifyIPAddress, verifyUrl, verifyCarNum,
verifyPostalCode, verifyFullName, verifyCnAndSpace, verifyEnAndSpace, verifyAndSpace,
verifyNumberIntegerAndFloat, verifiyNumberInteger, verifyNumberComma, verifyNumberPercentage,
verifyTextColor, verifyNumberCnUppercase, judgementIdCard } from '/@/utils/toolsValidate';
if (!verifyPhone(state.form.phone)) return ElMessage.error('手机号格式不正确');
verifyIdCard('110101199003072316'); // true
judgementIdCard('110101199003072316'); // 返回性别/生日等解析结果18. commonFunction.ts(默认导出一个工厂函数)
import commonFunction from '/@/utils/commonFunction';
const commonFun = commonFunction();
commonFun.dateFormatYMD(row, column, cellValue); // 'YYYY-mm-dd'
commonFun.dateFormatYMDHMS(row, column, cellValue); // 'YYYY-mm-dd HH:MM:SS'
commonFun.percentFormat(row, column, cellValue); // '12%'
commonFun.scaleFormat('1.23456', 2); // '1.23'
commonFun.scale2Format('1.23456'); // '1.23'
commonFun.groupSeparator(1234567.891); // '1,234,567.89'
await commonFun.copyText('要复制的内容'); // 复制并弹「复制成功」19. request.ts(非契约接口)
import { service, request2, getToken, clearAccessTokens } from '/@/utils/request';
// 独立 axios 实例,baseURL = window.__env__.VITE_API_URL,超时 50s,自带拦截器
const res = await request2({ url: '/api/xxx/import', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } });components/importButton/index.vue 用的就是 request2。业务接口请优先走 getAPI,只在契约没覆盖的场景(如第三方回调、特殊 FormData)用它。
20. 其它
| 文件 | 用法 |
|---|---|
theme.ts | const { useChangeColor } = ... → 主色切换(getLightColor / getDarkColor) |
sysInfo.ts | await loadSysInfo(tenantId) 拉系统信息;updateFavicon(url) 换站点图标 |
idleTimeout.ts | initIdleTimeout({ timeout }) / updateIdleTimeout(ms) / destroyIdleTimeout(),App.vue 已初始化 |
json-utils.ts | StringToObj(value) 安全地把字符串转对象(失败返回原值) |
setIconfont.ts | setIntroduction.cssCdn() / jsCdn(),App.vue 的 onBeforeMount 调用 |
getStyleSheets.ts | 初始化图标字体(iconSelector 用) |
themeTransition.ts | 主题切换过渡动画(View Transitions) |
flowLoading.ts | 流程/长任务的 loading 态 |
documentFileUrl.ts / documentPreview.ts / documentShareUrl.ts | 文档中心专用的地址/预览/分享辅助 |
build.ts | buildConfig.cdn() / buildConfig.external,vite 开启 CDN 时使用 |
calendar.js | 日历(农历/节假日)计算 |
⚠️ 注意事项 / 常见坑
Local/Session的键自动加admin.net.pro:前缀,直接去 DevTools 里搜 key 要带前缀。Session.get('token')读的是 Cookie,不是 sessionStorage;Session.clear()会连 Cookie 一起清。formatFileSize入参单位是 KB(后端统一存 KB),formatBytes入参是字节,别混用。feature()的错误已由拦截器弹过提示,业务里不要重复ElMessage.error。useInfo.ts的函数是「取值函数」不是常量:userId()带括号,别写成userId。downloadStreamFile的res必须是 blob 响应,接口调用要传{ responseType: 'blob' }。matchPinyin对纯英文标题返回false,搜索时要同时做普通字符串匹配。NextLoading与ElLoading不要叠加,路由初始化已自动 start。- 新工具函数优先扩展已有文件,不要每个页面各写一份格式化逻辑。
小结
- 接口:
getAPI+feature;鉴权:auth()/useInfo;存储:Local/Session。 - 格式化:
format.ts+formatTime.ts+commonFunction.ts三件套。 - 校验:表单用
formRule.ts,零散值用toolsValidate.ts。 - 文件:
download.ts+base64Conver.ts;转换:crypto.ts+gpsConvertor.ts+pinyinMatch.ts。