## 功能模块 - [bidirectional-mapping](#bidirectional-mapping) - [tree](#tree)
npm install kk-utils-library``js`
npm install kk-utils-library -S
pnpm add kk-utils-library -S
bidirectional-mapping 是 kk-utils 里的工具之一,其作用是键值对双向映射
它可以把一个字符串、数字、对象、数组按我们的映射规则进行转换
BiMap:用来生成映射规则的一个工具
BiMapConversion:用来把目标数据转换成映射规则处理后的数据
#### 编写映射规则
`js`
const map = new BiMap([
['a', 'A'], // 把'a'转成'A',也可以反过来把'A'转成'a'
[
'b',
'B',
{
transformType: String // 把键名对应的value进行处理,比如转成字符串,转成数字,转成Boolean
}
],
[
'c',
'C', // 数组嵌套数组表示c是复杂对象,里面的数据还要继续转换,c.d会变成C.D
[
[
'd',
'D',
{
transformSourceType: 'object', // 转换数据的类型 object、map、array
transformType: Number,
transformInverted: true, // 是否可以反过来转换 如为true就会把原本值为'否'变成0
transform: {
0: '否',
1: '是'
}
}
],
[
'e',
'E',
{
transformType: Boolean
}
],
[
'f',
'F',
[
[
'g',
'G',
{
transformSourceType: 'map',
transformType: Number,
transformInverted: true,
transform: new Map([
[0, 'zero'],
[1, 'one']
])
}
],
[
'h',
'H',
{
transformSourceType: 'array',
transformType: Number,
transformInverted: true,
transformLabelKey: 'label',
transformValueKey: 'value',
transform: [
{
label: '否',
value: 0
},
{
label: '是',
value: 1
}
]
}
]
]
]
]
],
[
'children',
'CHILDREN',
{
recursion: true // 树结构 递归处理
}
],
[
'k',
'K',
new BiMap([
[0, '零'],
[1, '一']
]) // 深层对象映射可以使用数组嵌套也可以直接使用BiMap BiMap可以处理键名转换也可以处理值的转换
]
]);
#### 转换数据
##### 写一个测试数据
`js`
const data = [
{
a: 1,
b: 1,
c: {
d: 1,
e: 1,
f: [
{
g: 1,
h: 1
}
]
},
children: [
{
a: 0,
b: 0,
c: {
d: 0,
e: 0,
f: [
{
g: 0,
h: 0
}
]
},
k: 0
},
{
a: 1,
b: 1,
c: {
d: 1,
e: 1,
f: [
{
g: 1,
h: 1
}
]
},
k: 1
}
],
k: 1
}
];
##### 使用映射规则转换数据
`js`
const mapData = BiMapConversion(data, map);
console.log(mapData);
##### 转换结果
`js`
[
{
A: 1,
B: '1',
C: {
D: '是',
E: true,
F: [
{
G: 'one',
H: '是'
}
]
},
CHILDREN: [
{
A: 0,
B: '0',
C: {
D: '否',
E: false,
F: [
{
G: 'zero',
H: '否'
}
]
},
K: '零'
},
{
A: 1,
B: '1',
C: {
D: '是',
E: true,
F: [
{
G: 'one',
H: '是'
}
]
},
K: '一'
}
],
K: '一'
}
];
##### 截图对比
可以看到测试数据已经按我定义好的映射规则进行了转换,无论是键名还是值,都转换成功了的
比如和后端对接接口的时候,后端键名不规范,我们写逻辑的时候就要去重新命名,多处用同一接口的话,就要到处维护,不方便。再比如同一个字段的值一会是字符串,一会是数字,就会对我们的逻辑代码造成不易擦觉得影响,所以可以使用这个工具在接口处统一做出入数据管理
#### 在接口处统一做出入数据管理
比如我要对接后端的反馈接口的 CURD
#### 定义公共分页映射
`js`
// src/constants/bi-map/common/pagination/index.js
import { BiMap } from 'kk-utils-library/bidirectional-mapping';
// 分页参数
export const $paginationReqItemMap = new BiMap([
[
'pagination',
'pagination',
[
['page', 'pageNumber'],
['pageSize', 'pageSize']
]
]
]);
#### 定义接口映射
`js
// src/constants/bi-map/feedback/index.js
import { BiMap } from 'kk-utils-library/bidirectional-mapping';
import { $paginationReqItemMap } from '../common/pagination';
// 意见反馈列表
export const $feedbackListReqItemMap = new BiMap([
['id', 'id'], // ID
['type', 'emergencyDegreeType'], // 紧急程度类型
['topic', 'theme'], // 主题
['content', 'content'] // 内容
]);
export const $feedbackListResItemMap = new BiMap([
['id', 'id'], // ID
['type', 'emergencyDegreeType'], // 紧急程度类型
['topic', 'theme'], // 主题
['content', 'content'] // 内容
]);
// 意见反馈分页
export const $feedbackPageReqItemMap = new BiMap([
...$feedbackListReqItemMap.sourceData, // 复用 避免多次定义
...$paginationReqItemMap.sourceData // 复用 避免多次定义
]);
export const $feedbackPageResItemMap = $feedbackListResItemMap; // 相同映射规则直接指向共用
// 意见反馈详情
export const $feedbackDetailReqItemMap = new BiMap([['id', 'id']]);
export const $feedbackDetailResItemMap = $feedbackListResItemMap;
// 意见反馈新建
export const $feedbackCreateReqItemMap = $feedbackListReqItemMap;
export const $feedbackCreateResItemMap = new BiMap([]);
// 意见反馈编辑
export const $feedbackUpdateReqItemMap = $feedbackListReqItemMap;
export const $feedbackUpdateResItemMap = new BiMap([]);
// 意见反馈删除
export const $feedbackDeleteReqItemMap = new BiMap([['id', 'id']]);
export const $feedbackDeleteResItemMap = new BiMap([]);
`
#### 接口处使用映射做出入口参数管理
`js
// src/apis/feedback/index.js
import { BiMapConversion } from 'kk-utils-library/bidirectional-mapping';
import {
$feedbackCreateReqItemMap,
$feedbackCreateResItemMap,
$feedbackDeleteReqItemMap,
$feedbackDeleteResItemMap,
$feedbackDetailReqItemMap,
$feedbackDetailResItemMap,
$feedbackListReqItemMap,
$feedbackListResItemMap,
$feedbackPageReqItemMap,
$feedbackPageResItemMap,
$feedbackUpdateReqItemMap,
$feedbackUpdateResItemMap
} from '@/constants/bi-map/feedback';
// 分页获取意见反馈
export function getFeedbackPage(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackPageReqItemMap);
Service.get('/api/feedbacks', params)
.then((res) => {
res.data = BiMapConversion(res.data || {}, $feedbackPageResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
// 获取意见反馈列表
export function getFeedbackList(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackListReqItemMap);
Service.get('/api/feedbacks/list', params)
.then((res) => {
res.data = BiMapConversion(res.data || [], $feedbackListResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
// 查询意见反馈详情
export function getFeedbackDetail(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackDetailReqItemMap);
Service.get(/api/feedbacks/${params.id}, { ...params, populate: '*' })
.then((res) => {
res.data = BiMapConversion(res.data || {}, $feedbackDetailResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
// 新建意见反馈
export function createFeedback(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackCreateReqItemMap);
Service.post('/api/feedbacks', { data: params })
.then((res) => {
res.data = BiMapConversion(res.data || {}, $feedbackCreateResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
// 更新意见反馈
export function updateFeedback(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackUpdateReqItemMap);
Service.put(/api/feedbacks/${params.id}, { data: params })
.then((res) => {
res.data = BiMapConversion(res.data || {}, $feedbackUpdateResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
// 删除意见反馈
export function deleteFeedback(Service) {
return (params) =>
new Promise((resolve, reject) => {
params = BiMapConversion(params || {}, $feedbackDeleteReqItemMap);
Service.delete(/api/feedbacks/${params.id}, params)`
.then((res) => {
res.data = BiMapConversion(res.data || {}, $feedbackDeleteResItemMap);
resolve(res);
})
.catch((error) => reject(error));
});
}
这样只需要在接口封装处对 params 出参做映射转换和接收到数据时对 res 里的 data 做映射转换,我们就可以在项目里直接使用自己定义的 key,出参给后台的时候也会反过来把我们的 key 根据映射规则转成后台原本的 key 再传递给后台的
除了以上业务使用,我们还能用来对单个数据处理
`js`
// 我们用回上面定义的new BiMap
const mapping_a = map.get('A'); // a
const mapping_A = map.get('a'); // A
是kk-utils里的工具之一,其作用是有多个针对树结构的方法函数,是我在写项目时积累下来的,几乎可以覆盖日常所需当然,初始的代码是借鉴
GitHub上newarea0大佬的代码,基于此源码的基础上进行修改和新增适合我业务的功能$3
绝大部分函数的基础配置项都有以下选项可用#### strategy
设置搜索策略,默认策略为
prepre:深度优先,正序搜索post:深度优先,反序搜索breadth:广度优先`js
import { filter } from 'kk-utils-library/tree';
const result = filter(tree, node => node.id > 2, { strategy: 'post' })
`#### childrenKey
自定义子节点
key名,默认值为children`js
import { filter } from 'kk-utils-library/tree';
const result = filter(tree, node => node.id > 2, { childrenKey: 'items' })
`#### getChildrenKey
设置一棵树上多种自定义子节点
key名
`js
const getChildrenKeyTree = [
{
key: '1',
children: [
{
key: '2',
subItems: [
{
key: '3'
}
]
}
]
},
{
key: '4'
}
];
const getChildrenKeyResult = filter(
getChildrenKeyTree,
(node) => node.id > 2,
{
getChildrenKey: (tree, meta) => {
if (meta.depth === 1) {
return 'subItems';
}
}
}
);
`
$3
#### forEach
遍历树形数组/对象,对每个节点执行回调
`js
forEach(tree, callback, [options])
`
- tree:树形数组/对象
- callback:回调函数,对每个节点执行回调
- options:配置项,可选,对象类型,支持基础配置项`js
import { forEach } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 111
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
forEach(data, (node) => {
console.log(node.key);
});
// 1
// 11
// 111
// 112
// 12
// 122
// 1221
// 1222
`#### filter
遍历树形数组/对象,并把返回非真值的节点剔除(不会影响原结构,返回的树是新生成的)
`js
filter(tree, callback, [options])
`
- tree:树形数组/对象
- callback:回调函数,对每个节点执行回调
- options:配置项,可选,对象类型,支持基础配置项`js
import { filter } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 99
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = filter(data, (node) => node.key < 100);
console.log('result', result);
// {
// "key": 1,
// "children": [
// {
// "key": 11,
// "children": [
// {
// "key": 99
// }
// ]
// },
// {
// "key": 12,
// "children": []
// }
// ]
// }
`#### conditionsFilter
遍历树形数组/对象,并把返回非真值的节点剔除
这个方法和上面的
filter的区别在于:
- conditionsFilter不支持基础配置项
- conditionsFilter在父级为true,而子级没数据的时候,可以选择携带出下一级的数据或者所有下级数据,这在比如区域列表过滤的时候,可以通过搜索省级带出所有市级或者所有子区域
- conditionsFilter在能搜索匹配到最后一级的时候,并不会携带出兄弟节点
- conditionsFilter支持函数过滤和对象/数组结构的值匹配过滤,但是值匹配过滤并不是全等,而是包含关系
- `js
conditionsFilter(tree, conditions, [options])
`
- tree:树形数组
- conditions:过滤条件,
- 可以为函数(item) => boolean
- 也可以为对象{ name: '名称搜索', code: '编码搜索‘ },多个条件是&&的关系,必须都满足
- options:配置项,可选,对象类型,不支持基础配置项
- childrenKey:自定义子节点key名,默认值为children
- withChildren:子级没数据的时候是否携带子级数据返回,默认值为true
- allChildren:是否返回所有子级数据,默认值为false`js
import { conditionsFilter } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 99
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = conditionsFilter(data, (node) => node.key === 122);
console.log('result', result);
// [
// {
// key: 1,
// children: [
// {
// key: 12,
// children: [
// {
// key: 122,
// children: [
// {
// key: 1221
// },
// {
// key: 1222
// }
// ]
// }
// ]
// }
// ]
// }
// ];
`#### map
遍历树形数组/对象,根据返回的对象,组成新的树
`js
map(tree, callback, [options])
`
- tree:树形数组/对象
- callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
- options:配置项,可选,对象类型,支持基础配置项`js
import { map } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 111
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = map(data, (node) => ({ name:
No.${node.key} }));
console.log('result', result);
// {
// "name": "No.1",
// "children": [
// {
// "name": "No.11",
// "children": [
// { "name": "No.111" },
// { "name": "No.112" }
// ]
// },
// {
// "name": "No.12",
// "children": [
// {
// "name": "No.122",
// "children": [
// { "name": "No.1221" },
// { "name": "No.1222" }
// ]
// }
// ]
// }
// ]
// }
`#### find
遍历树形数组/对象,找到第一个返回非空值的节点
`js
find(tree, callback, [options])
`
- tree:树形数组/对象
- callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
- options:配置项,可选,对象类型,支持基础配置项`js
import { find } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 111
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = find(data, (node) => node.key < 100 && node.key > 10);
console.log('result', result);
// {
// "key": 11,
// "children": [
// {
// "key": 111
// },
// {
// "key": 112
// }
// ]
// }
`
#### some
遍历树形数组/对象,判断是否存在符合条件的节点
`js
some(tree, callback, [options])
`
- tree:树形数组/对象
- callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
- options:配置项,可选,对象类型,支持基础配置项`js
import { some } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 111
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = some(data, (node) => node.key < 100 && node.key > 10);
console.log('result', result);
// true
`#### toArray
将树形数组/对象转换为一维数组,数组会包含所有节点
`js
toArray(tree, [options])
`
- tree:树形数组/对象
- options:配置项,可选,对象类型,支持基础配置项
- leafKey:叶子节点的key,默认为isLeaf
- withChildren:是否携带子级数据,默认为false`js
import { toArray } from 'kk-utils-library/tree';const data = [
{
key: '1',
children: [
{
key: '2',
children: [
{
key: '3'
}
]
}
]
}
];
const result = toArray(data);
console.log('result', result);
// [
// {
// key: '1',
// isLeaf: false
// },
// {
// key: '2',
// isLeaf: false
// },
// {
// key: '3',
// isLeaf: true
// }
// ];
`#### fromArray
将数组转换为树形数组/对象
`js
fromArray(array, [options])
`
- array:数组
- options:配置项,可选,对象类型,不支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children
- parentKey:自定义父节点key名,默认值为parentId`js
import { fromArray } from 'kk-utils-library/tree';const data = [
{
id: '1',
name: '1'
},
{
id: '2',
name: '2',
parentId: '1'
},
{
id: '3',
name: '3',
parentId: '1'
},
{
id: '4',
name: '4',
parentId: '2'
},
{
id: '5',
name: '5'
}
];
const result = fromArray(data);
console.log('result', result);
// [
// {
// id: '1',
// name: '1',
// children: [
// {
// id: '2',
// name: '2',
// parentId: '1',
// children: [
// {
// id: '4',
// name: '4',
// parentId: '2'
// }
// ]
// },
// {
// id: '3',
// name: '3',
// parentId: '1'
// }
// ]
// },
// {
// id: '5',
// name: '5'
// }
// ];
`#### toTree
将数组转换为树形数组
这个方法和上面的
fromArray的区别在于:
- toTree支持root顶层标识配置
- toTree支持levelKey节点层级key配置
- toTree支持leafKey叶子节点key配置`js
toTree(tree, conditions, [options])
`
- tree:树形数组
- conditions:过滤条件,
- 可以为函数(item) => boolean
- 也可以为对象{ name: '名称搜索', code: '编码搜索‘ },多个条件是&&的关系,必须都满足
- options:配置项,可选,对象类型,不支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children
- parentKey:自定义父节点key名,默认值为parentId
- root:顶层标识配置
- levelKey:节点层级key配置
- leafKey:叶子节点key配置`js
import { toTree } from 'kk-utils-library/tree';const data = [
{
id: '1',
name: '1'
},
{
id: '2',
name: '2',
parentId: '1'
},
{
id: '3',
name: '3',
parentId: '1'
},
{
id: '4',
name: '4',
parentId: '2'
},
{
id: '5',
name: '5'
}
];
const result = toTree(data);
console.log('result', result);
// [
// {
// id: '1',
// name: '1',
// children: [
// {
// id: '2',
// name: '2',
// parentId: '1',
// children: [
// {
// id: '4',
// name: '4',
// parentId: '2',
// children: null,
// level: 3,
// isLeaf: true
// }
// ],
// level: 2,
// isLeaf: false
// },
// {
// id: '3',
// name: '3',
// parentId: '1',
// children: null,
// level: 2,
// isLeaf: true
// }
// ],
// level: 1,
// isLeaf: false
// },
// {
// id: '5',
// name: '5',
// children: null,
// level: 1,
// isLeaf: true
// }
// ];
`
#### getDepthAndLength
获取树结构的深度和最长子级长度`js
getDepthAndLength(tree, [options])
`
- tree:树形数组
- options:配置项,可选,对象类型,支持基础配置项`js
import { getDepthAndLength } from 'kk-utils-library/tree';const data = [
{
key: 1,
children: [
{
key: 11,
children: [
{
key: 99
},
{
key: 112
}
]
},
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222
}
]
}
]
}
]
}
];
const result = getDepthAndLength(data);
console.log('result', result);
// { "depth": 3, "length": 2 } // 深度和最长子级数量
`#### fixParentId
给树形数组每一项补上parentId
`js
fixParentId(array, [options])
`
- array:数组
- options:配置项,可选,对象类型,不支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children
- parentKey:自定义父节点key名,默认值为parentId`js
import { fixParentId } from 'kk-utils-library/tree';const data = [
{
id: 1,
children: [
{
id: 11,
children: [
{
id: 99
},
{
id: 112
}
]
},
{
id: 12,
children: [
{
id: 122,
children: [
{
id: 1221
},
{
id: 1222
}
]
}
]
}
]
}
];
const result = fixParentId(data);
console.log('result', result);
// [
// {
// id: 1,
// children: [
// {
// id: 11,
// children: [
// {
// id: 99,
// parentId: 11
// },
// {
// id: 112,
// parentId: 11
// }
// ],
// parentId: 1
// },
// {
// id: 12,
// children: [
// {
// id: 122,
// children: [
// {
// id: 1221,
// parentId: 122
// },
// {
// id: 1222,
// parentId: 122
// }
// ],
// parentId: 12
// }
// ],
// parentId: 1
// }
// ]
// }
// ];
`#### findAncestor
从树形数组里根据传入的节点
ID查找出它的所有祖先元素,返回一个有层级关系的tree数据`js
findAncestor(array, [options])
`
- array:数组
- options:配置项,可选,对象类型,不支持基础配置项
- root顶层标识配置
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children
- parentKey:自定义父节点key名,默认值为parentId
- withChildren:是否携带子级数据,默认为false`js
import { findAncestor } from 'kk-utils-library/tree';const data = [
{
id: 1,
children: [
{
id: 11,
children: [
{
id: 99,
parentId: 11
},
{
id: 112,
parentId: 11
}
],
parentId: 1
},
{
id: 12,
children: [
{
id: 122,
children: [
{
id: 1221,
parentId: 122
},
{
id: 1222,
parentId: 122
}
],
parentId: 12
}
],
parentId: 1
}
]
}
];
const result = findAncestor(data, 1221);
console.log('result', result);
// {
// id: 1,
// children: [
// {
// id: 12,
// children: [
// {
// id: 122,
// children: [
// {
// id: 1221,
// parentId: 122
// }
// ],
// parentId: 12
// }
// ],
// parentId: 1
// }
// ]
// }
`
excel-js
excel-js是kk-utils里的工具之一(1.2.0版本开始支持),其作用是有多个针对导出导出的方法函数,是我在写项目时积累下来的,几乎可以覆盖日常所需这个工具是基于
GitHub上开源的exceljs的代码,基于此工具库的基础上进行二次封装适合我业务的功能$3
#### importExcel
导入Excel
`js
importExcel(File, [options])
`
- File:要导入的Excel文件
- options:配置项,可选,对象类型
- allWorksheets:boolean,是否导入所有工作表,无论true还是false,返回的结果都是二维数组,默认为false
- getValue:(cell) => string,获取单元格的文本值,默认为(cell) => cell.text
- headerStartLine:number,表头开始行数,默认1开始
- headerTotalLine:number,表头总行数,默认1
- toJsonArray:boolean,是否转换成JSON格式,默认true
- false:则返回的数据是[[key, key, key], [value, value, value], [value,value,value]]这种格式
- true:则返回的数据是[{key, value}, {key, value}, {key, value}]这种格式`html
`
这是我要导入的Excel文件,第一行就是表头,总共一行表头,所以使用默认的headerStartLine和headerTotalLine就可以了,如果是多行表头或者开始不是第一行则自行调整或者传入对应参数`js
import { importExcel } from 'kk-utils-library/excel-js';const fileInput = document.getElementById('file');
const importButton = document.getElementById('import');
importButton.addEventListener('click', () => {
const file = fileInput.files[0];
if (!file) {
alert('请选择文件');
return;
}
importExcel(file, {
headerStartLine: 1,
headerTotalLine: 1
}).then((data) => {
console.log('data', data);
// [
// [
// {
// 操作类型: '新增',
// 客户名称: '测试1',
// 客户编码: 'TEST001',
// 客户类型: '供应商',
// 省: '辽宁省',
// 市: '沈阳市',
// 区: '辽中县'
// },
// {
// 操作类型: '修改',
// 客户名称: '测试2',
// 客户编码: 'TEST002',
// 客户类型: '客户',
// 省: '山西省',
// 市: '太原市',
// 区: '晋源区'
// },
// {
// 操作类型: '删除',
// 客户名称: '测试3',
// 客户编码: 'TEST003',
// 客户类型: '客户与供应商',
// 省: '吉林省',
// 市: '长春市',
// 区: '宽城区'
// },
// {
// 操作类型: '不变',
// 客户名称: '测试4',
// 客户编码: 'TEST004',
// 客户类型: '供应商',
// 省: '湖南省',
// 市: '长沙市',
// 区: '雨花区'
// }
// ]
// ];
});
});
`#### exportExcel
导出Excel
`js
exportExcel([options])
`
- filename:导出的文件名,默认为excel
- sheets:object[],要导出的工作表
- sheetName:string,表名,默认为SheetJS[index + 1]
- withDefaultStyle:boolean,携带默认样式,默认true,全局默认样式可通过setGlobalDefaultStyle()配置
- adaptiveWidth:boolean,自适应列宽,默认true
- adaptiveWidthOptions:object,自适应列宽配置项
- minWidth:number,最小列宽,默认5
- maxWidth:number,最大列宽,默认50
- interstice:number,列宽额外空隙,默认5
- ignoreLines:number[],计算列宽忽略的行,因为有些行会有合并单元格的操作,这样会导致合并行的列度最大并使用合并行的列宽作为整列的宽,所以可以传入合并行的行数来进行列宽计算忽略
- processing:(worksheet) => void,对工作表对象做操作处理,这个是在自动列宽和默认样式之前执行的
- extraProcessing:(worksheet) => void,对工作表对象做操作额外处理,这个是在自动列宽和默认样式之后执行的
- processing:(workbook) => void,对Excel对象做操作处理`html
``js
import { exportExcel } from 'kk-utils-library/excel-js';const exportButton = document.getElementById('export');
exportButton.addEventListener('click', () => {
const data = [
['姓名', '年龄', '性别'],
['张三', 18, '男'],
['李四', 19, '女'],
['王五', 20, '男']
];
exportExcel({
filename: 'test',
sheets: [
{
processing: (worksheet) => {
worksheet.addRows(data);
},
extraProcessing: (worksheet) => {
_merge(worksheet.getRow(1), {
fill: {
type: 'pattern',
pattern: 'solid',
fgColor: {
argb: 'FF99CCFF'
}
}
});
_merge(worksheet.getRow(2).getCell(2), {
fill: {
type: 'pattern',
pattern: 'solid',
fgColor: {
argb: 'FFFF4949'
}
}
});
}
}
]
});
});
`然后就可以看到下载了一份名为test.xlsx的文件,打开就是我们要的数据和效果了
!截屏2025-04-16 11.39.43.png
#### coordinatesFromRange
从一个区间范围获取所有的坐标矩阵
`js
coordinatesFromRange(range)
`
- range:[number, number, number, number]坐标点,四个参数分别代表开始行 开始列 结束行 结束列`js
import { coordinatesFromRange } from 'kk-utils-library/excel-js';const coordinatesResult = coordinatesFromRange([2, 3, 3, 4]);
console.log('coordinatesResult', coordinatesResult);
// [
// [2, 3],
// [2, 4],
// [3, 3],
// [3, 4]
// ];
`#### isCoordinateInCoordinates
判断一个坐标点是否在坐标矩阵内
`js
isCoordinateInCoordinates(coordinate, coordinates)
`
- coordinate:[number, number]坐标点
- coordinates:坐标矩阵`js
import { isCoordinateInCoordinates } from 'kk-utils-library/excel-js';const isCoordinateInCoordinatesResult = isCoordinateInCoordinates(
[2, 3],
coordinatesResult
);
const isCoordinateInCoordinatesResult2 = isCoordinateInCoordinates(
[12, 13],
coordinatesResult
);
console.log('isCoordinateInCoordinatesResult', isCoordinateInCoordinatesResult);
// true
console.log('isCoordinateInCoordinatesResult2', isCoordinateInCoordinatesResult2);
// false
`#### isCoordinateInRange
判断一个坐标点是否在坐标范围内
`js
isCoordinateInRange(coordinate, range)
`
- coordinate:[number, number]坐标点
- range:[number, number, number, number]坐标点,四个参数分别代表开始行 开始列 结束行 结束列`js
import { isCoordinateInRange } from 'kk-utils-library/excel-js';const isCoordinateInRangeResult = isCoordinateInRange(
[2, 3],
[2, 3, 5, 8]
);
const isCoordinateInRangeResult2 = isCoordinateInRange(
[12, 13],
[2, 3, 5, 8]
);
console.log('isCoordinateInRangeResult', isCoordinateInRangeResult);
// true
console.log('isCoordinateInRangeResult2', isCoordinateInRangeResult2);
// false
`#### getHorizontalMerges
获取横向合并数据,此数据是专门给
exceljs用的`js
getHorizontalMerges(excelData)
`
- excelData:二维数组`js
import {
getHorizontalMerges,
freeMerge as fm, // 代表自由合并 上下都合并,必须是正方形/长方形 起点为fm左上角单元格
horizontalMerge as hm, // 代表横向合并 起点为hm前一个单元格
verticalMerge as vm // 代表纵向合并 起点为vm上一个单元格
} from 'kk-utils-library/excel-js';const horizontalMergeAndVerticalMergeTestData = [
[11, 12, hm, hm, 15, 16, 17, 18, 19],
[21, 22, 23, 24, 25, fm, fm, 28, 29],
[31, 32, vm, 34, 35, fm, fm, 38, 39],
[41, 42, vm, 44, vm, 46, hm, hm, hm],
[51, 52, 53, 54, vm, 56, 57, 58, 59],
[61, 62, vm, 64, vm, 66, 67, 68, 69],
[71, 72, vm, 74, 75, 76, 77, 78, 79],
[81, 82, vm, 84, 85, 86, 87, 88, 89],
[91, 92, 93, 94, 95, 96, 97, 98, 99]
];
const horizontalMergesResult = getHorizontalMerges(
horizontalMergeAndVerticalMergeTestData
);
console.log('horizontalMergesResult', horizontalMergesResult);
// [
// [1, 2, 1, 4],
// [4, 6, 4, 9]
// ];
`#### getVerticalMerges
获取纵向合并数据,此数据是专门给
exceljs用的`js
getVerticalMerges(excelData)
`
- excelData:二维数组`js
import {
getVerticalMerges,
freeMerge as fm, // 代表自由合并 上下都合并,必须是正方形/长方形 起点为fm左上角单元格
horizontalMerge as hm, // 代表横向合并 起点为hm前一个单元格
verticalMerge as vm // 代表纵向合并 起点为vm上一个单元格
} from 'kk-utils-library/excel-js';const horizontalMergeAndVerticalMergeTestData = [
[11, 12, hm, hm, 15, 16, 17, 18, 19],
[21, 22, 23, 24, 25, fm, fm, 28, 29],
[31, 32, vm, 34, 35, fm, fm, 38, 39],
[41, 42, vm, 44, vm, 46, hm, hm, hm],
[51, 52, 53, 54, vm, 56, 57, 58, 59],
[61, 62, vm, 64, vm, 66, 67, 68, 69],
[71, 72, vm, 74, 75, 76, 77, 78, 79],
[81, 82, vm, 84, 85, 86, 87, 88, 89],
[91, 92, 93, 94, 95, 96, 97, 98, 99]
];
const verticalMergesResult = getVerticalMerges(
horizontalMergeAndVerticalMergeTestData
);
console.log('verticalMergesResult', verticalMergesResult);
// [
// [2, 3, 4, 3],
// [3, 5, 6, 5],
// [5, 3, 8, 3]
// ];
`#### getMatrixMerges
获取纵向和横向的合并数据,此数据是专门给
exceljs用的`js
getMatrixMerges(excelData)
`
- excelData:二维数组`js
import {
getMatrixMerges,
freeMerge as fm, // 代表自由合并 上下都合并,必须是正方形/长方形 起点为fm左上角单元格
horizontalMerge as hm, // 代表横向合并 起点为hm前一个单元格
verticalMerge as vm // 代表纵向合并 起点为vm上一个单元格
} from 'kk-utils-library/excel-js';const freeMergeTestData = [
[11, 21, fm, fm, fm, 61, fm, fm, 91],
[21, 22, fm, fm, fm, 26, 27, 28, 29],
[31, 32, fm, fm, fm, 36, fm, fm, 39],
[41, 42, 43, 44, 45, 46, fm, fm, 49],
[51, 52, fm, fm, 55, 56, fm, fm, 59],
[fm, 62, fm, fm, 65, 66, 67, 68, 69],
[fm, 72, fm, fm, 75, 76, 77, 78, 79],
[81, 82, fm, fm, 85, 86, 87, 88, 89],
[91, 92, 93, 94, 95, 96, 97, 98, 99]
];
const matrixMergesResult = getMatrixMerges(freeMergeTestData);
console.log('matrixMergesResult', matrixMergesResult);
// [
// [1, 2, 3, 5],
// [1, 6, 1, 8],
// [2, 6, 5, 8],
// [4, 2, 8, 4],
// [5, 1, 7, 1]
// ];
`#### getMerges
获取合并数据,此数据是专门给
exceljs用的
此方法涵盖了前面三种getMerge方法,会自动识别数据里的hm, vm, fm`js
getMerges(excelData)
`
- excelData:二维数组`js
import {
getMerges,
freeMerge as fm, // 代表自由合并 上下都合并,必须是正方形/长方形 起点为fm左上角单元格
horizontalMerge as hm, // 代表横向合并 起点为hm前一个单元格
verticalMerge as vm // 代表纵向合并 起点为vm上一个单元格
} from 'kk-utils-library/excel-js';const horizontalMergeAndVerticalMergeTestData = [
[11, 12, hm, hm, 15, 16, 17, 18, 19],
[21, 22, 23, 24, 25, fm, fm, 28, 29],
[31, 32, vm, 34, 35, fm, fm, 38, 39],
[41, 42, vm, 44, vm, 46, hm, hm, hm],
[51, 52, 53, 54, vm, 56, 57, 58, 59],
[61, 62, vm, 64, vm, 66, 67, 68, 69],
[71, 72, vm, 74, 75, 76, 77, 78, 79],
[81, 82, vm, 84, 85, 86, 87, 88, 89],
[91, 92, 93, 94, 95, 96, 97, 98, 99]
];
const mergesResult = getMerges(horizontalMergeAndVerticalMergeTestData);
console.log('mergesResult', mergesResult);
// [
// [1, 2, 1, 4],
// [4, 6, 4, 9],
// [2, 3, 4, 3],
// [3, 5, 6, 5],
// [5, 3, 8, 3],
// [1, 5, 3, 7]
// ];
`#### getDefaultStyle
获取全局默认样式
`js
getDefaultStyle()
``js
import { getDefaultStyle } from 'kk-utils-library/excel-js';const beforeStyle = getDefaultStyle();
console.log('beforeStyle', beforeStyle);
// {
// font: {
// size: 14
// },
// alignment: {
// vertical: 'top',
// wrapText: true
// },
// border: {
// top: {
// style: 'thin'
// },
// left: {
// style: 'thin'
// },
// bottom: {
// style: 'thin'
// },
// right: {
// style: 'thin'
// }
// },
// fill: {
// type: 'pattern',
// pattern: 'solid',
// fgColor: {
// argb: 'FF99CCFF'
// }
// }
// }
`#### getDefaultStyle
获取全局默认样式
`js
getDefaultStyle()
``js
import { setGlobalDefaultStyle } from 'kk-utils-library/excel-js';const afterStyle = setGlobalDefaultStyle({
font: {
size: 14
},
fill: {
type: 'pattern',
pattern: 'solid',
fgColor: {
argb: 'FF99CCFF'
}
}
});
const afterStyle = getDefaultStyle();
console.log('afterStyle', afterStyle);
// {
// font: {
// size: 14
// },
// alignment: {
// vertical: 'top',
// wrapText: true
// },
// border: {
// top: {
// style: 'thin'
// },
// left: {
// style: 'thin'
// },
// bottom: {
// style: 'thin'
// },
// right: {
// style: 'thin'
// }
// },
// fill: {
// type: 'pattern',
// pattern: 'solid',
// fgColor: {
// argb: 'FF99CCFF'
// }
// }
// }
`#### setAdaptiveWidth
设置Excel的单元格自适应宽度,如果你自己使用exceljs则需要自己使用
setAdaptiveWidth去处理,kk-utils里的导出默认是true了,可以自己传入调整参数,ExcelJs的参数需自己看文档`js
setAdaptiveWidth(Worksheet, [options])
``js
import { setAdaptiveWidth } from 'kk-utils-library/excel-js';
import ExcelJS from 'exceljs';const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('SheetJS');
worksheet.addRows([
['姓名', '简称', '年龄', '性别'],
['小明', '无', 18, '男'],
['古力那扎尔·拜合提亚尔', '古力娜扎', 16, '女'],
[
'巴勃罗·迭戈·荷瑟·山迪亚哥·弗朗西斯科·德·保拉·居安·尼波莫切诺·克瑞斯皮尼亚诺·德·罗斯·瑞米迪欧斯·西波瑞亚诺·德·拉·山迪西玛·特立尼达·玛利亚·帕里西奥·克里托·瑞兹·布拉斯科·毕加索',
'毕加索',
92,
'男'
]
]);
console.log('before worksheet widths', _map(worksheet.columns, 'width'));
// [null, null, null, null];
setAdaptiveWidth(worksheet, {
minWidth: 15,
maxWidth: 50
});
console.log('after worksheet widths', _map(worksheet.columns, 'width'));
// [50, 17, 15, 15];
`#### getDataHeader
获取最后渲染数据用的表头,因为表头我们可能需要合并,所以塞数据的时候原始表头不适用,需要使用列一一对应的表头才能更方便加入表体数据
`js
getDataHeader(excelData)
`
- excelData:二维数组`js
import { getDataHeader } from 'kk-utils-library/excel-js';const dataHeader = getDataHeader([
['编码', '时间', '已批准', '已核销', '未核销', hm, '已支付', '待支付'],
[vm, vm, vm, vm, '申请中', '未申请', vm, vm]
]);
console.log('dataHeader', dataHeader);
// [
// '编码',
// '时间',
// '已批准',
// '已核销',
// '申请中',
// '未申请',
// '已支付',
// '待支付'
// ];
`#### getColumnLetterByNumber
根据列下标
1开始获取对应的Excel字母列`js
getColumnLetterByNumber(number)
``js
import { getColumnLetterByNumber } from 'kk-utils-library/excel-js';const letter = getColumnLetterByNumber(5);
console.log('letter', letter);
// E
`#### getDepthAndLength
获取树结构的深度和最长子级长度
`js
getDepthAndLength(tree, [options])
`
- tree:树形数组
- options:配置项,可选,对象类型,支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children`js
import { getDepthAndLength } from 'kk-utils-library/excel-js';const getDepthAndLengthTree = [
{
key: 1,
children: [
{
{
key: 12,
children: [
{
key: 122,
children: [
{
key: 1221
},
{
key: 1222,
children: [
{
key: 12221
},
{
key: 12222
},
{
key: 12223
},
{
key: 12224
},
{
key: 12225
},
{
key: 12226
}
]
}
]
}
]
}
]
}
];
const getDepthAndLengthResult = getDepthAndLength(getDepthAndLengthTree);
console.log('getDepthAndLengthResult', getDepthAndLengthResult);
// { depth: 5, length: 6 }
`#### getFillHorizontalMergeKeyMap
获取表头横向合并列所对应的合并长度
`js
getFillHorizontalMergeKeyMap(Tree, [options])
`-
tree:树形数组
- options:配置项,可选,对象类型,支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children`js
import { getFillHorizontalMergeKeyMap } from 'kk-utils-library/excel-js';const treeHeader = [
{
prop: 'index',
label: '序号'
},
{
prop: 'budgetCycleItemName',
label: '预算周期项名称'
},
{
prop: 'expectedShipmentAmount',
label: '预发货金额',
children: [
{
prop: 'originalExpectedShipmentAmount',
label: '调整前金额'
},
{
prop: 'adjustedExpectedShipmentAmount',
label: '调整的金额'
},
{
prop: 'finalExpectedShipmentAmount',
label: '调整后金额'
}
]
},
{
prop: 'budgetAmount',
label: '预算金额',
children: [
{
prop: 'originalBudgetAmount',
label: '调整前金额'
},
{
prop: 'adjustedBudgetAmount',
label: '调整的金额'
},
{
prop: 'finalBudgetAmount',
label: '调整后金额'
}
]
}
];
const getFillHorizontalMergeKeyMapResult =
getFillHorizontalMergeKeyMap(treeHeader);
console.log(
'getFillHorizontalMergeKeyMapResult',
getFillHorizontalMergeKeyMapResult
);
// {
// index: 0,
// budgetCycleItemName: 0,
// expectedShipmentAmount: 2,
// originalExpectedShipmentAmount: 0,
// adjustedExpectedShipmentAmount: 0,
// finalExpectedShipmentAmount: 0,
// budgetAmount: 2,
// originalBudgetAmount: 0,
// adjustedBudgetAmount: 0,
// finalBudgetAmount: 0
// }
`#### tableHeaderToMultidimensionalArray
把树结构表头处理成Excel的表头
`js
tableHeaderToMultidimensionalArray(Tree, [options])
`-
tree:树形数组
- options:配置项,可选,对象类型,支持基础配置项
- itemKey:指定节点key名,默认值为id
- childrenKey:自定义子节点key名,默认值为children
- labelKey: 自定义节点文本key名,默认值为label`js
import { tableHeaderToMultidimensionalArray } from 'kk-utils-library/excel-js';const treeHeader = [
{
prop: 'index',
label: '序号'
},
{
prop: 'budgetCycleItemName',
label: '预算周期项名称'
},
{
prop: 'expectedShipmentAmount',
label: '预发货金额',
children: [
{
prop: 'originalExpectedShipmentAmount',
label: '调整前金额'
},
{
prop: 'adjustedExpectedShipmentAmount',
label: '调整的金额'
},
{
prop: 'finalExpectedShipmentAmount',
label: '调整后金额'
}
]
},
{
prop: 'budgetAmount',
label: '预算金额',
children: [
{
prop: 'originalBudgetAmount',
label: '调整前金额'
},
{
prop: 'adjustedBudgetAmount',
label: '调整的金额'
},
{
prop: 'finalBudgetAmount',
label: '调整后金额'
}
]
}
];
const multidimensionalArray = tableHeaderToMultidimensionalArray(treeHeader);
console.log('multidimensionalArray', multidimensionalArray);
// [
// [
// '序号',
// '预算周期项名称',
// '预发货金额',
// '-hm',
// '-hm',
// '预算金额',
// '-hm',
// '-hm'
// ],
// [
// '-vm',
// '-vm',
// '调整前金额',
// '调整的金额',
// '调整后金额',
// '调整前金额',
// '调整的金额',
// '调整后金额'
// ]
// ];
`#### conversionToJsonArray
把Excel数据转成数组对象格式
`js
conversionToJsonArray(ExcelData, [options])
`- excelData:二维数组
- options:配置项,可选,对象类型
- headerStartLine:
number,表头开始行数,默认1开始
- headerTotalLine:number,表头总行数,默认1`js
import { conversionToJsonArray } from 'kk-utils-library/excel-js';const arrayData = [
['操作类型', '客户名称', '客户编码', '省', '市', '区'],
[
'不变',
'苏州秦曼商贸有限公司',
'11605028',
'苏北省(作废)',
'苏州市',
'吴中区'
],
[
'不变',
'测试-嘉士柏河南省郑州——麻三定/麻坤丽',
'JSB12001003',
'河南省',
'郑州市',
'中原区'
]
];
const conversionToJsonArrayResult = conversionToJsonArray(arrayData);
console.log('conversionToJsonArrayResult', conversionToJsonArrayResult);
// [
// {
// 操作类型: '不变',
// 客户名称: '苏州秦曼商贸有限公司',
// 客户编码: '11605028',
// 省: '苏北省(作废)',
// 市: '苏州市',
// 区: '吴中区'
// },
// {
// 操作类型: '不变',
// 客户名称: '测试-嘉士柏河南省郑州——麻三定/麻坤丽',
// 客户编码: 'JSB12001003',
// 省: '河南省',
// 市: '郑州市',
// 区: '中原区'
// }
// ];
``