onchain-utility
v0.0.19
Published
This package contains misc utilities for onchain.
Readme
Base
_isMoment
判断是否是Moment对象,并且不依赖moment包
_isMoment({}) // falsesd
_isMoment(Moment()) // trueamakeDestructurable
优化返回值结构
const params = makeDestructurable({
name: 'AIC',
age: 16
}, ['AIC', 16]);
const { name, age } = params;
const [name, age] = params;createEnumObject
创建一个对象枚举,主要用于解决真正的枚举导出类型和导出前枚举不相等的问题
createEnumObject({
SET: 'SET',
ADD: 'ADD',
DELETE: 'DELETE',
CLEAR: 'CLEAR',
})toRawType
接收任意数据,生成对应类型的字符串
toRawType(() => {}) // 'Function'
toRawType(new Map) //'Map'
toRawType({}) // 'Object'
toRawType([]) // 'Array'
toRawType(0) // 'Number'hasOwnProperty
判断对象是否存在某个属性
const data = { name: 'aic' };
hasOwnProperty(data, 'name') // true
hasOwnProperty(data, 24) // falseisEmptyObject
是否是空对象
isEmptyObject({}) // true,
isEmptyObject({name: 'aic'}) // falsetoTypeString
接收任意值,返回Object.prototype.toString格式的字符串,例如 [object Object]
toTypeString({}) // '[object Object]'
toTypeString([]) // '[object Array]'
toTypeString(() => {}) // '[object Function]'isString
判断值是否为字符串
isString('aic') // true
isString(123) // falseisNumber
判断值是否为数字
isNumber(123) // true
isNumber('123') // falseisNaN
判断值是否为NaN
isNaN(NaN) // true
isNaN('aic') // false
isNaN(undefined) // falseisBoolean
判断值是否为布尔值
isBoolean(true) // true
isBoolean(0) // falseisUndefined
判断值是否为undefined
isUndefined(undefined) // true
isUndefined(null) // falseisNull
判断值是否为null
isNull(null) // true
isNull(undefined) // falseisNil
判断值是否为null或undefined
isNil(null) // true
isNil(undefined) // true
isNil(NaN) // false
isNil(0) // falseisObject
判断值是否为普通对象(Object.prototype.toString 结果为 'Object')
isObject({}) // true
isObject([]) // false
isObject(null) // falseisArray
判断值是否为数组
isArray([]) // true
isArray({}) // falseisFunction
判断值是否为函数
isFunction(() => {}) // true
isFunction({}) // falseisIntegerKey
判断字符串是否为整型 key
isIntegerKey('0') // true
isIntegerKey('abc') // false
isIntegerKey('-1') // false
isIntegerKey('NaN') // falsegetHTMLTagString
接收一个对象
type:string;标签名称,例如:div或a或span...,attributes:Record<string, any>;html标签属性,例如:{style:'color:#fff;font-size:12px !important;'}context:string;标签内容。
返回html字符串
getHTMLTagString({
type:"span",
attributes: {
style:'color:#fff;font-size:12px !important;'
},
context: 'text'
}) // <span style="color:#fff;font-size:12px !important;">text</span>generateSecureUUID
生成UUID
generateSecureUUID() // 'a6ab72c0-31ab-49bb-a591-54f776c0b2d9'Base64
getBase64Regular
生成base64匹配正则
const regular = getBase64Regular();
const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
regular.test(png) // truebase64ToMimeType
接收base64字符串,返回
mimeType:string;MIME类型;例如:"image/png"suffix:string;文件格式;例如:"png"type:string;文件类型;例如:"image"
const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
base64ToMimeType(png); // { "mimeType":"image/png", "suffix":"png", "type":"image" }base64ToFile
接收对象
text:string;base64字符串type:string | undefined;文件类型;例如:"image"filename:string | undefined;自定义文件名称suffix:string | undefined;文件格式;例如:"png"
返回File对象
const png = `data:image/png;base64,iVBORwOKGgoAAA
ANSUhEUgAAALgA...whHJTEQgHn4pg1J8
1Av8HEfyAJO5uun8AAAAASUVORK5CYIl=`;
base64ToFile({text: png}) // File {name: 'f37e4949-f912-45f8-abde-109aa8e60a28.undefined', lastModified: 1768899504506, lastModifiedDate: Tue Jan 20 2026 16:58:24 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 9470, …};toBase64UTF8
接收任意值返回base64字符串
const element = document.createElement('equation');
const equation = toBase64UTF8(this.__equation);
element.setAttribute('data-lexical-equation', equation);fromBase64UTF8
接收base64字符串返回任意值
const element = document.createElement('equation');
const equation = toBase64UTF8(this.__equation);
element.setAttribute('data-lexical-equation', equation);Language
t
用于国际化的文本格式化处理,将字符串模板中的 {key} 占位符替换为 obj 中对应的值
t('Hello {name}, you are {age} years old', { name: 'aic', age: 12 }) // 'Hello aic, you are 12 years old'
t('Hello world') // 'Hello world'getI18nText
根据pathCode从语言映射表中查询对应文本
languageMap:Record<string, string | undefined>;语言映射表pathCode:string | undefined;查找的键名placeholder:string | undefined;当未找到时返回的占位符
const languageMap = { greeting: 'Hello' };
getI18nText(languageMap, 'greeting', '') // 'Hello'
getI18nText(languageMap, 'missing', '默认文本') // '默认文本'baseTemplate
处理国际化模板的基础方法,组合getI18nText和t两个工具,先从语言包取出模板,再替换占位符
const LanguageMap = { greet: 'Hello {name}' };
baseTemplate(LanguageMap, 'greet', { variate: { name: 'aic' }, placeholder: 'Hi' }) // 'Hello aic'initTranslateI18nFn
初始化全局国际化函数,挂载到window.translateI18n
initTranslateI18nFn((...params) => {
return baseTemplate(LanguageMap, ...params)
})translateI18n
全局国际化函数,优先使用window.translateI18n,未初始化则使用默认的baseTemplate
translateI18n('greet', { variate: { name: 'aic' }, placeholder: 'Hi' })Traversal
dfs
深度优先遍历(同步),根据getNewStack返回子节点继续向下递归
data:T[];待遍历的数据getNewStack:(node: T, parent?: T) => T[];返回当前节点的子节点数组
const tree = [
{ id: 1, children: [{ id: 2 }, { id: 3 }] },
{ id: 4, children: [{ id: 5 }] },
];
dfs(tree, (node) => node.children || [])
// [{id:1,...}, {id:2}, {id:3}, {id:4,...}, {id:5}]bfs
广度优先遍历(同步)
data:T[];待遍历的数据getNewStack:(node: T, parent?: T) => T[];返回当前节点的子节点数组
const tree = [
{ id: 1, children: [{ id: 2 }, { id: 3 }] },
{ id: 4, children: [{ id: 5 }] },
];
bfs(tree, (node) => node.children || [])
// [{id:1,...}, {id:4,...}, {id:2}, {id:3}, {id:5}]asyncDfs
异步深度优先遍历,getNewStack可以返回Promise<T[]>或T[],用于子节点需要异步加载的场景
const result = await asyncDfs(tree, async (node) => {
const children = await fetchChildren(node.id);
return children;
});Tree
levelTransformTree
将扁平数据按层级字符串(如 1.2.3)转换为树结构
data:T[];扁平数据splitSymbol:string;层级分隔符,默认为.getLevel:(item: T) => string;获取每个节点的层级字符串
const list = [
{ id: '1', name: 'root' },
{ id: '1.1', name: 'child1' },
{ id: '1.2', name: 'child2' },
];
levelTransformTree({
data: list,
getLevel: (item) => item.id,
})
// [{ id: '1', name: 'root', children: [{id:'1.1',...},{id:'1.2',...}] }]arrayAttributeFlat
将树形结构扁平化为数组
data:T[];树形数据childrenKey:string;子节点字段名,默认为'children'getChildren:(item: T) => T[];可选,自定义获取子节点的方法fn:(item: T, index: number, parent?: T) => void;可选,遍历过程中触发的回调
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }] }];
arrayAttributeFlat({ data: tree }) // [{id:1,...}, {id:2}, {id:3}]getTreeRelation
获取指定节点在树中的关系信息(自身、父级、前后兄弟节点、层级索引、祖先链)
data:T[];树形数据childrenKey:string;子节点字段名lookup:(item: T, index: number) => boolean;用于查找目标节点的判断函数
const tree = [
{ id: 1, children: [{ id: 2 }, { id: 3 }] },
{ id: 4 },
];
getTreeRelation({
data: tree,
lookup: (item) => item.id === 2,
})
// { parent: {id:1,...}, self: {id:2}, beforeSibling: [], afterSibling: [{id:3}], index: 0, stack: [...] }getTreeAncestors
获取指定节点的全部祖先节点(从根到直接父级)
data:T[];树形数据key:string;目标节点的唯一标识signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
getTreeAncestors({ data: tree, key: '3' }) // [{id:1,...}, {id:2,...}]getTreeDescendants
获取指定节点的全部子孙节点
data:T[];树形数据key:string;目标节点的唯一标识signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
getTreeDescendants({ data: tree, key: '1' }) // [{id:2,...}, {id:3}]getSiblings
获取指定节点同级兄弟节点的signKey数组
data:T[];树形数据key:string;目标节点的唯一标识signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }, { id: 4 }] }];
getSiblings({ data: tree, key: '3' }) // ['2', '3', '4']deleteTreeNodes
根据selectedKeys删除树中匹配的节点及其全部子孙,返回被删除的节点列表
data:T[];树形数据selectedKeys:string[];要删除的节点唯一标识数组signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'onDelete:(item: T) => void;可选,每个节点被删除前的回调
const tree = [{ id: 1, children: [{ id: 2, children: [{ id: 3 }] }] }];
const removed = deleteTreeNodes({ data: tree, selectedKeys: ['2'] });
// [{id:2,...}, {id:3}]getSelectedNodeHierarchyStatus
判断多个选中节点是否跨层级,以及是否同时包含了某节点及其子孙
data:T[];树形数据selectedKeys:string[];选中节点的唯一标识数组signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'
返回:
isDifferentDepths:boolean;选中节点是否跨层级isIncludedParent:boolean;选中是否包含了祖先节点和其子孙节点
getSelectedNodeHierarchyStatus({ data: tree, selectedKeys: ['1', '2'] })
// { isDifferentDepths: true, isIncludedParent: true }mergeAdjacent
按层级将相邻的选中项合并为连续的区间,用于批量操作时的范围识别
data:T[];树形数据selectKeys:string[];选中节点的唯一标识数组signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'
const tree = [{ id: 1, children: [{ id: 2 }, { id: 3 }, { id: 4 }] }];
mergeAdjacent({ data: tree, selectKeys: ['2', '3'] })
// [['2', '3']]generatedAcrossHierarchySort
生成跨层级排序的工具函数集
signKey:string;唯一标识字段名,默认为'id'
返回:
crossLayerSort: 对同一层级的选中项按索引排序getInsertIdx: 计算跨层级插入时的目标索引nodes: 内部维护的层级索引缓存
const { crossLayerSort, getInsertIdx } = generatedAcrossHierarchySort();move
在树中移动节点(结合isMoveUp使用,支持跨层级合并移动)
data:T[];树形数据selectKeys:string[];要移动的节点唯一标识数组isMoveUp:boolean;是否向上移动signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'onMove:(params: { selected: T[]; parent: T }) => boolean;可选,移动前的回调,返回false可阻止移动onMoved:(moves: T[]) => void;可选,移动完成后的回调onError:(params: { selected: T[]; isMoveUp?: boolean }) => boolean | void;可选,移动失败时的回调
move({ data: tree, selectKeys: ['3'], isMoveUp: true })movUp
将选中的节点在树中向上移动一层(move 的便捷封装,等价于 isMoveUp: true)
movUp({ data: tree, selectKeys: ['3'] })moveDown
将选中的节点在树中向下移动一层(move 的便捷封装,等价于 isMoveUp: false)
moveDown({ data: tree, selectKeys: ['3'] })upgrade
将选中的节点在树中升级一级(移动到父级的父级之下),可选地携带后续跟随节点
data:T[];树形数据selectKeys:string[];要升级的节点唯一标识数组isFollow:boolean;是否携带后续节点isSupportMultipleRoot:boolean;是否支持多根节点signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'onUpgrade:(params: OnUpgradeParams<T>) => Promise<boolean> | boolean;可选,升级前的回调onUpgraded:(upgrades: T[], params: ExpandedState<...>) => void;可选,升级完成后的回调onStopFollow:(item: T) => boolean;可选,判断某个节点是否中断跟随onFilterFollow:(item: T[]) => T[];可选,过滤跟随的节点
await upgrade({ data: tree, selectKeys: ['2'], isFollow: true })downgrade
将选中的节点在树中降级一级(移动到前一个兄弟节点的子级之下)
data:T[];树形数据selectKeys:string[];要降级的节点唯一标识数组signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'onDowngrade:(params: OnDowngradedParams<T>) => Promise<boolean> | boolean;可选,降级前的回调onDowngraded:(downgrades: T[], params: ExpandedState<...>) => void;可选,降级完成后的回调
await downgrade({ data: tree, selectKeys: ['3'] })insert
在树中指定锚点节点附近插入新节点
data:T[];树形数据anchor:string;锚点节点的唯一标识value:T[];要插入的节点数组signKey:string;唯一标识字段名,默认为'id'childrenKey:string;子节点字段名,默认为'children'isAddChildLevel:boolean;是否插入为锚点的子级,否则插入为同级(在锚点之后)
insert({ data: tree, anchor: '1', value: [{ id: '5' }] })treeSearch
在树中按条件搜索匹配节点,匹配节点及其祖先保留,匹配节点的子树原样保留
data:T[];树形数据searchValue:(item: T) => boolean;判断节点是否匹配的函数childrenKey:string;子节点字段名,默认为'children'
const matched = treeSearch({
data: tree,
searchValue: (item) => item.name.includes('aic'),
});
// 返回新的树,仅包含匹配分支Hooks
reactive
react响应式数据更新
useReactive
无限层次响应式代理, 包含object,array,set,map,WeakMap,WeakSet
const Dome = () => {
const store = useReactive({
name: 'aic',
age: 12,
set: new Set,
map: new Map,
list: [{hobby: 'play game'}, {hobby: ''}]
})
return <div onClick={() => {
store.age++ // 更新渲染
store.list[1].hobby = 'play basketball' // 更新渲染
}}>
<span>{store.name}</span>
{store.list.map(({ hobby }) => <span>{hobby}</span>)}
</div>
}useShallowReactive
单层对象响应式代理
const Dome = () => {
const store = useShallowReactive({
name: 'aic',
age: 12,
set: new Set,
map: new Map,
list: [{hobby: 'play game'}, {hobby: ''}]
})
return <div onClick={() => {
store.age++ // 更新渲染
store.list[1].hobby = 'play basketball' // 不会触发渲染
}}>
<span>{store.name}</span>
{store.list.map(({ hobby }) => <span>{hobby}</span>)}
</div>
}protection
在代理的过程中用来保护某些属性不受响应式代理
const Dome = () => {
const store = useReactive({
name: 'aic',
age: 12,
set: new Set,
map: new Map,
list: [protection({hobby: 'play game'}), {hobby: ''}]
})
return <div onClick={() => {
store.list[0].hobby = '' // 不会触发渲染
store.list[1].hobby = 'play basketball' // 更新渲染
store.set = protection(new Set); // 设置值的时候进行保护
store.set.add('1') // 不会触发渲染
}}>
<span>{store.name}</span>
{store.list.map(({ hobby }) => <span>{hobby}</span>)}
</div>
}toRaw
接收一个代理对象,返回这个对象的原始对象,解决某些组件无法使用Proxy后的对象
const Table = ({list, setHobby}: {list: any[], setHobby: (params: any) => void) => {
return list.map((item) => <span onClick={(item) => { item.hobby = 'init' }}>{item.hobby}</span>)
}
const Dome = () => {
const store = useReactive({
name: 'aic',
age: 12,
set: new Set,
map: new Map,
list: [{hobby: 'play game'}, {hobby: ''}]
});
return <Table list={toRaw(store.list)} setHobby={() => {}} />
}toProxy
接收一个原始对象,返回这个对象的代理对象,解决转为原始对象后,修改数据无法更新渲染
const Table = ({list, setHobby}: {list: any[], setHobby: (params: any) => void) => {
return list.map((item) => <span onClick={() => setHobby(item)}>{item.hobby}</span>)
}
const Dome = () => {
const store = useReactive({
name: 'aic',
age: 12,
set: new Set,
map: new Map,
list: [{hobby: 'play game'}, {hobby: ''}]
});
return <Table list={toRaw(store.list)} setHobby={(item) => {
const hobby = toProxy(item);
hobby = 'init'
}} />
}useStore
简单的 react hook 状态管理器
const Dome = () => {
const { store, setStore, resetStore, latestStore, setSilenceStore } = useStore(() => {
return {
name: 'aic',
age: 12
}
});
useEffect(() => {
setStore({
age: 24
})
}, []);
// 使用
return <div>{store.name}</div>
}
