|
@@ -483,22 +483,24 @@ function isOptionNode(node) {
|
|
|
return node && node.depth() % 2 === 1 && node.data.type === 0
|
|
|
}
|
|
|
|
|
|
-// 从指引节点,获取分类特征数据
|
|
|
-function getItemCharacterData(nodes, prefix) {
|
|
|
+// 从指引节点,获取分类特征、必套定额数据
|
|
|
+function getItemClassData(nodes, prefix) {
|
|
|
const processNodes = nodes.filter(node => isProcessNode(node));
|
|
|
const classGroups = []; // 同层必填选项的数组(二维数组)
|
|
|
processNodes.forEach(processNode => {
|
|
|
const classItems = [];
|
|
|
const optionNodes = processNode.children.filter(node => isOptionNode(node));
|
|
|
optionNodes.forEach(optionNode => {
|
|
|
- // const name = prefix ? `${prefix}@${optionNode.data.name}` : optionNode.data.name;
|
|
|
if (optionNode.parent && optionNode.parent.data.required && (!optionNode.children
|
|
|
|| !optionNode.children.length
|
|
|
|| (optionNode.children[0].data && optionNode.children[0].data.rationID)
|
|
|
|| !optionNode.children.some(node => isProcessNode(node)))) {
|
|
|
- classItems.push(optionNode.data.name);
|
|
|
+ // 必套定额
|
|
|
+ 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 });
|
|
|
} else {
|
|
|
- classItems.push(...getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : ''));
|
|
|
+ classItems.push(...getItemClassData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : ''));
|
|
|
}
|
|
|
});
|
|
|
if (classItems.length) {
|
|
@@ -507,7 +509,10 @@ function getItemCharacterData(nodes, prefix) {
|
|
|
});
|
|
|
// 拼接上一文本
|
|
|
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
|
|
|
+ return item;
|
|
|
+ });
|
|
|
}
|
|
|
// 二维数组内容排列组合
|
|
|
while (classGroups.length > 1) {
|
|
@@ -516,12 +521,57 @@ function getItemCharacterData(nodes, prefix) {
|
|
|
const mergedClassItems = [];
|
|
|
for (let i = 0; i < prevClassItems.length; i++) {
|
|
|
for (let j = 0; j < nextClassItems.length; j++) {
|
|
|
- mergedClassItems.push(`${prevClassItems[i]}@${nextClassItems[j]}`);
|
|
|
+ // 拼接文本
|
|
|
+ const mergedName = `${prevClassItems[i].name}@${nextClassItems[j].name}`;
|
|
|
+ // 拼接必套定额
|
|
|
+ const mergedRationIDs = [...prevClassItems[i].requiredRationIDs, ...nextClassItems[j].requiredRationIDs];
|
|
|
+ mergedClassItems.push({name: mergedName, requiredRationIDs: mergedRationIDs});
|
|
|
}
|
|
|
}
|
|
|
classGroups.splice(0, 2, mergedClassItems);
|
|
|
}
|
|
|
- return classGroups[0] ? [...new Set(classGroups[0])] : []; // 去重
|
|
|
+ // 去重(类别别名要唯一)
|
|
|
+ const items = classGroups[0] || [];
|
|
|
+ const nameMap = {};
|
|
|
+ const rst = [];
|
|
|
+ items.forEach(item => {
|
|
|
+ if (!nameMap[item.name]) {
|
|
|
+ rst.push(item);
|
|
|
+ }
|
|
|
+ nameMap[item.name] = true;
|
|
|
+ });
|
|
|
+ return rst;
|
|
|
+}
|
|
|
+
|
|
|
+// 获取选套定额:把所有分类数据的必套定额确定好了先。选套定额就是清单下所有定额除了必套的
|
|
|
+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);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return [...new Set(optionalRationIDs)];
|
|
|
+}
|
|
|
+
|
|
|
+// 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额
|
|
|
+function getErrorRationIDs(requiredRationIDs, optionalRationIDs, allRationIDs) {
|
|
|
+ const errorRationIDs = [];
|
|
|
+ allRationIDs.forEach(rationID => {
|
|
|
+ if (!requiredRationIDs.includes(rationID) && !optionalRationIDs.includes(rationID)) {
|
|
|
+ errorRationIDs.push(rationID);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return [...new Set(errorRationIDs)];
|
|
|
}
|
|
|
|
|
|
// 生成清单分类
|
|
@@ -551,28 +601,23 @@ async function generateClassData(libID) {
|
|
|
}
|
|
|
const guidanceTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
|
|
|
guidanceTree.loadDatas(guidanceItems);
|
|
|
- /* if (!guidanceTree.check(guidanceTree.roots)) {
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log(billNode.data);
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- console.log('==================================清单下精灵树结构问题=========================================');
|
|
|
- } */
|
|
|
- const itemCharacterData = getItemCharacterData(guidanceTree.roots);
|
|
|
- itemCharacterData.forEach(itemCharacter => {
|
|
|
+ const itemClassData = getItemClassData(guidanceTree.roots); // 必套定额在这个方法内就获取了,避免重复执行递归方法
|
|
|
+ const allRationIDs = guidanceTree.items.filter(node => !!node.data.rationID).map(node => node.data.rationID);
|
|
|
+ // 选套定额ID
|
|
|
+ const optionalRationIDs = getOptionalRationIDs(itemClassData, allRationIDs);
|
|
|
+ itemClassData.forEach(item => {
|
|
|
+ // 错套定额
|
|
|
+ const errorRationIDs = getErrorRationIDs(item.requiredRationIDs, optionalRationIDs, allRationIDs);
|
|
|
billClassData.push({
|
|
|
- itemCharacter,
|
|
|
+ itemCharacter: item.name,
|
|
|
class: classNum++,
|
|
|
- classCode: `${billNode.data.code}@${itemCharacter}`,
|
|
|
+ classCode: `${billNode.data.code}@${item.name}`,
|
|
|
compilationID: lib.compilationId,
|
|
|
name: billNode.data.name,
|
|
|
code: billNode.data.code,
|
|
|
+ requiredRationIDs: item.requiredRationIDs || [],
|
|
|
+ optionalRationIDs,
|
|
|
+ errorRationIDs,
|
|
|
});
|
|
|
});
|
|
|
});
|
|
@@ -595,15 +640,22 @@ async function getClassExcelData(libID) {
|
|
|
if (!lib) {
|
|
|
throw new Error('无有效精灵库');
|
|
|
}
|
|
|
+ console.log('start');
|
|
|
+ const date = Date.now();
|
|
|
const classData = await billClassModel.find({ compilationID: lib.compilationId }).lean();
|
|
|
- const excelData = [['类别', '编码', '清单名称', '必填特征排列组合', '类别别名']];
|
|
|
+ console.log('end');
|
|
|
+ console.log(Date.now() -date);
|
|
|
+ const excelData = [['类别', '编码', '清单名称', '必填特征排列组合', '类别别名', '必套定额', '选套定额', '错套定额']];
|
|
|
classData.forEach(item => {
|
|
|
const excelItem = [
|
|
|
item.class,
|
|
|
item.code,
|
|
|
item.name,
|
|
|
item.itemCharacter,
|
|
|
- item.classCode
|
|
|
+ item.classCode,
|
|
|
+ (item.requiredRationIDs || []).join('@'),
|
|
|
+ (item.optionalRationIDs || []).join('@'),
|
|
|
+ (item.errorRationIDs || []).join('@'),
|
|
|
];
|
|
|
excelData.push(excelItem);
|
|
|
});
|