npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@geyj/web-utils

v1.0.8

Published

Web工具库

Downloads

272

Readme

Web工具库

使用

// 安装
npm install @geyj/web-utils
// 引入
import {
    getMonthDays, getWeekDayOfMonthFirstDay, getCalendar, getComplexCalendar,
    gzip, ungzip, compressImage,
    sleep, debounce, throttle,
    getFileType, networkFileToBlob, blobToBase64, downloadBlob, downloadNetworkFile, previewOffice,
    formatThousandth, formatFileSize, formatDuration,
    print,
    getUniqueId,
    listToTree, treeToList, groupByKey,
    isPhone, isEmail, isURL, isNumericString, isIDNumber, isDate,
    observerTargetElement
} from '@geyj/web-utils'

说明

1.日历

test('getMonthDays:2023年12月有31天', () => {
    expect(getMonthDays(2023, 12)).toBe(31);
});

test('getWeekDayOfMonthFirstDay:2023年12月的第一天是星期5', () => {
    expect(getWeekDayOfMonthFirstDay(2023, 12)).toBe(5);
});
const calendar = JSON.stringify([
    [27, 28, 29, 30, 1, 2, 3],
    [4, 5, 6, 7, 8, 9, 10],
    [11, 12, 13, 14, 15, 16, 17],
    [18, 19, 20, 21, 22, 23, 24],
    [25, 26, 27, 28, 29, 30, 31],
    [1, 2, 3, 4, 5, 6, 7]
])
test(`getCalendar:2023年12月日历简易数据是${calendar}`, () => {
    expect(JSON.stringify(getCalendar(2023, 12))).toBe(calendar)
});

2.执行控制

//防抖
debounce(searchHandler, 1000)
//节流
throttle(clickHandler, 3000)
test('sleep:时间间隔应该非常接近于1000ms', async () => {
    const time = 1000;
    const before = new Date().getTime();

    await sleep(time);

    const after = new Date().getTime();
    const difference = after - before;

    expect(difference).toBeGreaterThanOrEqual(time);
});

3.格式化

test('formatThousandth:千分位格式化', () => {
    expect(formatThousandth(12345678.9)).toBe('12,345,678.9');
});

test('formatFileSize:文件尺寸格式化', () => {
    expect(formatFileSize(1023)).toBe('1023B');
    expect(formatFileSize(1024)).toBe('1.00KB');
    expect(formatFileSize(1024 ** 2)).toBe('1.00MB');
    expect(formatFileSize(1024 ** 3)).toBe('1.00GB');
    expect(formatFileSize(1024 ** 4)).toBe('1.00TB');
});

test('formatDuration:时长格式化', () => {
    expect(formatDuration(59)).toBe('59秒');
    expect(formatDuration(60)).toBe('1.00分');
    expect(formatDuration(60 ** 2 - 60)).toBe('59.00分');
    expect(formatDuration(60 ** 2)).toBe('1.00时');
    expect(formatDuration(60 ** 2 * (24 - 1))).toBe('23.00时');
    expect(formatDuration(60 ** 2 * 24)).toBe('1.00天');
});

4.id

test('getUniqueId:获取8位唯一id值', () => {
    expect(getUniqueId().length).toBe(8);
});
test('getUniqueId:1000个id都不一致', () => {
    const idCount = 1000
    const ids = new Set(Array(idCount).fill(null).map(() => getUniqueId()));
    expect(ids.size).toBe(idCount);
});

5.数据结构

describe('listToTree:', () => {
    test('converts a flat list to a tree structure', () => {
        const list = [
            { id: 1, parentId: null, name: 'Root' },
            { id: 2, parentId: 1, name: 'Child 1' },
            { id: 3, parentId: 1, name: 'Child 2' },
            { id: 4, parentId: 2, name: 'Grandchild 1' }
        ];

        const tree = [
            {
                id: 1, parentId: null, name: 'Root', children: [
                    {
                        id: 2, parentId: 1, name: 'Child 1', children: [
                            { id: 4, parentId: 2, name: 'Grandchild 1', children: [] }
                        ]
                    },
                    { id: 3, parentId: 1, name: 'Child 2', children: [] }
                ]
            }
        ];

        expect(listToTree(list, 'parentId', 'id')).toEqual(tree);
    });

    test('handles empty list', () => {
        expect(listToTree([], 'parentId', 'id')).toEqual([]);
    });

    test('handles list with no root elements', () => {
        const list = [
            { id: 2, parentId: 1, name: 'Child 1' },
            { id: 3, parentId: 2, name: 'Child 2' }
        ];

        expect(listToTree(list, 'parentId', 'id')).toEqual([]);
    });
});

describe('treeToList:', () => {
    const treeData = [
        {
            id: 1, children: [
                {
                    id: 2, children: [
                        { id: 5, children: [] },
                        { id: 6, children: [] }
                    ]
                },
                {
                    id: 3, children: [
                        { id: 7, children: [] }
                    ]
                },
                { id: 4, children: [] }
            ]
        }
    ];

    it('converts a tree to a flat list using DFS', () => {
        const expectedListDFS = [
            { id: 1, children: [{ id: 2, children: [{ id: 5, children: [] }, { id: 6, children: [] }] }, { id: 3, children: [{ id: 7, children: [] }] }, { id: 4, children: [] }] },
            { id: 2, children: [{ id: 5, children: [] }, { id: 6, children: [] }] },
            { id: 5, children: [] },
            { id: 6, children: [] },
            { id: 3, children: [{ id: 7, children: [] }] },
            { id: 7, children: [] },
            { id: 4, children: [] }
        ];
        expect(treeToList(treeData, 'dfs')).toEqual(expectedListDFS);
    });

    it('converts a tree to a flat list using BFS', () => {
        const expectedListBFS = [
            { id: 1, children: [{ id: 2, children: [{ id: 5, children: [] }, { id: 6, children: [] }] }, { id: 3, children: [{ id: 7, children: [] }] }, { id: 4, children: [] }] },
            { id: 2, children: [{ id: 5, children: [] }, { id: 6, children: [] }] },
            { id: 3, children: [{ id: 7, children: [] }] },
            { id: 4, children: [] },
            { id: 5, children: [] },
            { id: 6, children: [] },
            { id: 7, children: [] }
        ];
        expect(treeToList(treeData, 'bfs')).toEqual(expectedListBFS);
    });

    it('handles empty tree', () => {
        expect(treeToList([], 'dfs')).toEqual([]);
        expect(treeToList([], 'bfs')).toEqual([]);
    });

    it('handles single node tree', () => {
        const singleNodeTree = [{ id: 1, children: [] }];
        const expectedList = [{ id: 1, children: [] }];
        expect(treeToList(singleNodeTree, 'dfs')).toEqual(expectedList);
        expect(treeToList(singleNodeTree, 'bfs')).toEqual(expectedList);
    });
});

describe('groupByKey:', () => {
    const listData = [
        { category: 'fruit', name: 'apple' },
        { category: 'vegetable', name: 'carrot' },
        { category: 'fruit', name: 'banana' },
        { category: 'vegetable', name: 'spinach' }
    ];

    it('groups items by a specified key', () => {
        const expectedResult = [
            {
                key: 'fruit',
                list: [
                    { category: 'fruit', name: 'apple' },
                    { category: 'fruit', name: 'banana' }
                ]
            },
            {
                key: 'vegetable',
                list: [
                    { category: 'vegetable', name: 'carrot' },
                    { category: 'vegetable', name: 'spinach' }
                ]
            }
        ];
        expect(groupByKey(listData, 'category')).toEqual(expectedResult);
    });

    it('handles empty list', () => {
        expect(groupByKey([], 'category')).toEqual([]);
    });
    it('handles list with items missing the group key', () => {
        const mixedListData = [
            { category: 'fruit', name: 'apple' },
            { name: 'carrot' },
            { category: 'vegetable', name: 'spinach' }
        ];
        const expectedResult = [
            {
                key: 'fruit',
                list: [{ category: 'fruit', name: 'apple' }]
            },
            {
                key: undefined,
                list: [{ name: 'carrot' }]
            },
            {
                key: 'vegetable',
                list: [{ category: 'vegetable', name: 'spinach' }]
            }
        ];
        expect(groupByKey(mixedListData, 'category')).toEqual(expectedResult);
    });
});

6.验证

test('isPhone:验证是否为手机号', () => {
    expect(isPhone('17888888888')).toBe(true);

    expect(isPhone('1788888888')).toBe(false);
    expect(isPhone('07888888888')).toBe(false);
    expect(isPhone('27888888888')).toBe(false);
    expect(isPhone('37888888888')).toBe(false);
    expect(isPhone('0574-88832337')).toBe(false);
});
test('isEmail:验证是否为邮箱', () => {
    expect(isEmail('[email protected]')).toBe(true);

    expect(isEmail('example@')).toBe(false);
    expect(isEmail('@example.com')).toBe(false);
});
test('isURL:验证是URL', () => {
    expect(isURL('http://blog.okoknb.cn')).toBe(true);
    expect(isURL('https://www.blog.okoknb.cn')).toBe(true);
    expect(isURL('https://blog.okoknb.cn/page?id=1')).toBe(true);
    expect(isURL('blog.okoknb.cn')).toBe(true);
    expect(isURL('www.blog.okoknb.cn')).toBe(true);

    expect(isURL('justastring')).toBe(false);
    expect(isURL('http:/example.com')).toBe(false);
    expect(isURL('ftp://example.com')).toBe(false);
});
test('isNumericString:验证是数字字符串', () => {
    expect(isNumericString('1')).toBe(true);
    expect(isNumericString('1.1314')).toBe(true);
    expect(isNumericString('-1')).toBe(true);
    expect(isNumericString('-0')).toBe(true);

    expect(isNumericString('a')).toBe(false);
    expect(isNumericString('-')).toBe(false);
    expect(isNumericString('-a')).toBe(false);
});
test('isIDNumber:验证是否为中国大陆的身份证号码', () => {
    expect(isIDNumber('11010519491231002X')).toBe(true);
    expect(isIDNumber('330283199509092311')).toBe(true);

    expect(isIDNumber('11111111111111111X')).toBe(false);
    expect(isIDNumber('123745532416443411')).toBe(false);
    expect(isIDNumber('111111111111111111')).toBe(false);
    expect(isIDNumber('akiisajdajdlkadjss')).toBe(false);
});
test('isDate:验证是否为YYYY-MM-DD的日期格式', () => {
    expect(isDate('1996-07-04')).toBe(true);
    expect(isDate('0000-00-00')).toBe(true);

    expect(isDate('aaaa-00-00')).toBe(false);
    expect(isDate('1996/07/04')).toBe(false);
    expect(isDate(new Date().toDateString())).toBe(false);
});

6.压缩

describe('gzip | ungzip', () => {
    it('原始字符串通过gzip压缩后的结果,再通过ungzip解压后的结果应该与原始字符串相同', () => {
        console.log(mock().objectArray)
        const str = JSON.stringify(mock().objectArray)
        const gzipStr = gzip(str)
        const ungzipStr = ungzip(gzipStr)
        expect(ungzipStr).toBe(str)
    });
});

describe('compressImage', () => {
    it('压缩后图像文件大小 < 原始图像文件大小', async () => {
        const blob = image
        const compressBlob = await compressImage(blob, { quality:0.2 })
        expect(compressBlob.size).toBeLessThan(blob.size)
    });
});

6.文件

//根据文件名称或Url获取文件类型
console.log(getFileType('xxx.png')) //png
//网络文件转Blob
console.log(await networkFileToBlob('https://xxx.png')) //Blob
//Blob转Base64
console.log(await blobToBase64(Blob)) //base64
//下载Blob
await downloadBlob({ blob,fileName })
//网络文件下载
await downloadNetworkFile({ fileUrl, fileName })

7.打印

print({printDom})

8.观察

const observerCallback = () => {
    console.log('观察body变化后要执行的后续流程')
}
observerTargetElement('body', observerCallback)

9.http

import { useHttpClient, HttpClient } from '@geyj/web-utils'
import router from '@/router'
const httpPhp: HttpClient = useHttpClient({ baseURL: import.meta.env.VITE_PHP_API_BASE_URL })

httpPhp.onResponseError((error) => {
    if (error.response?.status === 401) {
        localStorage.setItem('token', '')
        if (location.hash.includes('/login')) return
        router.replace({ path: '/login', query: { from: '401' } })
    }
})

const httpFront: HttpClient = useHttpClient({ baseURL: import.meta.env.VITE_AI_API_BASE_URL })

export { httpPhp, httpFront }