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 🙏

© 2025 – Pkg Stats / Ryan Hefner

kk-utils-library

v1.2.1

Published

## 功能模块 - [bidirectional-mapping](#bidirectional-mapping) - [tree](#tree)

Readme

A Javascript Utils Library

功能模块

安装

npm install kk-utils-library -S
pnpm add kk-utils-library -S

bidirectional-mapping

bidirectional-mapping 是 kk-utils 里的工具之一,其作用是键值对双向映射

它可以把一个字符串、数字、对象、数组按我们的映射规则进行转换

BiMap:用来生成映射规则的一个工具

BiMapConversion:用来把目标数据转换成映射规则处理后的数据

使用方法

编写映射规则

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可以处理键名转换也可以处理值的转换
  ]
]);

转换数据

写一个测试数据
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
  }
];
使用映射规则转换数据
const mapData = BiMapConversion(data, map);
console.log(mapData);
转换结果
[
  {
    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: '一'
  }
];
截图对比

截屏2025-04-10 14.35.14.png

可以看到测试数据已经按我定义好的映射规则进行了转换,无论是键名还是值,都转换成功了的

融入业务使用

比如和后端对接接口的时候,后端键名不规范,我们写逻辑的时候就要去重新命名,多处用同一接口的话,就要到处维护,不方便。再比如同一个字段的值一会是字符串,一会是数字,就会对我们的逻辑代码造成不易擦觉得影响,所以可以使用这个工具在接口处统一做出入数据管理

在接口处统一做出入数据管理

比如我要对接后端的反馈接口的 CURD

定义公共分页映射

// 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']
    ]
  ]
]);

定义接口映射

// 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([]);

接口处使用映射做出入口参数管理

// 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 再传递给后台的

其他

除了以上业务使用,我们还能用来对单个数据处理

// 我们用回上面定义的new BiMap
const mapping_a = map.get('A'); // a
const mapping_A = map.get('a'); // A

tree

treekk-utils里的工具之一,其作用是有多个针对树结构的方法函数,是我在写项目时积累下来的,几乎可以覆盖日常所需

当然,初始的代码是借鉴GitHubnewarea0大佬的代码,基于此源码的基础上进行修改和新增适合我业务的功能

基础配置项

绝大部分函数的基础配置项都有以下选项可用

strategy

设置搜索策略,默认策略为pre

pre:深度优先,正序搜索

post:深度优先,反序搜索

breadth:广度优先

import { filter } from 'kk-utils-library/tree';
const result = filter(tree, node => node.id > 2, { strategy: 'post' })

childrenKey

自定义子节点key名,默认值为children

import { filter } from 'kk-utils-library/tree';
const result = filter(tree, node => node.id > 2, { childrenKey: 'items' })

getChildrenKey

设置一棵树上多种自定义子节点key

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';
      }
    }
  }
);

可用方法

forEach

遍历树形数组/对象,对每个节点执行回调

forEach(tree, callback, [options])
  • tree:树形数组/对象
  • callback:回调函数,对每个节点执行回调
  • options:配置项,可选,对象类型,支持基础配置项
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

遍历树形数组/对象,并把返回非真值的节点剔除(不会影响原结构,返回的树是新生成的)

filter(tree, callback, [options])
  • tree:树形数组/对象
  • callback:回调函数,对每个节点执行回调
  • options:配置项,可选,对象类型,支持基础配置项
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支持函数过滤和对象/数组结构的值匹配过滤,但是值匹配过滤并不是全等,而是包含关系
conditionsFilter(tree, conditions, [options])
  • tree:树形数组
  • conditions:过滤条件,
    • 可以为函数(item) => boolean
    • 也可以为对象{ name: '名称搜索', code: '编码搜索‘ },多个条件是&&的关系,必须都满足
  • options:配置项,可选,对象类型,不支持基础配置项
    • childrenKey:自定义子节点key名,默认值为children
    • withChildren:子级没数据的时候是否携带子级数据返回,默认值为true
    • allChildren:是否返回所有子级数据,默认值为false
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

遍历树形数组/对象,根据返回的对象,组成新的树

map(tree, callback, [options])
  • tree:树形数组/对象
  • callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
  • options:配置项,可选,对象类型,支持基础配置项
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

遍历树形数组/对象,找到第一个返回非空值的节点

find(tree, callback, [options])
  • tree:树形数组/对象
  • callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
  • options:配置项,可选,对象类型,支持基础配置项
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

遍历树形数组/对象,判断是否存在符合条件的节点

some(tree, callback, [options])
  • tree:树形数组/对象
  • callback:每次迭代调用的函数,需要返回一个对象,返回的对象上无需包括子节点
  • options:配置项,可选,对象类型,支持基础配置项
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

将树形数组/对象转换为一维数组,数组会包含所有节点

toArray(tree, [options])
  • tree:树形数组/对象
  • options:配置项,可选,对象类型,支持基础配置项
    • leafKey:叶子节点的key,默认为isLeaf
    • withChildren:是否携带子级数据,默认为false
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

将数组转换为树形数组/对象

fromArray(array, [options])
  • array:数组
  • options:配置项,可选,对象类型,不支持基础配置项
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
    • parentKey:自定义父节点key名,默认值为parentId
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配置
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配置
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

获取树结构的深度和最长子级长度

getDepthAndLength(tree, [options])
  • tree:树形数组
  • options:配置项,可选,对象类型,支持基础配置项
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

fixParentId(array, [options])
  • array:数组
  • options:配置项,可选,对象类型,不支持基础配置项
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
    • parentKey:自定义父节点key名,默认值为parentId
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数据

findAncestor(array, [options])
  • array:数组
  • options:配置项,可选,对象类型,不支持基础配置项
    • root顶层标识配置
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
    • parentKey:自定义父节点key名,默认值为parentId
    • withChildren:是否携带子级数据,默认为false
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-jskk-utils里的工具之一(1.2.0版本开始支持),其作用是有多个针对导出导出的方法函数,是我在写项目时积累下来的,几乎可以覆盖日常所需

这个工具是基于GitHub上开源的exceljs的代码,基于此工具库的基础上进行二次封装适合我业务的功能

可用方法

importExcel

导入Excel

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}]这种格式
<input type="file" id="file" accept=".xlsx" />
<button id="import">导入所选文件</button>

这是我要导入的Excel文件,第一行就是表头,总共一行表头,所以使用默认的headerStartLine和headerTotalLine就可以了,如果是多行表头或者开始不是第一行则自行调整或者传入对应参数

截屏2025-04-16 11.42.34.png

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

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对象做操作处理
<button id="export">导出测试文件</button>
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

从一个区间范围获取所有的坐标矩阵

coordinatesFromRange(range)
  • range:[number, number, number, number]坐标点,四个参数分别代表开始行 开始列 结束行 结束列
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

判断一个坐标点是否在坐标矩阵内

isCoordinateInCoordinates(coordinate, coordinates)
  • coordinate:[number, number]坐标点
  • coordinates:坐标矩阵
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

判断一个坐标点是否在坐标范围内

isCoordinateInRange(coordinate, range)
  • coordinate:[number, number]坐标点
  • range:[number, number, number, number]坐标点,四个参数分别代表开始行 开始列 结束行 结束列
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用的

getHorizontalMerges(excelData)
  • excelData:二维数组
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用的

getVerticalMerges(excelData)
  • excelData:二维数组
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用的

getMatrixMerges(excelData)
  • excelData:二维数组
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

getMerges(excelData)
  • excelData:二维数组
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

获取全局默认样式

getDefaultStyle()
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

获取全局默认样式

getDefaultStyle()
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的参数需自己看文档

setAdaptiveWidth(Worksheet, [options])
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

获取最后渲染数据用的表头,因为表头我们可能需要合并,所以塞数据的时候原始表头不适用,需要使用列一一对应的表头才能更方便加入表体数据

getDataHeader(excelData)
  • excelData:二维数组
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字母列

getColumnLetterByNumber(number)
import { getColumnLetterByNumber } from 'kk-utils-library/excel-js';

const letter = getColumnLetterByNumber(5);
console.log('letter', letter);
// E

getDepthAndLength

获取树结构的深度和最长子级长度

getDepthAndLength(tree, [options])
  • tree:树形数组
  • options:配置项,可选,对象类型,支持基础配置项
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
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

获取表头横向合并列所对应的合并长度

getFillHorizontalMergeKeyMap(Tree, [options])
  • tree:树形数组
  • options:配置项,可选,对象类型,支持基础配置项
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
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的表头

tableHeaderToMultidimensionalArray(Tree, [options])
  • tree:树形数组
  • options:配置项,可选,对象类型,支持基础配置项
    • itemKey:指定节点key名,默认值为id
    • childrenKey:自定义子节点key名,默认值为children
    • labelKey: 自定义节点文本key名,默认值为label
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数据转成数组对象格式

conversionToJsonArray(ExcelData, [options])
  • excelData:二维数组
  • options:配置项,可选,对象类型
    • headerStartLine:number,表头开始行数,默认1开始
    • headerTotalLine:number,表头总行数,默认1
import { conversionToJsonArray } from 'kk-utils-library/excel-js';

const arrayData = [
  ['操作类型', '客户名称', '客户编码', '省', '市', '区'],
  [
    '不变',
    '苏州秦曼商贸有限公司',
    '11605028',
    '苏北省(作废)',
    '苏州市',
    '吴中区'
  ],
  [
    '不变',
    '测试-嘉士柏河南省郑州——麻三定/麻坤丽',
    'JSB12001003',
    '河南省',
    '郑州市',
    '中原区'
  ]
];
const conversionToJsonArrayResult = conversionToJsonArray(arrayData);
console.log('conversionToJsonArrayResult', conversionToJsonArrayResult);
// [
//   {
//     操作类型: '不变',
//     客户名称: '苏州秦曼商贸有限公司',
//     客户编码: '11605028',
//     省: '苏北省(作废)',
//     市: '苏州市',
//     区: '吴中区'
//   },
//   {
//     操作类型: '不变',
//     客户名称: '测试-嘉士柏河南省郑州——麻三定/麻坤丽',
//     客户编码: 'JSB12001003',
//     省: '河南省',
//     市: '郑州市',
//     区: '中原区'
//   }
// ];