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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@otwb/common

v2.0.7

Published

otwb 通用工具库

Readme

@otwb/common 通用工具库

许可证

MIT

安装

npm i @otwb/common

部分功能需要您安装依赖 vue@3

基础工具

@otwb/common/array 数组

removeIf 移除数组中符合条件的元素

declare function removeIf<T>(array: T[], condition: (data: T, index: number) => boolean, once: true): T | undefined

declare function removeIf<T>(array: T[], condition: (data: T, index: number) => boolean, once?: false): T[]
  • 参数array:待处理的数组
  • 参数condition:判断条件
  • 参数once:是否只移除第一个符合条件的元素,默认 false
  • 返回值:移除的元素

示例:

const array = [1, 2, 3, 4, 5]
removeIf(array, (item) => item > 2)
// [1, 2]

const array = [1, 2, 3, 4, 5]
removeIf(array, (item) => item > 2, true)
// 3

@otwb/common/code 代码

formatCode 格式化代码,使用 prettier

declare function formatCode(
    code: string,
    lang: 'js' | 'ts' | 'css' | 'html' | (string & {}),
    options?: import('prettier').Options,
): Promise<string>
  • 参数code:待格式化的代码
  • 参数lang:代码的语言
  • 参数options:prettier 选项
  • 返回值:格式化后的代码

示例:

await formatCode('console.log("Hello World")', 'js')
// console.log("Hello World");

await formatCode('console.log("Hello World")', 'js', { semi: false, singleQuote: true })
// console.log('Hello World');

@otwb/common/color 颜色

isDarkColor 判断颜色是否为暗色

declare function isDarkColor(color: string): boolean
  • 参数color:待判断的颜色
  • 返回值:是否为暗色

示例:

isDarkColor('#000000')
// true

isDarkColor('#ffffff')
// false

brightness 提亮颜色 (hsv 色彩空间的 v)

declare function brightness(color: string, amount: number): string
  • 参数color:待处理的颜色
  • 参数amount:提亮的度,负数时亮度降低
  • 返回值:处理后的颜色

示例:

brightness('#18a058', 1)
// #36ad6a

brightness('#18a058', -1)
// #0c7a43

toRGB 颜色转 RGB

declare function toRGB(color: string): [r: number, g: number, b: number]
  • 参数color:待转换的颜色
  • 返回值:RGB 数组

示例:

toRGB('#18a058')
// [24, 160, 88]

getLightness 获取颜色的亮度(hsl 色彩空间的 l)

declare function getLightness(color: string): number
  • 参数color:待处理的颜色
  • 返回值:亮度

示例:

getLightness('#18a058')
// 36.07843137254902

getAlpha 获取颜色的透明度

declare function getAlpha(color: string): number
  • 参数color:待处理的颜色
  • 返回值:透明度

示例:

getAlpha('rgba(255, 0, 0, 0.5)')
// 0.5

@otwb/common/dom DOM

string2dom 字符串转 DOM(不安全版本,使用 innerHTML)

declare function string2dom<T extends HTMLElement = HTMLElement>(text: string): T
  • 参数text:待转换的字符串
  • 返回值:DOM

示例:

string2dom('<div>Hello World</div>')
// <div>Hello World</div>

string2domSafely 字符串转 DOM,(安全版本,使用 DOMParser)

declare function string2domSafely<T extends HTMLElement = HTMLElement>(text: string): T
  • 参数text:待转换的字符串
  • 返回值:DOM

示例:

string2domSafely('<div>Hello World</div>')
// <div>Hello World</div>

@otwb/common/excel Excel

table2Excel 表格转 Excel

拆分开 theadtbody,可以更好的配合组件库的表格组件

declare function table2Excel(table: HTMLTableElement, sheetName?: string): Blob

declare function table2Excel(thead: HTMLTableSectionElement, tbody: HTMLTableSectionElement, sheetName?: string): Blob
  • 参数table:待转换的表格
  • 参数thead:待转换的表头
  • 参数tbody:待转换的表格体
  • 参数sheetName:工作表名称,默认 Sheet1
  • 返回值:Blob

示例:

table2Excel(document.querySelector('table'))
table2Excel(document.querySelector('thead'), document.querySelector('tbody'))
// Blob { size: 1024, type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }

@otwb/common/fn 函数工具

isFunction 判断是否为函数

declare function isFunction(val: unknown): val is Function
  • 参数val:待判断的值
  • 返回值:是否为函数

示例:

isFunction(() => {})
// true

isFunction({})
// false

debounce 创建一个去抖函数,延迟调用func直到wait之后

如果leadingtrailing选项为true,则func为仅当去抖动函数在超时的后沿调用。 如果wait为 0 且leadingfalse,则推迟func调用直到下一个刻度,类似于超时为 0 的 setTimeout。 如果在有requestAnimationFrame的环境中省略waitfunc调用将被推迟到绘制下一帧(通常约为 16 毫秒)。

declare function debounce<Fn extends (...args: any[]) => any>(
    fn: Fn,
    wait?: number,
    options?: {
        leading?: boolean
        trailing?: boolean
        maxWait?: number
    },
): Fn & {
    cancel: () => void
    flush: () => ReturnType<Fn>
    pending: boolean
}
  • 参数fn:执行的函数
  • 参数wait:防抖时间,单位毫秒,默认 0.
  • 参数options:选项
    • 参数options.leading:在超时前调用,默认 false.
    • 参数options.trailing:在超时后调用,默认 true.
    • 参数options.maxWait:最大等待时长.
  • 返回值:处理后的函数
    • cancel:取消执行.
    • flush:立即执行.
    • pending:是否正等待执行.

示例:

const fn = debounce(() => console.log('hello world'), 1000)
fn()
setTimeout(fn, 500)
// 1.5s 后输出 'hello world'

throttle 创建一个节流函数,在每个wait时长内最多执行一次

如果leadingtrailing选项为true,则func为仅当去抖动函数在超时的后沿调用。 如果wait为 0 且leadingfalse,则推迟func调用直到下一个刻度,类似于超时为 0 的 setTimeout。 如果在有requestAnimationFrame的环境中省略waitfunc调用将被推迟到绘制下一帧(通常约为 16 毫秒)。

declare function throttle<Fn extends (...args: any[]) => unknown>(
    fn: Fn,
    wait?: number,
    options?: {
        leading?: boolean
        trailing?: boolean
    },
): Fn & {
    cancel: () => void
    flush: () => ReturnType<Fn>
    pending: boolean
}
  • 参数fn:执行的函数
  • 参数wait:节流时间,单位毫秒,默认 0.
  • 参数options:选项
    • 参数options.leading:在超时前调用,默认 true.
    • 参数options.trailing:在超时后调用,默认 true.
    • 参数options.maxWait:最大等待时长.
  • 返回值:处理后的函数
    • cancel:取消执行.
    • flush:立即执行.
    • pending:是否正等待执行.

示例:

const fn = throttle(() => console.log('hello world'), 1000)
fn()
setTimeout(fn, 500)
// 1s 后输出 'hello world'

@otwb/common/mime MIME

getType 根据扩展名获取 MIME 类型

declare function getType(extensionOrPath: string): string | undefined
  • 参数extensionOrPath扩展名或路径。如 .png zip xxx.dat
  • 返回值:MIME类型。如:text/plain 示例:
getType('.png')
// 'image/png'

getExtensions 根据 MIME 类型获取扩展名

declare function getExtensions(type: string): string[] | undefined
  • 参数type:MIME类型。如:text/plain
  • 返回值:扩展名数组。如:['.png']

示例:

getExtensions('image/png')
// ['png']

@otwb/common/object 对象

clone 克隆对象(深拷贝)

包括原型、函数和循环引用

declare function clone<T extends {}>(obj: T): T
  • 参数obj:待克隆的对象
  • 返回值:克隆后的对象

示例:

const obj = { a: 1, b: { c: 2 } }
const cloned = clone(obj)
// { a: 1, b: { c: 2 } }

console.debug(cloned === obj, cloned.b === obj.b)
// false false

copy 简单的深拷贝

使用 JSON.stringifyJSON.parse 实现

declare function copy<T extends {}>(obj: T): T
  • 参数obj:待复制的对象
  • 返回值:复制后的对象

示例:

const obj = { a: 1, b: { c: 2 } }
const copied = copy(obj)
// { a: 1, b: { c: 2 } }

console.debug(copied === obj, copied.b === obj.b)
// false false

omitProps 移除对象的指定属性

declare function omitProps<T extends {}, Key extends keyof T | (string & {})>(obj: T, ...keys: Key[]): Omit<T, Key>
  • 参数obj:待处理的对象
  • 参数keys:待移除的属性
  • 返回值:处理后的对象

示例:

const obj = { a: 1, b: 2, c: 3 }
omitProps(obj, 'a', 'b')
// { c: 3 }

pickProps 从对象中挑选指定属性

declare function pickProps<T extends {}, Key extends keyof T | (string & {})>(obj: T, ...keys: Key[]): Pick<T, Key>
  • 参数obj:待处理的对象
  • 参数keys:待挑选的属性
  • 返回值:处理后的对象

示例:

const obj = { a: 1, b: 2, c: 3 }
pickProps(obj, 'a', 'b')
// { a: 1, b: 2 }

isDiff 比较两个对象是否有差异

declare function isDiff<O1, O2>(obj1: O1, obj2: O2, options?: IsDiffOptions): boolean

interface IsDiffOptions {
    ignore?: string[] | ((k: string) => boolean | undefined | void)
}
  • 参数obj1:待比较的对象
  • 参数obj2:待比较的对象
  • 参数options:选项
    • 参数options.ignore:忽略的属性
  • 返回值:是否有差异

示例:

isDiff({ a: 1, b: 2 }, { a: 1, b: 2 })
// false

isDiff({ a: 1, b: 2 }, { a: 1, b: 3 })
// true

isDiff({ a: 1, b: 2 }, { a: 1, b: 3 }, { ignore: ['b'] })
// false

isNone 判断对象是否为空

declare function isNone(val: unknown): boolean
  • 参数val:待判断的值
  • 返回值:是否为空

示例:

isNone(null)
// true

isNone(undefined)
// true

isNone('')
// true

isNone(' ')
// true

isNone(0)
// false

isNone(Infinity)
// false

isNone(NaN)
// true

isNone(false)
// false

isNone([])
// true

isNone([1])
// false

isNone({})
// true

isNone({ a: 1 })
// false

isNullish 判断对象是否为 nullish

declare function isNullish(val: unknown): val is null | undefined
  • 参数val:待判断的值
  • 返回值:是否为 nullish

示例:

isNullish(null)
// true

isNullish(undefined)
// true

isNullish('')
// false

isNullish(0)
// false

isNullish(Infinity)
// false

isNullish(NaN)
// false

isNullish([])
// false

isNone({})
// false

isObject 判断对象是否为对象

declare const isObject: (val: unknown) => val is Record<any, any>
  • 参数val:待判断的值
  • 返回值:是否为对象

示例:

isObject({})
// true

isObject([])
// true

isObject(() => {})
// true

isObject('')
// false

isObject(0)
// false

isObject(Infinity)
// false

isObject(NaN)
// false

isPlainObject 判断对象是否为纯对象

declare function isPlainObject(val: unknown): val is object
  • 参数val:待判断的值
  • 返回值:是否为纯对象

示例:

isPlainObject({})
// true

isPlainObject([])
// false

isPlainObject(() => {})
// false

isPlainObject('')
// false

isPlainObject(0)
// false

isPlainObject(Infinity)
// false

isPlainObject(NaN)
// false

merge 合并对象

declare function merge(...objects: any[]): any
  • 参数objects:待合并的对象
  • 返回值:合并后的对象

示例:

const obj1 = { a: 1, b: 2 }
const obj2 = { c: 3, d: 4 }
merge(obj1, obj2)
// { a: 1, b: 2, c: 3, d: 4 }

@otwb/common/qs 查询字符串

parse 解析查询字符串

declare function parse(search: string): URLSearchParams
  • 参数search:待解析的查询字符串,如:a=1&b=xxx
  • 返回值:解析后的查询参数对象

示例:

parse('a=1&b=2&c=3')
// URLSearchParams { 'a' => '1', 'b' => '2', 'c' => '3' }

stringify 将查询参数对象转换为查询字符串

declare function stringify(object: Record<string, MaybeArray<string | number | boolean> | undefined | null>): string
  • 参数object:待转换的查询参数对象
  • 返回值:转换后的查询字符串 示例:
stringify({ a: 1, b: 2, c: 3 })
// 'a=1&b=2&c=3'

@otwb/common/save 数据保存

saveAs 尝试使用 showSaveFilePicker 保存文件,如果该 API 未可用 则调用 downloadAs

saveAs 保存为文件

declare function saveAs(content: MaybePromise<Response | Blob | ReadableStream>, name?: string): Promise<void>
  • 参数content:待保存的内容
  • 参数name:保存的文件名
  • 返回值:promise

示例:

saveAs(new Blob(['Hello World'], { type: 'text/plain' }), 'hello.txt')

saveAs(fetch('http://www.ithinkdt.com/cloud/doc'), 'hello.txt')

downloadAs 下载为文件

declare function downloadAs(content: MaybePromise<Response | Blob | ReadableStream>, name?: string): Promise<void>
  • 参数content:待下载的内容
  • 参数name:保存的文件名
  • 返回值:promise

示例:

downloadAs(new Blob(['Hello World'], { type: 'text/plain' }), 'hello.txt')

downloadAs(fetch('http://www.ithinkdt.com/cloud/doc'), 'hello.txt')

@otwb/common/string 字符串

measureText 测量文本宽度

declare function measureText(text: string, font: string): number
  • 参数text:待测量的文本
  • 参数font:文本的字体样式
  • 返回值:文本的宽度

示例:

measureText('Hello World', '16px sans-serif')
// 100

a2b base64 转字符串,同 globalThis.atob,支持 Unicode

declare function a2b(data: string): string
  • 参数data:待转换的 base64 字符串
  • 返回值:转换后的字符串

示例:

a2b('YSDEgCDwkICAIOaWhyDwn6aE')
// 'a Ā 𐀀 文 🦄'

b2a 字符串转 base64,同 globalThis.btoa,支持 Unicode

declare function b2a(str: string): string
  • 参数str:待转换的字符串
  • 返回值:转换后的 base64 字符串

示例:

b2a('a Ā 𐀀 文 🦄')
// 'YSDEgCDwkICAIOaWhyDwn6aE'

hyphenate 驼峰转连字符

declare function hyphenate(str: string): string
  • 参数str:待处理的字符串
  • 返回值:处理后的字符串

示例:

hyphenate('helloWorld')
// 'hello-world'

hyphenate('helloWorld')
// 'hello-world'

camelize 连字符转驼峰

declare function camelize(str: string): string
  • 参数str:待处理的字符串
  • 返回值:处理后的字符串

示例:

camelize('hello-world')
// 'helloWorld'

capitalize 首字母大写

declare function capitalize(str: string): string
  • 参数str:待处理的字符串
  • 返回值:处理后的字符串

示例:

capitalize('hello world')
// 'Hello world'

@otwb/common/tree 树

flattenTree 扁平化树

declare function flattenTree<T extends {}>(tree: T[], options?: FlattenTreeOptions<T>): T[]

interface FlattenTreeOptions<T extends {}> {
    childrenKey?: (keyof T) & string
}
  • 参数tree:待处理的树
  • 参数options:选项
    • 参数options.childrenKey:子节点键名,默认 children
  • 返回值:处理后的树

示例:

const tree = [
    {
        id: 1,
        name: 'a',
        children: [
            {
                id: 2,
                name: 'b',
                children: [
                    {
                        id: 3,
                        name: 'c',
                    },
                ],
            },
        ],
    },
]
flattenTree(tree)
// [
//     { id: 1, name: 'a', children: [{ id: 2, name: 'b', children: [{ id: 3, name: 'c' }] }] },
//     { id: 2, name: 'b', children: [{ id: 3, name: 'c' }] },
//     { id: 3, name: 'c' },
// ]

array2Tree 数组转树

declare function array2Tree<T extends {}>(data: T[], options?: Array2TreeOptions<T>): T[]

interface Array2TreeOptions<T extends {}, KeyofT extends (keyof T) & string = (keyof T) & string> {
    rootId?: string
    idKey?: KeyofT
    parentKey?: KeyofT
    childrenKey?: KeyofT
    sortKey?: KeyofT
}
  • 参数data:待处理的数组
  • 参数options:选项
    • 参数options.rootId:根节点ID,默认自动计算
    • 参数options.idKey:数据的ID字段名,默认 id
    • 参数options.parentKey:数据的父节点ID字段名,默认 parentId
    • 参数options.childrenKey:数据的下级节点字段名,默认 children
    • 参数options.sortKey:数据的排序字段名
  • 返回值:处理后的树

示例:

const data = [
    { id: 1, name: 'a', parentId: 0 },
    { id: 2, name: 'b', parentId: 1 },
    { id: 3, name: 'c', parentId: 2 },
]
array2Tree(data)
// [
//     {
//          id: 1,
//          name: 'a',
//          children: [
//              {
//                  id: 2,
//                  name: 'b',
//                  children: [
//                      {
//                          id: 3,
//                          name: 'c'
//                       }
//                  ]
//              }
//          ]
//      },
// ]

walkTree 遍历树

declare function walkTree<T extends {}>(
    data: T[],
    each: (item: T, index: number, parent: T | undefined, children: T[] | undefined) => MaybePromise<boolean | void>,
    options?: WalkTreeOptions<T> & {
        path?: false
        async: true
    },
): Promise<boolean>

declare function walkTree<T extends {}>(
    data: T[],
    each: (item: T, index: number, parents: T[], children: T[] | undefined) => MaybePromise<boolean | void>,
    options?: WalkTreeOptions<T> & {
        path: true
        async: true
    },
): Promise<boolean>

declare function walkTree<T extends {}>(
    data: T[],
    each: (item: T, index: number, parent: T | undefined, children: T[] | undefined) => boolean | void,
    options?: WalkTreeOptions<T> & {
        async?: false
        path?: false
    },
): boolean

declare function walkTree<T extends {}>(
    data: T[],
    each: (item: T, index: number, parents: T[], children: T[] | undefined) => boolean | void,
    options?: WalkTreeOptions<T> & {
        async?: false
        path: true
    },
): boolean

interface WalkTreeOptions<T extends {}, KeyofT extends (keyof T) & string = (keyof T) & string> {
    async?: boolean
    childrenKey?: KeyofT
    order?: 'pre' | 'post'
}
  • 参数data:待处理的树
  • 参数each:遍历函数
  • 参数options:选项
    • 参数options.async:是否异步等待 each 的返回结果,默认 false
    • 参数options.childrenKey:数据的下级节点字段名,默认 children
    • 参数options.order:遍历顺序,默认 pre
    • 参数options.path:是否返回所有祖先路径,默认 false
  • 返回值:遍历结果

示例:

const data = [{ id: 'a', children: [{ id: 'b' }] }, { id: 'c' }]
walkTree(data, (item, index, parent, children) => {
    console.log(item.id, index, parent?.id)
})
// a 0 undefined
// b 0 a
// c 1 undefined

@otwb/common/type 类型

MaybeArray 可能是数组的类型

declare type MaybeArray<T> = T | T[]

示例:

const a: MaybeArray<string> = 'a'
a = ['a', 'b'] // ok

MaybePromise 可能是Promise的类型

declare type MaybePromise<T> = T | Promise<T>

示例:

const a: MaybePromise<string> = 'a'
a = Promise.resolve('a') // ok

Awaitable 可等待的

declare type Awaitable<T> = T | PromiseLike<T>

示例:

const a: Awaitable<string> = 'a'
a = {
    then(onfulfilled, onrejected) {
        onfulfilled('a')
    },
} // ok

Mutable 将不可变转换为可变

declare type Mutable<T> = {
    -readonly [P in keyof T]: T[P]
}

示例:

const a: { readonly val: string } = { val: 'a' }
a.val = 'a2' // error
const b: Mutable<typeof a> = { val: 'b' }
b.val = 'b2' // ok

RequiredKeys 部分键设为必须

type RequiredKeys<T, K extends keyof T = keyof T> = T & {
    [P in K]-?: NonNullable<T[P]>
}

示例:

const a: { val?: string } = {}
const b: RequiredKeys<typeof a, 'val'> = {} // error

PartialKeys 部分键设为可选

type PartialKeys<T, K extends keyof T = keyof T> = T & {
    [P in K]?: T[P]
}

示例:

const a: { val: string } = { val: 'a' }
const b: PartialKeys<typeof a, 'val'> = {} // ok

Union2Intersection 联合类型转交叉类型

type Union2Intersection<Union> = (Union extends any ? (argument: Union) => void : never) extends (
    argument: infer I,
) => void
    ? I
    : never

示例:

type A = Union2Intersection<'a' | 'b' | 'c'>
// type A = "a" & "b" & "c"

Union2Tuple 联合类型转元组

/** 并集转元组 */
type Union2Tuple<Union> = Union2TupleRecurser<Union, []>

type Union2UoF<Union> = Union extends any ? (argument: Union) => void : never
type Union2IoF<Union> = Union2Intersection<Union2UoF<Union>>

type Firs2fUnion<Union> = Union2IoF<Union> extends { (argument: infer T): void } ? T : never

type Union2TupleRecurser<Union, Tuple extends any[]> = [
    Firs2fUnion<Union>,
    Exclude<Union, Firs2fUnion<Union>>,
] extends [infer First, ...infer Lasts]
    ? [First] extends [never]
        ? Tuple
        : Union2TupleRecurser<Lasts, [First, ...Tuple]>
    : never

示例:

type A = Union2Tuple<'a' | 'b' | 'c'>
// type A = ["a", "b", "c"]

@otwb/common/url URL处理

normalizeUrl 标准化URL

declare function normalizeUrl(url: string): string
  • 参数url:待标准化的URL
  • 返回值:标准化后的URL

示例:

normalizeUrl('http://www.ithinkdt.com/cloud//doc')
// 'http://www.ithinkdt.com/cloud/doc'

Request 请求

Dict 字典

简化对数据字典的使用

注册插件

import { createApp } from 'vue'
import dict from '@otwb/common/dict'

const app = createApp(...)

app.use(dict, {
    fetch: (dictTypes) => {
        // 从后端获取字典数据
        return fetch('/api/dict', {
            method: 'GET',
            params: {
                dictTypes: dictTypes.join(',')
            },
        }).then((res) => res.json())
    }
})

...
app.mount('#app')

字典资源使用

useDict 获取字典数据

declare function useDict<T extends object>(
    dictType: MaybeRef<DictTypeKey<T>>,
    options?: UseDictOptions<T>,
): UseDictReturn<DictItem<T>[]>

type DictTypeKey<T extends object = object> = (string | symbol) & {
    __T?: T
}

type UseDictOptions<T extends object> = {
    onGet?: (dicts: DictItem<T>[]) => void
}

type UseDictReturn<Result> = Result & {
    loading: boolean
} & [result: Result, loading: Ref<boolean>]

interface _DictItemBase {
    label: string
    value: string
    remark?: string
    disabled?: boolean
}

type DictItem<T extends object = object> = T & _DictItemBase
  • 参数dictType:字典类型
  • 参数options:选项
    • 参数options.onGet:获取到字典数据后回调
  • 返回值:字典数据

示例

import { useDict } from '@otwb/common/dict'

// 获取字典数据
const types = useDict('type', {
    onGet: (data) => {
        console.debug(`已获取到字典 type`, data)
    },
})
// [{ value: '1', label: '类型一' }, { value: '2', label: '类型二'}]

console.debug('字典 type 加载状态:', types.loading)
// true

// 也可以直接解构
// const [ types, typesLoading ] = useDict('type')

useDictMap 获取字典映射数据

declare function useDictMap<T extends object>(
    dictType: MaybeRef<DictTypeKey<T>>,
    options?: UseDictOptions<T>,
): UseDictReturn<DictValueMap<T>>

type DictValueMap<T extends object = object> = Map<string, DictItem<T>>
  • 参数dictType:字典类型
  • 参数options:选项
    • 参数options.onGet:获取到字典数据后回调
  • 返回值:字典映射数据

示例

import { useDictMap } from '@otwb/common/dict'

const typeMap = useDictMap('type', {
    onGet: (data) => {
        console.debug(`已获取到字典 type`, data)
    },
})
// Map {
//     '1' => { value: '1', label: '类型一' },
//     '2' => { value: '2', label: '类型二'}
// }

console.debug('字典 type 映射加载状态:', typeMap.loading)
// false

// 同样可以直接解构
// const [ typeMap, typeMapLoading ] = useDictMap('type')

registerDict 注册字典资源

declare function registerDict<T extends object>(
    dictType: DictTypeKey<T>,
    getter: (getDict: <F extends object>(dictType: DictTypeKey<F>) => Promise<DictItem<F>[]>) => Promise<DictItem<T>[]>,
    cached?: boolean,
): Unregister

type Unregister = () => void

declare function unregisterDict<T extends object>(dictType: DictTypeKey<T>): void
  • 参数dictType:字典类型
  • 参数getter:资源加载函数
  • 参数cached:是否缓存,默认 true
  • 返回值:取消注册函数

示例

import { registerDict, unregisterDict } from '@otwb/common/dict'

const someDictKey = Symbol()

// 注册资源加载函数
const unregister = registerDict(someDictKey, () => {
    return fetch('/api/some', {
        method: 'GET',
    }).then((res) => res.json())
})

// 使用字典
const options = useDict(someDictKey)

// 取消注册
unregister()
// or
unregisterDict(someDictKey)

可以使用此方法从系统字典中定制数据

import { registerDict } from '@otwb/common/dict'

// 注册资源加载函数
registerDict('customKey', (getDict) => {
    return getDict('dictKey').then((data) => {
        return data.filter((item) => item.value !== 'some')
    })
})

I18n 国际化

多语言处理

注册插件

import { createApp } from 'vue'
import i18n from '@otwb/common/i18n'

const app = createApp(...)

const locale = ref('zh-CN')
app.use(i18n, {
    locale,
    // 未找到资源时回退的语言
    defaultLocale: 'en-US',
    // 关闭响应式,如果您在切换语言时刷新页面
    reactive: false
})

...
app.mount('#app')

定义并使用资源

使用 defineI18n 定义资源后使用

declare function defineI18n<Message extends object>(
    loader: (locale: Locale) => Promise<Message>,
    options?: {
        reactive?: boolean
    },
): I18nComposable<Message>

type I18nComposable<Message extends object> = (locale?: MaybeRef<Locale>) => I18nInstance<ParseMessage<Message>>

interface I18nInstance<ParsedMessage> {
    locale: Ref<Locale>
    t<Key extends keyof ParsedMessage>(this: void, key: Key): string
    t<Key extends keyof ParsedMessage, Parameter extends ParsedMessage[Key] extends (p: infer P) => string ? P : never>(
        this: void,
        key: Key,
        parameter: Parameter,
        locale?: Locale,
    ): string
}
  • 参数loader:资源加载函数
  • 参数options:选项
    • 参数options.reactive:是否响应式,默认跟随插件配置
  • 返回值:国际化实例

示例

// file locales/zh-CN.ts
export default {
    a: {
        b: 'a.b',
        c: (count: number) => `a.c: count is ${count}`,
    },
    d: (data: { p1: string; p2: boolean }) => `d: p1 is ${data.p1}, p2 is ${p2}`,
}

// file: use-abc-i18n.ts
import { defineI18n } from '@otwb/common/i18n'

export default defineI18n(
    (locale) => {
        return import(`./locales/${locale}.ts`).then((m: typeof import('./locales/zh-CN')) => m.default)
    },
    {
        // 可单独控制资源是否为响应式
        reactive: true,
    },
)

// file: AbCde.tsx
import { defineComponent } from 'vue'
import useAbcI18n from './use-abc-i18n'

export default defineComponent({
    name: 'AbCde',
    setup() {
        const { t } = useAbcI18n()
        return (
            <div>
                <span>{t('a.b')}</span>
                <span>{t('a.c', 2)}</span>
                <span>{t('d', { p1: 'p1', p2: true })}</span>
            </div>
        )
    },
})