Browse Source

清单指引类别库算法更新

lishihao 3 years ago
parent
commit
76db80f3e2

+ 95 - 97
modules/std_billsGuidance_lib/facade/facades.js

@@ -493,7 +493,7 @@ function isOptionNode(node) {
     }
 
     // 这里判断有无无定额的情况,有的就返回true,没有就返回false
-    function isRequireData(node) {
+    function isAllRationData(node) {
         let isRequired = true;
         if (node.children && node.children.length) {
             node.children.forEach(subNode => {
@@ -515,55 +515,26 @@ function isOptionNode(node) {
 
     // 获取选套定额
     function getOptionalData(node, list = []) {
-        node.children.forEach(element => {
-            if (element.children && element.children.length) {
-                element.children.forEach(item => {
-                    if (item.data.rationID) {
-                        list.push(item.data.rationID);
-                    } else if (isProcessNode(item)) {
-                        getOptionalData(item, list)
-                    }
-                })
-            }
-        });
-        return list;
-    }
-
-    // 获取最底层的定额
-    // 只判断必套的和最简单的情况,选套的还没加
-    function getSubData(nodes, requireRation = [], optionalRation = []) {
-        nodes.forEach(node => {
-            if (node.children && node.children.length > 0) {
-                // node为白色,儿子为蓝色
-                if (isProcessNode(node.children[0])) {
-                    getSubData(node.children, requireRation, optionalRation);
-                } else {
-                    // node为蓝色
-                    const nodeRation = [];
-                    // 判断当前蓝色节点有没有打勾必填,有就再走一次遍历,没有就下一步判断
-                    if (hasRequireData(node)) {
-                        // 获取必套的逻辑
-                        getSubData(node.children, requireRation, optionalRation);
-                    } else {
-                        // 判断当前蓝色节点下有没有无定额的情况,存在无定额的就下面全是选套,无无定额的情况就下面全是必套
-                        if (isRequireData(node)) {
-                            node.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, requireRation, optionalRation);
-                                }
-                            })
-                        } else {
-                            // 这里是选套的逻辑
-                            optionalRation.push(...getOptionalData(node));
+        if (isProcessNode(node)) {
+            node.children.forEach(element => {
+                if (element.children && element.children.length) {
+                    element.children.forEach(item => {
+                        if (item.data.rationID) {
+                            list.push(item.data.rationID);
+                        } else if (isProcessNode(item)) {
+                            getOptionalData(item, list)
                         }
-                    }
-                    if (nodeRation.length) requireRation.push(nodeRation);
+                    })
                 }
-            }
-        })
-        return { requireRation, optionalRation };
+            });
+        } else {
+            node.children.forEach(element => {
+                if (element.data.rationID) {
+                    list.push(element.data.rationID);
+                }
+            });
+        }
+        return list;
     }
 
     //获取定额数据
@@ -572,7 +543,7 @@ function isOptionNode(node) {
     // classGroups classCode文字和id键值对
     // classCodeList 各个classCode的pID和ID的关系
     // prefixID为上一个必填项的选项ID
-    function getItemData(nodes, requireRationData={}, optionalRationData={}, classGroups={},  prefixID = '') {
+    function getItemData(nodes, requireRationData={}, optionalRationData={}, classGroups={},  prefixID = '', prefixSonID = '',IDData={}) {
         const processNodes = nodes.filter(node => isProcessNode(node));
         // const classGroups = []; // 同层必填选项的数组(二维数组)
         processNodes.forEach(processNode => {
@@ -586,9 +557,9 @@ function isOptionNode(node) {
                     //白色节点下没有蓝色节点,就是到底了
                     if (!optionNode.children.some(subOptionNode => isProcessNode(subOptionNode))) {
                         // 是否必套定额
-                        if (isRequireData(optionNode)) {
+                        if (isAllRationData(optionNode)) {
                             optionNode.children.forEach(subOptionNode => {
-                                requireRationData[optionNode.data.ID].push([subOptionNode.data.rationID])
+                                requireRationData[optionNode.data.ID].push([subOptionNode.data.rationID]);
                             })
                         } else {
                             optionalRationData[optionNode.data.ID] = getOptionalData(optionNode);
@@ -603,13 +574,13 @@ function isOptionNode(node) {
                         // 后代项是否有必填
                         if (hasRequireData(optionNode)) {
                             //后代项有必填
-                            getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID)
-                            
+                            prefixSonID='';
+                            getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID,prefixSonID,IDData);
                         } else {
                             //后代项无必填
                             optionNode.children.forEach(subOptionNode => {
                                 // 是否必套定额
-                                if (isRequireData(optionNode)) {
+                                if (isAllRationData(optionNode)) {
                                     if (!requireRationData[subOptionNode.parent.data.ID]) requireRationData[subOptionNode.parent.data.ID] = [];
                                     requireRationData[subOptionNode.parent.data.ID].push(getOptionalData(subOptionNode));
                                 } else {
@@ -623,32 +594,43 @@ function isOptionNode(node) {
             } else {
                 // 蓝色节点,非必填
                 if (hasRequireData(processNode)) {
-                    // debugger
                      //后代项有必填
                    if(isProcessNode(processNode)){
                         //蓝色
                         processNode.children.forEach((subProcessNode)=>{
                             subProcessNode.children.forEach((sSubProcessNode)=>{
-                                getItemData([sSubProcessNode], requireRationData, optionalRationData, classGroups,prefixID)
+                                //这里是特殊处理,因为原来的逻辑是直接把定额绑到必填白色选项中下面的,每个蓝色的一组,但是这样是不对的,需要绑定在必填白色选项下的蓝色节点,所以这里就需要传入蓝色节点的id
+                                const requireChildrenID=sSubProcessNode.parent.data.required?prefixSonID:sSubProcessNode.data.ID;
+                                IDData[sSubProcessNode.data.ID]=prefixID;
+                                getItemData(
+                                    [sSubProcessNode], 
+                                    requireRationData,
+                                    optionalRationData,
+                                    classGroups,
+                                    prefixID,
+                                    requireChildrenID,
+                                    IDData
+
+                                )
                             })
                         })
                    }
                 } else {
                     let key = processNode.data.ID;
                     if (prefixID) key = prefixID;
+                    if (prefixSonID) key = prefixSonID;
                     // 是否必套定额
-                    if (isRequireData(processNode)) {
-                        if (!requireRationData[key]) requireRationData[processNode.data.ID] = [];
+                    if (isAllRationData(processNode)) {
+                        if (!requireRationData[key]) requireRationData[key] = [];
                         requireRationData[key].push(getOptionalData(processNode));
                     } else {
-                        if (!optionalRationData[key]) optionalRationData[processNode.data.ID] = [];
+                        if (!optionalRationData[key]) optionalRationData[key] = [];
                         optionalRationData[key].push(...getOptionalData(processNode));
                     }
                 }
             }
-           
         })
-        return { requireRationData, optionalRationData, classGroups}
+        return { requireRationData, optionalRationData, classGroups,IDData}
     }
 
 
@@ -735,42 +717,45 @@ function getItemClassData(nodes, prefix,
 }
 
 // 获取选套定额:把所有分类数据的必套定额确定好了先。选套定额就是清单下所有定额除了必套的
-function getOptionalRationIDs(itemClassData, allRationIDs) {
-    // 所有必套定额
-    let requiredRationIDs = [];
-    itemClassData.forEach(item => {
-        if (item.requiredRationIDs) {
-            requiredRationIDs.push(...item.requiredRationIDs);
-        }
-    });
-    requiredRationIDs = [...new Set(requiredRationIDs)];
-    // 选套定额就是清单下所有定额除了必套的
-    const optionalRationIDs = [];
-    allRationIDs.forEach(rationID => {
-        if (!requiredRationIDs.includes(rationID)) {
-            optionalRationIDs.push(rationID);
-        }
-    });
+function getOptionalRationIDs(optionalRationData) {
+    const optionalRationIDs=[];
+   Object.values(optionalRationData).forEach(optionalRation=>{
+       if(optionalRation.length) optionalRationIDs.push(...optionalRation);
+    })
     return [...new Set(optionalRationIDs)];
 }
 
-// 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
-function getErrorRationIDs(requiredRationIDList, optionalRationIDs, allRationIDs) {
+  // 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
+  function getErrorRationIDs(requiredRationIDList, optionalRationIDs, allRationIDs) {
+    const finRequireData=[];
+    requiredRationIDList.forEach(requiredRationIDs=>{
+        finRequireData.push(...requiredRationIDs);
+    })
     const errorRationIDs = [];
     allRationIDs.forEach(rationID => {
-        requiredRationIDList.forEach(requiredRationIDs=>{
-            if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
-                errorRationIDs.push(rationID);
-            }
-        })
-        
+        if (!finRequireData.includes(rationID) && !optionalRationIDs.includes(rationID)) {
+            errorRationIDs.push(rationID);
+        }
     });
     return [...new Set(errorRationIDs)];
 }
 
 //把classcode和必套选套定额结合在一起
-function combineData(classCodeData,requireRationData,optionalRationData,classGroups){
-    return  classCodeData.map(classCodeData=>{
+function combineData(codeData,requireRationData,classGroups,IDData){
+    //这里需要把绑定在子节点的定额更新到必填的白色选项中(classcode的值)
+    const requireCombineData={};
+    Object.keys(IDData).forEach(key=>{
+        if(!requireCombineData[IDData[key]])requireCombineData[IDData[key]]=new Set();
+        if(requireRationData[key]){
+         requireRationData[key].forEach(subData=>{
+             subData.forEach(subItem=>{
+                 requireCombineData[IDData[key]].add(subItem);
+             })
+         })
+        }
+     })
+
+    return  codeData.map(classCodeData=>{
         const errorRationIDs=[];
         const optionalRationIDs=[];
         const requiredRationIDs=[];
@@ -783,21 +768,32 @@ function combineData(classCodeData,requireRationData,optionalRationData,classGro
                 }else{
                     name=classGroups[classCodeID]
                 };
-                if(requireRationData[classCodeID]&&requireRationData[classCodeID].length) requiredRationIDs.push(...requireRationData[classCodeID]);
-                if(optionalRationData[classCodeID]&&optionalRationData[classCodeID].length) optionalRationIDs.push(...optionalRationData[classCodeID]);
+                const unitRation=[];
+                requireRationData[classCodeID].forEach(subItem=>{
+                    unitRation.push(...new Set(subItem));
+                })
+                if(requireRationData[classCodeID]&&requireRationData[classCodeID].length) {
+                    requiredRationIDs.push(unitRation);
+                }
+                if(requireCombineData[classCodeID]&&requireCombineData[classCodeID].size){
+                    requiredRationIDs.push([...requireCombineData[classCodeID]]);
+                }
             })
             return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
         }else{
+            const unitRation=[];
+            requireRationData[classCodeIDs].forEach(subItem=>{
+                unitRation.push(...new Set(subItem));
+            })
             name=classGroups[classCodeIDs];
-            if(requireRationData[classCodeIDs]&&requireRationData[classCodeIDs].length) requiredRationIDs.push(...requireRationData[classCodeIDs]);
-            if(optionalRationData[classCodeIDs]&&optionalRationData[classCodeIDs].length) optionalRationIDs.push(...optionalRationData[classCodeIDs]);
+            if(requireRationData[classCodeIDs]&&requireRationData[classCodeIDs].length) requiredRationIDs.push(unitRation);
+            if(requireCombineData[classCodeIDs]&&requireCombineData[classCodeIDs].size) requiredRationIDs.push([...requireCombineData[classCodeIDs]]);
             return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
         }
     })
 }
 
 
-
 // 生成清单分类
 async function generateClassData(libID) {
     const lib = await billsGuideLibModel.findOne({ ID: libID }).lean();
@@ -827,16 +823,18 @@ 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 classData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
-        const {requireRationData,optionalRationData,classGroups} = getItemData(guidanceTree.roots);
-        const itemClassData= combineData(classData,requireRationData,optionalRationData,classGroups);
+        const classCodeData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
+        const {requireRationData,optionalRationData,classGroups,IDData} = getItemData(guidanceTree.roots);
+        const itemClassData= combineData(classCodeData,requireRationData,classGroups,IDData);
+        //因为所有的选套都是一样的,所以这里就直接赋值了
+        const optionalRationIDs = getOptionalRationIDs(optionalRationData);
         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, item.optionalRationIDs, allRationIDs);
+            const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, optionalRationIDs, allRationIDs);
             if(billNode.data.ID == '1d32fa34-0a9b-11ea-a33d-5388f9783b09') console.log(item.name)
             billClassData.push({
                 itemCharacter: item.name,
@@ -846,7 +844,7 @@ async function generateClassData(libID) {
                 name: billNode.data.name,
                 code: billNode.data.code,
                 requiredRationIDs: item.requiredRationIDs || [],
-                optionalRationIDs:item.optionalRationIDs || [],
+                optionalRationIDs:optionalRationIDs || [],
                 errorRationIDs,
             });
         });

+ 194 - 134
web/maintain/billsGuidance_lib/js/billsGuidance.js

@@ -2597,32 +2597,16 @@ const billsGuidance = (function () {
     }
 
     // 判断蓝色子项和孙子项是否打勾了必填
-    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
-    function isRequireData(node) {
+    function isAllRationData(node) {
         let isRequired = true;
         if (node.children && node.children.length) {
             node.children.forEach(subNode => {
@@ -2631,83 +2615,146 @@ const billsGuidance = (function () {
                 }
             })
         }
+        //这里是兼容第一层直接是定额的白色节点
+        if (node.children && node.children.length) {
+            node.children.forEach(subNode => {
+                if (subNode.data.rationID) {
+                    isRequired = true;
+                }
+            })
+        }
         return isRequired;
     }
     // 获取选套定额
     function getOptionalData(node, list = []) {
-        if(isProcessNode(node)){
+        if (isProcessNode(node)) {
             node.children.forEach(element => {
                 if (element.children && element.children.length) {
                     element.children.forEach(item => {
                         if (item.data.rationID) {
-                            list.push(item.data.name);
+                            list.push(item.data.rationID);
                         } else if (isProcessNode(item)) {
                             getOptionalData(item, list)
                         }
                     })
                 }
             });
-        }else{
+        } else {
             node.children.forEach(element => {
                 if (element.data.rationID) {
-                    list.push(element.data.name);
-                } 
+                    list.push(element.data.rationID);
+                }
             });
         }
-        
         return list;
     }
 
-    // 获取最底层的定额
-    // 只判断必套的和最简单的情况,选套的还没加
-    function getSubData(nodes, requireRation = [], optionalRation = [],hasRequire=false) {
-        if(hasRequire){
-            debugger;
-        }
-        nodes.forEach(node => {
-            if (node.children && node.children.length > 0) {
-                // node为白色,儿子为蓝色
-                if (isProcessNode(node.children[0])) {
-                    getSubData(node.children, requireRation, optionalRation,hasRequire);
-                } else {
-                    // node为蓝色
-                    const nodeRation = [];
-                    // 判断当前蓝色节点有没有打勾必填,有就再走一次遍历,没有就下一步判断
-                    if (hasRequireData(node)) {
-                        // 获取必套的逻辑
-                        getSubData(node.children, requireRation, optionalRation,true);
+
+    //获取定额数据
+    // requireRationData必套定额对象
+    // optionalRationData 选逃定额对象
+    // classGroups classCode文字和id键值对
+    // classCodeList 各个classCode的pID和ID的关系
+    function getItemData(nodes, requireRationData={}, optionalRationData={}, classGroups={},  prefixID = '', prefixSonID = '',IDData={}) {
+        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 (isAllRationData(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 {
-                        // 判断当前蓝色节点下有没有无定额的情况,存在无定额的就下面全是选套,无无定额的情况就下面全是必套
-                        if (isRequireData(node)) {
-                            node.children.forEach((subNode) => {
-                                if (subNode.children && subNode.children[0] && subNode.children[0].data.rationID) {
-                                   nodeRation.push(subNode.children[0].data.name);
+                        const kV = {};
+                        kV[optionNode.data.ID] = optionNode.data.name;
+                        Object.assign(classGroups, kV);
+                        // 后代项是否有必填
+                        if (hasRequireData(optionNode)) {
+                            //后代项有必填
+                            prefixSonID='';
+                            getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID,prefixSonID,IDData);
+                        } else {
+                            //后代项无必填
+                            optionNode.children.forEach(subOptionNode => {
+                                // 是否必套定额
+                                if (isAllRationData(optionNode)) {
+                                    if (!requireRationData[subOptionNode.parent.data.ID]) requireRationData[subOptionNode.parent.data.ID] = [];
+                                    requireRationData[subOptionNode.parent.data.ID].push(getOptionalData(subOptionNode));
                                 } else {
-                                  getSubData(subNode.children, requireRation, optionalRation,hasRequire);
+                                    if (!optionalRationData[subOptionNode.parent.data.ID]) optionalRationData[subOptionNode.data.ID] = [];
+                                    optionalRationData[subOptionNode.parent.data.ID].push(...getOptionalData(subOptionNode));
                                 }
                             })
-                        } else {
-                            // 这里是选套的逻辑
-                            optionalRation.push(...getOptionalData(node));
                         }
                     }
-                    if (nodeRation.length) requireRation.push(nodeRation);
+                })
+            } else {
+                // 蓝色节点,非必填
+                if (hasRequireData(processNode)) {
+                     //后代项有必填
+                   if(isProcessNode(processNode)){
+                        //蓝色
+                        processNode.children.forEach((subProcessNode)=>{
+                            subProcessNode.children.forEach((sSubProcessNode)=>{
+                                //这里是特殊处理,因为原来的逻辑是直接把定额绑到必填白色选项中下面的,每个蓝色的一组,但是这样是不对的,需要绑定在必填白色选项下的蓝色节点,所以这里就需要传入蓝色节点的id
+                                const requireChildrenID=sSubProcessNode.parent.data.required?prefixSonID:sSubProcessNode.data.ID;
+                                IDData[sSubProcessNode.data.ID]=prefixID;
+                                getItemData(
+                                    [sSubProcessNode], 
+                                    requireRationData,
+                                    optionalRationData,
+                                    classGroups,
+                                    prefixID,
+                                    requireChildrenID,
+                                    IDData
+
+                                )
+                            })
+                        })
+                   }
+                } else {
+                    let key = processNode.data.ID;
+                    if (prefixID) key = prefixID;
+                    if (prefixSonID) key = prefixSonID;
+                    // 是否必套定额
+                    if (isAllRationData(processNode)) {
+                        if (!requireRationData[key]) requireRationData[key] = [];
+                        requireRationData[key].push(getOptionalData(processNode));
+                    } else {
+                        if (!optionalRationData[key]) optionalRationData[key] = [];
+                        optionalRationData[key].push(...getOptionalData(processNode));
+                    }
                 }
             }
         })
-        return { requireRation, optionalRation };
+        return { requireRationData, optionalRationData, classGroups,IDData}
     }
 
 
+
+
     /* 生成特征分类: 前端测试使用,想要前端测试,需要在zhiyin.html中,将id=generate-classa的按钮放开 */
-    function getItemCharacterData(nodes, prefix) {
+    function getItemCharacterData(nodes, prefix ,prefixID) {
         const processNodes = nodes.filter(node => isProcessNode(node));
         const classGroups = []; // 同层必填选项的数组(二维数组)
         processNodes.forEach(processNode => {
             const classItems = [];
             const optionNodes = processNode.children.filter(node => isOptionNode(node));
-            let nodeRequireRation = [];
-            let optionalRationList = [];
             optionNodes.forEach(optionNode => {
                 // const name = prefix ? `${prefix}@${optionNode.data.name}` : optionNode.data.name;
                 if (optionNode.parent && optionNode.parent.data.required && (!optionNode.children
@@ -2716,78 +2763,44 @@ const billsGuidance = (function () {
                     || !optionNode.children.some(node => isProcessNode(node)))) {
 
                     // 必套定额
-                    const requiredRationIDs = optionNode.children && optionNode.children.length ?
-                        optionNode.children.filter(node => !!node.data.rationID).map(node => node.data.name) : [];
-                    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 {
-                            // 这里是选套的逻辑
-                            optionalRationList.push(...getOptionalData(optionNode));
-                        }
-                       }
-                    }
-
                     // 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 : '');
-                    console.log(childrenClassItem);
-                    console.log({optionNode:optionNode.data.name,nodeRequireRation, optionalRationList});
-                    // debugger
+                    const childrenClassItem = getItemCharacterData(
+                        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;
             });
         }
-        // debugger
         // 二维数组内容排列组合
         while (classGroups.length > 1) {
             const prevClassItems = classGroups[0];
@@ -2797,11 +2810,8 @@ const billsGuidance = (function () {
                 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);
@@ -2820,44 +2830,94 @@ const billsGuidance = (function () {
     }
 
     // 获取选套定额:把所有分类数据的必套定额确定好了先。选套定额就是清单下所有定额除了必套的
-    function getOptionalRationIDs(itemClassData, guideNodes) {
-        // 所有必套定额
-        let requiredRationIDs = [];
-        itemClassData.forEach(item => {
-            if (item.requiredRationIDs) {
-                requiredRationIDs.push(...item.requiredRationIDs);
-            }
-        });
-        requiredRationIDs = [...new Set(requiredRationIDs)];
-        // 选套定额就是清单下所有定额除了必套的
-        const optionalRationIDs = [];
-        guideNodes.forEach(node => {
-            if (node.data.rationID && !requiredRationIDs.includes(node.data.rationID)) {
-                optionalRationIDs.push(node.data.rationID);
-            }
-        });
+    function getOptionalRationIDs(optionalRationData) {
+        const optionalRationIDs=[];
+       Object.values(optionalRationData).forEach(optionalRation=>{
+           if(optionalRation.length) optionalRationIDs.push(...optionalRation);
+        })
         return [...new Set(optionalRationIDs)];
     }
 
     // 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
-    function getErrorRationIDs(requiredRationIDs, optionalRationIDs, guideNodes) {
+    function getErrorRationIDs(requiredRationIDList, optionalRationIDs, guideNodes) {
+        const finRequireData=[];
+        requiredRationIDList.forEach(requiredRationIDs=>{
+            finRequireData.push(...requiredRationIDs);
+        })
         const errorRationIDs = [];
         guideNodes.forEach(node => {
-            if (node.data.rationID && !requiredRationIDs.includes(node.data.rationID) && !optionalRationIDs.includes(node.data.rationID)) {
+            if (node.data.rationID && !finRequireData.includes(node.data.rationID) && !optionalRationIDs.includes(node.data.rationID)) {
                 errorRationIDs.push(node.data.rationID);
             }
         });
         return [...new Set(errorRationIDs)];
     }
 
+    //把classcode和必套选套定额结合在一起
+    function combineData(codeData,requireRationData,classGroups,IDData){
+        //这里需要把绑定在子节点的定额更新到必填的白色选项中(classcode的值)
+        const requireCombineData={};
+        Object.keys(IDData).forEach(key=>{
+            if(!requireCombineData[IDData[key]])requireCombineData[IDData[key]]=new Set();
+            if(requireRationData[key]){
+             requireRationData[key].forEach(subData=>{
+                 subData.forEach(subItem=>{
+                     requireCombineData[IDData[key]].add(subItem);
+                 })
+             })
+            }
+         })
+
+        return  codeData.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]
+                    };
+                    const unitRation=[];
+                    requireRationData[classCodeID].forEach(subItem=>{
+                        unitRation.push(...new Set(subItem));
+                    })
+                    if(requireRationData[classCodeID]&&requireRationData[classCodeID].length) {
+                        requiredRationIDs.push(unitRation);
+                    }
+                    if(requireCombineData[classCodeID]&&requireCombineData[classCodeID].size){
+                        requiredRationIDs.push([...requireCombineData[classCodeID]]);
+                    }
+                })
+                return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
+            }else{
+                const unitRation=[];
+                requireRationData[classCodeIDs].forEach(subItem=>{
+                    unitRation.push(...new Set(subItem));
+                })
+                name=classGroups[classCodeIDs];
+                if(requireRationData[classCodeIDs]&&requireRationData[classCodeIDs].length) requiredRationIDs.push(unitRation);
+                if(requireCombineData[classCodeIDs]&&requireCombineData[classCodeIDs].size) requiredRationIDs.push([...requireCombineData[classCodeIDs]]);
+                return {name,requiredRationIDs,optionalRationIDs,errorRationIDs}
+            }
+        })
+    }
+
     $('#generate-class').click(() => {
         if (bills.tree.selected && bills.tree.selected.guidance.tree) {
-            const classData = getItemCharacterData(bills.tree.selected.guidance.tree.roots);
-            const optionalRationIDs = getOptionalRationIDs(classData, bills.tree.selected.guidance.tree.items);
+           const classCodeData = getItemCharacterData(bills.tree.selected.guidance.tree.roots);
+           const {requireRationData,optionalRationData,classGroups,IDData} = getItemData(bills.tree.selected.guidance.tree.roots);
+           const classData= combineData(classCodeData,requireRationData,classGroups,IDData);
+           //因为所有的选套都是一样的,所以这里就直接赋值了
+            const optionalRationIDs = getOptionalRationIDs(optionalRationData);
             classData.forEach(item => {
+                item.optionalRationIDs=optionalRationIDs
                 item.errorRationIDs = getErrorRationIDs(item.requiredRationIDs, optionalRationIDs, bills.tree.selected.guidance.tree.items);
             })
-            console.log(optionalRationIDs);
+            // console.log(optionalRationIDs);
             console.log(classData);
         }
     });