Parcourir la source

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/ConstructionCost

TonyKang il y a 4 ans
Parent
commit
e5dccc63f3

+ 1 - 1
modules/all_models/calc_program_model.js

@@ -36,7 +36,7 @@ let stdCalcPrograms = new Schema({
 },{versionKey:false, _id: false});
 
 let projectCalcPrograms = new Schema({
-    ID: String,
+    ID: {type: String, index: true},
     projectID: {type: Number, index: true},
     name: String,
     libID: Number,

+ 2 - 1
modules/all_models/projects.js

@@ -14,10 +14,11 @@ const shareSchema = new Schema({
     shareDate: String,
 }, {versionKey: false, _id: false});
 const ProjectSchema = new Schema({
+    "isTwoLevel": { type: Boolean, default: false },
     "ID": {type: Number, index: true},
     "ParentID": Number,
     "NextSiblingID": Number,
-    "userID": String,
+    "userID": {type:String,index: true},
     "importedByInterface": {type: Boolean, default: false},
     "code": {type: String, default: ''},
     "name": String,

+ 1 - 0
modules/dict/custom.utf8

@@ -0,0 +1 @@
+硅酸盐水泥

+ 4 - 0
modules/dict/jieba.js

@@ -0,0 +1,4 @@
+let path = require('path');
+let nodejieba = require('nodejieba');
+nodejieba.load({userDict:path.join(__dirname,'/custom.utf8')})
+module.exports=nodejieba 

+ 1 - 1
modules/glj/models/unit_price_model.js

@@ -415,7 +415,7 @@ class UnitPriceModel extends BaseModel {
             codeList.push(tmp.code);
         }
 
-        // 查找即将更替的单价文件是否存在对应的工料机数据 -- (这里只根据code和名称初步过滤,因为其它的几项更改的概率不大,在下一步的比较中再精确匹配)
+        // 查找即将更替的单价文件是否存在对应的工料机数据 -- (这里只根据code初步过滤,因为其它的几项更改的概率不大,在下一步的比较中再精确匹配)
         let condition = {unit_price_file_id: changeUnitPriceId, code: {"$in": codeList}};
         let targetUnitList = await this.findDataByCondition(condition, null, false, ['code','name','specs','unit','type']);
 

+ 104 - 30
modules/main/facade/info_price_facade.js

@@ -25,6 +25,13 @@ var segment = new Segment();
 // 使用默认的识别模块及字典,载入字典文件需要1秒,仅初始化时执行一次即可
 segment.useDefault();
 
+let nodejieba = require('../../dict/jieba');
+
+let nameWeightMap ={ 
+ // '普通':-99
+}
+
+
 
 async function getOptions(data,compilation){//data 是预留对象,暂时不用
   let compilationID = compilation._id;
@@ -105,6 +112,12 @@ async function getDataByCondition(data,compilation){
   if (data.code) { 
     result = await getDataByCode(data.code, data);
     if (result.totalSize > 0) return result;
+
+    //如果分类编码有结果,但是确没有匹配的条数,说明关键字没匹配上,直接返回没有信息价
+    if(result.allItems > 0 && result.totalSize === 0){
+      return result;
+    }
+
   }
  
   //编号匹配不上的情况:
@@ -127,16 +140,38 @@ async function getDataByCondition(data,compilation){
 async function getDataByCode(code, data) { 
   let condition = { ...data.condition };
   condition.code = code;
-  let totalSize = await infoItemsModel.find(condition).count();
+ /*  
+  2021-03-31 先按编号去取,所有匹配结果再过滤,不能再分类了
+ let totalSize = await infoItemsModel.find(condition).count();
   if (data.lastID) { //有最后一行说明是查询下一页
     condition["_id"] = {$gt:mongoose.Types.ObjectId(data.lastID)};
   }
   let items = [];
   if (totalSize > 0) { 
     items = await infoItemsModel.find(condition).lean().sort({"_id":1}).limit(50);
-  }
+  } */
    
-  return {totalSize,items}
+ //新需求 --------- 
+  let  allItems = await infoItemsModel.find(condition).lean().sort({"_id":1});
+  let items = [];
+  if(data.keyWord && allItems.length > 1){
+    for(let item of allItems){
+      if(item.name.indexOf(data.keyWord) != -1) items.push(item) //有完全匹配的,就不用编码下的返回所有数据了
+    }
+  }
+  //没有完全匹配的,分词匹配关键字
+  if (items.length === 0){
+    let nameArray =  nodejieba.cut(data.keyWord);
+    nameArray = cusSegment(nameArray,data.keyWord);
+    let result = getMatchPrice(allItems,nameArray);
+    items = result.items;
+  }
+
+  let totalSize = items.length;
+
+  //新需求结束 --------- 
+
+  return {totalSize,items,allItems}
 }
 
 
@@ -152,8 +187,10 @@ async function getDataByKeyWord(keyword, data) {
 
 //按全关键字匹配
 async function getDataByFullKeyWord(keyword, data){
-  data.condition.name = new RegExp(keyword);
-  let items = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
+  let items = [];  
+  data.condition.name =  new RegExp(keyword);
+  items = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
+ 
   delete data.condition.name;
   return{totalSize:items.length,items}
 }
@@ -177,6 +214,52 @@ function handelThreeWord(word){
   }
 }
 
+function getShortNameArray(nameArray){
+  let newArray = [];
+  for(let n of nameArray){
+    if(n.length >= 5){
+      newArray.push(...nodejieba.cutSmall(n,3)) 
+    }else{
+      newArray.push(n);
+    }
+  }
+  return newArray;
+
+}
+
+function getMatchPrice(allInfoPrice,nameArray,needHandleLongWord = true){
+  let items = [];
+  let maxNum = 0;//最大匹配数
+  let matchMap = {};//匹配储存
+  let handleLongWord = false;
+  if(needHandleLongWord){
+    for(let na of nameArray){
+      if(na.length >= 5) handleLongWord = true;
+    }
+  }
+  
+  for (let info of allInfoPrice) { 
+    //specs
+    let mstring = info.name + info.specs;
+    mstring = mstring.replace(/混凝土/g, "砼");
+    info.mstring = mstring;
+    let matchCount = 0;
+    for (let na of nameArray) { 
+      if (mstring.indexOf(na) != -1) { 
+        matchCount++;
+        if(needHandleLongWord && na.length >= 5) handleLongWord = false//有5个字的,并且匹配上了,这里就为false不用再处理一次了
+      }
+    }  
+    if (matchCount > 0) { 
+      matchMap[matchCount] ? matchMap[matchCount].push(info) : matchMap[matchCount] = [info];
+      if (matchCount > maxNum) maxNum = matchCount;
+    }
+  }
+  if (maxNum > 0) items = matchMap[maxNum];
+  totalSize = items.length
+  return {totalSize,items,handleLongWord};
+}
+
 //自定义特殊处理
 function cusSegment(nameArray,keyword){
   let temArr = [];
@@ -201,15 +284,12 @@ function cusSegment(nameArray,keyword){
 
 //模糊匹配
 async function getDataByFuzzyMatch(keyword, data){
-  let items = [];
+
   let nameArray = [];
   if (keyword.length < 3) {
     nameArray.push(keyword)
   } else { 
-    nameArray = segment.doSegment(keyword, {
-      simple: true, //不返回词性
-      stripPunctuation: true //去除标点符号
-    });
+    nameArray = nodejieba.cut(keyword)
   }
  
   //自定义处理
@@ -217,29 +297,23 @@ async function getDataByFuzzyMatch(keyword, data){
 
   console.log(nameArray);
 
-  let allInfoPrice = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
 
-  let maxNum = 0;//最大匹配数
-  let matchMap = {};//匹配储存
+  let allInfoPrice = await infoItemsModel.find(data.condition).lean().sort({"_id":1});
 
-  for (let info of allInfoPrice) { 
-    //specs
-    let mstring = info.name + info.specs;
-    mstring = mstring.replace(/混凝土/g, "砼");
-    info.mstring = mstring;
-    let matchCount = 0;
-    for (let na of nameArray) { 
-      if (mstring.indexOf(na) != -1) { 
-        matchCount++;
-      }
-    }  
-    if (matchCount > 0) { 
-      matchMap[matchCount] ? matchMap[matchCount].push(info) : matchMap[matchCount] = [info];
-      if (matchCount > maxNum) maxNum = matchCount;
-    }
+  let {totalSize,items,handleLongWord}  = getMatchPrice(allInfoPrice,nameArray)
+  if(handleLongWord === true){
+    nameArray = getShortNameArray(nameArray);
+    console.log(`二次匹配:[${nameArray}]`);
+    let newResult  = getMatchPrice(allInfoPrice,nameArray,false)
+    totalSize = newResult.totalSize;
+    items = newResult.items;
   }
-  if (maxNum > 0) items = matchMap[maxNum];
-  totalSize = items.length
+
+  //关键词按权重排序,为了给结果排序
+  nameArray = _.sortBy(nameArray,(name)=>{
+    if(nameWeightMap[name]) return nameWeightMap[name]*-1;//sortBy是升序排序,我们要的是权重越小排到越后即倒序 所以这里乘以-1
+    return 1
+  })
 
   //按匹配位置排序 如[ '橡胶', '胶圈', '给水' ] 先显示橡胶
   items = _.sortBy(items,(item)=>{

+ 1 - 1
modules/main/facade/ration_facade.js

@@ -663,7 +663,7 @@ function newPriceDataFromPGlj(np,unitPriceFileId){
 async function getUnitPriceData(newProjectGLJList,gljCodes,unitPriceFileId){
   let unitPriceMap = {};
   let newUnitPriceList = [];
-  let unitPriceList = await unitPriceModel.find({unit_price_file_id: unitPriceFileId,'code':{'$in':gljCodes}}).lean();
+  let unitPriceList = await unitPriceModel.find({unit_price_file_id: unitPriceFileId}).lean();
   for(let u of unitPriceList){
     unitPriceMap[getIndex(u)]=u;
   }

+ 1 - 1
modules/ration_glj/controllers/ration_glj_controller.js

@@ -235,7 +235,7 @@ async function replaceGLJ(req, res){
         let rdata= await ration_glj_facade.replaceGLJ(data,req.session.sessionCompilation);
         result.data=rdata;
     }catch (err){
-        logger.err(err);
+        logger.err(err.stack);
         result.error=1;
         result.message = err.message;
     }

+ 2 - 1
package.json

@@ -54,7 +54,8 @@
     "socket.io": "2.0.3",
     "ua-parser-js": "^0.7.14",
     "uuid": "^3.1.0",
-    "wiredep": "^4.0.0"
+    "wiredep": "^4.0.0",
+    "nodejieba": "2.4.2"
   },
   "scripts": {
     "start": "node server.js",

+ 2 - 3
web/building_saas/complementary_ration_lib/js/gljSelect.js

@@ -36,7 +36,6 @@ let gljSelOprObj = {
     switchToGljId: function (gljList) {
         for(let glj of gljList){
             glj.gljId = glj.ID;
-            delete glj.ID;
         }
     },
     /* getSelGljItems: function(gljData) {
@@ -324,7 +323,7 @@ let gljSelOprObj = {
                     cacheSection.push(data[i]);
                 }
             }
-            sheetCommonObj.cleanSheet(me.workBook.getSheet(0), me.setting, -1);
+            // sheetCommonObj.cleanSheet(me.workBook.getSheet(0), me.setting, -1);
             sheetsOprObj.showDataForGljSel(me.workBook.getSheet(0), me.setting, cacheSection, rationGLJOprObj.distTypeTree);
             me.workBook.getSheet(0).setRowCount(cacheSection.length);
             cacheSection = null;
@@ -399,7 +398,7 @@ $(document).ready(function () {
         gljSelOprObj.initView(gljLibId, false);
     });
     $('#gljSearchKeyword').on('keyup', _.debounce(function () {
-        gljSelOprObj.filterDatasAndShow();  
+        gljSelOprObj.filterDatasAndShow();
     }, 300));
     $('#selGlj').on('shown.bs.modal', function () {
         if (gljSelOprObj.workBook) {

+ 4 - 4
web/building_saas/main/html/main.html

@@ -1875,13 +1875,13 @@
         </button>
       </div>
       <div class="modal-body">
-        <div class="form-check ">
+        <div class="form-check zlfb-check">
           <input class="form-check-input " type="checkbox" checked id="bill_resort">
           <label class="form-check-label">
             清单排序
           </label>
         </div>
-        <div class="form-check">
+        <div class="form-check zlfb-check">
           <input class="form-check-input" type="checkbox" id="bill_recode">
           <label class="form-check-label">
             清单重新编码
@@ -1896,8 +1896,8 @@
           <div class="card-body p-2">
             <h5 class="card-title">操作说明</h5>
             <p class="card-text">
-              勾选“清单排序”,表示分部整理后对每个分部下的清单进行排序;</br>
-              勾选“清单重新编码”,表示分部整理后对每个分部下的清单重新生成流水号;
+              勾选“清单排序”,表示对每个分部下的清单进行排序;</br>
+              勾选“清单重新编码”,表示对每个分部下的清单重新生成流水号。
             </p>
           </div>
         </div>

+ 4 - 0
web/building_saas/main/js/models/project_glj.js

@@ -79,6 +79,10 @@ ProjectGLJ.prototype.loadDataSync = async function () {
 ProjectGLJ.prototype.refreshByDatas = function(datas){
     this.datas = datas;
     this.calcQuantity();
+    this.datas.gljMap =  {};
+    for(let g of this.datas.gljList){
+        this.datas.gljMap[gljUtil.getIndex(g)]=g;
+     }
 };
 
 

+ 1 - 0
web/building_saas/main/js/models/ration_glj.js

@@ -832,6 +832,7 @@ let ration_glj = {
             oldData.original_code = glj.code;
             oldData.unit = glj.unit;
             oldData.specs = glj.specs;
+            oldData.type = glj.gljType;
             oldData.model = glj.model;
             oldData.basePrice = glj.basePrice;
             oldData.marketPrice = glj.basePrice;

+ 1 - 0
web/building_saas/main/js/views/project_view.js

@@ -177,6 +177,7 @@ var projectObj = {
         setButtonValid(canUpMove(selected) , $('#upMove'));
         setButtonValid(canDownMove(selected), $('#downMove'));
         setButtonValid(!projectObj.project.isBillsLocked(), $('#ZLFB_btn'));
+        setButtonValid(!projectObj.project.isBillsLocked(), $('#ZLQD_btn'));
 
     },
     checkCommonField: function (editingText, colSetting) {

+ 12 - 4
web/over_write/js/chongqing_2018_export.js

@@ -1063,9 +1063,14 @@ const XMLStandard = (function () {
             let appraisalSummary = new AppraisalSummary();
             project.children.unshift(appraisalSummary);
             //单项工程
-            for (let eng of projectData.children) {
-                let engElement = await loadEngineering(projectData.summaryInfo, eng);
-                project.children.push(engElement);
+            for (const eng of projectData.children) {
+                if (projectData.isTwoLevel) {
+                    const tenders = await loadEngineering(projectData.summaryInfo, eng);
+                    project.children.push(...tenders);
+                } else {
+                    const engElement = await loadEngineering(projectData.summaryInfo, eng);
+                    project.children.push(engElement);
+                }
             }
             //主要清单汇总、评审材料汇总 排在后面
             project.children = [...project.children.slice(2), mainBillsSummaryEle, appraisalSummary];
@@ -1089,6 +1094,7 @@ const XMLStandard = (function () {
         }
         /*
          * 加载单项工程数据
+         * 如果导入有isTwoLevel标记(导入文件只有两层),则导出只导出两层,忽略单项工程层
          * @param {Object}summaryInfo(项目汇总信息映射) {Object}engData(单项工程数据)
          * */
         async function loadEngineering(summaryInfo, engData) {
@@ -1097,6 +1103,7 @@ const XMLStandard = (function () {
             //费用构成
             let feeForm = new FeeFrom(summaryInfo[engData.ID]);
             engineering.children.push(feeForm);
+            const tenders = [];
             //分批次获取单位工程
             let tenderDetailMap = _cache.getItem('tenderDetailMap');
             for (let tenderData of engData.children) {
@@ -1113,8 +1120,9 @@ const XMLStandard = (function () {
                 }
                 tenderGljs = [];    //清空单位工程内所有的人材机(ID)
                 engineering.children.push(tender);
+                tenders.push(tender);
             }
-            return engineering;
+            return projectData.isTwoLevel ? tenders : engineering;
         }
         /*
          * 加载单位工程数据

+ 20 - 2
web/over_write/js/chongqing_2018_import.js

@@ -113,6 +113,9 @@ const importXML = (() => {
             count += eng.tenders.length;
         });
         obj.projectCount = count;
+        if (source.isTwoLevel) {
+            obj.isTwoLevel = true;
+        }
         return obj;
     }
     //基本信息相关
@@ -165,6 +168,20 @@ const importXML = (() => {
     }
     //单项工程
     function loadEng(source) {
+        const engs = arrayValue(source, ['标段', '单项工程']);
+        // 支持导入无单项工程数据,如果只有没有单项工程,自动生成一个单项工程数据
+        if (!engs.length) {
+            countData.projectCount++;
+            const eng = {
+                code: '1',
+                name: '单项工程',
+                projType: 'Engineering',
+                tenders: loadTender(source, ['标段', '单位工程']),
+            }
+            // 标记为两层结构,导出需要用到(虽然自动生成了三层,但是导出的时候需要只导出两层)
+            source.isTwoLevel = true;
+            return [eng];
+        }
         return arrayValue(source, ['标段', '单项工程']).map(src => {
             countData.projectCount++;
             return {
@@ -176,8 +193,8 @@ const importXML = (() => {
         });
     }
     //单位工程
-    function loadTender(engSrc) {
-        return arrayValue(engSrc, ['单位工程']).map(src => {
+    function loadTender(engSrc, path = ['单位工程']) {
+        return arrayValue(engSrc, path).map(src => {
             countData.projectCount++;
             countData.unitPriceFileCount++;
             return {
@@ -1621,6 +1638,7 @@ const importXML = (() => {
         xmlObj.ID = IDPlaceholder.project++;
         //获取到的转换后的最终上传数据
         let postConstructData = {
+            isTwoLevel: !!xmlObj.isTwoLevel,
             ID: xmlObj.ID,
             ParentID: xmlObj.ParentID,
             NextSiblingID: xmlObj.NextSiblingID,