|
@@ -484,28 +484,12 @@ function isOptionNode(node) {
|
|
|
}
|
|
|
|
|
|
// 判断蓝色子项和孙子项是否打勾了必填
|
|
|
- function hasRequireData(node, hasRequire = false) {
|
|
|
- if (node.children
|
|
|
- && node.children.length
|
|
|
- && node.children[0].children
|
|
|
- && node.children[0].children.length
|
|
|
- && isProcessNode(node.children[0].children[0])
|
|
|
- ) {
|
|
|
- node.children.forEach(subNode => {
|
|
|
- if (subNode.children && subNode.children.length) {
|
|
|
- subNode.children.forEach(subSubNode => {
|
|
|
- if (subSubNode.data.required) {
|
|
|
- hasRequire = true;
|
|
|
- }
|
|
|
- })
|
|
|
- } else {
|
|
|
- subNode.children.forEach(subSubNode => {
|
|
|
- hasRequire = hasRequireData(subSubNode.children);
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- return hasRequire;
|
|
|
+ function hasRequireData(node) {
|
|
|
+ const requiredList = node.getPosterity().filter(subNode =>
|
|
|
+ subNode.data.required === true
|
|
|
+ );
|
|
|
+ if (requiredList.length) { return true }
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
// 这里判断有无无定额的情况,有的就返回true,没有就返回false
|
|
@@ -518,8 +502,17 @@ function isOptionNode(node) {
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
+ //这里是兼容第一层直接是定额的白色节点
|
|
|
+ if (node.children && node.children.length) {
|
|
|
+ node.children.forEach(subNode => {
|
|
|
+ if (subNode.data.rationID) {
|
|
|
+ isRequired = true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
return isRequired;
|
|
|
}
|
|
|
+
|
|
|
// 获取选套定额
|
|
|
function getOptionalData(node, list = []) {
|
|
|
node.children.forEach(element => {
|
|
@@ -572,11 +565,97 @@ function isOptionNode(node) {
|
|
|
})
|
|
|
return { requireRation, optionalRation };
|
|
|
}
|
|
|
-// 从指引节点,获取分类特征、必套定额数据
|
|
|
-function getItemClassData(nodes, prefix) {
|
|
|
- let nodeRequireRation = [];
|
|
|
- let optionalRationList = [];
|
|
|
+
|
|
|
+ //获取定额数据
|
|
|
+ // requireRationData必套定额对象
|
|
|
+ // optionalRationData 选逃定额对象
|
|
|
+ // classGroups classCode文字和id键值对
|
|
|
+ // classCodeList 各个classCode的pID和ID的关系
|
|
|
+ // prefixID为上一个必填项的选项ID
|
|
|
+ function getItemData(nodes, requireRationData={}, optionalRationData={}, classGroups={}, prefixID = '') {
|
|
|
const processNodes = nodes.filter(node => isProcessNode(node));
|
|
|
+ // const classGroups = []; // 同层必填选项的数组(二维数组)
|
|
|
+ processNodes.forEach(processNode => {
|
|
|
+ // 蓝色节点,必填
|
|
|
+ if (processNode.data.required) {
|
|
|
+ // 白色节点
|
|
|
+ const optionNodes = processNode.children.filter(node => isOptionNode(node));
|
|
|
+ optionNodes.forEach(optionNode => {
|
|
|
+ if (!requireRationData[optionNode.data.ID]) requireRationData[optionNode.data.ID] = [];
|
|
|
+ if (!optionalRationData[optionNode.data.ID]) optionalRationData[optionNode.data.ID] = [];
|
|
|
+ //白色节点下没有蓝色节点,就是到底了
|
|
|
+ if (!optionNode.children.some(subOptionNode => isProcessNode(subOptionNode))) {
|
|
|
+ // 是否必套定额
|
|
|
+ if (isRequireData(optionNode)) {
|
|
|
+ optionNode.children.forEach(subOptionNode => {
|
|
|
+ requireRationData[optionNode.data.ID].push([subOptionNode.data.rationID])
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ optionalRationData[optionNode.data.ID] = getOptionalData(optionNode);
|
|
|
+ }
|
|
|
+ const kV = {};
|
|
|
+ kV[optionNode.data.ID] = optionNode.data.name;
|
|
|
+ Object.assign(classGroups, kV);
|
|
|
+ } else {
|
|
|
+ const kV = {};
|
|
|
+ kV[optionNode.data.ID] = optionNode.data.name;
|
|
|
+ Object.assign(classGroups, kV);
|
|
|
+ // 后代项是否有必填
|
|
|
+ if (hasRequireData(optionNode)) {
|
|
|
+ //后代项有必填
|
|
|
+ getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID)
|
|
|
+
|
|
|
+ } else {
|
|
|
+ //后代项无必填
|
|
|
+ optionNode.children.forEach(subOptionNode => {
|
|
|
+ // 是否必套定额
|
|
|
+ if (isRequireData(optionNode)) {
|
|
|
+ if (!requireRationData[subOptionNode.parent.data.ID]) requireRationData[subOptionNode.parent.data.ID] = [];
|
|
|
+ requireRationData[subOptionNode.parent.data.ID].push(getOptionalData(subOptionNode));
|
|
|
+ } else {
|
|
|
+ if (!optionalRationData[subOptionNode.parent.data.ID]) optionalRationData[subOptionNode.data.ID] = [];
|
|
|
+ optionalRationData[subOptionNode.parent.data.ID].push(...getOptionalData(subOptionNode));
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 蓝色节点,非必填
|
|
|
+ if (hasRequireData(processNode)) {
|
|
|
+ // debugger
|
|
|
+ //后代项有必填
|
|
|
+ if(isProcessNode(processNode)){
|
|
|
+ //蓝色
|
|
|
+ processNode.children.forEach((subProcessNode)=>{
|
|
|
+ subProcessNode.children.forEach((sSubProcessNode)=>{
|
|
|
+ getItemData([sSubProcessNode], requireRationData, optionalRationData, classGroups,prefixID)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let key = processNode.data.ID;
|
|
|
+ if (prefixID) key = prefixID;
|
|
|
+ // 是否必套定额
|
|
|
+ if (isRequireData(processNode)) {
|
|
|
+ if (!requireRationData[key]) requireRationData[processNode.data.ID] = [];
|
|
|
+ requireRationData[key].push(getOptionalData(processNode));
|
|
|
+ } else {
|
|
|
+ if (!optionalRationData[key]) optionalRationData[processNode.data.ID] = [];
|
|
|
+ optionalRationData[key].push(...getOptionalData(processNode));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ })
|
|
|
+ return { requireRationData, optionalRationData, classGroups}
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+// 从指引节点,获取分类特征、必套定额数据
|
|
|
+function getItemClassData(nodes, prefix,
|
|
|
+ prefixID) {
|
|
|
+ const processNodes = nodes.filter(node => isProcessNode(node));
|
|
|
const classGroups = []; // 同层必填选项的数组(二维数组)
|
|
|
processNodes.forEach(processNode => {
|
|
|
const classItems = [];
|
|
@@ -589,78 +668,41 @@ function getItemClassData(nodes, prefix) {
|
|
|
|| !optionNode.children.some(node => isProcessNode(node)))) {
|
|
|
|
|
|
// 必套定额
|
|
|
- const requiredRationIDs = optionNode.children && optionNode.children.length ?
|
|
|
- optionNode.children.filter(node => !!node.data.rationID).map(node => node.data.rationID) : [];
|
|
|
- classItems.push({ name: optionNode.data.name, requiredRationIDs: [requiredRationIDs], optionalRationIDs: [] });
|
|
|
+ classItems.push({ name: optionNode.data.name, ID: optionNode.data.ID });
|
|
|
|
|
|
} else {
|
|
|
-
|
|
|
- if (optionNode.parent&& optionNode.parent.data.required) {
|
|
|
- //这里就开始遍历必填项下面的节点
|
|
|
- const { requireRation, optionalRation } = getSubData([optionNode]);
|
|
|
- nodeRequireRation = requireRation;
|
|
|
- optionalRationList = optionalRation;
|
|
|
- }else {
|
|
|
- // 非必填的蓝色节点
|
|
|
- // 子项是否有勾中必填
|
|
|
- if( hasRequireData(optionNode.parent)){
|
|
|
- // 获取必套的逻辑
|
|
|
- const { requireRation, optionalRation }= getSubData([optionNode.parent]);
|
|
|
- nodeRequireRation = requireRation;
|
|
|
- optionalRationList = optionalRation;
|
|
|
- }else{
|
|
|
- // 子项没有勾中必填
|
|
|
- // 判断当前蓝色节点下有没有无定额的情况,存在无定额的就下面全是选套,无无定额的情况就下面全是必套
|
|
|
- if (isRequireData(optionNode.parent)) {
|
|
|
- optionNode.children.forEach((subNode) => {
|
|
|
- if (subNode.children && subNode.children[0] && subNode.children[0].data.rationID) {
|
|
|
- nodeRation.push(subNode.children[0].data.rationID);
|
|
|
- } else {
|
|
|
- getSubData(subNode.children);
|
|
|
- }
|
|
|
- })
|
|
|
- } else {
|
|
|
-
|
|
|
- // 这里是选套的逻辑
|
|
|
- if(optionNode.children&&optionNode.children.length){
|
|
|
- optionalRationList.push(...getOptionalData(optionNode.parent));
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// classItems.push(...getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : ''));
|
|
|
- const childrenClassItem = getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '');
|
|
|
+ const childrenClassItem = getItemClassData(
|
|
|
+ optionNode.children,
|
|
|
+ optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '' ,
|
|
|
+ optionNode.parent && optionNode.parent.data.required ? optionNode.data.ID : ''
|
|
|
+ );
|
|
|
+
|
|
|
|
|
|
//如果返回的子项为空,但是父项又勾选了必填,则要把本身存入数组
|
|
|
if (optionNode.parent
|
|
|
&& optionNode.parent.data.required
|
|
|
&& childrenClassItem.length === 0
|
|
|
) {
|
|
|
- classItems.push({ name: optionNode.data.name, requiredRationIDs: nodeRequireRation, optionalRationIDs: optionalRationList });
|
|
|
+ classItems.push({ name: optionNode.data.name,ID: optionNode.data.ID });
|
|
|
} else {
|
|
|
- if (childrenClassItem.length > 0) {
|
|
|
-
|
|
|
- childrenClassItem.map(item => {
|
|
|
- item.requiredRationIDs =nodeRequireRation;
|
|
|
- item.optionalRationIDs = optionalRationList;
|
|
|
- })
|
|
|
- }
|
|
|
+
|
|
|
classItems.push(...childrenClassItem);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
if (classItems.length) {
|
|
|
classGroups.push(classItems);
|
|
|
-
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+
|
|
|
// 拼接上一文本
|
|
|
if (classGroups[0] && classGroups[0].length) {
|
|
|
// classGroups[0] = classGroups[0].map(name => prefix ? `${prefix}@${name}` : name);
|
|
|
classGroups[0] = classGroups[0].map(item => {
|
|
|
item.name = prefix ? `${prefix}@${item.name}` : item.name
|
|
|
+ item.ID = prefixID ? `${prefixID}@${item.ID}` : item.ID
|
|
|
return item;
|
|
|
});
|
|
|
}
|
|
@@ -673,11 +715,8 @@ function getItemClassData(nodes, prefix) {
|
|
|
for (let j = 0; j < nextClassItems.length; j++) {
|
|
|
// 拼接文本
|
|
|
const mergedName = `${prevClassItems[i].name}@${nextClassItems[j].name}`;
|
|
|
- // 拼接必套定额
|
|
|
- const mergedRationIDs = [...prevClassItems[i].requiredRationIDs, ...nextClassItems[j].requiredRationIDs];
|
|
|
- const mergedOptionalRationIDs = [...prevClassItems[i].optionalRationIDs, ...nextClassItems[j].optionalRationIDs]
|
|
|
-
|
|
|
- mergedClassItems.push({ name: mergedName, requiredRationIDs: mergedRationIDs, optionalRationIDs: mergedOptionalRationIDs });
|
|
|
+ const mergedID = `${prevClassItems[i].ID}@${nextClassItems[j].ID}`;
|
|
|
+ mergedClassItems.push({ name: mergedName,ID:mergedID});
|
|
|
}
|
|
|
}
|
|
|
classGroups.splice(0, 2, mergedClassItems);
|
|
@@ -716,16 +755,49 @@ function getOptionalRationIDs(itemClassData, allRationIDs) {
|
|
|
}
|
|
|
|
|
|
// 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
|
|
|
-function getErrorRationIDs(requiredRationIDs, optionalRationIDs, allRationIDs) {
|
|
|
+function getErrorRationIDs(requiredRationIDList, optionalRationIDs, allRationIDs) {
|
|
|
const errorRationIDs = [];
|
|
|
allRationIDs.forEach(rationID => {
|
|
|
- if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
|
|
|
- errorRationIDs.push(rationID);
|
|
|
- }
|
|
|
+ requiredRationIDList.forEach(requiredRationIDs=>{
|
|
|
+ if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
|
|
|
+ errorRationIDs.push(rationID);
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
});
|
|
|
return [...new Set(errorRationIDs)];
|
|
|
}
|
|
|
|
|
|
+//把classcode和必套选套定额结合在一起
|
|
|
+function combineData(classCodeData,requireRationData,optionalRationData,classGroups){
|
|
|
+ return classCodeData.map(classCodeData=>{
|
|
|
+ const errorRationIDs=[];
|
|
|
+ const optionalRationIDs=[];
|
|
|
+ const requiredRationIDs=[];
|
|
|
+ let name='';
|
|
|
+ const classCodeIDs=classCodeData.ID;
|
|
|
+ if(/@/.test(classCodeIDs)){
|
|
|
+ classCodeIDs.split('@').forEach((classCodeID)=>{
|
|
|
+ if(name){
|
|
|
+ name=name+'@'+classGroups[classCodeID]
|
|
|
+ }else{
|
|
|
+ name=classGroups[classCodeID]
|
|
|
+ };
|
|
|
+ if(requireRationData[classCodeID]&&requireRationData[classCodeID].length) requiredRationIDs.push(...requireRationData[classCodeID]);
|
|
|
+ if(optionalRationData[classCodeID]&&optionalRationData[classCodeID].length) optionalRationIDs.push(...optionalRationData[classCodeID]);
|
|
|
+ })
|
|
|
+ return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
|
|
|
+ }else{
|
|
|
+ name=classGroups[classCodeIDs];
|
|
|
+ if(requireRationData[classCodeIDs]&&requireRationData[classCodeIDs].length) requiredRationIDs.push(...requireRationData[classCodeIDs]);
|
|
|
+ if(optionalRationData[classCodeIDs]&&optionalRationData[classCodeIDs].length) optionalRationIDs.push(...optionalRationData[classCodeIDs]);
|
|
|
+ return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// 生成清单分类
|
|
|
async function generateClassData(libID) {
|
|
|
const lib = await billsGuideLibModel.findOne({ ID: libID }).lean();
|
|
@@ -755,14 +827,16 @@ async function generateClassData(libID) {
|
|
|
const guidanceTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
|
|
|
guidanceTree.loadDatas(guidanceItems);
|
|
|
//console.log('getItemClassData start',billNode.data.name,billNode.data.code,billNode.data.ID);
|
|
|
- const itemClassData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
|
|
|
+ const classData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
|
|
|
+ const {requireRationData,optionalRationData,classGroups} = getItemData(guidanceTree.roots);
|
|
|
+ const itemClassData= combineData(classData,requireRationData,optionalRationData,classGroups);
|
|
|
const allRationIDs = guidanceTree.items.filter(node => !!node.data.rationID).map(node => node.data.rationID);
|
|
|
// 选套定额ID
|
|
|
// const optionalRationIDs = getOptionalRationIDs(itemClassData, allRationIDs);
|
|
|
//if(itemClassData.length > 1000) console.log('getItemClassData end',billNode.data.name,billNode.data.code,billNode.data.ID,itemClassData.length)
|
|
|
itemClassData.forEach(item => {
|
|
|
// 错套定额
|
|
|
- const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, optionalRationIDs, allRationIDs);
|
|
|
+ const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, item.optionalRationIDs, allRationIDs);
|
|
|
if(billNode.data.ID == '1d32fa34-0a9b-11ea-a33d-5388f9783b09') console.log(item.name)
|
|
|
billClassData.push({
|
|
|
itemCharacter: item.name,
|