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

7.9 KiB
Raw Blame History

工具函数

:::tip 说明 前端工具函数位于 src/utils/ 目录,提供认证管理、本地存储、日期格式化、树结构操作、文件下载等通用能力。 :::

auth.ts

认证与通用工具函数,位于 src/utils/auth.ts:

// 外部链接判断
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 提供消息提示和确认弹窗:

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 中还包含树结构转换工具:

// 数组转树
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:

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 定义了存储键名:

// 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:

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 图标:

// src/utils/index.ts
import { h } from 'vue';
import { NIcon } from 'naive-ui';

export function renderIcon(icon) {
    return () => h(NIcon, null, { default: () => h(icon) });
}

使用示例:

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:

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:

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 库。