Ver código fonte

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

TonyKang 5 anos atrás
pai
commit
7629d14af4

+ 1 - 0
config/gulpConfig.js

@@ -81,6 +81,7 @@ module.exports = {
         'web/building_saas/main/js/models/main_consts.js',
         'web/over_write/config/compilation_config.js',
         'public/web/common_util.js',
+        'public/web/encoding_util.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',
         'web/building_saas/glj/js/common_spread.js',

+ 2 - 1
logs/log4js.json

@@ -1,4 +1,5 @@
-{
+{        
+  "disableClustering": true, 
   "customBaseDir" :"../logs/ConstructionCost/",
   "customDefaultAtt" :{
     "type": "dateFile",

+ 3 - 0
modules/main/controllers/installation_controller.js

@@ -74,6 +74,9 @@ async function  resetToDefault(req, res) {
         let data = req.body.data;
         data = JSON.parse(data);
         let returnData= await installation_facade.resetToDefault(data.projectID,data.installFees);
+        if(data.rationInstallations && data.rationInstallations.length > 0){
+          await ration_installation_facade.updateFromInstallationFee(data.projectID,data.rationInstallations);
+        }
         result.data=returnData;
     }catch (err){
         logger.err(err);

+ 38 - 26
modules/main/controllers/project_controller.js

@@ -209,43 +209,55 @@ module.exports = {
         }
         res.json(result);
     },
-    getDecodedStr: async function(req, res) {
-        /* const data = JSON.parse(req.body.data);
-        const unit8ArraySouces = data.unit8ArraySouces;
+    getDecodedData: async function(req, res) {
+        const responseData = {
+            err: 0,
+            msg: '',
+            data: null
+        };
         try {
-
+            const { unit8ArraySouces, encoding } = JSON.parse(req.body.data);
+            responseData.data = unit8ArraySouces.map(unit8ArraySrc => {
+                const buffer = toArrayBuffer(unit8ArraySrc);
+                return iconv.decode(Buffer.from(buffer), encoding);
+            });;
         } catch (err) {
-            
-        } */
+            responseData.err = 1;
+            responseData.msg = typeof error === 'object' ? '解码失败' : error;
+        } finally {
+            res.json(responseData);
+        }
+
         function toArrayBuffer(src) {
-            var ab = new ArrayBuffer(src.length);
-            var view = new Uint8Array(ab);
-            for (var i = 0; i < src.length; ++i) {
+            const ab = new ArrayBuffer(src.length);
+            const view = new Uint8Array(ab);
+            for (let i = 0; i < src.length; i++) {
                 view[i] = +src[i];
             }
             return ab;
         }
+    },
+    getEncodedData: async function(req, res) {
         const responseData = {
             err: 0,
             msg: '',
             data: null
         };
-
-        const form = new multiparty.Form();
-        form.parse(req, async function(err, fields, files) {
-            try{
-                const unit8ArrayList = JSON.parse(fields.blobList[0]);
-                unit8ArrayList.forEach(unit8ArrayNumbers => {
-                    const buffer = toArrayBuffer(unit8ArrayNumbers);
-                    const str = iconv.decode(Buffer.from(buffer), 'gbk');
-                });
-            }
-            catch (error){
-                responseData.err = 1;
-                responseData.msg = typeof error === 'object' ? '文件名编码失败' : error;
-                res.json(responseData);
-            }
-
-        });
+        try {
+            const { source, encoding, toBase64 } = JSON.parse(req.body.data);
+            const data = {};
+            source.forEach(str => {
+                const u8 = iconv.encode(str, encoding);
+                data[str] = toBase64 
+                    ? Buffer.from(u8).toString('base64')
+                    : u8;
+            });
+            responseData.data = data;
+        } catch (err) {
+            responseData.err = 1;
+            responseData.msg = typeof error === 'object' ? '编码失败' : error;
+        } finally {
+            res.json(responseData);
+        }
     }
 };

+ 2 - 1
modules/main/routes/project_route.js

@@ -19,7 +19,8 @@ module.exports = function (app) {
     projectRouter.post('/getDefaultColSetting', projectController.getDefaultColSetting);
     projectRouter.post('/getSEIProjects', projectController.getSEIProjects);
     projectRouter.post('/loadSEIProjectData', projectController.loadSEIProjectData);
-    projectRouter.post('/getDecodedStr', projectController.getDecodedStr);
+    projectRouter.post('/getDecodedData', projectController.getDecodedData);
+    projectRouter.post('/getEncodedData', projectController.getEncodedData);
     app.use('/project',projectRouter);
 };
 

+ 11 - 1
modules/pm/facade/pm_facade.js

@@ -2402,10 +2402,19 @@ async function importProjects(data,req,updateData) {
 
 
 async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap){
-    let bills = [],rations = [],projectGLJs = [],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],evaluateList=[],bidList=[],contractorList=[];
+    let bills = [],rations = [],projectGLJs = [],installationFees=[],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],evaluateList=[],bidList=[],contractorList=[];
     let newProjectSetting =null,newCalcProgramsFile = null,newLabourCoe = null;
     let billsIDMap = {},projectGLJIDMap={},rationIDMap = {};
     let newProjectID = projectIDMap[data.projSetting.projectID];
+    //生成安装增加费设置
+    if(data.installationFees && data.installationFees.length > 0){
+        for(let ins of data.installationFees){
+          delete ins._id;
+          ins.ID = uuidV1();
+          ins.projectID = newProjectID;
+          installationFees.push(ins);
+        }
+    }
     //生成新的清单;
     if(data.bills && data.bills.length > 0){
         for(let b of data.bills){
@@ -2479,6 +2488,7 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
     if(newProjectSetting) await projectSettingModel.create(newProjectSetting);
     if(bills.length > 0) await insertMany(bills,billsModel);
     if(rations.length > 0) await insertMany(rations,rationModel);
+    if(installationFees.length > 0) await insertMany(installationFees,installationFeeModel);
     if(projectGLJs.length > 0) await insertMany(projectGLJs,gljListModel);
     if(rationGLJs.length > 0) await insertMany(rationGLJs,rationGLJModel);
     if(rationCoes.length > 0) await insertMany(rationCoes,rationCoeModel);

+ 4 - 2
modules/ration_glj/facade/ration_glj_facade.js

@@ -940,12 +940,14 @@ function getData(projectID, callback, isReport) {
                                         if (ration.quantityCoe._doc.labour)
                                             coe = ration.quantityCoe._doc.labour;
                                     }
-                                    else if ([gljType.GENERAL_MATERIAL,
+                                    else if (
+                                        [gljType.GENERAL_MATERIAL,
                                         gljType.CONCRETE,
                                         gljType.MORTAR,
                                         gljType.MIX_RATIO,
                                         gljType.COMMERCIAL_CONCRETE,
-                                        gljType.COMMERCIAL_MORTAR].includes(glj.type)){
+                                        gljType.COMMERCIAL_MORTAR,
+                                        gljType.OTHER_MATERIAL].includes(glj.type)){
                                         if (ration.quantityCoe._doc.material)
                                             coe = ration.quantityCoe._doc.material;
                                     }

+ 2 - 2
package.json

@@ -63,9 +63,9 @@
   },
   "scripts": {
     "start": "C:\\Users\\mai\\AppData\\Roaming\\npm\\babel-node.cmd server.js",
-    "server": "node --max-old-space-size=2048 server.js",
+    "server": "pm2-docker pm2_server.json",
     "socket": "node socket.js",
-    "import": "node --max-old-space-size=2048 importserver.js",
+    "import": "pm2-docker pm2_import.json",
     "dev_server": "SET NODE_ENV=qa&& babel-node server.js",
     "dev_socket": "SET NODE_ENV=qa&& babel-node socket.js",
     "dev_import": "SET NODE_ENV=qa&& babel-node importserver.js"

+ 12 - 0
pm2_import.json

@@ -0,0 +1,12 @@
+{
+  "apps": {
+      "name": "constructionimport",
+      "script": "importserver.js",
+      "output": "/home/logs/out.log",
+      "error": "/home/logs/err.log",
+      "log_date_format": "YYYY-MM-DD HH:mm Z",
+      "exec_mode": "cluster_mode",                
+      "instances": 3, 
+      "merge_logs": true
+    }
+}

+ 9 - 0
pm2_server.json

@@ -0,0 +1,9 @@
+{
+  "apps": {
+      "name": "constructionimport",
+      "script": "server.js",
+      "node_args":"--max-old-space-size=2048",
+      "exec_mode": "cluster_mode",                
+      "instances": 3
+    }
+}

+ 5 - 0
public/common_constants.js

@@ -129,6 +129,10 @@ const commonConstants = (() => {
         //甲定乙供
         JDYG: '甲定乙供'
     };
+    const SourceType = {
+        BILLS: 'bills',
+        RATION: 'ration',
+    };
     return {
         fixedFlag,
         billType,
@@ -137,5 +141,6 @@ const commonConstants = (() => {
         TaxType,
         supplyType,
         supplyText,
+        SourceType,
     }
 })();

+ 1 - 1
public/web/common_ajax.js

@@ -163,7 +163,7 @@ $.ajaxSetup({
 
 
 
-async function ajaxPost(url, data, isPlainData = false, timeout = 200000) {
+function ajaxPost(url, data, isPlainData = false, timeout = 200000) {
     return new Promise(function (resolve, reject) {
         $.ajax({
             type:"POST",

+ 13 - 0
public/web/encoding_util.js

@@ -0,0 +1,13 @@
+const encodingUtil = (() => {
+
+    /**
+     * 根据编码方式获取编码数据
+     * @param {Array} source - 字符串集合数据源
+     * @param {String} encoding - 编码方式
+     */
+    async function getEncodedData(source, encoding, toBase64 = false) {
+        return await ajaxPost('/project/getEncodedData', { source, encoding, toBase64 });
+    }
+
+    return { getEncodedData }
+})();

+ 1 - 0
web/building_saas/main/html/main.html

@@ -2462,6 +2462,7 @@
     <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
     <script type="text/javascript" src="/web/over_write/config/compilation_config.js"></script>
     <script type="text/javascript" src="/public/web/common_util.js"></script>
+    <script type="text/javascript" src="/public/web/encoding_util.js"></script>
     <script type="text/javascript" src="/web/building_saas/glj/js/project_glj.js"></script>
     <script type="text/javascript" src="/web/building_saas/glj/js/composition.js"></script>
     <script type="text/javascript" src="/web/building_saas/glj/js/common_spread.js"></script>

+ 14 - 14
web/building_saas/main/js/models/calc_program.js

@@ -1629,7 +1629,7 @@ class CalcProgram {
         me.compiledFeeTypeMaps = {};
         me.compiledFeeTypeNames = [];
         me.compiledCalcBases = {};
-        me.saveForReports = [];
+        // me.saveForReports = [];
 
         me.feeRates = this.project.FeeRate.datas.rates;
         me.labourCoes = this.project.labourCoe.datas.coes;
@@ -1647,16 +1647,16 @@ class CalcProgram {
 
 
         // 存储费率临时数据,报表用。
-        if (isInit && me.saveForReports.length > 0){
-            let saveDatas = {};
-            saveDatas.projectID = projectObj.project.projectInfo.ID;
-            saveDatas.calcItems = me.saveForReports;
-            CommonAjax.post('/calcProgram/saveCalcItems', saveDatas, function (result) {
-                if (result){
-                    me.saveForReports = [];
-                };
-            });
-        };
+        // if (isInit && me.saveForReports.length > 0){
+        //     let saveDatas = {};
+        //     saveDatas.projectID = projectObj.project.projectInfo.ID;
+        //     saveDatas.calcItems = me.saveForReports;
+        //     CommonAjax.post('/calcProgram/saveCalcItems', saveDatas, function (result) {
+        //         if (result){
+        //             me.saveForReports = [];
+        //         };
+        //     });
+        // };
     };
 
     compilePublics(){
@@ -1756,9 +1756,9 @@ class CalcProgram {
                     let cfr = me.compiledFeeRates[item.feeRateID];
                     item.feeRate = cfr ? cfr.rate : 100;
 
-                    if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){
-                        me.saveForReports.push({templatesID: template.ID, calcItem: item});
-                    }
+                    // if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){
+                    //     me.saveForReports.push({templatesID: template.ID, calcItem: item});
+                    // }
                 };
 
                 // 字段名映射

+ 1 - 1
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -883,7 +883,7 @@ const XML_EXPORT_BASE = (() => {
         getParsedData,
         setupCode,
         propertyCheck,
-        softCheck
+        softCheck,
     });
 
     // 开始标签

+ 9 - 24
web/building_saas/main/js/models/importStdInterfaceBase.js

@@ -289,29 +289,14 @@ const importXMLBase = (() => {
             { templateLibID });
     }
 
-    // 解码文件名
-    function getDecodedNames(bufferList) {
-        //const blobs = bufferList.map(buffer => new Blob([buffer]));
-        const formData = new FormData();
-        formData.append('blobList', JSON.stringify(bufferList));
-        return new Promise((resolve, reject) => {
-            $.ajax({
-                url: '/project/getDecodedStr',
-                type: 'POST',
-                data: formData,
-                cache: false,
-                contentType: false,
-                processData: false,
-                success: function(response){
-                    if (response.err === 0) {
-                        resolve(response.data);
-                    } else {
-                        reject(response.msg);
-                    }
-                },
-                error: reject
-            });
-        });
+    /**
+     * 根据编码内容解码数据
+     * @param {Array} unit8ArraySouces - unit8array的数据源
+     * @param {String} encoding - 编码方式
+     * @return {Promise Array}
+     */
+    async function getDecodedData(unit8ArraySouces, encoding) {
+        return await ajaxPost('/project/getDecodedData', { unit8ArraySouces, encoding });
     }
 
     const UTIL = Object.freeze({
@@ -329,7 +314,7 @@ const importXMLBase = (() => {
         getItemsRecur,
         extractItemsRecur,
         readAsTextSync,
-        getDecodedNames,
+        getDecodedData,
     });
 
     // 获取接受上传的文件类型正则表达式

+ 26 - 1
web/building_saas/main/js/models/installation_fee.js

@@ -742,6 +742,17 @@ var installation_fee = {
             });
         };
 
+        installation_fee.prototype.setRationInstallToDefual = function(){
+           let ration_install=projectObj.project.ration_installation;
+           let rationInstallations = [];
+           for(let ri of ration_install.datas){
+              if(ri.unifiedSetting == 0 || ri.ruleId!==""){
+                rationInstallations.push({ID:ri.ID,doc:{'ruleId':"",unifiedSetting:1}});
+              }
+           } 
+           return rationInstallations;
+        }
+
         installation_fee.prototype.resetToDefault = function (callback) {
             let me = this;
             let installFees = [];
@@ -753,10 +764,24 @@ var installation_fee = {
                 installFees.push(ins);
             }
             //data.projectID,data.installFees
+            let rationInstallations = me.setRationInstallToDefual();
+
+
             $.bootstrapLoading.start();
-            CommonAjax.post("/installation/resetToDefault",{projectID:project.ID(),installFees:installFees},function (result) {
+            CommonAjax.post("/installation/resetToDefault",{projectID:project.ID(),installFees:installFees,rationInstallations:rationInstallations},function (result) {
                 console.log(result)
                 me.datas = result;
+                 //更新定额安装费的缓存
+                 if(rationInstallations.length > 0){
+                  for(let ri of rationInstallations){
+                      let c_ri = project.ration_installation.getByID(ri.ID);
+                      if(c_ri){
+                          for(let rkey in ri.doc){
+                              c_ri[rkey] = ri.doc[rkey];
+                          }
+                      }
+                  }
+                }
                 if(callback){
                     callback();
                 }

+ 24 - 3
web/building_saas/main/js/models/overHeight.js

@@ -217,9 +217,12 @@ const OVER_HEIGHT = (() => {
     }
 
     // 超高降效列的控制,右键计取触发
-    function switchVisible() {
+    function switchVisible(visible) {
         const curVisible = colSettingObj.getVisible('overHeight');
-        colSettingObj.setVisible('overHeight', !curVisible);
+        if (curVisible === visible) {
+            return;
+        }
+        colSettingObj.setVisible('overHeight', visible);
         colSettingObj.updateColSetting(true);
     }
 
@@ -812,6 +815,7 @@ const OVER_HEIGHT = (() => {
         // 取消计取费用,只删除超高子目
         if (isCancelCalc) {
             postData.deleteData = getDeleteData();
+            postData.updateData = getUpdateData();
             return postData;
         }
         // 没有新的选项行为,获取当前项目的选项行为
@@ -1016,8 +1020,25 @@ const OVER_HEIGHT = (() => {
         TREE_SHEET_HELPER.massOperationSheet(sheet, func);
     }
 
+    // 请空所有清单和定额的超高文本
+    function clearOverHeightText() {
+        const sheet = projectObj.mainController.sheet;
+        const func = () => {
+            const nodes = projectObj.project.mainTree.items;
+            const overHeightCol = getOverHeightCol();
+            nodes.forEach((node, index) => {
+                if (node.data.overHeight) {
+                    sheet.setText(index, overHeightCol, '');
+                }
+            });
+        };
+        TREE_SHEET_HELPER.massOperationSheet(sheet, func);
+    }
+
     // 取消超高降效,删除所有超高子目
     function cancelCalc() {
+        switchVisible(false);
+        clearOverHeightText();
         handleConfirmed(true);
     }
 
@@ -1094,7 +1115,7 @@ const OVER_HEIGHT = (() => {
             return;
         }
         // 存在不同,重算
-       await handleConfirmed();
+        await handleConfirmed();
     }
 
     // 事件监听

+ 5 - 2
web/building_saas/main/js/views/glj_col.js

@@ -292,8 +292,11 @@ let gljCol = {
                 projectGljObject.initSheet(projectGljObject.projectGljSheet, projectGljObject.projectGljSetting);
             }
             if (subSpread) {
-                gljOprObj.initSheet(subSpread.getSheet(0), false);
-                gljOprObj.initSheet(subSpread.getSheet(2), false);
+                gljOprObj.refreshView();
+                calcProgramObj.refreshCalcProgram(projectObj.project.mainTree.selected);
+
+                //gljOprObj.initSheet(subSpread.getSheet(0), false);
+                //gljOprObj.initSheet(subSpread.getSheet(2), false);
             }
         };
     },

+ 3 - 3
web/building_saas/main/js/views/glj_view.js

@@ -115,11 +115,11 @@ var gljOprObj = {
     scopeSheet:null,
     scopeDatas:[],
     scopeSelectedIDMap:{},
-    initSheet: function (sheet) {
+    initSheet: function (sheet,name="ration_glj") {
         var me = this;
         me.sheet = sheet;
         sheetCommonObj.initSheet(me.sheet, me.setting, 30);
-        sheet.name('ration_glj');
+        sheet.name(name);
         me.bindSheetEvent(sheet);
         sheet.bind(GC.Spread.Sheets.Events.CellDoubleClick, me.onCellDoubleClick);
         sheet.bind(GC.Spread.Sheets.Events.ClipboardChanged, me.onClipboardChanged);
@@ -790,9 +790,9 @@ var gljOprObj = {
         let calcOptions=projectObj.project.projectInfo.property.calcOptions;
         let decimalObj = projectObj.project.projectInfo.property.decimal;
         let labourCoeDatas =  projectObj.project.labourCoe.datas;
-        let tenderCoe = projectObj.project.projectGLJ.getTenderPriceCoe(glj);
         glj = glj?glj:_.find(proGLJ.datas.gljList, {'id': data.projectGLJID});
         if(glj){
+            let tenderCoe = projectObj.project.projectGLJ.getTenderPriceCoe(glj);
             let result = gljUtil.getGLJPrice(glj,proGLJ.datas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil,tenderCoe);
             data.marketPrice = result.marketPrice;
             data.tenderPrice = result.tenderPrice;

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

@@ -1464,7 +1464,7 @@ var projectObj = {
                                 return projectReadOnly;
                             },
                             callback: function (key, opt) {
-                                OVER_HEIGHT.switchVisible();
+                                OVER_HEIGHT.switchVisible(true);
                             }
                         },
                         'setOption': {

+ 5 - 0
web/building_saas/main/js/views/tender_price_view.js

@@ -500,6 +500,11 @@ $(function () {
             }
             tender_obj.showTenderData();
             projectObj.project.projectGLJ.calcQuantity();
+            // 刷新造价书界面的相关显示。刷新放在这里是为了切换UI时体验更流畅。
+            if($('#linkJSCX').hasClass('active'))
+                calcProgramObj.refreshCalcProgram(projectObj.project.mainTree.selected, 2)
+            if($('#linkGLJ').hasClass('active'))
+                gljOprObj.refreshView();
         };
         projectObj.project.calcProgram.doTenderCalc(callback);
     });

+ 2 - 1
web/building_saas/pm/js/pm_newMain.js

@@ -360,7 +360,7 @@ const projTreeObj = {
                 return !selectedItem;
             },
             callback: function (key, opt) {
-                let selected = projTreeObj.tree.selected;
+                const selected = projTreeObj.tree.selected;
                 if (selected && selected.data.ID) {
                     SHARE_TO.initModal(selected.data.ID);
                 }
@@ -940,6 +940,7 @@ const projTreeObj = {
             return lineNum * defaultHeight;
         };
         TreeNodeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
+            if(style.backColor == undefined) style.backColor = "white"  
             if (style.backColor) {
                 ctx.save();
                 ctx.fillStyle = style.backColor;

+ 1 - 1
web/common/components/share/index.js

@@ -180,7 +180,7 @@ const SHARE_TO = (() => {
                                                 <label class="custom-control-label" for="allow-copy">允许拷贝</label>
                                             </div>
                                             <div class="custom-control custom-checkbox">
-                                                <input type="checkbox" class="custom-control-input" id="allow-edit" checked="">
+                                                <input type="checkbox" class="custom-control-input" id="allow-edit">
                                                 <label class="custom-control-label" for="allow-edit">允许编辑</label>
                                             </div>
                                         </div>

+ 1 - 1
web/over_write/js/chongqing_2018.js

@@ -51,7 +51,7 @@ let isCQ2018 = true;
 
 if(typeof baseMaterialTypes !== 'undefined'){
     baseMaterialTypes.push(gljType.OTHER_MATERIAL);
-    allMaterialTypes.delete(gljType.EQUIPMENT);
+    // allMaterialTypes.delete(gljType.EQUIPMENT);  新规定里重庆材料类型里需要包含设备费。
     baseMachineTypes.delete(gljType.MACHINE_COMPOSITION);
     baseMachineTypes.push(gljType.INSTRUMENT, gljType.FUEL_POWER_FEE, gljType.DEPRECIATION_FEE,
         gljType.INSPECTION_FEE, gljType.MAINTENANCE, gljType.DISMANTLING_FREIGHT_FEE,

+ 101 - 38
web/over_write/js/guangdong_2018_export.js

@@ -433,7 +433,7 @@ const XMLStandard = (function () {
             const attrs = [
                 // 项目编号
                 {
-                    name: 'Number', dName: '项目编号', required: true,
+                    name: 'Number', dName: '工程编号', required: true, minLen: 1,
                     value: _util.getValueByKey(basicInformation, 'projNum')
                 },
                 // 项目名称
@@ -449,7 +449,7 @@ const XMLStandard = (function () {
                 // 工程类型
                 {
                     name: 'ProjectType', dName: '工程类型', required: true,
-                    value: getProjectType(projectData, true)
+                    value: _util.getValueByKey(basicInformation, 'projectType')
                 },
                 // 建设性质
                 {
@@ -483,7 +483,7 @@ const XMLStandard = (function () {
                 },
                 // 建设单位
                 {
-                    name: 'BulidUnit', dName: '建设单位', required: true,
+                    name: 'BulidUnit', dName: '建设单位', required: true, minLen: 1,
                     value: _util.getValueByKey(basicInformation, 'constructingUnits')
                 },
                 // 建设单位法定代表人或其授权人
@@ -499,7 +499,7 @@ const XMLStandard = (function () {
                 // 数据交换标准编号
                 {
                     name: 'StandardNumber', required: true,
-                    value: 'DBJ/T XX-XX-2018'
+                    value: 'DBJ/T 15-145-2018'
                 },
                 // 建设(编制)范围
                 {
@@ -513,7 +513,7 @@ const XMLStandard = (function () {
                 },
                 // 建设规模单位
                 {
-                    name: 'Unit', dName: '建设规模单位', required: true,
+                    name: 'Unit', dName: '建设规模单位', required: true, minLen: 1,
                     value: _util.getValueByKey(basicInformation, 'unit')
                 },
                 // 技术经济指标(元) 取单方造价
@@ -619,7 +619,7 @@ const XMLStandard = (function () {
                 },
                 // 招标单位编制时间
                 {
-                    name: 'TenderCompileDate', mustHasValue: true,
+                    name: 'TenderCompileDate', mustHasValue: true, required: true,
                     value: _util.getValueByKey(basicInformation, 'tenderCompileDate')
                 },
                 // 招标单位审核人
@@ -1081,7 +1081,7 @@ const XMLStandard = (function () {
                 },
                 // 工程类型
                 {
-                    name: 'ProjectType', dName: '工程类型', required: true,
+                    name: 'ProjectType', dName: '工程类型', required: true, minLen: 1,
                     value: _util.getValueByKey(projectFeature, 'projType')
                 },
                 // 计价模式
@@ -1096,7 +1096,7 @@ const XMLStandard = (function () {
                 },
                 // 地区类别 取建设项目-基本信息-地区类别,基本信息那已经自检一次,这里不自检 //type: _type.INT, required: true, enumeration: Object.values(AreaKind), enumerationHint: Object.keys(AreaKind)
                 {
-                    name: 'AreaKind', dName: '地区类别', 
+                    name: 'AreaKind', dName: '地区类别',
                     value: AreaKind[_util.getValueByKey(basicInformation, 'regionalCategories')]
                 },
                 // 金额
@@ -1126,7 +1126,7 @@ const XMLStandard = (function () {
                 },
                 // 建设规模单位 取单位工程-工程特征-建设规模单位
                 {
-                    name: 'Unit', dName: '建设规模单位', required: true,
+                    name: 'Unit', dName: '建设规模单位', required: true, minLen: 1,
                     value: _util.getValueByKey(projectFeature, 'buildScaleUnit')
                 },
                 // 占总投资比例(%)
@@ -1433,23 +1433,24 @@ const XMLStandard = (function () {
         // 清单项目
         function WorkElement(node, kind, specialty) {
             const bills = node.data;
+            const row = node.serialNo() + 1;
             const attrs = [
                 // 项目编码
-                { name: 'Number', dName: '项目编码', required: true, value: bills.code || '' },
+                { name: 'Number', dName: '项目编码', required: true, value: bills.code || '', minLen: 1, failHint: `第${row}行清单-“项目编码”` },
                 // 项目名称
-                { name: 'Name', dName: '项目名称', required: true, value: bills.name },
+                { name: 'Name', dName: '项目名称', required: true, value: bills.name, minLen: 1, failHint: `第${row}行清单-“项目名称”` },
                 // 项目特征
                 { name: 'Attr', value: bills.itemCharacterText },
                 // 工作内容
                 { name: 'WorkContent', value: bills.jocContentText },
                 // 计量单位
-                { name: 'Unit', dName: '计量单位', required: true, value: bills.unit },
+                { name: 'Unit', dName: '计量单位', required: true, value: bills.unit, minLen: 1, failHint: `第${row}行清单-“计量单位”` },
                 // 工程量
                 { name: 'Quantity', type: _type.DECIMAL, value: bills.quantity },
                 // 计算基数
                 { name: 'QtyFormula', value: getQtyFormula(node) },
                 // 单价(元)
-                { name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
+                //{ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
                 // 设备单价(元)指清单项目所采用设备的综合单价
                 { name: 'EquipmentPrice', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'equipment.unitFee') },
                 // 最低限价(元)
@@ -1457,7 +1458,7 @@ const XMLStandard = (function () {
                 // 最高限价(元)
                 { name: 'PriceHigh', type: _type.DECIMAL, value: bills.maxPrice },
                 // 费率(%)
-                { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
+                //{ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
                 // 合价(元)
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 主要清单
@@ -1479,6 +1480,13 @@ const XMLStandard = (function () {
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
+            // 单价费率,单价与费率在同一个费用项目中不允许同时存在。(对于套定额的清单,则输出单价不输出费率rate;对于基数计算的,则输出费率不输出单价)
+            const showPrice = !!(node.children && node.children.length && node.children[0].sourceType === commonConstants.SourceType.RATION)
+            if (showPrice) {
+                attrs.push({ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') });
+            } else {
+                attrs.push({ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' });
+            }
             _base.Element.call(this, 'WorkElement', attrs, '清单');
         }
         // 工程量计算表
@@ -1507,16 +1515,18 @@ const XMLStandard = (function () {
             _base.Element.call(this, 'WorkContent', attrs, '工序内容');
         }
         // 定额子目
-        function Norm(ration, libCode, kind) {
+        function Norm(node, libCode, kind) {
+            const ration = node.data;
+            const row = node.serialNo() + 1;
             // 通过定额库编码,读取编码中间的数字,如房建定额库编码是“GD 1 2018",工程专业取“1”。
             const reg = /GD (\d+)/;
             const matched = libCode.match(reg);
             const specialty = matched && matched[1] || '1';
             const attrs = [
                 // 定额编码
-                { name: 'Number', dName: '定额编码', required: true, value: ration.code },
-                { name: 'Name', dName: '定额名称', required: true, value: ration.name },
-                { name: 'Unit', dName: '计量单位', required: true, value: ration.unit },
+                { name: 'Number', dName: '定额编码', required: true, value: ration.code, minLen: 1, failHint: `第${row}行定额-“定额编码”` },
+                { name: 'Name', dName: '定额名称', required: true, value: ration.name, minLen: 1, failHint: `第${row}行定额-“定额名称”` },
+                { name: 'Unit', dName: '计量单位', required: true, value: ration.unit, minLen: 1, failHint: `第${row}行定额-“计量单位”` },
                 { name: 'Quantity', type: _type.DECIMAL, value: ration.quantity },
                 // 单价
                 { name: 'Price', type: _type.DECIMAL, value: _util.getFee(ration.fees, 'common.unitFee') },
@@ -1580,7 +1590,7 @@ const XMLStandard = (function () {
                 // 工料机编码
                 { name: 'Number', value: glj.code },
                 // 消耗量
-                { name: 'Quantity', type: _type.DECIMAL, value: quantity },
+                { name: 'Quantity', type: _type.DECIMAL, value: (+quantity).toFixed(Decimal.GLJ) },
                 // 不计价材料
                 { name: 'NOCost', typ: _type.BOOL, value: noCost },
                 // 备注
@@ -1659,9 +1669,9 @@ const XMLStandard = (function () {
                 // 计算基数
                 { name: 'QtyFormula', value: getQtyFormula(node) },
                 // 单价
-                { name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
+                //{ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
                 // 费率
-                { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
+                //{ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 费用代号
@@ -1671,7 +1681,14 @@ const XMLStandard = (function () {
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
-            _base.Element.call(this, 'SundryCostsItem', attrs, '其他项目费明细');
+            // 单价费率,单价与费率在同一个费用项目中不允许同时存在。(对于套定额的清单,则输出单价不输出费率rate;对于基数计算的,则输出费率不输出单价)
+            const showPrice = !!(node.children && node.children.length && node.children[0].sourceType === commonConstants.SourceType.RATION);
+            if (showPrice) {
+                attrs.push({ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') });
+            } else {
+                attrs.push({ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' });
+            }
+            _base.Element.call(this, 'SundryCostsItem', attrs, '清单');
         }
         // 暂列金额标题
         function ProvisionalSumsGroup(node) {
@@ -1701,9 +1718,9 @@ const XMLStandard = (function () {
                 // 计算基数
                 { name: 'QtyFormula', value: getQtyFormula(node) },
                 // 单价
-                { name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
+                //{ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
                 // 费率
-                { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
+                // { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 汇总类型
@@ -1711,6 +1728,13 @@ const XMLStandard = (function () {
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
+            // 单价费率,单价与费率在同一个费用项目中不允许同时存在。(对于套定额的清单,则输出单价不输出费率rate;对于基数计算的,则输出费率不输出单价)
+            const showPrice = !!(node.children && node.children.length && node.children[0].sourceType === commonConstants.SourceType.RATION);
+            if (showPrice) {
+                attrs.push({ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') });
+            } else {
+                attrs.push({ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' });
+            }
             _base.Element.call(this, 'ProvisionalSumsItem', attrs, '暂列金额明细');
         }
         // 材料设备暂估价明细
@@ -1724,7 +1748,7 @@ const XMLStandard = (function () {
                 // 型号规格
                 { name: 'Specification', value: glj.specs },
                 // 单位
-                { name: 'Unit', dName: '单位', required: true, value: bills.unit },
+                { name: 'Unit', dName: '单位', required: true, value: glj.unit },
                 // 工程量(总消耗量)
                 { name: 'Quantity', type: _type.DECIMAL, value: glj.quantity },
                 // 单价 取市场价
@@ -1768,9 +1792,9 @@ const XMLStandard = (function () {
                 // 计算基数
                 { name: 'QtyFormula', value: getQtyFormula(node) },
                 // 单价
-                { name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
+                //{ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
                 // 费率
-                { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
+                //{ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 汇总类型
@@ -1778,6 +1802,12 @@ const XMLStandard = (function () {
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
+            const showPrice = !!(node.children && node.children.length && node.children[0].sourceType === commonConstants.SourceType.RATION);
+            if (showPrice) {
+                attrs.push({ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') });
+            } else {
+                attrs.push({ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' });
+            }
             _base.Element.call(this, 'SpecialtyProvisionalPriceItem', attrs, '专业工程暂估价明细');
         }
         // 计日工标题
@@ -1884,9 +1914,9 @@ const XMLStandard = (function () {
                 // 计算基数
                 { name: 'QtyFormula', value: getQtyFormula(node) },
                 // 单价
-                { name: 'Price', value: _util.getFee(bills.fees, 'common.unitFee') },
+                //{ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') },
                 // 费率
-                { name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
+                //{ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' },
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 依据
@@ -1896,6 +1926,12 @@ const XMLStandard = (function () {
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
+            const showPrice = !!(node.children && node.children.length && node.children[0].sourceType === commonConstants.SourceType.RATION);
+            if (showPrice) {
+                attrs.push({ name: 'Price', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.unitFee') });
+            } else {
+                attrs.push({ name: 'Rate', type: _type.DECIMAL, value: !commonUtil.isEmptyVal(bills.feeRate) ? bills.feeRate : '100' });
+            }
             _base.Element.call(this, eleName, attrs, '索赔费用明细');
         }
         // 规费
@@ -1966,8 +2002,8 @@ const XMLStandard = (function () {
                 { name: 'Specification', value: glj.specs },
                 // 单位
                 { name: 'Unit', dName: '单位', required: true, value: glj.unit },
-                // 数量(总消耗量)
-                { name: 'Quantity', type: _type.DECIMAL, value: glj.quantity },
+                // 数量(总消耗量) 用toFixed来补零没问题,因为补的位数是大于计算取舍位数,不会有toFixed的精度问题(2.35.toFixed(2) = 2.3)
+                { name: 'Quantity', type: _type.DECIMAL, value: (+glj.quantity).toFixed(Decimal.GLJ) },
                 // 风险系数 从承包人主要材料设备-造价信息差额调整法读取,取不到则输出"0"
                 { name: 'ProviderRate', type: _type.DECIMAL, value: glj.riskCoe },
                 // 基准单价 从承包人主要材料设备-造价信息差额调整法读取,取不到则输出"0"
@@ -2132,10 +2168,14 @@ const XMLStandard = (function () {
             // 建设项目
             const constructionProject = new ConstructionProject(projectData, summaryInfo);
             // 系统信息
+            // 软件相关信息进行base64编码(gbk to base64)
+            const hardwareID = _util.generateHardwareId();
+            const encodedData = await encodingUtil.getEncodedData([projectData.softInfo, hardwareID], 'gbk', true);
+            const ID1 = encodedData[projectData.softInfo];
+            const ID2 = encodedData[hardwareID];
             const systemInfo = new SystemInfo({
-                // 软件相关信息进行base64编码
-                ID1: Base64.encode(projectData.softInfo),
-                ID2: _util.generateHardwareId(),
+                ID1,
+                ID2,
                 makeDate: moment(Date.now()).format('YYYY-MM-DDTHH:mm:ss'),
             });
             // 工程信息
@@ -2376,7 +2416,7 @@ const XMLStandard = (function () {
                 .map(node => new UnitWorksSummaryItem(node));
             otherGroup.children.push(...otherChildren);
             // 除固定类别为“分部分项工程”、“措施项目”、“其他项目”以外的大项费用,通常无子项,所以显示到Item中。(即使造价书有子项,也显示到Item中,不再列出其子项数据)
-            const flags = [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTEHER];
+            const flags = [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER];
             const otherRoots = tenderDetail.mainTree.roots
                 .filter(node => !flags.includes(node.getFlag()))
                 .map(node => new UnitWorksSummaryItem(node));
@@ -2475,7 +2515,7 @@ const XMLStandard = (function () {
         function loadRation(node, kind) {
             // 定额
             const libCode = getLibCode(node.data, tenderDetail.projectInfo.engineeringInfo.ration_lib);
-            const norm = new Norm(node.data, libCode, kind);
+            const norm = new Norm(node, libCode, kind);
             // 工程量计算表
             const expressElement = loadQuantityExpressions(tenderDetail.quantity_detail.datas, false, node.data.ID);
             if (expressElement.length) {
@@ -2858,9 +2898,32 @@ const XMLStandard = (function () {
             for (const file of files) {
                 zip.file(file.fileName, file.blob, { binary: true });
             }
-            const zipFile = await zip.generateAsync({ type: 'blob' });
+            // 导出的文件名需要用gbk编码,否则广联达导入不了
+            // encodeFileName不支持异步,先调用一次generateAsync,获取string与Unit8Array的数据源映射            
+            // 不用TextEncoder的原因:https://developer.mozilla.org/zh-CN/docs/Web/API/TextEncoder/TextEncoder
+            // - 在Firefox 48和Chrome 53之前,编码类型标签被接受为TextEncoder对象的参数
+            // - 现在这两个浏览器已经删除了除utf-8之外的任何编码器类型的支持,以符合规范。 传入TextEncoder构造函数的任何类型标签现在都将被忽略,并且将创建一个utf-8 TextEncoder。
+            const source = new Set();
+            await zip.generateAsync({
+                type: 'uint8array',
+                encodeFileName: (string) => {
+                    source.add(string);
+                    return string;
+                }
+            });
+            const encodedData = await encodingUtil.getEncodedData([...source], 'gbk');
+            const zipFile = await zip.generateAsync({
+                type: 'uint8array',
+                encodeFileName: (string) => {
+                    const abSource = encodedData[string].data;
+                    const ab = Uint8Array.from(abSource);
+                    return ab;
+                }
+            });
+            // TypedBuffer to ArrayBuffer to blob
+            const saveAsBlob = new Blob([zipFile.buffer]);
             const exportKindName = _config.EXPORT_KIND_NAME[exportKind];
-            saveAs(zipFile, `广东标准交换数据(${exportKindName}).COS`);
+            saveAs(saveAsBlob, `广东标准交换数据(${exportKindName}).COS`);
         }
     }
 

+ 47 - 18
web/over_write/js/guangdong_2018_import.js

@@ -319,6 +319,14 @@ const importXML = (() => {
             feeCode: getValue(divisionalSrc, ['_Code']),
             remark: getValue(divisionalSrc, ['_Remark']),
         };
+        // 特殊处理措施项目的费用代号(广联达这两条数据没有导出费用代号,跟我们的清单模板不匹配,从而导致从合并变成了新增),详看mergeBills中的mergeitems放啊
+        if (type === billType.BILL) {
+            if (item.code === 'AQFHWMSG') {
+                item.feeCode = FlagFeeCodeMap[fixedFlag.GREEN_MEASURE_FEE];
+            } else if (item.code === 'QTCSF') {
+                item.feeCode = FlagFeeCodeMap[fixedFlag.OTHER_MEASURE_FEE];
+            }
+        }
         if (importFileKind === FileKind.tender) {
             const summaryFees = getFeesFromBasicCost(divisionalSrc);
             const fees = [{ fieldName: 'common', totalFee: getValue(divisionalSrc, ['_Total']), unitFee: getValue(divisionalSrc, ['_TechnicalAndEconomicIndex']) }];
@@ -712,7 +720,7 @@ const importXML = (() => {
         }
         // 根据计税方式获取价格,一般计税对应不含税,简易对应含税
         function getPriceByTaxType(gljSrc, taxType) {
-            return taxType === TaxType.NORMAL
+            return +taxType === TaxType.NORMAL
                 ? {
                     base_price: getValue(gljSrc, ['_NoTaxOrgPrice']),
                     market_price: getValue(gljSrc, ['_NoTaxPrice'])
@@ -1541,24 +1549,45 @@ const importXML = (() => {
      */
     async function unzipFile(file) {
         const jsZip = new JSZip();
-        const zip = await jsZip.loadAsync(file);
-        // TODO
-        /* const fileNameByteList = [];
-        let index = 0;
-        const zip = await jsZip.loadAsync(file, {
-            // zip文件中有一个标志,用于说明文件名和注释是否使用UTF-8编码。 
-            // 如果未设置,则JSZip无法知道所使用的编码(它通常是操作系统的默认编码)
-            // 因此若文件名编码非utf-8,则会调用decodeFileName。我们将其认为是gbk编码
-            decodeFileName: function (bytes) {
-                if (bytes.length) {
-                    fileNameByteList.push(getUnit8Numbers(bytes));
-                    return String(index++);
-                } else {
-                    return '';
+        // 广联达导出的cos文件,内部文件的文件名是gbk编码,如果不处理的话无法导入
+        let zip;
+        if (typeof TextDecoder === 'undefined') {
+            const gbkTextDecoder = new TextDecoder('gbk');
+            zip = await jsZip.loadAsync(file, {
+                // zip文件中有一个标志,用于说明文件名和注释是否使用UTF-8编码。 
+                // 如果未设置,则JSZip无法知道所使用的编码(它通常是操作系统的默认编码)
+                // 因此若文件名编码非utf-8,则会调用decodeFileName。我们将其认为是gbk编码
+                decodeFileName: function (bytes) {
+                    if (bytes.length) {
+                        return gbkTextDecoder.decode(bytes);
+                    } else {
+                        return '';
+                    }
                 }
-            }
-        });
-        const decodedNames = await util.getDecodedNames(fileNameByteList); */
+            });
+        } else {
+            const fileNameByteList = [];
+            let index = 0;
+            zip = await jsZip.loadAsync(file, {
+                decodeFileName: function (bytes) {
+                    if (bytes.length) {
+                        fileNameByteList.push(getUnit8Numbers(bytes));
+                        return String(index++);
+                    } else {
+                        return '';
+                    }
+                }
+            });
+            const decodedNames = await util.getDecodedData(fileNameByteList, 'gbk');
+            // 将zip里的文件名替换成gbk解码后的名称
+            decodedNames.forEach((name, index) => {
+                if (zip.files[index]) {
+                    zip.files[index].name = name;
+                    zip.files[name] = zip.files[index];
+                    delete zip.files[index];
+                }
+            });
+        }
         const map = {};
         for (const fileName in zip.files) {
             // 将二进制数据转换成字符串