Files
djangoadmin-wiki/DjangoAdmin_FastAPI_AntdVue/zh/volume4/frontend/utils.md
T
2026-09-12 23:12:15 +08:00

242 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工具函数
:::tip 说明
前端工具函数位于 `src/utils/` 目录,提供认证管理、本地存储、日期格式化、树结构操作、文件下载等通用能力。
:::
## auth.ts
认证与通用工具函数,位于 `src/utils/auth.ts`:
```typescript
// 外部链接判断
export function isExternal(path: string) {
return /^(https?:|mailto:|tel:)/.test(path);
}
// 路径规范化
export function getNormalPath(path: string) {
if (path.length === 0 || !path || path == 'undefined') return path;
const newPath = path.replace('//', '/');
return newPath[newPath.length - 1] === '/' ? newPath.slice(0, -1) : newPath;
}
// 确认弹窗封装
export function confirm(msg: string, type: 'warning') {
return ElMessageBox.confirm(msg, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: type,
});
}
// 消息提示封装
export function message(msg: string, type = 'success') {
ElMessage[type](msg);
}
// 文件流下载
export function streamFileDownload(file: any, fileName = '文件名称.zip') {
const blob = new Blob([file], { type: 'application/octet-stream;charset=UTF-8' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
```
### 树结构操作
`auth.ts` 中还包含树结构转换工具:
```typescript
// 数组转树
export const arrayToTree = (
data: any[],
props = { id: 'id', parentId: 'pid', children: 'children' },
) => {
data = cloneDeep(data);
const { id, parentId, children } = props;
const result: any[] = [];
const map = new Map();
data.forEach((item) => {
map.set(item[id], item);
const parent = map.get(item[parentId]);
if (parent) {
parent[children] = parent[children] ?? [];
parent[children].push(item);
} else {
result.push(item);
}
});
return result;
};
// 树转数组(广度优先)
export const treeToArray = (data: any[], props = { children: 'children' }) => {
data = cloneDeep(data);
const newData = [];
const queue: any[] = [];
data.forEach((child) => queue.push(child));
while (queue.length) {
const item = queue.shift();
if (item[children]) {
item[children].forEach((child) => queue.push(child));
delete item[children];
}
newData.push(item);
}
return newData;
};
// 递归查找路径
export const findTreeByPath = (tree, path, result = []) => {
for (const node of tree) {
if (node.path === path) result.push(node);
if (node.children?.length) findTreeByPath(node.children, path, result);
}
return result;
};
```
## Storage.ts
本地存储封装,支持过期时间和 cookie,位于 `src/utils/Storage.ts`:
```typescript
const DEFAULT_CACHE_TIME = 7 * 24 * 60 * 60 * 1000; // 7 天
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
const Storage = class {
private storage = storage;
private prefixKey?: string = prefixKey;
private getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase(); // 自动大写
}
// 设置缓存(支持过期时间)
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
const stringData = JSON.stringify({
value,
expire: expire !== null ? Date.now() + expire : null,
});
this.storage.setItem(this.getKey(key), stringData);
}
// 读取缓存(过期自动删除)
get(key: string, def: any = null) {
const item = this.storage.getItem(this.getKey(key));
if (item) {
try {
const data = JSON.parse(item);
const { value, expire } = data;
if (expire === null || expire >= Date.now()) return value;
this.remove(key); // 过期删除
} catch (e) { return def; }
}
return def;
}
remove(key: string) { this.storage.removeItem(this.getKey(key)); }
clear(): void { this.storage.clear(); }
};
return new Storage();
};
export const storage = createStorage();
```
Store mutation-types 定义了存储键名:
```typescript
// src/store/mutation-types.ts
export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户 token
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
export const IS_LOCKSCREEN = 'IS-LOCKSCREEN'; // 是否锁屏
export const TABS_ROUTES = 'TABS-ROUTES'; // 标签页
export const FIRST_ROUTE = 'FIRST-ROUTE'; // 菜单第一个路由
```
## dateUtil.ts
日期格式化工具,基于 dayjs,位于 `src/utils/dateUtil.ts`:
```typescript
import dayjs from 'dayjs';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
const DATE_FORMAT = 'YYYY-MM-DD ';
export function formatToDateTime(date: Date, formatStr = DATE_TIME_FORMAT): string {
return dayjs(date).format(formatStr);
}
export function formatToDate(date: Date, formatStr = DATE_FORMAT): string {
return dayjs(date).format(formatStr);
}
```
`dateUtil` 也被 `vite.config.ts` 使用,记录构建时间戳:
```typescript
// vite.config.ts
import { formatToDateTime } from './src/utils/dateUtil';
const __APP_INFO__ = {
pkg: { dependencies, devDependencies, name, version },
lastBuildTime: formatToDateTime(new Date()),
};
```
## httpEnum.ts
HTTP 相关枚举定义,位于 `src/enums/httpEnum.ts`:
```typescript
export enum ResultEnum {
SUCCESS = 0, // 业务成功
ERROR = 1, // 业务失败
TIMEOUT = 401, // 登录超时
}
export enum RequestEnum {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
export enum ContentTypeEnum {
JSON = 'application/json;charset=UTF-8',
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
FORM_DATA = 'multipart/form-data;charset=UTF-8',
}
```
## pageEnum.ts
页面路径枚举,位于 `src/enums/pageEnum.ts`:
```typescript
export enum PageEnum {
BASE_LOGIN = '/login',
BASE_LOGIN_NAME = 'Login',
REDIRECT = '/redirect',
BASE_HOME = '/dashboard',
BASE_HOME_REDIRECT = '/dashboard/console',
ERROR_PAGE_NAME = 'ErrorPage',
}
```
:::tip 温馨提示
`Storage` 工具类自动将键名转为大写(如 `access-token` → `ACCESS-TOKEN`),存储时包含过期时间,读取时自动检查是否过期。`auth.ts` 中的 `confirm` 和 `message` 函数是对 ElementPlus 的简封装,在业务组件中直接调用即可。
:::
## 总结
工具函数库提供了 Storage(本地存储,支持过期)、auth(认证管理、树结构操作、文件下载)、dateUtil(日期格式化)、httpEnum/pageEnum(枚举定义)等通用能力。`confirm` 和 `message` 是 ElementPlus 的简封装,`arrayToTree`/`treeToArray` 处理菜单数据转换。