Bläddra i källkod

Merge branch 'budget' of http://192.168.1.41:3000/SmartCost/ConstructionCost into budget

zhangweicheng 4 år sedan
förälder
incheckning
3388a3f73f

+ 2 - 0
config/gulpConfig.js

@@ -82,6 +82,8 @@ module.exports = {
         'web/building_saas/main/js/models/main_consts.js',
         'web/over_write/config/compilation_config.js',
         'public/web/encoding_util.js',
+        'web/building_saas/budget-summary/js/budgetSummarySetting.js',
+        'web/building_saas/budget-summary/js/budgetSummarySheet.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',
         'web/building_saas/glj/js/common_spread.js',

+ 10 - 0
modules/main/controllers/bills_controller.js

@@ -268,6 +268,16 @@ module.exports = {
         } catch (err) {
             callback(req, res, 1, err, null);
         }
+    },
+    getBudgetSummary: async function (req, res) {
+        const data = JSON.parse(req.body.data);
+        try {
+            const bills = await bill_facade.getBudgetSummary(data.constructionID);
+            callback(req, res, 0, 'success', bills);
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err, null);
+        }
     }
 };
 

+ 73 - 1
modules/main/facade/bill_facade.js

@@ -17,7 +17,10 @@ let ration_installation_Model = mongoose.model('ration_installation');
 let ration_template_Model = mongoose.model('ration_template');
 let bill_Model = require('../models/bills').model;
 let billsLibDao = require("../../bills_lib/models/bills_lib_interfaces");
-
+const pmFacade = require('../../pm/facade/pm_facade');
+const { getSortedTreeData } = require('../../../public/common_util');
+const { constructionFeeNodeID, constructionEquipmentFeeNodeID, BudgetArea, fixedFlag } = require('../../../public/common_constants');
+const uuidV1 = require('uuid/v1');
 
 const GLJController = require("../../glj/controllers/glj_controller");
 
@@ -123,6 +126,75 @@ module.exports={
         if (bulks.length > 0) {
             await bill_Model.bulkWrite(bulks);
         }
+    },
+    // 获取概算汇总数据
+    getBudgetSummary: async function (constructionID) {
+        // 获取建设项目清单数据(工程建设其他费用 - 建设项目总概算)部分
+        const constructionOtherFeeBills = await bill_Model.find({ projectID: constructionID }).lean();
+        const sortedOtherFeeBills = getSortedTreeData('-1', constructionOtherFeeBills);
+        constructionOtherFeeBills.forEach(item => item.area = BudgetArea.CONSTRUCTION_OTHER_FEE);
+        // 获取工程费用数据
+        const constructionFeeBills = await this.getConstructionFeeData(constructionID, sortedOtherFeeBills[0].ID);
+        return [...constructionFeeBills, ...constructionOtherFeeBills];
+    },
+    // 获取工程费用数据,作为概算汇总数据的拼接树数据
+    getConstructionFeeData: async function (constructionID, nextID) {
+        const projects = await pmFacade.getPosterityProjects([constructionID], true, { _id: 0, ID: 1, ParentID: 1, NextSiblingID: 1, name: 1, 'property.feeStandardName': 1});
+        const construction = projects.find(p => p.ID === constructionID);
+        const items = getSortedTreeData(construction.ParentID, projects);
+        // 转换为uuid
+        const IDMap = {};
+        items.forEach((item, index) => {
+            if (index == 0) {
+                IDMap[item.ID] = constructionFeeNodeID;
+            } else {
+                IDMap[item.ID] = uuidV1()
+            }
+        });
+        // 将项目调整为工程费用数据
+        let curConstructionID;
+        let curSingleNo = 0;
+        let curSingleID;
+        let curUnitNo = 0;
+        let latestSingleNode;
+        items.forEach((item, index) => {
+            item.area = BudgetArea.CONSTRUCTION_FEE;
+            item.ID = IDMap[item.ID];
+            item.ParentID = IDMap[item.ParentID] || '-1';
+            item.NextSiblingID = IDMap[item.NextSiblingID] || '-1';
+            if (index === 0) {
+                curConstructionID = item.ID;
+                item.flags = [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_FEE }];
+                item.code = '1';
+                item.name = '工程费用';
+                item.NextSiblingID = nextID;
+            } else {
+                if (item.ParentID === curConstructionID) {
+                    latestSingleNode = item;
+                    curSingleNo += 1;
+                    curUnitNo = 0;
+                    curSingleID = item.ID;
+                    item.code = `1.${curSingleNo}`
+                } else if (item.ParentID === curSingleID) {
+                    curUnitNo += 1;
+                    item.code = `1.${curSingleNo}.${curUnitNo}`
+                }
+            }
+        });
+        const constructionFeeNode = items[0];
+        // 设备及工器具购置费
+        const constructionEquipmentNode = {
+            area: BudgetArea.CONSTRUCTION_FEE,
+            flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_EQUIPMENT_FEE }],
+            ID: constructionEquipmentFeeNodeID,
+            ParentID: constructionFeeNode.ID,
+            NextSiblingID: '-1',
+            code: `1.${curSingleNo + 1}`,
+            name: '设备及工器具购置费',
+        };
+        latestSingleNode.NextSiblingID = constructionEquipmentNode.ID;
+        items.push(constructionEquipmentNode);
+        return items;
     }
 };
 

+ 1 - 0
modules/main/routes/bills_route.js

@@ -22,6 +22,7 @@ module.exports = function (app) {
     billsRouter.post('/import', billsController.import);
     billsRouter.get('/downloadExamp', billsController.downloadExample);
     billsRouter.post('/insertBills', billsController.insertBills);
+    billsRouter.post('/getBudgetSummary', billsController.getBudgetSummary);
     app.use('/bills', billsRouter);
 };
 

+ 62 - 0
modules/main/templates/constructionBillsTemplate.js

@@ -0,0 +1,62 @@
+const { billType, fixedFlag } = require('../../../public/common_constants');
+/* 建设其他费清单模板 */
+const constructionBillsTemplate = [
+  { ID: 2, ParentID: -1, NextSiblingID: 3, code: '2', name: '工程建设其他费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_OTHER_FEE }], type: billType.DXFY },
+  { ID: 21, ParentID: 2, NextSiblingID: 22, code: '2.1', name: '前期费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 211, ParentID: 21, NextSiblingID: 212, code: '2.1.1', name: '土地征用及补偿费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2111, ParentID: 211, NextSiblingID: 2112, code: '2.1.1.1', name: '集体土地征地补偿安置费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2112, ParentID: 211, NextSiblingID: -1, code: '2.1.1.2', name: '国有土地上房屋征收与补偿费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 212, ParentID: 21, NextSiblingID: 213, code: '2.1.2', name: '临时占地费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 213, ParentID: 21, NextSiblingID: 214, code: '2.1.3', name: '树木及绿化赔偿费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 214, ParentID: 21, NextSiblingID: 215, code: '2.1.4', name: '道路及市政设施损坏赔偿费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 215, ParentID: 21, NextSiblingID: 216, code: '2.1.5', name: '管线迁改费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 216, ParentID: 21, NextSiblingID: -1, code: '2.1.6', name: '场地准备及临时设施费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 22, ParentID: 2, NextSiblingID: 23, code: '2.2', name: '与项目建设有关的其他费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 221, ParentID: 22, NextSiblingID: 222, code: '2.2.1', name: '项目建设管理费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 222, ParentID: 22, NextSiblingID: 223, code: '2.2.2', name: '建设管理代建费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 223, ParentID: 22, NextSiblingID: 224, code: '2.2.3', name: '工程建设监理费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 224, ParentID: 22, NextSiblingID: 225, code: '2.2.4', name: '招标代理服务费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 225, ParentID: 22, NextSiblingID: 226, code: '2.2.5', name: '招标交易服务费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 226, ParentID: 22, NextSiblingID: -1, code: '2.2.6', name: '前期工作费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2261, ParentID: 226, NextSiblingID: 2262, code: '2.2.6.1', name: '可行性研究费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2262, ParentID: 226, NextSiblingID: 2263, code: '2.2.6.2', name: '环境影响评价费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2263, ParentID: 226, NextSiblingID: 2264, code: '2.2.6.3', name: '节能评估费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2264, ParentID: 226, NextSiblingID: 2265, code: '2.2.6.4', name: '社会稳定风险评估费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2265, ParentID: 226, NextSiblingID: 2266, code: '2.2.6.5', name: '防洪评价费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2266, ParentID: 226, NextSiblingID: 2267, code: '2.2.6.6', name: '文物勘探及保护费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2267, ParentID: 226, NextSiblingID: -1, code: '2.2.6.7', name: '其他前期工作费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 227, ParentID: 22, NextSiblingID: 228, code: '2.2.7', name: '研究试验费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 228, ParentID: 22, NextSiblingID: 229, code: '2.2.8', name: '勘察设计费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2281, ParentID: 228, NextSiblingID: 2282, code: '2.2.8.1', name: '勘察费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2282, ParentID: 228, NextSiblingID: -1, code: '2.2.8.2', name: '设计费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 229, ParentID: 22, NextSiblingID: 2210, code: '2.2.9', name: '咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2291, ParentID: 229, NextSiblingID: 2292, code: '2.2.9.1', name: '设计咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2292, ParentID: 229, NextSiblingID: 2293, code: '2.2.9.2', name: '工程造价咨询费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2210, ParentID: 22, NextSiblingID: 2211, code: '2.2.10', name: '全过程工程咨询服务费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2211, ParentID: 22, NextSiblingID: 2212, code: '2.2.11', name: '引进技术和设备其他费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2212, ParentID: 22, NextSiblingID: 2213, code: '2.2.12', name: '专利及专有技术使用费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2213, ParentID: 22, NextSiblingID: 2214, code: '2.2.13', name: '工程保险费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2214, ParentID: 22, NextSiblingID: 2215, code: '2.2.14', name: '特殊设备安全监督检验费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2215, ParentID: 22, NextSiblingID: 2216, code: '2.2.15', name: '安全生产保障费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2216, ParentID: 22, NextSiblingID: 2217, code: '2.2.16', name: '水土保持补偿费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2217, ParentID: 22, NextSiblingID: 2218, code: '2.2.17', name: '城市基础设施配套费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2218, ParentID: 22, NextSiblingID: 2219, code: '2.2.18', name: '人防易地建设费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2219, ParentID: 22, NextSiblingID: -1, code: '2.2.19', name: '其他费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 23, ParentID: 2, NextSiblingID: -1, code: '2.3', name: '与试运行及生产有关的其他费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 231, ParentID: 23, NextSiblingID: 232, code: '2.3.1', name: '综合联调及试运行费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 232, ParentID: 23, NextSiblingID: -1, code: '2.3.2', name: '生产准备及开办费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2321, ParentID: 232, NextSiblingID: 2322, code: '2.3.2.1', name: '生产职工培训费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2322, ParentID: 232, NextSiblingID: 2323, code: '2.3.2.2', name: '生产办公和生活家具用具购置费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 2323, ParentID: 232, NextSiblingID: -1, code: '2.3.2.3', name: '工器具购置费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 3, ParentID: -1, NextSiblingID: 4, code: '3', name: '预备费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.BUDGET_RESERVE }], type: billType.DXFY },
+  { ID: 31, ParentID: 3, NextSiblingID: 32, code: '3.1', name: '基本预备费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.BASIC_BUDGET_RESERVE }], type: billType.BILL },
+  { ID: 32, ParentID: 3, NextSiblingID: -1, code: '3.2', name: '价差预备费', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.DIFF_BUDGET_RESERVE }], type: billType.BILL },
+  { ID: 4, ParentID: -1, NextSiblingID: 5, code: '4', name: '专项费用', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_SPECIAL_FEE }], type: billType.BILL },
+  { ID: 41, ParentID: 4, NextSiblingID: 42, code: '4.1', name: '建设期贷款利息', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 42, ParentID: 4, NextSiblingID: -1, code: '4.2', name: '铺底流动资金', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: null }], type: billType.BILL },
+  { ID: 5, ParentID: -1, NextSiblingID: -1, code: '5', name: '建设项目总概算', unit: '', calcBase: '', quantity: '', flags: [{ fieldName: 'fixed', flag: fixedFlag.CONSTRUCTION_BUDGET }], type: billType.BILL },
+];
+
+module.exports = {
+  constructionBillsTemplate,
+};

+ 21 - 0
modules/pm/controllers/new_proj_controller.js

@@ -13,8 +13,29 @@ const billsUtil = require('../../../public/billsUtil');
 
 const BillsTemplateModel = require("../models/templates/bills_template_model");
 const EngineeringLibModel = require("../../users/models/engineering_lib_model");
+const billsModel = mongoose.model("bills");
 
 module.exports = {
+    copyBillsTemplate: async (newProjID, billsDatas) => {
+        let uuidMaping = Object.create(null);
+        uuidMaping['-1'] = -1;
+        //建立uuid-ID映射
+        for(let bill of billsDatas){
+            uuidMaping[bill.ID] = uuidV1();
+        }
+        const reg = /@\d+/;
+        billsDatas.forEach(function (template) {
+            template.projectID = newProjID;
+            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;
+            const needToParseCalcBase = template.calcBase && reg.test(template.calcBase);
+            if (needToParseCalcBase) {
+                template.calcBase = billsUtil.parseCalcBase(template.calcBase, uuidMaping);
+            }
+        });
+        await billsModel.insertMany(billsDatas);
+    },
     copyTemplateData: async function (property, newProjID, callback) {
         async.parallel([
             async function (cb) {

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

@@ -50,6 +50,7 @@ let Projects = mongoose.model('projects');
 let mainColLibModel = mongoose.model('std_main_col_lib');
 let projSettingModel = mongoose.model('proj_setting');
 let optionModel = mongoose.model('options');
+const { constructionBillsTemplate } = require('../../main/templates/constructionBillsTemplate');
 
 let equipmentPurchaseModel = mongoose.model('equipment_purchase');
 function ProjectsDAO() {
@@ -244,6 +245,8 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                         data.updateData.property.overHeightOption = 1;
                     }
                 }else if (data.updateData.projType === projectType.project) {
+                    /* 创建概算清单 */
+                    await newProjController.copyBillsTemplate(data.updateData.ID, constructionBillsTemplate);
                     //更新基本信息
                     data.updateData.property.basicInformation.forEach(function (pData) {
                         pData.items.forEach(function (item) {

+ 34 - 1
public/common_constants.js

@@ -84,7 +84,26 @@
         // 概算幅度差
         BUDGET_ESTIMATE_DIFF: 37,
         // 其他费用(与其他项目不同,参考广东的用法)
-        OTHER_FEE: 38
+        OTHER_FEE: 38,
+
+        /* 概算汇总固定类别 */
+        // 工程费用
+        CONSTRUCTION_FEE: 1001,
+        // 设备及工器具购置费
+        CONSTRUCTION_EQUIPMENT_FEE: 1002,
+        // 工程建设其他费用
+        CONSTRUCTION_OTHER_FEE: 1003,
+        // 预备费
+        BUDGET_RESERVE: 1004,
+        // 基本预备费
+        BASIC_BUDGET_RESERVE: 1005,
+        // 价差预备费
+        DIFF_BUDGET_RESERVE: 1006,
+        // 专项费用
+        CONSTRUCTION_SPECIAL_FEE: 1007,
+        // 建设项目总概算
+        CONSTRUCTION_BUDGET: 1008,
+
     };
     // 清单类型
     const billType = {
@@ -183,6 +202,17 @@
         BLOCK_LIB: 3,
     };
 
+    // 工程费用ID(写死)
+    const constructionFeeNodeID = '69c0d020-a886-11eb-82ac-a71db2ad220b';
+    // 设备及工器具购置费ID(写死)
+    const constructionEquipmentFeeNodeID = '71d74730-a886-11eb-82ac-a71db2ad220b'
+
+    // 概算汇总清单所属范围
+    const BudgetArea = {
+        CONSTRUCTION_FEE: 1,
+        CONSTRUCTION_OTHER_FEE: 2
+    };
+
     return {
         fixedFlag,
         billType,
@@ -200,5 +230,8 @@
         PageTarget,
         BlankType,
         ShareLibType,
+        constructionFeeNodeID,
+        constructionEquipmentFeeNodeID,
+        BudgetArea,
     };
 });

+ 1 - 1
public/common_util.js

@@ -78,7 +78,7 @@ function deleteEmptyObject(arr) {
             }
             const NextIDMapping = {};
             sameDepthItems.forEach(item => NextIDMapping[item.NextSiblingID] = item);
-            let curItem = sameDepthItems.length > 1 ? sameDepthItems.find(item => item.NextSiblingID === -1) : sameDepthItems[0];
+            let curItem = sameDepthItems.length > 1 ? sameDepthItems.find(item => +item.NextSiblingID === -1) : sameDepthItems[0];
             const sorted = [];
             while (curItem) {
                 sorted.push(...sortSameDedth(curItem.ID, items));

+ 3 - 0
web/building_saas/budget-summary/html/budget-summary.html

@@ -0,0 +1,3 @@
+<div class="budget-summary">
+  <div class="sheet-wrapper" id="budget-summary-sheet"></div>
+</div>

+ 158 - 0
web/building_saas/budget-summary/js/budgetSummarySetting.js

@@ -0,0 +1,158 @@
+const budgetSummaryTreeSetting = {
+    rowHeaderWidth: 15,
+    treeCol: 0,
+    emptyRows: 0,
+    headRows: 1,
+    headRowHeight: [40],
+    defaultRowHeight: 21,
+    cols: [{
+        width: 140,
+        head: {
+            titleNames: ["编码"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "code",
+            vAlign: 1,
+            hAlign: 0,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["名称"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "name",
+            vAlign: 1,
+            hAlign: 0,
+            font: "Arial"
+        }
+    }, {
+        width: 60,
+        head: {
+            titleNames: ["单位"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "unit",
+            vAlign: 1,
+            hAlign: 1,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["数量"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "quantity",
+            type: 'number',
+            vAlign: 1,
+            hAlign: 2,
+            font: "Arial",
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["单价"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"],
+        },
+        data: {
+            field: "common.unitFee",
+            type: 'number',
+            vAlign: 1,
+            hAlign: 2,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        readOnly: true,
+        head: {
+            titleNames: ["金额"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "common.totalFee",
+            vAlign: 1,
+            hAlign: 2,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["计算基数"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "calcBase",
+            vAlign: 1,
+            hAlign: 0,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["费率"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "feeRate",
+            type: 'number',
+            vAlign: 1,
+            hAlign: 2,
+            font: "Arial"
+        }
+    }, {
+        width: 140,
+        head: {
+            titleNames: ["备注"],
+            spanCols: [1],
+            spanRows: [1],
+            vAlign: [1],
+            hAlign: [1],
+            font: ["Arial"]
+        },
+        data: {
+            field: "remark",
+            vAlign: 1,
+            hAlign: 0,
+            font: "Arial"
+        }
+    },
+    ]
+};

+ 334 - 0
web/building_saas/budget-summary/js/budgetSummarySheet.js

@@ -0,0 +1,334 @@
+/* 建设其他费表格相关 */
+const budgetSummaryObj = (() => {
+
+  const { isDef, isNumber } = window.commonUtil;
+  const { fixedFlag, BudgetArea } = window.commonConstants;
+
+  // 原始数据
+  let rawData = [];
+  // 建设其他费表格对象
+  let spread = null;
+  // 建设其他费树
+  let tree = null;
+
+  // 单位设置下拉框
+  const setUnitCombo = (sheet, data) => {
+    const unitCol = budgetSummaryTreeSetting.cols.findIndex(item => item.data.field === 'unit');
+    if (unitCol >= 0) {
+      TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
+        const comboBox = sheetCommonObj.getDynamicCombo();
+        comboBox
+          .itemHeight(10)
+          .items(['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
+            '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀'])
+          .editable(true);
+        data.forEach((item, index) => {
+          sheet.getCell(index, unitCol).cellType(comboBox);
+        })
+      });
+    }
+  }
+
+  const getFieldByCol = (col) => {
+    const item = budgetSummaryTreeSetting.cols[col];
+    return item && item.data && item.data.field || null;
+  }
+
+  // 单元格值验证器
+  const validator = {
+    text() {
+      return true
+    },
+    number(val) {
+      return !isDef(val) || isNumber(val);
+    },
+  };
+  const getValidator = (col) => {
+    const item = budgetSummaryTreeSetting.cols[col];
+    if (!item) {
+      return 'text';
+    }
+    return validator[item.data.type || 'text'];
+  }
+
+  /* 表格事件相关 */
+  // 恢复数据
+  const recover = (sheet, changedCells) => {
+    if (!tree) {
+      return;
+    }
+    changedCells.forEach(({ row, col }) => {
+      const node = tree.items[row];
+      const field = getFieldByCol(col);
+      if (!field || !node) {
+        return;
+      }
+      const orgVal = node.data[field];
+      sheet.setValue(row, col, orgVal);
+    })
+  }
+  // 编辑相关
+  const edit = (sheet, changedCells) => {
+    // 单元格值验证
+    const isValid = changedCells.every(({ row, col }) => {
+      const val = sheet.getValue(row, col);
+      return getValidator(col)(val);
+    });
+    // 验证不通过,恢复
+    if (!isValid) {
+      recover(sheet, changedCells);
+    }
+  }
+  const events = {
+    EnterCell(sender, args) {
+      args.sheet.repaint();
+    },
+    ValueChanged(sender, args) {
+      edit(args.sheet, [{ row: args.row, col: args.col }]);
+    },
+    RangeChanged(sender, args) {
+      edit(args.sheet, args.changedCells);
+    }
+  }
+  const bindEvents = (sheet) => {
+    Object.entries(events).forEach(([ev, evFunc]) => {
+      sheet.bind(GC.Spread.Sheets.Events[ev], evFunc)
+    })
+  }
+
+  /* 只读相关 */
+  const lockData = (sheet, nodes) => {
+    TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
+      // 工程费用区域,只读
+      const equipmentNode = nodes.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE);
+      if (equipmentNode) {
+        sheet.getRange(0, 0, equipmentNode.serialNo() + 1, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
+      }
+    });
+  }
+
+  /* 初始化表格 */
+  const initSpread = () => {
+    if (!spread) {
+      // spread = sheetCommonObj.createSpread($('#budget-summary-sheet')[0], 1);
+      spread = SheetDataHelper.createNewSpread($('#budget-summary-sheet')[0]);
+      sheetCommonObj.spreadDefaultStyle(spread);
+      // 设置表头
+      const sheet = spread.getSheet(0);
+      bindEvents(sheet);
+      const headers = sheetCommonObj.getHeadersFromTreeSetting(budgetSummaryTreeSetting);
+      sheetCommonObj.setHeader(sheet, headers);
+      initContextMenu();
+    } else {
+      spread.refresh();
+    }
+    return spread;
+  }
+
+  // 初始化树
+  const initTree = (data, sheet, setting) => {
+    tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true });
+    const controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting, false);
+    tree.loadDatas(data);
+    controller.showTreeData();
+    sheet.setRowCount(data.length);
+    setUnitCombo(sheet, data);
+    lockData(sheet, tree.items);
+  }
+
+  /* 右键菜单 */
+  // 更新树结构数据
+  const updateTree = (sheet, updateData) => {
+    // 更新数据
+    updateData.forEach(item => {
+      if (item.type === 'new') {
+        rawData.push(item.data)
+      } else if (item.type === 'update') {
+        const node = tree.findNode(item.data.ID);
+        if (node) {
+          Object.assign(node.data, item.data);
+        }
+      } else {
+        const removeIndex = rawData.findIndex(d => d.ID === item.data.ID);
+        if (removeIndex > -1) {
+          rawData.splice(removeIndex, 1);
+        }
+      }
+    });
+    // 重新初始化树
+    initTree(rawData, sheet, budgetSummaryTreeSetting);
+  }
+  // 插入
+  const insert = (sheet, selected) => {
+    const updateData = tree.getInsertData(selected.data.ParentID, selected.data.NextSiblingID, uuid.v1());
+    updateTree(sheet, updateData);
+    sheet.setActiveCell(sheet.getActiveRowIndex() + selected.posterityCount() + 1, sheet.getActiveColumnIndex())
+  }
+  // 删除
+  const remove = (sheet, selected) => {
+    const updateData = tree.getDeleteData(selected);
+    updateTree(sheet, updateData);
+  }
+  // 升级
+  const upLevel = (sheet, selected) => {
+    const updateData = selected.getUpLevelData();
+    updateTree(sheet, updateData);
+  }
+  // 降级
+  const downLevel = (sheet, selected) => {
+    const updateData = selected.getDownLevelData();
+    updateTree(sheet, updateData);
+  }
+  // 上移
+  const upMove = (sheet, selected) => {
+    const updateData = selected.getUpMoveData();
+    updateTree(sheet, updateData);
+    const prev = selected.preSibling;
+    const row = sheet.getActiveRowIndex() - prev.posterityCount() - 1;
+    sheet.setActiveCell(row, sheet.getActiveColumnIndex());
+    sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
+  }
+  // 下移
+  const downMove = (sheet, selected) => {
+    const updateData = selected.getDownMoveData();
+    updateTree(sheet, updateData);
+    const next = selected.nextSibling;
+    const row = sheet.getActiveRowIndex() + next.posterityCount() + 1;
+    sheet.setActiveCell(row, sheet.getActiveColumnIndex())
+    sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
+  }
+  // 是否是属于工程费用区域的节点
+  const isConstructionFeeArea = (node) => {
+    return node && node.data && node.data.area === BudgetArea.CONSTRUCTION_FEE;
+  }
+  // 初始化右键菜单
+  const initContextMenu = () => {
+    if (!spread) {
+      return;
+    }
+    let curRow;
+    let curNode;
+    const sheet = spread.getSheet(0);
+    $.contextMenu({
+      selector: '#budget-summary-sheet',
+      build: function ($trigger, e) {
+        const target = SheetDataHelper.safeRightClickSelection($trigger, e, spread);
+        curRow = target.row;
+        curNode = tree && tree.items[curRow] || null;
+        sheet.setActiveCell(target.row, target.col);
+        return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+      },
+      items: {
+        insert: {
+          name: '插入行',
+          icon: 'fa-sign-in',
+          disabled() {
+            return !curNode || (curNode.data.area === BudgetArea.CONSTRUCTION_FEE && curNode.getFlag() !== fixedFlag.CONSTRUCTION_FEE);
+          },
+          callback() {
+            insert(sheet, curNode);
+          }
+        },
+        remove: {
+          name: '删除',
+          icon: 'fa-remove',
+          disabled() {
+            return !curNode || isConstructionFeeArea(curNode);
+          },
+          callback() {
+            remove(sheet, curNode);
+          }
+        },
+        upLevel: {
+          name: '升级',
+          icon: 'fa-arrow-left',
+          disabled() {
+            return !curNode || !curNode.canUpLevel() || isConstructionFeeArea(curNode);
+          },
+          callback() {
+            upLevel(sheet, curNode);
+          }
+        },
+        downLevel: {
+          name: '降级',
+          icon: 'fa-arrow-right',
+          disabled() {
+            return !curNode || !curNode.canDownLevel() || isConstructionFeeArea(curNode);
+          },
+          callback() {
+            downLevel(sheet, curNode);
+          }
+        },
+        upMove: {
+          name: '上移',
+          icon: 'fa-arrow-up',
+          disabled() {
+            return !curNode || !curNode.canUpMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.preSibling);
+          },
+          callback() {
+            upMove(sheet, curNode);
+          }
+        },
+        downMove: {
+          name: '下移',
+          icon: 'fa-arrow-down',
+          disabled() {
+            return !curNode || !curNode.canDownMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.nextSibling);
+          },
+          callback() {
+            downMove(sheet, curNode);
+          }
+        },
+        refresh: {
+          name: '刷新数据',
+          icon: 'fa-refresh',
+          callback() {
+            init(projectObj.project.property.rootProjectID);
+          }
+        },
+      }
+    });
+  }
+
+  // 初始化
+  const init = async (constructionID) => {
+    try {
+      $.bootstrapLoading.start();
+      rawData = await ajaxPost('/bills/getBudgetSummary', { constructionID });
+      rawData.forEach((item) => {
+        if (item.quantity) {
+          item.quantity = parseFloat(item.quantity);
+        }
+        item.feesIndex = getFeeIndex(item.fees);
+        item.flagsIndex = {};
+        if (item.flags) {
+          item.flags.forEach((flag) => {
+            item.flagsIndex[flag.fieldName] = flag;
+            });
+        }
+    });
+      const spread = initSpread();
+      const sheet = spread.getSheet(0);
+      initTree(rawData, sheet, budgetSummaryTreeSetting);
+    } catch (err) {
+      console.log(err);
+      alert(err);
+    } finally {
+      $.bootstrapLoading.end();
+    }
+  }
+
+  // 点击tab,重新初始化
+  $('#tab-budget-summary').click(function () {
+    if (!$(this).hasClass('active')) {
+      init(projectObj.project.property.rootProjectID);
+    }
+  });
+
+
+  // 对外暴露
+  return {
+    getTree: () => tree,
+  };
+
+})();

+ 7 - 0
web/building_saas/css/custom.css

@@ -529,3 +529,10 @@ margin-right: 100px !important;
   display: inline-block;
 }
 
+/* 概算汇总 */
+.budget-summary {
+  height: calc(100vh - 40px);
+}
+.budget-summary .sheet-wrapper {
+  height: 100%;
+}

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

@@ -67,6 +67,8 @@
   <div class="main">
     <div class="main-nav">
       <ul class="nav nav-tabs flex-column" role="tablist">
+        <li class="nav-item"><a data-toggle="tab" href="#budget-summary" id="tab-budget-summary" role="tab">概算汇总</a>
+        </li>
         <li class="nav-item"><a class="active" data-toggle="tab" href="#zaojiashu" id="tab_zaojiashu" role="tab">造价书</a>
         </li>
         <li class="nav-item"><a data-toggle="tab" href="#project_glj" id="tab_project_glj" data-name="tab_project_glj"
@@ -85,6 +87,10 @@
     </div>
     <div class="content">
       <div class="tab-content">
+        <div class="tab-pane" id="budget-summary" role="tabpanel">
+          <%include ../../budget-summary/html/budget-summary.html %>
+        </div>
+
         <div class="tab-pane active" id="zaojiashu" role="tabpanel">
           <!--造价书-->
           <div class="toolsbar px-1 d-flex justify-content-between" id="headerToolsBar">
@@ -2822,6 +2828,8 @@
   <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/encoding_util.js"></script>
+  <script type="text/javascript" src="/web/building_saas/budget-summary/js/budgetSummarySetting.js"></script>
+  <script type="text/javascript" src="/web/building_saas/budget-summary/js/budgetSummarySheet.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>

+ 2 - 2
web/building_saas/pm/html/project-management.html

@@ -266,7 +266,7 @@
                         </div>
                     </div>
                     <span class="form-text text-danger" id="proj-valuation-info" style="display: none;">请选择计价规则</span>
-                    <div class="form-group row" id="project-dialog-fileKind">
+                    <div class="form-group row" style="display: none;" id="project-dialog-fileKind">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">文件类型</label>
                         <div class="col">
                             <div class="custom-control custom-radio custom-control-inline">
@@ -287,7 +287,7 @@
                                 <input type="radio" value="1" checked id="radioCommon" name="taxType" class="custom-control-input">
                                 <label class="custom-control-label" for="radioCommon">一般计税法</label>
                             </div>
-                             <div class="custom-control custom-radio custom-control-inline">
+                             <div class="custom-control custom-radio custom-control-inline" style="display: none;">
                                  <input type="radio" value="2" id="radioSimple" name="taxType" class="custom-control-input">
                                  <label class="custom-control-label" for="radioSimple">简易计税法</label>
                              </div>