瀏覽代碼

导入招标文件相关

zhongzewei 6 年之前
父節點
當前提交
1519f71e17

+ 1 - 0
config/gulpConfig.js

@@ -49,6 +49,7 @@ module.exports = {
         'lib/JSExpressionEval_src/Date.js',
         'web/building_saas/glj/js/socket.io.slim.js',
         'public/web/socket/connection.js',
+        'web/building_saas/main/js/models/importStandardInterface.js',
         'web/building_saas/pm/js/**/*.js',
         'lib/ztree/*.js',
         'lib/jquery-contextmenu/jquery.contextMenu.min.js'

+ 37 - 1
modules/pm/controllers/pm_controller.js

@@ -29,7 +29,8 @@ import SectionTreeDao from '../../complementary_ration_lib/models/sectionTreeMod
 let sectionTreeDao = new SectionTreeDao();
 let consts = require('../../main/models/project_consts');
 const rationLibModel = mongoose.model('std_ration_lib_map');
-
+const multiparty = require("multiparty");
+let logger = require("../../../logs/log_helper").logger;
 //统一回调函数
 let callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
@@ -38,6 +39,7 @@ let callback = function(req, res, err, message, data){
 
 module.exports = {
     checkRight: function (req, res) {
+        return true;
         if(typeof req.body.data === 'object'){
             req.body.data = JSON.stringify(req.body.data);
         }
@@ -320,6 +322,7 @@ module.exports = {
         rationValuation = await engineeringLibModel.getLib(rationValuation);
         let absoluteUrl = compilationData.overWriteUrl ? request.app.locals.rootDir + compilationData.overWriteUrl : request.app.locals.rootDir;
         let overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilationData.overWriteUrl : null;
+        let valuationOpts = billValuation.map(data => {return {name: data.name, id: data.id}});
         let renderData = {
             isFirst: isFirst,
             userAccount: request.session.userAccount,
@@ -327,6 +330,7 @@ module.exports = {
             compilationData: JSON.stringify(sessionCompilation),
             overWriteUrl: overWriteUrl,
             billValuation: JSON.stringify(billValuation),
+            valuationOpts: valuationOpts,
             rationValuation: JSON.stringify(rationValuation),
             engineeringList: JSON.stringify(engineering.List),
             compilationName: sessionCompilation.name,
@@ -746,5 +750,37 @@ module.exports = {
         } catch (err) {
             callback(req, res, 1, err, null);
         }
+    },
+    importInterface: async function(req, res) {
+        logger.info(`${req.ip} importInterface`);
+        const uploadOption = {
+            uploadDir: './public'
+        };
+        let uploadFullName = '';
+        const form = new multiparty.Form(uploadOption);
+        form.parse(req, async function(err, fields, files) {
+            try {
+                let file = files.file && files.file.length ? files.file[0] : null;
+                if (!file) {
+                    throw '无有效数据';
+                }
+                uploadFullName = file.path;
+                //获取源数据
+                let srcStr = fs.readFileSync(file.path, 'utf8');
+                if (!srcStr) {
+                    throw '无有效数据'
+                }
+                let srcData = JSON.parse(srcStr);
+                fs.unlinkSync(file.path);
+                let projectData = await pm_facade.importProject(srcData, req.session.sessionUser.id, req.session.sessionCompilation._id);
+                callback(req, res, 0, '', projectData);
+            } catch (err) {
+                console.log(err);
+                if(uploadFullName && fs.existsSync(uploadFullName)){
+                    fs.unlinkSync(uploadFullName);
+                }
+                callback(req, res, 1, err, null);
+            }
+        });
     }
 };

+ 369 - 3
modules/pm/facade/pm_facade.js

@@ -26,7 +26,8 @@ module.exports={
     changeFile:changeFile,
     getBasicInfo: getBasicInfo,
     getProjectFeature: getProjectFeature,
-    getProjectByGranularity: getProjectByGranularity
+    getProjectByGranularity: getProjectByGranularity,
+    importProject: importProject
 };
 
 
@@ -64,6 +65,7 @@ let engineeringModel = mongoose.model('engineering_lib');
 let basicInfoModel = mongoose.model('std_basic_info_lib');
 let projectFeatureModel = mongoose.model('std_project_feature_lib');
 let productModel = mongoose.model('product');
+import BillsTemplateModel from "../models/templates/bills_template_model";
 
 let featureLibModel =  mongoose.model("std_project_feature_lib");
 let scMathUtil = require('../../../public/scMathUtil').getUtil();
@@ -74,8 +76,22 @@ import CounterModel from "../../glj/models/counter_model";
 import moment from 'moment';
 import billsFlags from '../../common/const/bills_fixed';
 const notDeleted = [{deleteInfo: null}, {'deleteInfo.deleted': false}];
-
-
+import {
+    defaultDecimal,
+    billsQuantityDecimal,
+    basicInformation,
+    projectFeature,
+    displaySetting,
+    calcOptions,
+    tenderSetting,
+    G_FILE_VER
+} from '../models/project_property_template';
+import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
+let labourCoeFacade = require('../../main/facade/labour_coe_facade');
+let calcProgramFacade = require('../../main/facade/calc_program_facade');
+let mainColLibModel = mongoose.model('std_main_col_lib');
+import EngineeringLibModel from "../../users/models/engineering_lib_model";
+let installationFacade = require('../../main/facade/installation_facade');
 
 //拷贝例题项目
 //@param {String}userID {Array}projIDs拷贝的例题项目ID(建设项目、文件夹)@return {Boolean}
@@ -1143,4 +1159,354 @@ async function getProjectByGranularity(tenderID, granularity, userID, versionNam
         version = product.version || '';
     constructionProject._doc.softInfo = new Buffer(`${company};${versionName};${version};${userID}`).toString('base64');
     return constructionProject;
+}
+
+/*
+* 项目详细数据都导入完成了,再生成项目数据(项目管理界面数据)
+* */
+async function importProject(importObj, userID, compilationID) {
+    let toInsertProjects = [importObj];  //待新增项目数据
+    //获取项目ID
+    let projectIDs = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.project, importObj.projectCount);
+    let curID = projectIDs.sequence_value - importObj.projectCount + 1;
+    importObj.ID = curID++;
+    await setupProject(importObj);
+    //设置项目ID及相关数据
+    for (let i = 0; i < importObj.engs.length; i++) {
+        let curEng = importObj.engs[i],
+            nextEng = importObj.engs[i + 1];
+        curEng.ID = curID++;
+        curEng.ParentID = importObj.ID;
+        await setupProject(curEng);
+        toInsertProjects.push(curEng);
+        for (let j = 0; j < curEng.tenders.length; j++) {
+            let curTender = curEng.tenders[j],
+                nextTender = curEng.tenders[j + 1];
+            curTender.ID = curID++;
+            curTender.ParentID = curEng.ID;
+            curTender.NextSiblingID = nextTender ? curID : -1;
+            curTender.property.rootProjectID = importObj.ID;
+            await setupProject(curTender);
+            //插入单位工程的详细数据
+            await importTenderDetail(curTender);
+            toInsertProjects.push(curTender);
+        }
+        curEng.NextSiblingID = nextEng ? curID : -1;
+    }
+    //项目内部数据设置、新增完毕后,插入项目本身的数据,更新前节点数据
+    let bulks = [];
+    //如果有前节点,更新前节点
+    if (importObj.preID !== -1) {
+        bulks.push({
+            updateOne: {filter: {ID: importObj.preID}, update: {$set: {NextSiblingID: importObj.ID}}}
+        });
+    }
+    let needFields = ['ID', 'ParentID', 'NextSiblingID', 'name', 'projType', 'userID', 'compilation',
+                     'createDateTime', 'fileVer', 'property', 'shareInfo', 'fullFolder'],
+        rst = [];
+    for (let insertP of toInsertProjects) {
+        let needData= {};
+        for (let needField of needFields) {
+            needData[needField] = insertP[needField];
+        }
+        rst.push(needData);
+        bulks.push({
+            insertOne: {document: needData}
+        });
+    }
+    if (bulks.length > 0) {
+        await projectModel.bulkWrite(bulks);
+    }
+    return rst;
+
+
+    //给项目数据设置一些需要的数据
+    async function setupProject(data) {
+        data.userID = userID;
+        data.compilation = compilationID;
+        data.fileVer = G_FILE_VER;
+        data.createDateTime = new Date();
+        if (data.projType === 'Project') {
+            await setupConstruct(data);
+        } else if (data.projType === 'Tender') {
+            await setupTender(data);
+        }
+    }
+    //给建设项目设置一些数据
+    async function setupConstruct(data) {
+        //更新基本信息, 按照key匹配
+        let infoLib = await getBasicInfo(data.compilation);
+        if (infoLib) {
+            let flatDatas = [];
+            infoLib.info.forEach(ifData => flatDatas.push(...ifData.items));
+            data.basicInformation.forEach(importI => {
+                let matchData = flatDatas.find(f => f.key === importI.key);
+                if (matchData) {
+                    matchData.value = importI.value;
+                }
+            });
+        }
+        data.property.basicInformation = infoLib ? infoLib.info : [];
+    }
+    //给单位工程设置一些数据
+    async function setupTender(data) {
+        //小数位数
+        data.property.decimal = defaultDecimal;
+        //清单工程量精度
+        data.property.billsQuantityDecimal = billsQuantityDecimal;
+        //呈现选项
+        data.property.displaySetting = displaySetting;
+
+        data.property.billsCalcMode = 0;
+        data.property.zanguCalcMode = 0;
+        //计算选项
+        data.property.calcOptions = calcOptions;
+        //安装增加费
+        if(data.property.isInstall === true || data.property.isInstall === 'true'){//判断是否安装工程
+            await installationFacade.copyInstallationFeeFromLib(data.ID, data.property.engineering_id);
+        }
+        //锁定清单
+        data.property.lockBills = false;
+        //工料机单价调整系数
+        data.property.tenderSetting = tenderSetting;
+        //工程特征相关更新
+        let featureLib = await getProjectFeature(data.property.valuation, data.property.engineeringName, data.property.feeStandardName);
+        if (featureLib) {
+            //把导入的数据设置到默认生成的数据中,按名称匹配 (导入项不确定,因此无法确定获取项的key)
+            data.projectFeature.forEach(importF => {
+                let matchData = featureLib.feature.find(f => f.dispName === importF.name);
+                if (matchData) {
+                    matchData.value = importF.value;
+                }
+            });
+            //设置工程专业的值为费用标准的值..
+            let engData = featureLib.feature.find(function (d) {
+                return d.key === 'engineering';
+            });
+            if (engData) {
+                engData.value = data.feeName;
+            }
+        }
+        data.property.projectFeature = featureLib ? featureLib.feature : [];
+    }
+}
+
+//插入单位工程内部详细数据
+async function importTenderDetail(tenderData) {
+    //单价文件
+    let upFile = {
+        name: tenderData.name,
+        project_id: tenderData.ID,
+        user_id: tenderData.userID,
+        root_project_id: tenderData.property.rootProjectID
+    };
+    let unitPriceFileModel = new UnitPriceFileModel();
+    let addResult = await unitPriceFileModel.add(upFile);
+    if (!addResult) {
+        throw '新增单价文件失败';
+    }
+    tenderData.property.unitPriceFile.id = parseInt(addResult.id);
+    //费率文件
+    let feeRateFileID = await feeRate_facade.newFeeRateFile(tenderData.userID, tenderData);
+    tenderData.property.feeFile = feeRateFileID ? feeRateFileID : -1;
+    //人工系数文件
+    let lcFile = await labourCoeFacade.newProjectLabourCoe(tenderData);
+    tenderData.property.labourCoeFile = lcFile ? lcFile : null;
+    let cpFile = await calcProgramFacade.newProjectCalcProgramFile(tenderData);
+    tenderData.property.calcProgramFile = cpFile ? cpFile : null;
+    //列设置
+    let engineeringModel = new EngineeringLibModel();
+    let engineering = await engineeringModel.getEngineering(tenderData.property.engineering_id);
+    let mainTreeCol = await mainColLibModel.findOne({ID: tenderData.property.colLibID});
+    await projectSettingModel.create({projectID: tenderData.ID, main_tree_col: mainTreeCol.main_tree_col, glj_col: engineering.glj_col});
+    //清单
+    await importBills(tenderData);
+}
+
+//插入单位工程的清单
+async function importBills(tenderData) {
+    //获取清单模板数据
+    let billsTemplateModel = new BillsTemplateModel();
+    let templateData = await billsTemplateModel.getTemplateDataForNewProj(tenderData.property.templateLibID);
+    let billsDatas = JSON.parse(JSON.stringify(templateData));
+    //设置同层序号,为了后续过滤掉非必要模板数据、插入接口清单数据后,能生成正确的顺序的树
+    setSeqByNext(billsDatas);
+    function getFlag(data) {
+        return data.flags && data.flags[0] && data.flags[0].flag || '';
+    }
+    //过滤掉不含清单固定类别的模板数据 (导入接口只包含必要的清单模板数据)
+    let needfulDatas = billsDatas.filter(data => getFlag(data));
+    sortToTreeData(tenderData.ID, needfulDatas);
+    let newDatas = [];
+    //如果模板存在分部分项,插入分部分项数据
+    insertAndUpdate(billsFlags.SUB_ENGINERRING, tenderData.fbfx);
+    //技术措施项目
+    insertAndUpdate(billsFlags.CONSTRUCTION_TECH, tenderData.csxm.jscs);
+    //组织措施项目
+    insertAndUpdate(billsFlags.CONSTRUCTION_ORGANIZATION, tenderData.csxm.zzcs);
+    //暂列
+    insertAndUpdate(billsFlags.PROVISIONAL, tenderData.other.provisional);
+    //专业工程暂估
+    insertAndUpdate(billsFlags.ENGINEERING_ESITIMATE, tenderData.other.engineeringPro);
+    //计日工-人工
+    insertAndUpdate(billsFlags.LABOUR, tenderData.other.dayWork.labour);
+    //计日工-材料
+    insertAndUpdate(billsFlags.MATERIAL, tenderData.other.dayWork.material);
+    //计日工-机械
+    insertAndUpdate(billsFlags.MACHINE, tenderData.other.dayWork.machine);
+    //总承包服务费
+    insertAndUpdate(billsFlags.TURN_KEY_CONTRACT, tenderData.other.service);
+    //索赔
+    insertAndUpdate(billsFlags.CLAIM, tenderData.other.claim);
+    //签证
+    insertAndUpdate(billsFlags.VISA, tenderData.other.visa);
+    //其他
+    //找出材料工程设备暂估价
+    let materialPro = tenderData.other.others.find(data => data.name === '材料(工程设备)暂估价');
+    if (materialPro) {
+        tenderData.other.others.splice(tenderData.other.others.indexOf(materialPro), 1);
+        update(billsFlags.MATERIAL_PROVISIONAL, materialPro);
+    }
+    insertAndUpdate(billsFlags.OTHER, tenderData.other.others);
+    //规费和税金,非规费税金
+    let notChargeAndTax = [];
+    tenderData.chargeTax.forEach(data => {
+        if (data.name === '规费') {
+            update(billsFlags.CHARGE, data);
+        } else if (data.name === '税金') {
+            update(billsFlags.TAX, data);
+        } else {
+            notChargeAndTax.push(data);
+        }
+    });
+    //除了规费税金,就是税金下的增值税等数据
+    insertAndUpdate(billsFlags.TAX, notChargeAndTax);
+    insertAndUpdate(billsFlags.CHARGE, tenderData.chargeTax);
+    newDatas.push(...needfulDatas);
+    console.log(newDatas);
+    await billsModel.insertMany(newDatas);
+    //数据插入到数据库中
+    return newDatas;
+    //给新增数据设置ID数据,并且添加到待新增数组中
+    function setIDs(projectID, parentID, datas) {
+        newDatas.push(...datas);
+        datas.forEach((data) => {
+            data.projectID = tenderData.ID;
+            data.ID = uuidV1();
+            data.ParentID = parentID;
+        });
+        for (let i = 0; i < datas.length; i++) {
+            let cur = datas[i],
+                next = datas[i + 1];
+            cur.NextSiblingID = next ? next.ID : -1;
+            if (cur.items && cur.items.length) {
+                setIDs(projectID, cur.ID, cur.items);
+            }
+        }
+    }
+    //单独更新一条数据
+    function update(flag, data) {
+        let target = needfulDatas.find(data => getFlag(data) === flag);
+        if (target) {
+            for (let [k, v] of Object.entries(data)) {
+                target[k] = v;
+            }
+        }
+    }
+    //插入、更新数据
+    function insertAndUpdate(flag, inserts) {
+        if (!inserts || !inserts.length) {
+            return;
+        }
+        let target = needfulDatas.find(data => getFlag(data) === flag);
+        if (target) {
+            let targetChildren = needfulDatas.filter(data => data.ParentID == target.ID);
+            for (let insertData of inserts) {
+                //存在同名模板数据
+                let sameNameData = targetChildren.find(data => data.name === insertData.name);
+                if (sameNameData) {
+                    //将插入数据的子数据插入到同名模板数据中
+                    setIDs(tenderData.ID, sameNameData.ID, insertData.items);
+                    //更新该模板数据的一些值
+                    for (let [k, v] of Object.entries(insertData)) {
+                        sameNameData[k] = v;
+                    }
+                } else {    //不存在同名模板,直接将数据插入到目标中
+                    setIDs(tenderData.ID, target.ID, [insertData]);
+                }
+            }
+        }
+    }
+
+}
+
+/*
+* 将同层的清单数据,按照树结构顺序设置序号
+* @param {Array} billsData(清单模板数据)
+* */
+function setSeqByNext(billsData) {
+    //获取同层数据
+    let sameDepthDatas = {};    //按照ParentID区分
+    for (let data of billsData) {
+        if (!sameDepthDatas[data.ParentID]) {
+            sameDepthDatas[data.ParentID] = [];
+        }
+        sameDepthDatas[data.ParentID].push(data);
+    }
+    //设置同层节点的序号
+    for (let sameDepth of Object.values(sameDepthDatas)) {
+        let sortedData = getDataBySorted(sameDepth),
+            seq = 1;
+        sortedData.forEach((data => data.seq = seq++));
+    }
+    //将每个同层的数据设置好序号
+    //获取根据树结构NextSibling排序的同层数据
+    function getDataBySorted(datas) {
+        //链断了返回空数组
+        let target = [];
+        let last = datas.find(data => data.NextSiblingID == -1);
+        while (last && target.length !== datas.length) {
+            target.push(last);
+            last = datas.find(data => data.NextSiblingID == last.ID);
+        }
+        //链断了即清单模板数据的树结构有问题,这里做下兼容:
+        // 1.数据没问题,返回排序过后的同层数据数组    2.数据有问题,返回不排序的同层数组
+        return target.length === datas.length ? target.reverse() : datas;
+    }
+}
+
+//根据同层序号,设置NextSiblingID
+function sortSeqToNextSibling(needfulData) {
+    let sameDepthDatas = {};    //按照ParentID区分
+    for (let data of needfulData) {
+        if (!sameDepthDatas[data.ParentID]) {
+            sameDepthDatas[data.ParentID] = [];
+        }
+        sameDepthDatas[data.ParentID].push(data);
+    }
+    for (let sameDepth of Object.values(sameDepthDatas)) {
+        sameDepth.sort((a, b) => a.seq - a.seq);
+        for (let i = 0; i < sameDepth.length; i++) {
+            let cur = sameDepth[i],
+                next = sameDepth[i + 1];
+            cur.NextSiblingID = next ? next.ID : -1;
+        }
+    }
+}
+
+//清单模板数据转换为uuID树结构数据
+function sortToTreeData(projectID, needfulData) {
+    sortSeqToNextSibling(needfulData);
+    let uuidMaping = {};
+    uuidMaping['-1'] = -1;
+    //建立uuid-ID映射
+    for(let bill of needfulData){
+        uuidMaping[bill.ID] = uuidV1();
+    }
+    needfulData.forEach(function (template) {
+        template.projectID = projectID;
+        template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
+        template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
+        template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
+    });
 }

+ 8 - 3
modules/pm/models/project_model.js

@@ -28,7 +28,8 @@ import {
     projectFeature,
     displaySetting,
     calcOptions,
-    tenderSetting
+    tenderSetting,
+    G_FILE_VER
 } from './project_property_template';
 import optionSetting from '../../options/models/optionTypes';
 import fixedFlag from '../../common/const/bills_fixed';
@@ -54,7 +55,7 @@ let optionModel = mongoose.model('options');
 function ProjectsDAO() {
 }
 
-let G_FILE_VER = '1.0.1';
+//let G_FILE_VER = '1.0.1';
 
 ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, callback) {
     try {//
@@ -147,7 +148,6 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     Object.keys(data.updateData.property.unitPriceFile).length > 0 &&
                     data.updateData.property.unitPriceFile.id === '') {
                     let unitPriceFileModel = new UnitPriceFileModel();
-
                     let insertData = {
                         name: data.updateData.name,
                         project_id: data.updateData.ID,
@@ -297,6 +297,11 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
     }
 };
 
+//导入接口数据
+ProjectsDAO.prototype.importInterface = async function (srcData) {
+
+};
+
 ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback) {
     let updateType = {update: 'update', delete: 'delete'};
     let deleteInfo = Object.create(null);

+ 3 - 1
modules/pm/models/project_property_template.js

@@ -138,4 +138,6 @@ const projectFeature = [
     {dispName: '门窗材料及装饰', key: 'doorsWindowsMaterial', value: ''}
 ];
 
-export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting,calcOptions,tenderSetting};
+let G_FILE_VER = '1.0.1';
+
+export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting,calcOptions,tenderSetting, G_FILE_VER};

+ 2 - 0
modules/pm/routes/pm_route.js

@@ -64,6 +64,8 @@ module.exports = function (app) {
     pmRouter.post('/getBasicInfo', pmController.getBasicInfo);
     pmRouter.post('/getProjectFeature', pmController.getProjectFeature);
     pmRouter.post('/getProjectByGranularity', pmController.getProjectByGranularity);
+
+    pmRouter.post('/importInterface', pmController.importInterface);
     app.use('/pm/api', pmRouter);
 };
 

+ 50 - 0
public/web/PerfectLoad.js

@@ -85,3 +85,53 @@ jQuery.bootstrapLoading = {
         $("#loadingPage").remove();
     }
 }
+
+const SCComponent = (() => {
+    const InitProgressBar = (() => {
+        function ProgressBar($modal, $title, $content, $bar) {
+            this.$modal = $modal;
+            this.$title = $title;
+            this.$content = $content;
+            this.$bar = $bar;
+        }
+        ProgressBar.prototype.start = function (title, content) {
+            this.$title.text(title);
+            this.$content.text(content);
+            this.$bar.css('width', `0%`);
+            this.$modal.modal('show');
+            this.outer = setInterval(() => {
+                let curWidth = parseInt(this.$bar[0].style.width.replace('%', ''));
+                curWidth = parseInt(curWidth + 2);
+                this.$bar.css('width', `${curWidth}%`);
+                if (curWidth >= 80) {
+                    clearInterval(this.outer);
+                }
+            }, 100);
+            this.inner = setInterval(() => {
+                let curWidth = parseFloat(this.$bar[0].style.width.replace('%', ''));
+                if (curWidth >= 80) {
+                    curWidth = parseFloat(curWidth + 0.1);
+                    this.$bar.css('width', `${curWidth}%`);
+                    if (curWidth >= 95) {
+                        clearInterval(this.inner);
+                    }
+                }
+            }, 500);
+        };
+        ProgressBar.prototype.end = function () {
+            if (this.outer) {
+                clearInterval(this.outer);
+            }
+            if (this.inner) {
+                clearInterval(this.inner);
+            }
+            $('#progressBar').css('width', '100%');
+            setTimeout(() => {
+                this.$modal.modal('hide');
+            }, 500);
+        };
+        return ProgressBar;
+    })();
+
+    return {InitProgressBar}
+})();

+ 24 - 17
web/building_saas/main/js/models/exportStandardInterface.js

@@ -58,6 +58,7 @@ const XMLStandard = (function () {
         return typeof v !== 'undefined' && v !== null;
     }
     return function (userID, granularity) {
+        //导出粒度
         this.GRANULARITY = {
             PROJECT: 1,         //导出建设项目
             ENGINEERING: 2,     //导出单项工程
@@ -339,7 +340,7 @@ const XMLStandard = (function () {
             let attrs = [
                 {name: '建筑分类', value: source.feature.buildingClass, required: true},
                 {name: '工程分类', value: source.feature.projClass, required: true},
-                {name: '建设规模', value: 'todo', required: true},
+                {name: '建设规模', value: source.feature.projectScale, required: true},
                 {name: '工程类别', value: getValueByKey(source.basicInformation, 'projectCategory'), required: true} //取建设项目的工程类别
             ];
             element.call(this, '工程特征', attrs);
@@ -532,7 +533,7 @@ const XMLStandard = (function () {
                 {name: '计算基础说明', value: source.calcBase},
                 {name: '费率', value: source.feeRate, required: true},
                 {name: '金额', value: getFee(source.fees, 'common.totalFee'), required: true},
-                {name: '暂估价标志', value: getFee(source.fees, 'common.totalFee')},
+                {name: '暂估价标志', value: !!source.isEstimate, type: TYPE.BOOL},
                 {name: '备注', value: source.remark},
                 {name: '费用类别', value: source.feeType, enumeration: ['120201', '1204', '10041', '1206'], required: true},
             ];
@@ -838,7 +839,7 @@ const XMLStandard = (function () {
             element.call(this, '评审材料明细', attrs);
         }
 
-        async function setTimeoutSync(handle, time) {
+        function setTimeoutSync(handle, time) {
             return new Promise(function(resolve, reject) {
                 setTimeout(function () {
                     if (handle && typeof handle === 'function') {
@@ -1155,7 +1156,7 @@ const XMLStandard = (function () {
                     rowCode: `F${serialNo}`,
                     name: node.data.name,
                     calcBase: node.data.calcBase,
-                    feeRate: node.data.feeRate !== '' ? node.data.feeRate : 100,
+                    feeRate: node.data.feeRate ? node.data.feeRate : 100,
                     fees: node.data.fees,
                     feeType: FEE_TYPE[flag] || FEE_TYPE['0'],
                     remark: node.data.remark
@@ -1227,15 +1228,16 @@ const XMLStandard = (function () {
                 //创建项目特征节点
                 if (feature.length > 0) {
                     let itemChac = new ItemCharacter();
-                    let reg = /(.{0,}):(.{0,})/;
+                    //let reg = /(.{0,}):(.{0,})/;
                     for (let f of feature) {
-                        let source = {name: '', value: ''};
+                        /*let source = {name: '', value: ''};
                         let execRst = reg.exec(f);
                         if (execRst) {
                            source.name = execRst[1].replace(/^\d+\.{1}/, '').trim();  //去除开头(1.)序号,首位空格
                            source.value = execRst[2].trim();
                         }
-                        let featureEle = new Feature(source);
+                        let featureEle = new Feature(source);*/
+                        let featureEle = new Feature({name: f.trim(), value: ''});
                         itemChac.children.push(featureEle);
                     }
                     bills.children.push(itemChac);
@@ -1244,8 +1246,9 @@ const XMLStandard = (function () {
                 if (job.length > 0) {
                     let jobContent = new JobContent();
                     for (let j of job) {
-                        let data = j.replace(/^\d+\.{1}/, '').trim();
-                        let content = new Content(data);
+                        /*let data = j.replace(/^\d+\.{1}/, '').trim();
+                        let content = new Content(data);*/
+                        let content = new Content(j.trim());
                         jobContent.children.push(content);
                     }
                     bills.children.push(jobContent);
@@ -1436,7 +1439,7 @@ const XMLStandard = (function () {
                             code: node.data.code,
                             name: node.data.name,
                             calcBase: node.data.calcBase,
-                            feeRate: node.data.feeRate !== '' ? node.data.feeRate : 100,
+                            feeRate: node.data.feeRate ? node.data.feeRate : 100,
                             fees: node.data.fees,
                             remark: node.data.remark,
                             feeType: FEE_TYPE[getNodeFlag(node)] || FEE_TYPE['0']
@@ -1445,6 +1448,12 @@ const XMLStandard = (function () {
                         parent.children.push(formula);
                     }
                 }
+                if (parent instanceof ZZCSClass) {  //组织措施分类下的只能有一个组织措施分类
+                    let filters = parent.children.filter(data => data instanceof ZZCSClass);
+                    if (filters.length) {
+                        failList.push('组织措施分类下只能有一个组织措施分类');
+                    }
+                }
             }
             function loadJSCS(parent, nodes) {
                 for (let node of nodes) {
@@ -1486,7 +1495,6 @@ const XMLStandard = (function () {
                 otherEle.children.push(loadEngEstimate(engEstimateNode));
             }
             //添加计日工元素
-            debugger;
             let dayWorkNode = detail.Bills.tree.items.find(node => getNodeFlag(node) === fixedFlag.DAYWORK);
             let dayWorkEle = new DayWork({fees: dayWorkNode.data.fees});
                 if (dayWorkNode && dayWorkNode.children.length > 0) {
@@ -1640,7 +1648,7 @@ const XMLStandard = (function () {
                             name: node.data.name,
                             serviceContent: node.data.serviceContent,
                             calcBaseValue: node.data.calcBaseValue,
-                            feeRate: node.data.feeRate !== '' ? node.data.feeRate : 100,
+                            feeRate: node.data.feeRate ? node.data.feeRate : 100,
                             fees: node.data.fees,
                             remark: node.data.remark
                         };
@@ -1676,7 +1684,7 @@ const XMLStandard = (function () {
                         code: node.data.code,
                         name: node.data.name,
                         calcBase: node.data.calcBase,
-                        feeRate: node.data.feeRate !== '' ? node.data.feeRate : 100,
+                        feeRate: node.data.feeRate ? node.data.feeRate : 100,
                         commonTotalFee: totalFee,
                         notSummary: belongFlag && belongFlag === fixedFlag.MATERIAL_PROVISIONAL,   //不计入合价,只有材料(工程设备)暂估价固定节点
                         remark: node.data.remark
@@ -1715,7 +1723,7 @@ const XMLStandard = (function () {
                     rowCode: `F${serialNo}`,
                     name: node.data.name,
                     calcBase: node.data.calcBase,
-                    feeRate: node.data.feeRate !== '' ? node.data.feeRate : 100,
+                    feeRate: node.data.feeRate ? node.data.feeRate : 100,
                     fees: node.data.fees,
                     feeType: FEE_TYPE[getNodeFlag(node)] || FEE_TYPE['0'],
                     remark: node.data.remark
@@ -1889,7 +1897,7 @@ const XMLStandard = (function () {
                         name: calcItem.name,
                         calcBase: calcBase,
                         statement: calcItem.statement,
-                        feeRate: calcItem.feeRate !== '' ? calcItem.feeRate : 100,
+                        feeRate: calcItem.feeRate ? calcItem.feeRate : 100,
                         feeType: feeType,
                         remark: calcItem.memo
                     };
@@ -1972,7 +1980,6 @@ const XMLStandard = (function () {
         this.toXml = async function (tenderID) {
             let eleData = await loadProject(tenderID);
             this.datas = eleData;
-            //console.log(eleData);
             if (!eleData) {
                 return;
             }
@@ -1983,7 +1990,7 @@ const XMLStandard = (function () {
             //格式化
             xmlStr = formatXml(xmlStr);
             let blob = new Blob([xmlStr], {type: 'text/plain;charset=utf-8'});
-            saveAs(blob, '重庆标准交换数据.qtf');
+            saveAs(blob, '重庆标准交换数据.QTF');
         }
     }
 })();

+ 354 - 25
web/building_saas/main/js/models/importStandardInterface.js

@@ -8,9 +8,37 @@
  * @version
  */
 /*
-* 尽可能减少服务器压力,前端提取后端所需数据
+* 尽可能减少服务器压力,前端提取后端所需数据,提取成更符合我们程序数据结构的数据
+* 源数据中,含有前缀"_"的数据,为原xml节点中的属性,不含前缀的数据为原xml节点的节点名
 * */
 const ImportXML = (() => {
+    //文件类型
+    const FileKind = {
+        '招标': 1,
+        '投标': 2,
+    };
+    //计税方法
+    const TaxTypeMap = {
+        '一般计税法': 1,
+        '一般计税': 1,
+        '简易计税法': 2,
+        '简易计税': 2,
+        '': 1   //默认
+    };
+    //清单类型
+    const BillsType ={
+        DXFY: 1, //大项费用
+        FB: 2,   //分部
+        FX: 3,   //分项
+        BILLS: 4, //清单
+        BX: 5    //补项
+    };
+    //项目类型
+    const ProjectType = {
+        Project: 'Project',
+        Engineering: 'Engineering',
+        Tender: 'Tender'
+    };
     //读取文件转换为utf-8编码的字符串
     function readAsTextSync(file) {
         return new Promise((resolve, reject) => {
@@ -26,10 +54,10 @@ const ImportXML = (() => {
     }
     /*
     * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程'];
-    * @param {Object}obj原始对象 {Array}fields字段数组
+    * @param {Object}source源数据 {Array}fields字段数组
     * */
-    function getValue(obj, fields) {
-        let cur = obj;
+    function getValue(source, fields) {
+        let cur = source;
         for (let field of fields) {
             if (!cur[field]) {
                 return '';
@@ -43,7 +71,7 @@ const ImportXML = (() => {
     }
     return function () {
         /*
-        * 获取某字段的值,返回数组
+        * 获取某字段的值,强制返回数组
         * */
         function arrayValue(source, fields) {
             let target = getValue(source, fields);
@@ -55,36 +83,337 @@ const ImportXML = (() => {
             return target;
         }
         //标段
-        function project(source) {
-            return {
+        function loadProject(source) {
+            let obj = {
+                projType: ProjectType.Project,
                 name: getValue(source, ['标段', '_项目名称']),
-                engs: eng(source)
+                engs: loadEng(source),
+                property: {
+                    taxType: TaxTypeMap[getValue(source, ['标段', '_计税方法'])],
+                    fileKind: FileKind[getValue(source, ['标段', '_文件类型'])],
+                    compilationIllustration: getValue(source, ['标段', '编制说明', '_内容'])
+                },
+                basicInformation: loadBasicInfo(source),
             };
+            //统计项目数量
+            let count = 1 + obj.engs.length;
+            obj.engs.forEach(eng => {
+                count += eng.tenders.length;
+            });
+            obj.projectCount = count;
+            return obj;
+        }
+        //基本信息相关
+        function loadBasicInfo(source) {
+            let info = [];
+            info.push({key: 'projNum', value: getValue(source, ['标段', '_项目编号'])});
+            info.push({key: 'constructionUnit', value: getValue(source, ['标段', '_建设单位'])});
+            //项目信息
+            let projInfoSrc = getValue(source, ['标段', '项目信息']);
+            if (projInfoSrc) {
+                info.push({key: 'projectScale', value: getValue(projInfoSrc, ['_工程规模'])});
+                info.push({key: 'projLocation', value: getValue(projInfoSrc, ['_工程所在地'])});
+                info.push({key: 'projAddress', value: getValue(projInfoSrc, ['_工程地址'])});
+                info.push({key: 'buildingUnit', value: getValue(projInfoSrc, ['_施工单位'])});
+                info.push({key: 'establishmentUnit', value: getValue(projInfoSrc, ['_编制单位'])});
+                info.push({key: 'auditUnit', value: getValue(projInfoSrc, ['_审核单位'])});
+                info.push({key: 'buildingUnitAuthor', value: getValue(projInfoSrc, ['_编制人'])});
+                info.push({key: 'auditUnitAuditor', value: getValue(projInfoSrc, ['_审核人'])});
+                info.push({key: 'commencementDate', value: getValue(projInfoSrc, ['_开工日期'])});
+                info.push({key: 'completionDate', value: getValue(projInfoSrc, ['_竣工日期'])});
+                info.push({key: 'establishDate', value: getValue(projInfoSrc, ['_编制日期'])});
+                info.push({key: 'auditDate', value: getValue(projInfoSrc, ['_审核日期'])});
+                info.push({key: 'materialPricePeriod', value: getValue(projInfoSrc, ['_材料价格期'])});
+                info.push({key: 'contractPriceType', value: getValue(projInfoSrc, ['_合同价类型'])});
+                //招标信息
+                info.push({key: 'tenderingAgency', value: getValue(projInfoSrc, ['招标信息', '_招标代理机构'])});
+                info.push({key: 'tenderCostEngineer', value: getValue(projInfoSrc, ['招标信息', '_造价工程师'])});
+                info.push({key: 'tenderCostEngineerNo', value: getValue(projInfoSrc, ['招标信息', '_造价工程师注册证号'])});
+                info.push({key: 'tenderPeriod', value: getValue(projInfoSrc, ['招标信息', '_招标工期'])});
+                info.push({key: 'engineeringCost', value: getValue(projInfoSrc, ['招标信息', '_控制总价'])});
+                let firstEng = arrayValue(source, ['标段', '单项工程'])[0];
+                if (firstEng) {
+                    let firstTender = arrayValue(firstEng, ['单位工程'])[0];
+                    if (firstTender) {
+                        info.push({key: 'projectCategory', value: getValue(firstTender, ['工程特征', '_工程类别'])});
+                    }
+                }
+            }
+            return info;
         }
         //单项工程
-        function eng(source) {
-            let engSrcs = arrayValue(source, ['标段', '单项工程']),
-                rst = [];
-            for (let src of engSrcs) {
-                rst.push({
+        function loadEng(source) {
+            return arrayValue(source, ['标段', '单项工程']).map(src => {
+                return {
+                    projType: ProjectType.Engineering,
                     name: getValue(src, ['_名称']),
-                    tenders: tender(src)
-                });
-            }
-            return rst;
+                    tenders: loadTender(src)
+                };
+            });
         }
         //单位工程
-        function tender(engSrc) {
-            let tenderSrcs = arrayValue(engSrc, ['单位工程']),
-                rst = [];
-            for (let src of tenderSrcs) {
-                rst.push({
+        function loadTender(engSrc) {
+            return arrayValue(engSrc, ['单位工程']).map(src => {
+                return {
+                    projType: ProjectType.Tender,
                     name: getValue(src, ['_名称']),
                     engineering: getValue(src, ['_专业']),
+                    taxType: TaxTypeMap[getValue(src, ['_计税方法'])],
+                    projectFeature: loadProjectFeature(src),
+                    fbfx: loadFBFX(src),
+                    csxm: loadCSXM(src),
+                    other: loadOther(src),
+                    chargeTax: loadChargeTax(src)
+                };
+            });
+        }
+        //工程特征相关,特征项没有固定的项,不清楚导入的项无法用已有key对应,而且工程特征名称不会有重复,因此用名称与我们软件的工程特征对应
+        function loadProjectFeature(tenderSrc) {
+            let feature = [];
+            feature.push({name: '建筑分类', value: getValue(tenderSrc, ['工程特征', '_建筑分类'])});
+            feature.push({name: '工程分类', value: getValue(tenderSrc, ['工程特征', '_工程分类'])});
+            feature.push({name: '建设规模', value: getValue(tenderSrc, ['工程特征', '_建设规模'])});
+            let items = arrayValue(tenderSrc, ['工程特征', '特征项']);
+            feature.push(...items.map(item => {
+                return {name: getValue(item, ['_名称']), value: getValue(item, ['_内容'])};
+            }));
+            return feature;
+        }
+        /*
+         * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
+         * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
+         *         eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
+         *        {Function} 获得源数据后,需要提取的数据方法
+         * @return {Array}
+         * */
+        function getItemsRecur(src, fields, extractFuc) {
+            let itemsSrc = [],
+                curField = [''];
+            for (let field of fields) {
+                itemsSrc = arrayValue(src, field);
+                if (itemsSrc.length) {
+                    curField = field;
+                    break;
+                }
+            }
+            return itemsSrc.map(itemSrc => {
+                let obj = extractFuc(itemSrc, curField);
+                obj.items = getItemsRecur(itemSrc, fields, extractFuc);
+                return obj;
+            });
+        }
+        //分部分项,两种情况 分部分项-清单分部-清单项目、分部分项-清单项目
+        //return {Array}
+        function loadFBFX(tenderSrc) {
+            let fbfxSrc = getValue(tenderSrc, ['分部分项清单']),
+                fields = [['清单分部'], ['清单项目']];
+            return getItemsRecur(fbfxSrc, fields, (itemSrc, curField) => {
+                if (curField[0] === fields[0][0]) {    //提取分部所需数据
+                    return {
+                        type: BillsType.FB,
+                        code: getValue(itemSrc, ['_编号']),
+                        name: getValue(itemSrc, ['_名称']),
+                        remark: getValue(itemSrc, ['_备注']),
+
+                    }
+                } else {    //提取清单项目所需数据
+                    return extractBills(itemSrc, BillsType.FX);
+                }
+            });
+        }
+        //提取清单项目数据
+        function extractBills(fxSrc, type) {
+            return {
+                type: type, //清单类型
+                code: getValue(fxSrc, ['_项目编码']),
+                name: getValue(fxSrc, ['_项目名称']),
+                unit: getValue(fxSrc, ['_单位']),
+                isEstimate: JSON.parse(getValue(fxSrc, ['_暂估清单标志'])),
+                mainBills: JSON.parse(getValue(fxSrc, ['_主要清单标志'])),
+                quantity: getValue(fxSrc, ['_工程量']),
+                remark: getValue(fxSrc, ['_备注']),
+                itemCharacterText: itemCharacterText(fxSrc)
+            };
+            //特征及内容文本(默认显示在项目特征列)
+            function itemCharacterText(fxSrc) {
+                let textArr = [];
+                //项目特征
+                let itemCharacter = getValue(fxSrc, ['项目特征']);
+                if (itemCharacter) {
+                    textArr.push('[项目特征]');
+                    let features = arrayValue(itemCharacter, ['特征']);
+                    textArr.push(...features.map(feature => {
+                        let fName = getValue(feature, ['_特征名称']),
+                            fDesr = getValue(feature, ['_特征描述']);
+                        //若不存在特征描述,同时特征名称中含有“:”,则直接返回特征名称 (广联达招标文件中,特征名称包含了特征名:值)
+                        if (!fDesr && /[\:,:]/.test(fName)) {
+                            return fName;
+                        } else {
+                            return `${fName}: ${fDesr}`;
+                        }
+                    }));
+                }
+                //工作内容
+                if (textArr.length) {
+                    textArr.push('[工作内容]');
+                }
+                let jobContent = getValue(fxSrc, ['工程内容']);
+                if (jobContent) {
+                    let jobs = arrayValue(jobContent, ['内容']);
+                    textArr.push(...jobs.map(job => getValue(job, ['_内容'])));
+                }
+                return textArr.join('\n');
+            }
+        }
+        //措施项目
+        function loadCSXM(tenderSrc) {
+            let target = {};
+            let zzcsxmSrc = getValue(tenderSrc, ['措施项目清单', '组织措施清单']);
+            target.zzcs = loadZZCS(zzcsxmSrc);
+            let jscsxmSrc = getValue(tenderSrc, ['措施项目清单', '技术措施清单']);
+            target.jscs = loadJSCS(jscsxmSrc);
+            return target;
+            //组织措施清单
+            function loadZZCS(zzcsSrc) {
+                let fields = [['组织措施分类'], ['公式计算措施项']];
+                return getItemsRecur(zzcsSrc, fields, (itemSrc, curField) => {
+                    if (curField[0] === fields[0][0]) {    //组织措施分类
+                        return {
+                            type: BillsType.BILLS,
+                            code: getValue(itemSrc, ['_编码']),
+                            name: getValue(itemSrc, ['_名称']),
+                            remark: getValue(itemSrc, ['_备注'])
+                        }
+                    } else {
+                        return {    //公式计算措施项
+                            type: BillsType.BILLS,
+                            code:getValue(itemSrc, ['_序号']),
+                            name: getValue(itemSrc, ['_名称']),
+                            calcBase: getValue(itemSrc, ['_计算基础表达式']),
+                            feeRate: getValue(itemSrc, ['_费率']),
+                            isEstimate: JSON.parse(getValue(itemSrc, ['_暂估价标志'])),
+                            remark: getValue(itemSrc, ['_备注'])
+                        };
+                    }
+                });
+            }
+            //技术措施清单
+            function loadJSCS(jscsSrc) {
+                let fields = [['技术措施分类'], ['清单项目']];
+                return getItemsRecur(jscsSrc, fields, (itemSrc, curField) => {
+                    if (curField[0] === fields[0][0]) { //技术措施分类
+                        return {
+                            type: BillsType.BILLS,
+                            code: getValue(itemSrc, ['_编号']),
+                            name: getValue(itemSrc, ['_名称'])
+                        }
+                    } else {    //清单项目
+                        return extractBills(itemSrc, BillsType.BILLS);
+                    }
                 });
             }
-            return rst;
         }
+        //其他项目
+        function loadOther(tenderSrc) {
+            let otherSrc = getValue(tenderSrc, ['其他项目清单']);
+            //暂列金额
+            let provisional = arrayValue(otherSrc, ['暂列金额', '暂列金额明细']).map(src => {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_编号']),
+                    name: getValue(src, ['_项目名称']),
+                    unit: getValue(src, ['_计量单位']),
+                    remark: getValue(src, ['_备注'])
+                };
+            });
+            //专业工程暂估价
+            let engineeringPro = arrayValue(otherSrc, ['专业工程暂估价', '专业工程暂估明细']).map(src => {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_编号']),
+                    name: getValue(src, ['_工程名称']),
+                    engineeringContent: getValue(src, ['_工程内容']),
+                    remark: getValue(src, ['_备注'])
+                };
+            });
+            //计日工
+            function extractDayWorkItem(src) {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_编号']),
+                    name: getValue(src, ['_名称']),
+                    unit: getValue(src, ['_单位']),
+                    quantity: getValue(src, ['_数量']),
+                    remark: getValue(src, ['_备注'])
+                };
+            }
+            let labour = arrayValue(otherSrc, ['计日工', '人工', '计日工项目']).map(extractDayWorkItem),
+                material = arrayValue(otherSrc, ['计日工', '材料', '计日工项目']).map(extractDayWorkItem),
+                machine = arrayValue(otherSrc, ['计日工', '施工机械', '计日工项目']).map(extractDayWorkItem);
+            let dayWork = {labour, material, machine};
+            //总承包服务费
+            let fields = [['总承包服务费分类'], ['总承包服务费费用项']],
+                serviceSrc = getValue(otherSrc, ['总承包服务费']);
+            let service = getItemsRecur(serviceSrc, fields, (src, curField) => {
+                if (curField[0] === fields[0][0]) {
+                    return {    //总承包服务费分类
+                        type: BillsType.BILLS,
+                        code: getValue(src, ['_编号']),
+                        name: getValue(src, ['_名称']),
+                        remark: getValue(src, ['_备注'])
+                    }
+                } else {
+                    return {    //总承包服务费费用项
+                        type: BillsType.BILLS,
+                        code:getValue(src, ['_编号']),
+                        name: getValue(src, ['_工程名称']),
+                        //calcBase: getValue(src, ['_计算基础']),
+                        serviceContent: getValue(src, ['_服务内容']),
+                        feeRate: getValue(src, ['_费率']),
+                        remark: getValue(src, ['_备注'])
+                    };
+                }
+            });
+            //索赔、签证
+            function extractClaimVisa(src) {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_编号']),
+                    name: getValue(src, ['_项目名称']),
+                    unit: getValue(src, ['_计量单位']),
+                    quantity: getValue(src, ['_数量']),
+                    claimVisa: getValue(src, ['_依据'])
+                };
+            }
+            let claim = arrayValue(otherSrc, ['索赔计价汇总', '签证索赔计价汇总费用项']).map(extractClaimVisa),
+                visa = arrayValue(otherSrc, ['现场签证计价汇总', '签证索赔计价汇总费用项']).map(extractClaimVisa);
+            //其他
+            let others = arrayValue(otherSrc, ['其他', '其他列项']).map(src => {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_编号']),
+                    name: getValue(src, ['_项目名称']),
+                    calcBase: getValue(src, ['_计算基础']),
+                    feeRate: getValue(src, ['_费率']),
+                    remark: getValue(src, ['_备注'])
+                }
+            });
+            return {provisional, engineeringPro, dayWork, service, claim, visa, others};
+        }
+        //规费和税金清单
+        function loadChargeTax(tenderSrc) {
+            return arrayValue(tenderSrc, ['规费和税金清单', '费用项']).map(src => {
+                return {
+                    type: BillsType.BILLS,
+                    code: getValue(src, ['_序号']),
+                    name: getValue(src, ['_名称']),
+                    calcBase: getValue(src, ['_计算基础表达式']),
+                    feeRate: getValue(src, ['_费率']),
+                    remark: getValue(src, ['_备注'])
+                };
+            });
+        }
+        //公式计算措施项
         this.extractData = async (file) => {
             //将二进制文件转换成字符串
             let xmlStr = await readAsTextSync(file);
@@ -96,8 +425,8 @@ const ImportXML = (() => {
             }
             console.log(xmlObj);
             //提取数据
-            console.log(project(xmlObj));
-            return project(xmlObj);
+            console.log(loadProject(xmlObj));
+            return loadProject(xmlObj);
         };
     };
 })();

+ 24 - 32
web/building_saas/pm/html/project-management.html

@@ -734,10 +734,10 @@
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">欢迎使用纵横建筑计价</h5>
+                <h5 id="progress-title" class="modal-title">欢迎使用纵横建筑计价</h5>
             </div>
             <div class="modal-body">
-                <h5 class="my-3">首次加载例题,请稍候……</h5>
+                <h5 id="progress-content" class="my-3">首次加载例题,请稍候……</h5>
                 <div class="progress mb-3">
                     <div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
                 </div>
@@ -764,7 +764,7 @@
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">选择文件</label>
                         <div class="col">
                             <div class="custom-file custom-file-sm">
-                                <input type="file" class="custom-file-input" id="customFile" accept=".xml,.qtf,.QTF,.xlsx" lang="zh">
+                                <input type="file" class="custom-file-input" id="customFile" accept=".xml,.qtf,.QTF" lang="zh">
                                 <label class="custom-file-label" for="customFile" style="white-space: nowrap; overflow: hidden;">请选择导入文件</label>
                             </div>
                             <div class="alert alert-success mt-3" id="uploadAlert" role="alert" style="display: none;"></div>
@@ -781,11 +781,10 @@
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">计价规则</label>
                         <div class="col">
-                            <select class="form-control form-control-sm">
-                                <option selected>赣建价[2017]7号</option>
-                                <option value="1">One</option>
-                                <option value="2">Two</option>
-                                <option value="3">Three</option>
+                            <select id="import-valuation" class="form-control form-control-sm">
+                                <% for (let valuation of valuationOpts) { %>
+                                <option value="<%= valuation.id %>"><%= valuation.name %></option>
+                                <% } %>
                             </select>
                         </div>
                     </div>
@@ -807,52 +806,45 @@
             </div>
             <div class="modal-body">
                 <form>
-                    <p>该文件共有10个单位工程待导入</p>
+                    <p>该文件共有0个单位工程待导入</p>
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">单项工程</label>
                         <div class="col">
-                            <input id="tbc-engName" type="text" class="form-control form-control-sm" value="AA单项工程" readonly="">
+                            <input id="tbc-engName" type="text" class="form-control form-control-sm" value="" readonly="">
                         </div>
                     </div>
+                    <span class="form-text text-danger" id="tbc-engName-info" style="display: none;">无有效单项工程</span>
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">单位工程</label>
                         <div class="col">
-                            <input id="tbc-tenderName" type="text" class="form-control form-control-sm" value="XXXX单位" readonly="">
+                            <input id="tbc-tenderName" type="text" class="form-control form-control-sm" value="" readonly="">
                         </div>
                     </div>
+                    <span class="form-text text-danger" id="tbc-tenderName-info" style="display: none;">无有效单位工程</span>
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">工程专业</label>
                         <div class="col">
                             <select id="tbc-engineering" class="form-control form-control-sm" readonly>
-                                <option selected>建筑工程</option>
-                                <option value="1">One</option>
-                                <option value="2">Two</option>
-                                <option value="3">Three</option>
                             </select>
                         </div>
                     </div>
+                    <span class="form-text text-danger" id="tbc-engineering-info" style="display: none;">无有效工程专业</span>
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">费用标准</label>
                         <div class="col">
-                            <select class="form-control form-control-sm">
-                                <option selected>建筑工程</option>
-                                <option value="1">One</option>
-                                <option value="2">Two</option>
-                                <option value="3">Three</option>
+                            <select id="tbc-feeStandard" class="form-control form-control-sm">
                             </select>
                         </div>
                     </div>
+                    <span class="form-text text-danger" id="tbc-feeStandard-info" style="display: none;">无有效费用标准</span>
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">计算程序</label>
                         <div class="col">
-                            <select class="form-control form-control-sm">
-                                <option selected>标准模板</option>
-                                <option value="1">One</option>
-                                <option value="2">Two</option>
-                                <option value="3">Three</option>
+                            <select id="tbc-calcProgram" class="form-control form-control-sm">
                             </select>
                         </div>
                     </div>
+                    <span class="form-text text-danger" id="tbc-calcProgram-info" style="display: none;">无有效计算程序</span>
                     <div class="d-flex justify-content-center">
                         <div class="btn-group btn-group-sm " role="group">
                             <button id="tbc-prev" type="button" class="btn btn-outline-primary" disabled="">上一工程</button>
@@ -864,7 +856,7 @@
             </div>
             <div class="modal-footer">
                 <a id="import-prev" href="javascript:void(0);" data-toggle="modal" data-target="#import1" class="btn btn-primary">上一步</a>
-                <button type="button" class="btn btn-primary">确认导入</button>
+                <button disabled id="import-confirm" type="button" class="btn btn-primary">确认导入</button>
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
             </div>
         </div>
@@ -873,11 +865,10 @@
 <!-- JS. -->
 <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
 <script>GC.Spread.Sheets.LicenseKey = '<%- LicenseKey %>';</script>
-<!--<script src="/lib/x2js/xml2json.min.js"></script>-->
+<script src="/lib/x2js/xml2json.min.js"></script>
 <!-- inject:js -->
 <script src="/web/building_saas/js/global.js"></script>
 <script src="/public/web/uuid.js"></script>
-<script src="/public/web/PerfectLoad.js"></script>
 <script src="/public/web/date_util.js"></script>
 <script src="/web/building_saas/pm/js/pm_tree.js"></script>
 <script src="/public/web/id_tree.js"></script>
@@ -888,10 +879,10 @@
 <script src="/lib/JSExpressionEval_src/Date.js"></script>
 <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
 <script src="/public/web/socket/connection.js"></script>
+<script src="/web/building_saas/main/js/models/importStandardInterface.js"></script>
 <script src="/web/building_saas/pm/js/pm_ajax.js"></script>
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>
-<!--<script src="/web/building_saas/main/js/models/importStandardInterface.js"></script>
-<script src="/web/building_saas/pm/js/pm_import.js"></script>-->
+<script src="/web/building_saas/pm/js/pm_import.js"></script>
 <script src="/web/building_saas/pm/js/pm_gc.js"></script>
 <script src="/web/building_saas/pm/js/pm_share.js"></script>
 <!-- zTree -->
@@ -908,10 +899,11 @@
     autoFlashHeight();
 </script>
 <script type="text/javascript">
-    let billValuation = '<%- billValuation %>';
-    let rationValuation = '<%- rationValuation %>';
+    let billValuation = JSON.parse('<%- billValuation %>');
+    let rationValuation = JSON.parse('<%- rationValuation %>');
     let engineeringList = '<%- engineeringList %>';
     let compilationData = '<%- compilationData %>';
     compilationData = JSON.parse(compilationData.replace(/[\s\r\n]/g, ""));//去掉空格字符
+    console.log(billValuation);
 </script>
 </html>

+ 277 - 14
web/building_saas/pm/js/pm_import.js

@@ -7,11 +7,11 @@
  * @date 2019/5/15
  * @version
  */
-
+//操作接口为eventListen
 const importView = (() => {
     let xmlObj = null;  //导入xml转化后的对象
     let tbcObj = null;  //待确认对象
-    //显示、隐藏提示相关信息
+    //显示、隐藏提示上传文件窗口相关提示信息
     function showUploadAlert(success, msg){
         if(!success){
             $('#uploadAlert').removeClass('alert-success');
@@ -35,42 +35,105 @@ const importView = (() => {
         for (let eng of xmlObj.engs) {
             if (Array.isArray(eng.tenders)) {
                 for (let tender of eng.tenders) {
+                    tender.temp = {};  //暂时存放的信息(不是从导入文件中获取的,记录当前工程已阅、选择的费用标准、计算程序)
                     tender.engName = eng.name;
                 }
-                rst = [...rst, ...eng.tenders];
+                rst.push(...eng.tenders);
             }
         }
         return rst;
     }
+    //确认界面提示信息隐藏
+    function hideTBCInfo() {
+        $('#tbc-engName-info').hide();
+        $('#tbc-tenderName-info').hide();
+        $('#tbc-engineering-info').hide();
+        $('#tbc-calcProgram-info').hide();
+    }
     //待确认界面相关
     class TBC {
-        constructor(xmlObj) {
+        constructor(xmlObj, valuation) {
             this.datas = getTenderDatas(xmlObj);
-            this.curIdx = 0;
+            this.valuationType = 'bill';    //计价方式 暂时默认为清单计价
+            this.valuation = valuation;     //计价规则数据
+            this.engineeringList = valuation ? valuation.engineering_list : []; //工程专业库数据
+            this.curIdx = 0;    //当前单位工程数据索引
             this.init();
         }
         //初始化
         init() {
-            this.next(0);   //初始化的时候,显示第第一份数据
+            this.next(this.curIdx);
             $('#importInterface .modal-content:eq(1) p:eq(0)').text(`该文件共有${this.datas.length}个单位工程待导入`);  //信息统计信息
         }
+        checkConfirmed() {
+            //检查数据是否都确认过了
+            let allConfirmed = this.datas.every(data => data.temp.confirmed);
+            if (allConfirmed) {
+                $('#import-confirm').prop('disabled', false);
+            }
+        }
+        //检测当前待确认工程是否有效,有效才可确认
+        checkValid() {
+            let engName = $('#tbc-engName').val();
+            if (!engName) {
+                return $('#tbc-engName-info').show();
+            } else {
+                $('#tbc-engName-info').hide();
+            }
+            let tenderName = $('#tbc-tenderName').val();
+            if (!tenderName) {
+                return $('#tbc-tenderName-info').show();
+            } else {
+                $('#tbc-tenderName-info').hide();
+            }
+            let engineering = $('#tbc-engineering').val();
+            if (!engineering) {
+                return $('#tbc-engineering-info').show();
+            } else {
+                $('#tbc-engineering-info').hide();
+            }
+            let feeStandard = $('#tbc-feeStandard').val();
+            if (!feeStandard) {
+                return $('#tbc-feeStandard-info').show();
+            } else {
+                $('#tbc-feeStandard-info').hide();
+            }
+            let calcProgram = $('#tbc-calcProgram').val();
+            if (!calcProgram) {
+                return $('#tbc-calcProgram-info').show();
+            } else {
+                $('#tbc-calcProgram-info').hide();
+            }
+            return null;
+        }
         //下一工程
         next(idx = null) {
             this.curIdx = idx !== null ? idx : this.curIdx + 1;
+            if (this.curIdx && this.checkValid()) {
+                this.curIdx--;
+                return;
+            }
             let curData = this.datas[this.curIdx];
             //显示这个工程的相关信息
             $('#tbc-engName').val(curData.engName || '');
             $('#tbc-tenderName').val(curData.name || '');
             $('#tbc-engineering').val(curData.engineering || '');
-            curData.confirmed = true;   //增加确认信息(已读)
+            curData.temp.confirmed = true;   //增加确认信息(已读)
             if (this.curIdx === this.datas.length -1) { //到最后一个数据了,无法点击下一工程
                 $('#tbc-next').prop('disabled', true);
+            } else {
+                $('#tbc-next').prop('disabled', false);
             }
             if (this.curIdx !== 0) {    //不为第一个数据,可以点击上一工程
                 $('#tbc-prev').prop('disabled', false);
+            } else {
+                $('#tbc-prev').prop('disabled', true);
             }
             //更新x/x信息
             $('#tbc-info').text(`${this.curIdx + 1}/${this.datas.length} 已确认`);
+            //更新选项信息
+            this.initFeeOpts(curData);
+            this.checkConfirmed();
         }
         //上一工程
         prev(idx = null) {
@@ -81,28 +144,131 @@ const importView = (() => {
             $('#tbc-engineering').val(curData.engineering || '');
             if (this.curIdx === 0) {    //到第一个数据了,无法点击上一工程
                 $('#tbc-prev').prop('disabled', true);
+            } else {
+                $('#tbc-prev').prop('disabled', false);
             }
             if (this.curIdx !== this.datas.length -1 ) {    //不为最后一个数据了,可以点击下一工程
                 $('#tbc-next').prop('disabled', false);
+            } else {
+                $('#tbc-next').prop('disabled', true);
             }
             //更新x/x信息
             $('#tbc-info').text(`${this.curIdx + 1}/${this.datas.length} 已确认`);
+            //更新选项信息
+            this.initFeeOpts(curData);
+            this.checkConfirmed();
+        }
+        /*
+         *  初始化工程的费用选项等相关信息(专业工程、费用标准、计算程序等)
+         *  根据导入文件提取的专业工程等数据确定,需要我们软件有对应的工程专业数据,否则下拉选择为空
+         *  @param {Object}tenderData(当前确认单位工程数据) {Array}engineeringList(当前计价规则下的工程专业数据)
+         *  @return {void}
+         * */
+        initFeeOpts(tenderData) {
+            $('#tbc-engineering').empty();  //清空工程专业选项
+            $('#tbc-feeStandard').empty();  //清空费用标准选项
+            $('#tbc-calcProgram').empty();  //清空计算程序选项
+            //工程专业
+            let engineerings = this.engineeringList.filter(data => data.lib.name === tenderData.engineering);
+            if (!engineerings.length) {
+                return;
+            }
+            $('#tbc-engineering').html(`<option selected value="${tenderData.engineering}">${tenderData.engineering}</option>`);
+            //费用标准,若当前工程有费用标准数据(该数据本身没有费用标准数据,选择过了,就会记录),则选中该费用标准
+            let feeOptsHtml = engineerings.map(data =>
+                `<option ${tenderData.temp.feeStandard === data.lib.feeName ? 'selected' : ''} value="${data.lib.feeName}">${data.lib.feeName}</option>`).join('');
+            $('#tbc-feeStandard').html(feeOptsHtml);
+            if (!tenderData.temp.feeStandard) { //记录选中的费用标准
+                tenderData.temp.feeStandard = $('#tbc-feeStandard').val();
+            }
+            //根据工程专业及费用标准确定的当前选中的工程专业库
+            let curEngineering = engineerings.find(data => data.lib.feeName === tenderData.temp.feeStandard) || engineerings[0];
+            //根据计税方法过滤出来的计税组合
+            let taxDatas = curEngineering.lib.tax_group.filter(data => data.taxType == tenderData.taxType);
+            //计算程序,若当前工程有计算程序(该数据本身没有计算程序数据,选择过了,就会记录),则选中该计算程序
+            let calcOptsHtml = taxDatas.map(data =>
+                `<option ${tenderData.temp.calcProgram === data.program_lib.name ? 'selected' : ''} value="${data.program_lib.name}">${data.program_lib.name}</option>`);
+            $('#tbc-calcProgram').html(calcOptsHtml);
+            if (!tenderData.temp.calcProgram) {
+                tenderData.temp.calcProgram = $('#tbc-calcProgram').val();
+            }
         }
     }
+    /*
+     * 根据条件获取计税组合数据
+     * @param {Object}query ({engineeringName, feeName, taxType, calcProgram}) {Array}engineeringList(当前计价规则下的所有工程专业数据)
+     * @return {Object}
+     * */
+    function getTaxData(query, engineeringList) {
+        //工程专业名称 + 费用标准名称 确定一个工程专业数据
+        let engineering = engineeringList.find(data => query.engineeringName === data.lib.name &&
+                                                       query.feeStandard === data.lib.feeName);
+        if (!engineering || !Array.isArray(engineering.lib.tax_group)) {
+            return null;
+        }
+        return engineering.lib.tax_group.find(data => query.taxType == data.taxType &&
+                                                          query.calcProgram === data.program_lib.name);
+    }
+    //获取单位工程项目属性的一些数据(新建单位工程需要,前端能获取到的一些数据,还有一些数据需要后端获取: rootProjectID, projectFeature..)
+    /*
+    * @param {Object}tbcObj(TBC实例) {Object}curData(单位工程数据)
+    * @return {Object}
+    * */
+    function getProperty(tbcObj, curData) {
+        if (!tbcObj) {
+            throw '存在无效数据,无法生成项目。';
+        }
+        //计税数据
+        let query = {
+            engineeringName: curData.engineering,
+            feeStandard: curData.temp.feeStandard,
+            taxType: curData.taxType,
+            calcProgram: curData.temp.calcProgram
+        };
+        let taxData = getTaxData(query, tbcObj.engineeringList);
+        if (!taxData) {
+            throw '无效计税数据,无法生成项目。';
+        }
+        //当前工程专业数据
+        let curEngineering = tbcObj.engineeringList.find(data => query.engineeringName === data.lib.name &&
+        query.feeStandard === data.lib.feeName);
+        return {
+            region: '全省',   //地区
+            valuationType: tbcObj.valuationType,    //计价方式
+            valuation: tbcObj.valuation.id, //计价规则
+            valuationName: tbcObj.valuation.name,
+            engineering_id: curEngineering.engineering_id,  //工程专业
+            engineeringName: curEngineering.lib.name,
+            isInstall: !!curEngineering.lib.isInstall,  //是安装工程?
+            feeStandardName: curEngineering.lib.feeName,    //费用标准
+            engineering: curEngineering.lib.engineering,    //定额取费专业
+            projectEngineering: curEngineering.lib.projectEngineering,  //单位工程取费专业
+            featureLibID: curEngineering.lib.feature_lib[0] ? curEngineering.lib.feature_lib[0].id : '',    //工程特征
+            calcProgram: {name: taxData.program_lib.name, id: taxData.program_lib.id},  //计算程序
+            colLibID: taxData.col_lib.id,   //列设置
+            templateLibID: taxData.template_lib.id, //清单模板
+            unitPriceFile: {name: curData.name, id: ''},    //新建单价文件
+            feeFile: {name: curData.name, id: `newFeeRate@@${taxData.fee_lib.id}`}  //新建费率文件
+        };
+    }
     function eventListen() {
         //选择文件
         $('#customFile').change(async function() {
             let file = $(this)[0].files[0];
+            $('#import-confirm').prop('disabled', true);    //确认导入无效
+            $('.custom-file-label').text(`${file ? file.name : ''}`);   //设置选择框文本
+            $('#uploadAlert').hide();
+            $('.selFile').hide();
+            hideTBCInfo();
             if (file) {
-                let reg = /(xml|XML|qtf|QTF)$/g;
+                let reg = /(xml|XML|qtf|QTF)$/;
                 if(file.name && !reg.test(file.name)){
                     $('.selFile').hide();
                     showUploadAlert(false, '请选择xml或qtf文件。');
-                    $(this).val('');
+                    //$('.custom-file-label').text(`${file.name || ''}`);
+                    //$(this).val('');
                     return;
                 }
-                $('.custom-file-label').text(`${file.name}`);   //设置选择框文本
-                $('#uploadAlert').hide();
                 $.bootstrapLoading.start();
                 $('#loadingPage').css('z-index', '2000');
                 //转换数据
@@ -112,7 +278,8 @@ const importView = (() => {
                     $('.selFile input').val(xmlObj && xmlObj.name ? xmlObj.name : '');
                     $('.selFile').show();   //显示建设项目、计价规则
                 } catch (err) {
-                    showUploadAlert(false, '请选择xml或qtf文件。');
+                    console.log(err);
+                    showUploadAlert(false, err);
                     $(this).val('');
                 }
                 $.bootstrapLoading.end();
@@ -125,30 +292,116 @@ const importView = (() => {
                 showUploadAlert(false, '请选择导入文件。');
                 return;
             }
+            if (!xmlObj) {
+                showUploadAlert(false, '不存在有效数据。');
+                return;
+            }
             let projectName = $('.selFile input').val();
             if (!projectName) {
                 showUploadAlert(false, '不存在有效建设项目。');
                 return;
             }
+            //计价规则
+            let valuation = $('#import-valuation').val();
+            if (!valuation) {
+                showUploadAlert(false, '不存在有效计价规则。');
+                return;
+            }
+            if (!xmlObj.engs.length) {
+                showUploadAlert(false, '不存在单项工程数据。');
+                return;
+            }
+            if (!getTenderDatas(xmlObj).length) {
+                showUploadAlert(false, '不存在单位工程数据。');
+                return;
+            }
             $('#importInterface .modal-content:eq(0)').hide();  //隐藏第一步内容
             $('#importInterface .modal-content:eq(1)').show();  //显示第二步内容
             console.log(getTenderDatas(xmlObj));
             console.log(xmlObj);
-            tbcObj = new TBC(xmlObj);
+            let selValuation = billValuation.find(data => data.id === valuation);
+            tbcObj = new TBC(xmlObj, selValuation);
         });
         //上一步
         $('#import-prev').click(function () {
             $('#importInterface .modal-content:eq(0)').show();  //显示第一步内容
             $('#importInterface .modal-content:eq(1)').hide();  //隐藏第二步内容
+            hideTBCInfo();
+            $('#import-confirm').prop('disabled', true);    //确认导入无效
+            //清空确认字段
+            if (tbcObj) {
+                for (let data of tbcObj.datas) {
+                    delete data.temp.confirmed;
+                }
+            }
+        });
+        //变换费用标准
+        $('#tbc-feeStandard').change(function () {
+            let curData = tbcObj ? tbcObj.datas[tbcObj.curIdx] : null;
+            if (!curData) {
+                return;
+            }
+            curData.temp.feeStandard = $(this).val();
+        });
+        //变换计算程序
+        $('#tbc-calcProgram').change(function () {
+            let curData = tbcObj ? tbcObj.datas[tbcObj.curIdx] : null;
+            if (!curData) {
+                return;
+            }
+            curData.temp.calcProgram = $(this).val();
         });
         //待确认-下一工程
         $('#tbc-next').click(function () {
             tbcObj.next();
+
         });
         //待确认-上一工程
         $('#tbc-prev').click(function () {
             tbcObj.prev();
+            hideTBCInfo();
         });
+        //确认导入
+        $('#import-confirm').click(function () {
+            if (tbcObj && tbcObj.checkValid()) {
+                return;
+            }
+            //确定使用的计税组合
+            tbcObj.datas.map(data => {
+                data.property = getProperty(tbcObj, data);
+            });
+            //确定生成建设项目的父、前、后节点ID
+            let {parentProjectID, preProjectID, nextProjectID} = projTreeObj.getRelProjectID(projTreeObj.tree.selected);
+            xmlObj.ParentID = parentProjectID;
+            xmlObj.preID = preProjectID;
+            xmlObj.NextSiblingID = nextProjectID;
+            console.log(xmlObj);
+            let blob = new Blob([JSON.stringify(xmlObj)], {type: 'text/plain;charset=utf-8'});
+            console.log(blob);
+            let formData = new FormData();
+            formData.append('file', blob);
+            $('#importInterface').modal('hide');
+            let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+            pr.start('导入文件', '正在生成文件,请稍候……');
+            $.ajax({
+                url: '/pm/api/importInterface',
+                type: 'POST',
+                data: formData,
+                cache: false,
+                contentType: false,
+                processData: false,
+                success: function(response){
+                    if (response.data && Array.isArray(response.data)) {
+                        doAfterImport(response.data);
+                    }
+                    pr.end();
+                },
+                error: function(jqXHR){
+                    pr.end();
+                    alert(`与服务器通信发生错误${jqXHR.status} ${jqXHR.statusText}`);
+                }
+            });
+    });
         //导入窗口消失后
         $('#importInterface').on('hidden.bs.modal', function () {
             xmlObj = null;  //重置数据
@@ -159,8 +412,18 @@ const importView = (() => {
             $('.custom-file-label').text('');   //设置选择框文本
             $('#uploadAlert').hide();   //隐藏提示
             $('.selFile').hide();   //隐藏建设项目及计价规则
+            $('#import-confirm').prop('disabled', true);    //确认导入无效
+            hideTBCInfo();
         });
     }
+    //projectDatas在后端已经按照顺序排过了: project eng1 tender1-1 tender1-2 eng2 tender2-1 tender2-2
+    function doAfterImport(projectDatas) {
+        //插入节点
+        for (let data of projectDatas) {
+            let parent = projTreeObj.tree.items.find(node => node.data.ID === data.ParentID),
+                next = projTreeObj.tree.items.find(node => node.data.ID === data.NextSiblingID);
+            projTreeObj.insert(data, parent, next);
+        }
+    }
     return {eventListen};
 })();
-

+ 38 - 23
web/building_saas/pm/js/pm_newMain.js

@@ -417,7 +417,7 @@ const projTreeObj = {
                 "copyTo": me.contextMenuItems.copyTo,
                 "spr2": '--------',
                 "share": me.contextMenuItems.share,
-                //"importInterface": me.contextMenuItems.importInterface,
+                "importInterface": me.contextMenuItems.importInterface,
                 "spr3": '--------',
                 "manageFiles": me.contextMenuItems.manageFiles,
                 "refreshSummary": me.contextMenuItems.refreshSummary
@@ -1539,7 +1539,29 @@ const projTreeObj = {
     refreshWhenFileDateChange:function(projectID){//当单价文件、费率文件被修改后,刷新页面,共用的项目提示刷新图标
         //在一次的修改中,同一个建设下,单价文件、费率文件才有共用的情况,而refreshProjectData是传入单位工程ID,刷新整个建设项目,所以,只要传入修改的单位工程ID,刷新建设项目即可
         this.refreshProjectData(projectID);
+    },
+    //创建建设项目的时候,根据选中位置获取所要创建建设项目的父ID、上兄弟节点ID、后兄弟节点ID
+    getRelProjectID: function (selected) {
+        let parent, pre, next;
+        if(!selected || selected.data.projType === projectType.project){
+            parent = selected ? selected.parent : projTreeObj.tree._root;
+            pre = selected;
+            next = selected ? selected.nextSibling : projTreeObj.tree.firstNode();
+        } else if(selected.data.projType === projectType.folder){
+            parent = selected ? selected : projTreeObj.tree._root;
+            pre = selected ? selected.lastChild() : projTreeObj.tree.firstNode();
+            next = null;
+        } else if(selected.data.projType === projectType.engineering || selected.data.projType === projectType.tender){
+            let selectedProj = selected.parent.data.projType === projectType.project ? selected.parent : selected.parent.parent;
+            parent = selectedProj ? selectedProj.parent : projTreeObj.tree._root;
+            pre = selectedProj;
+            next = selectedProj ? selectedProj.nextSibling : projTreeObj.tree.firstNode();
+        }
+        return {parentProjectID: parent && parent.data ? parent.data.ID : -1,
+                preProjectID: pre && pre.data ? pre.data.ID : -1,
+                nextProjectID: next && next.data ? next.data.ID : -1};
     }
+
 };
 $(document).ready(function() {
     $('#sideTab').find('li').click(function () {
@@ -1724,7 +1746,7 @@ $(document).ready(function() {
     // 选择计价方式
     $("input[name='valuation_type']").click(function() {
         let type = $(this).val();
-        let targetData = type === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
+        let targetData = type === 'bill' ? billValuation : rationValuation;
         let html = '';
         for(let i = targetData.length - 1; i >=0; i--){
             let valuation = targetData[i];
@@ -2380,7 +2402,7 @@ $(document).ready(function() {
     });
 
     //导入相关
-    //importView.eventListen();
+    importView.eventListen();
 });
 function changeEngineering(){
     featureData = null;
@@ -2486,7 +2508,9 @@ function getWorkBookWidth(){
     return workBookWidth = $(window).width() - $('.pm-side').width() - 90;
 }
 
-function prepareInitialTimer() {
+/*function prepareInitialTimer(title, content) {
+    $('#progress .modal-title').text(title);
+    $('#progress .modal-body h5').text(content);
     $('#progressBar').css('width', `0%`);
     let outer = setInterval(function () {
         let curWidth = parseInt($('#progressBar')[0].style.width.replace('%', ''));
@@ -2507,7 +2531,7 @@ function prepareInitialTimer() {
         }
     }, 500);
     return {outer, inner};
-}
+}*/
 
 function initProjects(callback) {
     GetAllProjectData(function (datas) {
@@ -2548,21 +2572,16 @@ function initProjects(callback) {
  * @return {void}
  */
 function init() {
-    billValuation = billValuation.replace(/\n/g, '\\n');
-    rationValuation = rationValuation.replace(/\n/g, '\\n');
+    /*billValuation = billValuation.replace(/\n/g, '\\n');
+    rationValuation = rationValuation.replace(/\n/g, '\\n');*/
     //init spread and pmTree
     socketObject.connect('pm');//socket 连接;
     if (isFirst) {
-        $('#progress').modal('show');
-        let intervalTime = prepareInitialTimer();
+        let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+        pr.start('欢迎使用纵横建筑计价', '首次加载例题,请稍候……');
         CommonAjax.post('/pm/api/prepareInitialData', {user_id: userID}, function () {
             initProjects(function () {
-                clearInterval(intervalTime.outer);
-                clearInterval(intervalTime.inner);
-                $('#progressBar').css('width', '100%');
-                setTimeout(function () {
-                    $('#progress').modal('hide');
-                }, 500);
+                pr.end();
             });
         });
     } else {
@@ -2914,7 +2933,7 @@ function getEngineeringLib(cascadeName, engineeringList) {
 function getEngineeringList(){
     let valuation = $("#valuation").val();
     let valuationType = $("input[name='valuation_type']:checked").val();
-    let valuationData = valuationType === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
+    let valuationData = valuationType === 'bill' ? billValuation : rationValuation;
     let engineeringList = [];
     for(let tmp of valuationData) {
         if (tmp.id === valuation) {
@@ -3312,7 +3331,7 @@ function AddTender() {
         let valuationType = $("input[name='valuation_type']:checked").val();
 
         let engineering = undefined;
-        let valuationData = valuationType === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
+        let valuationData = valuationType === 'bill' ? billValuation : rationValuation;
         let engineeringList = [];
         for(let tmp of valuationData) {
             if (tmp.id === valuation) {
@@ -3693,15 +3712,11 @@ function GetTargetTreeNode(zTreeObj) {
  * @param {Array} engineeringList
  * @return {String}
  */
-function getEngineeringHtml(engineeringList) {//
+function getEngineeringHtml(engineeringList) {
     let result = '';
     if (engineeringList.length <= 0) {
         return result;
     }
-    let engineeringObject = {};
-    for(let tmp of engineering) {
-        engineeringObject[tmp.value] = tmp.name;
-    }
     let engLibNames = [];
     for(let i = 0; i < engineeringList.length; i++){
         let tmp = engineeringList[i];
@@ -4409,4 +4424,4 @@ function refreshProjSummary(project, summaryInfo) {
         }
     }
     projTreeObj.refreshNodeData(refreshNodes);
-}
+}