Files
djangoadmin-wiki/DjangoAdmin_Flask_NaiveVue/zh/volume4/frontend/utils.md
T
2026-09-13 23:01:19 +08:00

277 lines
7.9 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 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);
}
```
### 消息提示与确认弹窗
项目使用 NaiveUI 的 `useMessage` 和 `useDialog` 提供消息提示和确认弹窗:
```typescript
import { useMessage, useDialog } from 'naive-ui';
// 在 setup 中初始化
const message = useMessage();
const dialog = useDialog();
// 消息提示
message.success('操作成功');
message.error('操作失败');
message.warning('请注意');
// 确认弹窗
dialog.warning({
title: '提示',
content: '确定要删除?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
// 执行删除操作
},
});
```
:::warning 注意
`useMessage` 和 `useDialog` 必须在 Vue 组件的 `setup` 中调用。如果需要在非组件环境(如 Axios 拦截器)中使用,可通过 `window.__message` 和 `window.__dialog` 全局引用访问。
:::
### 树结构操作
`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);
}
```
## renderIcon 工具
图标渲染工具,用于在 TableAction 等组件中渲染 NaiveUI 图标:
```typescript
// src/utils/index.ts
import { h } from 'vue';
import { NIcon } from 'naive-ui';
export function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) });
}
```
使用示例:
```typescript
import { renderIcon } from '@/utils';
import { FormOutlined, DeleteOutlined } from '@vicons/antd';
const actionColumn = {
actions: [
{ label: '编辑', icon: renderIcon(FormOutlined), type: 'info' },
{ label: '删除', icon: renderIcon(DeleteOutlined), type: 'error' },
],
};
```
## 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`),存储时包含过期时间,读取时自动检查是否过期。项目使用 NaiveUI 的 `useMessage` 和 `useDialog` 提供消息提示和确认弹窗,`renderIcon` 工具函数用于在操作列等场景渲染 @vicons 图标。
:::
## 总结
工具函数库提供了 Storage(本地存储,支持过期)、auth(认证管理、树结构操作、文件下载)、dateUtil(日期格式化)、renderIcon(图标渲染)、httpEnum/pageEnum(枚举定义)等通用能力。消息提示使用 NaiveUI 的 `useMessage`,确认弹窗使用 `useDialog`,图标使用 `@vicons/antd` 库。