由于最近需要做文件的文档归类编号管理。比较理想的情况是这样的:1个公司下有多个项目,1个项目下有多个文档。但是实际的情况却不是这样的,实际的情况是公司、项目、案卷等是一种多级无限嵌套结构,而文件挂在最终的案卷下,而此时我们需要求得此处文件的文档归类编号,以便于文件查找。而在原始的数据中,并没有此功能,也就是说根本就没有当前所要查询的树节点中的层级内节点顺序编号。
而我的做法是这样的,既然源数据中没有层级内节点顺序编号,那就添加即可。因而我在做的时候,就是采用的sql查询,通过listNo的排序字段,保证查询出的树节点信息都是有序的,这是一个前提。然后依据此前提的背景下,通过给有效节点添加列表层级内排序属性字段listIndex
,再使用递归,保证将节点树中所有的当前文件父节点编号全部查询,并拼接成自己所想要的文件分类编号。
db.js数据源
var listObj = [
{
id : '1|'
,name : '公司1'
,parentId : -1
,nlevel : 0
,listNo : 1
}
,{
id : '5784|'
,name : '公司2'
,parentId : -1
,nlevel : 0
,listNo : 2
}
,{
id : '5798|'
,name : '公司3'
,parentId : -1
,nlevel : 0
,listNo : 5799
}
,{
id : '5806|'
,name : '公司4'
,parentId : -1
,nlevel : 0
,listNo : 5807
}
,{
id : '1|2|'
,name : '项目1'
,parentId : 1
,nlevel : 1
,listNo : 2
}
,{
id : '1|5783|'
,name : '项目2'
,parentId : 1
,nlevel : 1
,listNo : 5783
}
,{
id : '1|2|3|'
,name : '文件1'
,parentId : 2
,nlevel : 2
,listNo : 3
}
,{
id : '1|2|5781|'
,name : '文件2'
,parentId : 2
,nlevel : 2
,listNo : 10
}
];
var mapObj = {
id : '1|2|5781|'
,name : '文件2'
,parentId : 2
,nlevel : 2
,listNo : 10
};
classNo.js
let ClassNo = (function () {
const ROOT_NODE_PARENT_ID = -1;
/**
* 执行列表排序,排序规则为从小到大
* @param a 列表中前一个对象
* @param b 列表中后一个对象
* @returns {number}
*/
let sortASC = function (a,b) {
return a.listNo - b.listNo;
};
/**
* @param classNo 分类编号
* @param currentNode 当前节点
* @returns string 分类编号
*/
function appendParentNo(classNo,currentNode) {
return currentNode.listIndex + '.' + classNo;
}
/**
* '1|2|5781|' 这是id的数据类型,我们要从中获取第三条数据即为真实的id值
* @param id 传入的id字符串
* @returns {number} 返回number类型的数据
*/
function getTrueId(id){
// 以'|'作为字符串分隔符来切分字符串
let idArray = id.split('|');
// 去掉最后一个空的数据
idArray.pop();
// 获取真实的数据
let trueId = idArray.pop();
// 将返回的字符串转换成数字类型
return Number(trueId);
}
/**
* 格式化类型编号
* 现在的类型编号为:1.1.2,期望转换成的类型编号为01.01.02
* @param classNo 类型编号
* @returns {string} 返回的数据类型
*/
function formatClassNo(classNo) {
let result = ''; // 定义返回的结果
// 字符串切分成字符数组
let classNoArray = classNo.split('.');
classNoArray.forEach(str => {
// 如果当前项的字符长度等于1,则在前面补足0,否则不进行任何操作
str = addZero(str);
result += str + '.';
});
// 最后,切掉后面的'.'即可
return result.substring(0,result.length - 1);
}
/**
* 如果当前项的字符长度等于1,则在前面补足0,否则不进行任何操作
* @param str listIndex编号
* @returns {*} 返回格式化后的字符串
*/
function addZero(str){
// 如果字符长度为1,则在前面补足0
if(str.length === 1){
return '0' + str;
}
// 否则直接返回
return str;
}
/**
* 给列表添加listIndex索引
* @param list
*/
function addListIndexToListCollection(list) {
let listIndex = 1 // 列表排序
,rootNodeList = [] // 根节点数组
,addListIndexList = []; // 追加listIndex后的数组
// 将根节点全部添加到容器中
rootNodeList = getRootList(list);
// 循环遍历根节点,完成listIndex属性的添加操作
rootNodeList.forEach(node => {
// 追加listIndex属性
node.listIndex = listIndex;
// 将追加后的节点添加到addListIndexList容器中
addListIndexList.push(node);
// 递归添加listIndex
recursionListIndex(node,list,addListIndexList);
// listIndex完成自增加操作
listIndex ++;
});
// 将追加后的结果直接返回
return addListIndexList;
}
/**
* 递归追加listIndex属性
* @param parentNode 父级节点
* @param list 节点数组
* @param addListIndexList 追加listIndex属性的数组
*/
function recursionListIndex(parentNode, list , addListIndexList) {
// 创建当前层级节点集合
let currentNodeList = []
,listIndex = 1;
// 循环遍历获取本层级下的所有节点
list.forEach(node => {
// 如果当前节点的parentId等于父节点的id,则说明当前节点属于父节点的子节点,故将其添加到容器中
if(node.parentId === getTrueId(parentNode.id)){
currentNodeList.push(node);
}
});
// 完成集合的从小到大排序
currentNodeList.sort(sortASC);
// 循环遍历追加listIndex属性
currentNodeList.forEach(node => {
// 给当前层级追加listIndex属性
node.listIndex = listIndex;
// 将追加后的节点添加到addListIndexList容器中
addListIndexList.push(node);
// 继续递归下一层级
recursionListIndex(node,list,addListIndexList);
// 追加完成之后,listIndex自增
listIndex ++;
})
}
/**
* 获取列表中所有的根节点,同时将根节点按照列表内从小到大进行排序
* @param list
* @returns {Array}
*/
function getRootList(list) {
// 创建根节点容器
let rootNodeList = [];
// 循环遍历所有的节点,将根节点放入到根节点容器中
list.forEach(node => {
if(node.parentId === ROOT_NODE_PARENT_ID){
rootNodeList.push(node);
}
});
rootNodeList.sort(sortASC);
return rootNodeList;
}
/**
* 递归调用,获取类的层级拼接字符串
* @param addListIndexList 添加listIndex属性的集合
* @param map 所要查询的对象
*/
function getClassNo(addListIndexList,map) {
let classNo = '' // 分类编号
,currentNode = {}; // 当前节点
// 获取叶子节点中的编号
addListIndexList.forEach(node => {
// 如果当前节点id等于对象的id,则默认该当前节点为我们所要查询的叶子节点
if(node.id === map.id){
// 将当前节点标记为currentNode
currentNode = node;
// 获取叶子节点中的数据
classNo = classNo + currentNode.listIndex;
}
});
// 递归调用,查询该节点的所有父类节点编号,并拼接字符串
classNo = addALLParentNo(addListIndexList,currentNode,classNo);
return classNo;
}
/**
* 递归查找所有的父类节点
* @param validList 有效的工程结构树节点
* @param node 当前工程结构树节点
* @param classNo 返回的类型编号
*/
function addALLParentNo(validList, node, classNo) {
for(let i = 0,validListLength = validList.length; i < validListLength; i ++){
// 获取当前节点
let currentNode = validList[i];
// 获取真实的id
let trueId = getTrueId(currentNode.id);
// 如果当前节点的id等于子节点的parentId,则追加父节点分类编号
if(trueId === node.parentId){
// 追加父类分类编号
classNo = appendParentNo(classNo,currentNode);
// 当前节点上移
node = currentNode;
// 递归调用自身,并返回生成的结果
classNo = addALLParentNo(validList,node,classNo);
// 后续操作直接终止
break;
}
}
// 最终将生成的数据返回
return classNo;
}
/**
* 获取节点的完整树节点编号
* @param list 工程结构树对象数组
* @param map 当前工程结构树节点
* @returns {string} 返回的数据类型
*/
function getNodeFullClassNo(list,map){
let classNo = '' // 创建类型字符串
,addListIndexList = [];
// 对集合追加列表排序参数listIndex
addListIndexList = addListIndexToListCollection(list);
// 在集合中递归调用,查询并拼接档号分类字符串
classNo = getClassNo(addListIndexList,map);
// 格式化类型编号
return formatClassNo(classNo);
}
return {
getNodeFullClassNo : getNodeFullClassNo
};
})();
recursion.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/db.js"></script>
<script src="js/classNo.js"></script>
</head>
<body>
<script type="application/javascript">
var classNo = ClassNo.getNodeFullClassNo(listObj,mapObj);
console.log(classNo);
</script>
</body>
</html>
运行之后,显示的结果如下:
01.01.02
由于我们输入的是文件2
,该节点在位于同级中的第二位,所以说查询的编号为02
,其位于项目1
(编号为01
)下,而项目1
又位于公司1
(编号为01
)下,所以说完整的编号为01.01.02
,与上述程序运行结果相一致。