소스 검색

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

TonyKang 4 년 전
부모
커밋
084fd488e6
99개의 변경된 파일3105개의 추가작업 그리고 1314개의 파일을 삭제
  1. 8 0
      config/gulpConfig.js
  2. 2 0
      modules/all_models/bills.js
  3. 2 1
      modules/all_models/compleRation_ration.js
  4. 1 0
      modules/all_models/projects.js
  5. 1 0
      modules/all_models/ration.js
  6. 2 1
      modules/all_models/ration_glj.js
  7. 18 0
      modules/all_models/share_lib.js
  8. 54 21
      modules/complementary_glj_lib/controllers/gljController.js
  9. 63 1
      modules/complementary_glj_lib/models/gljModel.js
  10. 2 1
      modules/complementary_glj_lib/routes/routes.js
  11. 4 2
      modules/complementary_ration_lib/controllers/compleRationController.js
  12. 2 1
      modules/complementary_ration_lib/controllers/compleSectionTreeController.js
  13. 31 15
      modules/complementary_ration_lib/controllers/compleViewController.js
  14. 14 3
      modules/complementary_ration_lib/models/compleRationModel.js
  15. 27 16
      modules/complementary_ration_lib/models/searchModel.js
  16. 4 4
      modules/complementary_ration_lib/routes/routes.js
  17. 20 10
      modules/main/controllers/bills_controller.js
  18. 12 0
      modules/main/controllers/ration_controller.js
  19. 22 1
      modules/main/facade/ration_facade.js
  20. 79 4
      modules/pm/controllers/pm_controller.js
  21. 67 9
      modules/pm/facade/pm_facade.js
  22. 3 0
      modules/pm/routes/pm_route.js
  23. 38 9
      modules/ration_glj/controllers/ration_glj_controller.js
  24. 5 1
      modules/ration_glj/facade/ration_glj_facade.js
  25. 42 0
      modules/users/controllers/cld_controller.js
  26. 8 1
      modules/users/models/user_model.js
  27. 4 0
      modules/users/routes/cld_route.js
  28. 921 883
      package-lock.json
  29. 2 2
      package.json
  30. 9 1
      public/common_constants.js
  31. BIN
      public/static/示例1-分部分项工程项目清单.xlsx
  32. 0 0
      public/static/示例2-分部分项工程项目清单计价表.xlsx
  33. 4 1
      public/web/id_tree.js
  34. 63 14
      public/web/sheet/sheet_common.js
  35. 9 3
      public/web/tree_sheet/tree_sheet_helper.js
  36. 11 4
      web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html
  37. 12 6
      web/building_saas/complementary_glj_lib/js/glj.js
  38. 1 1
      web/building_saas/complementary_glj_lib/js/gljClassTree.js
  39. 6 4
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  40. 124 0
      web/building_saas/complementary_glj_lib/js/init.js
  41. 5 0
      web/building_saas/complementary_ration_lib/html/dinge.html
  42. 9 2
      web/building_saas/complementary_ration_lib/js/coe.js
  43. 2 1
      web/building_saas/complementary_ration_lib/js/gljSelect.js
  44. 71 2
      web/building_saas/complementary_ration_lib/js/init.js
  45. 5 5
      web/building_saas/complementary_ration_lib/js/installation.js
  46. 2 2
      web/building_saas/complementary_ration_lib/js/jobContent.js
  47. 12 6
      web/building_saas/complementary_ration_lib/js/ration.js
  48. 3 1
      web/building_saas/complementary_ration_lib/js/ration_assist.js
  49. 2 2
      web/building_saas/complementary_ration_lib/js/ration_coe.js
  50. 15 4
      web/building_saas/complementary_ration_lib/js/ration_glj.js
  51. 9 9
      web/building_saas/complementary_ration_lib/js/repository_glj.js
  52. 5 2
      web/building_saas/complementary_ration_lib/js/section_tree.js
  53. 14 0
      web/building_saas/css/custom.css
  54. 1 1
      web/building_saas/fee_rates/fee_rate.html
  55. 47 5
      web/building_saas/main/html/main.html
  56. 6 2
      web/building_saas/main/js/controllers/block_controller.js
  57. 40 2
      web/building_saas/main/js/controllers/project_controller.js
  58. 1 0
      web/building_saas/main/js/models/cache_tree.js
  59. 15 2
      web/building_saas/main/js/models/calc_program.js
  60. 17 12
      web/building_saas/main/js/models/exportStdInterfaceBase.js
  61. 25 0
      web/building_saas/main/js/models/fee_rate.js
  62. 56 11
      web/building_saas/main/js/models/project.js
  63. 5 3
      web/building_saas/main/js/models/ration.js
  64. 9 0
      web/building_saas/main/js/models/ration_glj.js
  65. 29 24
      web/building_saas/main/js/views/block_lib.js
  66. 2 2
      web/building_saas/main/js/views/calc_program_manage.js
  67. 2 2
      web/building_saas/main/js/views/config_material_view.js
  68. 105 2
      web/building_saas/main/js/views/fee_rate_view.js
  69. 7 5
      web/building_saas/main/js/views/glj_view.js
  70. 173 14
      web/building_saas/main/js/views/importBills.js
  71. 1 1
      web/building_saas/main/js/views/index_view.js
  72. 2 2
      web/building_saas/main/js/views/item_increase_fee_view.js
  73. 65 15
      web/building_saas/main/js/views/main_tree_col.js
  74. 1 1
      web/building_saas/main/js/views/mbzm_view.js
  75. 4 4
      web/building_saas/main/js/views/project_glj_view.js
  76. 2 1
      web/building_saas/main/js/views/project_info.js
  77. 1 1
      web/building_saas/main/js/views/project_property_basicInfo.js
  78. 1 1
      web/building_saas/main/js/views/project_property_bills_quantity_decimal.js
  79. 1 1
      web/building_saas/main/js/views/project_property_labour_coe_view.js
  80. 1 1
      web/building_saas/main/js/views/project_property_projFeature.js
  81. 124 44
      web/building_saas/main/js/views/project_view.js
  82. 89 0
      web/building_saas/main/js/views/select_FB_view.js
  83. 14 3
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  84. 17 7
      web/building_saas/main/js/views/std_ration_lib.js
  85. 89 4
      web/building_saas/main/js/views/sub_fee_rate_views.js
  86. 3 3
      web/building_saas/main/js/views/sub_view.js
  87. 8 0
      web/building_saas/main/js/views/tender_price_view.js
  88. 3 3
      web/building_saas/main/js/views/zmhs_view.js
  89. 13 5
      web/building_saas/pm/js/pm_newMain.js
  90. 1 1
      web/common/components/share/index.html
  91. 121 34
      web/common/components/share/index.js
  92. 2 2
      web/common/html/header.html
  93. 1 1
      web/over_write/js/chongqing_2018_export.js
  94. 12 0
      web/over_write/js/chongqing_2018_import.js
  95. 30 0
      web/over_write/js/guangdong_2018.js
  96. 35 22
      web/over_write/js/guangdong_2018_import.js
  97. 30 0
      web/over_write/js/jiangxi_2017.js
  98. 29 0
      web/over_write/js/neimenggu_2017.js
  99. 14 14
      web/users/html/index.html

+ 8 - 0
config/gulpConfig.js

@@ -186,6 +186,7 @@ module.exports = {
         'web/building_saas/main/js/views/fee_rate_view.js',
         'web/building_saas/main/js/views/quantity_edit_view.js',
         'web/building_saas/main/js/views/sub_fee_rate_views.js',
+        'web/building_saas/main/js/views/select_FB_view.js',
         'web/building_saas/main/js/views/calc_base_view.js',
         'web/building_saas/main/js/views/project_property_labour_coe_view.js',
         'web/building_saas/main/js/views/locate_view.js',
@@ -198,6 +199,9 @@ module.exports = {
         'lib/ztree/css/zTreeStyle.css',
     ],
     compleGlj_jspaths: [
+        'lib/pinyinjs/pinyin_dict_firstletter.js',
+        'lib/pinyinjs/pinyinUtil.js',
+        'web/common/components/share/index.js',
         'public/web/common_ajax.js',
         'public/web/treeDataHelper.js',
         'public/web/QueryParam.js',
@@ -207,6 +211,7 @@ module.exports = {
         'public/web/tree_sheet/tree_sheet_helper.js',
         'public/web/tools_const.js',
         'public/web/ration_glj_units.js',
+        'web/building_saas/complementary_glj_lib/js/init.js',
         'web/building_saas/complementary_glj_lib/js/glj.js',
         'web/building_saas/complementary_glj_lib/js/gljClassTree.js',
         'web/building_saas/complementary_glj_lib/js/gljComponent.js',
@@ -227,6 +232,9 @@ module.exports = {
         'lib/jquery-contextmenu/jquery.ui.position.js',
         'lib/ztree/jquery.ztree.core.js',
         'web/building_saas/complementary_ration_lib/js/global.js',
+        'lib/pinyinjs/pinyin_dict_firstletter.js',
+        'lib/pinyinjs/pinyinUtil.js',
+        'web/common/components/share/index.js',
         'public/web/id_tree.js',
         'public/web/tree_sheet/tree_sheet_controller.js',
         'public/web/tree_sheet/tree_sheet_helper.js',

+ 2 - 0
modules/all_models/bills.js

@@ -68,7 +68,9 @@ let billsSchema = new Schema({
     //是否记取面积增加费
     areaIncreaseFee:{type:Schema.Types.Mixed,default:false},//true 是,false否,null 不确定,三个状态
     outPutMaxPrice:{type:Schema.Types.Mixed,default:false},//输出最高限价 true 是,false否,null 不确定,三个状态
+    outPutLimitPrice:{type:Schema.Types.Mixed,default:false},//输出限价 true 是,false否,null 不确定,三个状态
     maxPrice:String,//最高限价
+    minPrice:String,//最低限价
     remark:String,
     engineeringContent:String,//工程内容
     serviceContent:String,//服务内容

+ 2 - 1
modules/all_models/compleRation_ration.js

@@ -10,7 +10,8 @@ const deleteSchema = require('../all_schemas/delete_schema');
 const compleRationGljItemSchema = new Schema({
     gljId: Number,
     consumeAmt: String,
-    type: String    //std or complementary
+    type: String,    //std or complementary
+    fromUser: String,
 
 }, { _id: false });
 

+ 1 - 0
modules/all_models/projects.js

@@ -18,6 +18,7 @@ const ProjectSchema = new Schema({
     "ParentID": Number,
     "NextSiblingID": Number,
     "userID": String,
+    "importedByInterface": {type: Boolean, default: false},
     "code": {type: String, default: ''},
     "name": String,
     "projType": String,

+ 1 - 0
modules/all_models/ration.js

@@ -54,6 +54,7 @@ let rationSchema = new Schema({
     type: Number,                               // 1 定额、2 量价、3 工料机定额
     subType: Number,                            // 子类型:1人工、201材料、301机械、4主材、5设备
     from:{type: String,default:'std'},          //std, cpt  来自标准、补充
+    fromUser: String, // 来自某个用户的定额(分享补充定额)
     isSubcontract: Boolean,                     // 是否分包
     installationKey:String,                   //用来记录安装增加费的关联字段
     // 定额特有属性:

+ 2 - 1
modules/all_models/ration_glj.js

@@ -36,7 +36,8 @@ var ration_glj = new Schema({
     rationItemQuantity:String,
     tenderQuantity:String,//调整后消耗量
     createType: {type: String,default:'normal'},//normal、add、replace  正常、添加工料机、替换工料机
-    from:{type: String,default:'std'}//std, cpt  来自标准工料机库、补充工料机库
+    from:{type: String,default:'std'},//std, cpt  来自标准工料机库、补充工料机库
+    fromUser: String, // 来自某个用户的人材机(分享补充人材机)
 },{versionKey:false});
 
 mongoose.model('ration_glj', ration_glj);

+ 18 - 0
modules/all_models/share_lib.js

@@ -0,0 +1,18 @@
+// 分享库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const shareSchema = new Schema({
+    ID: String,
+    compilationID: String,
+    owner: String, // 拥有者ID
+    receiver: String, // 接收者ID
+    libType: Number, // 1: 定额库; 2: 人材机库; 3: 组件模板库;
+    allowCooperate: {
+        type: Boolean,
+        default: false
+    },
+    shareDate: String,
+    updateDate: String
+}, {versionKey: false});
+
+mongoose.model('share_libs', shareSchema, 'share_libs');

+ 54 - 21
modules/complementary_glj_lib/controllers/gljController.js

@@ -6,39 +6,58 @@ const stdgljutil = require("../../../public/cache/std_glj_type_util");
 const GljDao = require("../models/gljModel");
 const EngineeringLibModel = require("../../users/models/engineering_lib_model");
 let config = require("../../../config/config.js");
-
+const pmFacade = require('../../pm/facade/pm_facade');
+const { ShareLibType } = require('../../../public/common_constants');
 let gljDao = new GljDao();
 let callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
 };
 
+async function getGljLibId(sessionCompilation) {
+    let gljLibId = null,
+        rationValuation = sessionCompilation.ration_valuation,
+        billValuation = sessionCompilation.bill_valuation,
+        engineeringLibModel = new EngineeringLibModel(),
+        valuationIDs = [] ;
+    for(let r of rationValuation){//{ "glj_lib.0": {$exists:1} }
+        if(r.id){
+            valuationIDs.push(r.id);
+        }
+    }
+    for(let b of billValuation){
+        if(b.id){
+            valuationIDs.push(b.id);
+        }
+    }
+    if(valuationIDs.length > 0){
+        let engineeringInfo = await engineeringLibModel.findDataByCondition({'valuationID': {"$in": valuationIDs},"glj_lib.0": {$exists:1}});//数组大于0
+        gljLibId = engineeringInfo.glj_lib.length > 0 && typeof engineeringInfo.glj_lib !== 'undefined' ? engineeringInfo.glj_lib[0].id : null;
+    }
+    return gljLibId;
+}
+
 class GljController extends BaseController{
     async redirectGlj(req, res){
-        let gljLibId = null, engineeringId, sessionCompilation = req.session.sessionCompilation,
-            rationValuation = sessionCompilation.ration_valuation,
-            billValuation = sessionCompilation.bill_valuation,
-            engineeringLibModel = new EngineeringLibModel(),
-            valuationIDs = [] ;
-        for(let r of rationValuation){//{ "glj_lib.0": {$exists:1} }
-            if(r.id){
-                valuationIDs.push(r.id);
+        const { userID } = req.params;
+        const sessionUserID = req.session.sessionUser.id;
+        const compilationID = req.session.sessionCompilation._id;
+        if (userID !== sessionUserID) {
+            const receiveList = await pmFacade.getReceiveLibList(sessionUserID, compilationID, ShareLibType.GLJ_LIB);
+            const isValid = !!receiveList.find(user => String(user._id) === userID);
+            if (!isValid) {
+                return res.redirect(`/complementaryGlj/${sessionUserID}`);
             }
         }
-        for(let b of billValuation){
-            if(b.id){
-                valuationIDs.push(b.id);
-            }
-        }
-        if(valuationIDs.length > 0){
-            let engineeringInfo = await engineeringLibModel.findDataByCondition({'valuationID': {"$in": valuationIDs},"glj_lib.0": {$exists:1}});//数组大于0
-            gljLibId = engineeringInfo.glj_lib.length > 0 && typeof engineeringInfo.glj_lib !== 'undefined' ? engineeringInfo.glj_lib[0].id : null;
-        }
-        let overWriteUrl = req.session.sessionCompilation && req.session.sessionCompilation.overWriteUrl &&
-                            req.session.sessionCompilation._id !== '5b4d581023a924000b760f2d' ? req.session.sessionCompilation.overWriteUrl : null;
+        const sessionCompilation = req.session.sessionCompilation;
+        const gljLibId = await getGljLibId(sessionCompilation);
+        let overWriteUrl = sessionCompilation && req.session.sessionCompilation.overWriteUrl &&
+            compilationID !== '5b4d581023a924000b760f2d' ? req.session.sessionCompilation.overWriteUrl : null;
         const priceProperties = sessionCompilation.priceProperties || [];
         const consumeAmtProperties = sessionCompilation.consumeAmtProperties || [];
+        const isReadOnly = userID !== sessionUserID;
         res.render('building_saas/complementary_glj_lib/html/tools-gongliaoji.html',{
-            userID: req.session.sessionUser.id,
+            isReadOnly,
+            userID: sessionUserID,
             gljLibId: gljLibId,
             compilationId: sessionCompilation._id,
             compilationName: sessionCompilation.name,
@@ -49,6 +68,20 @@ class GljController extends BaseController{
             overWriteUrl: overWriteUrl,
         });
     }
+
+    async prepareInitData(req, res) {
+        try {
+            const { userID, projection } = JSON.parse(req.body.data);
+            const sessionUserID = req.session.sessionUser.id;
+            const gljLibID = await getGljLibId(req.session.sessionCompilation);
+            const compilationID = req.session.sessionCompilation._id;
+            const initData = await gljDao.prepareInitData(gljLibID, userID, sessionUserID, compilationID, projection);
+            res.json({error: 0, message: 'success', data: initData});
+        } catch (err) {
+            console.log(err);
+            res.json({error: 1, message: 'fail', data: null});
+        }
+    }
     getGljDistType (req, res) {
         let gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
         if(gljDistTypeCache.length >0 ){

+ 63 - 1
modules/complementary_glj_lib/models/gljModel.js

@@ -1,3 +1,33 @@
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
+/*
+ * @Descripttion: 
+ * @Author: vian
+ * @Date: 2020-12-16 17:48:47
+ */
 /**
  * Created by Zhong on 2017/8/22.
  */
@@ -14,8 +44,27 @@ const libType = {
     stdGLJ: 1,
     complementaryGLJs: 2
 };
+const stdgljutil = require("../../../public/cache/std_glj_type_util");
+const { ShareLibType } = require('../../../public/common_constants');
 
 class GljDao {
+    
+    async prepareInitData(gljLibID, userID, sessionUserID, compilationID, projection) {
+        const pmFacade = require('../../pm/facade/pm_facade');
+        const receiveLibs = await pmFacade.getReceiveLibList(sessionUserID, compilationID, ShareLibType.GLJ_LIB);
+        const shareLibs = sessionUserID === userID ? await pmFacade.getLibShareList(sessionUserID, compilationID, ShareLibType.GLJ_LIB) : [];
+        const gljItems = await this.getGLJItemsSync(gljLibID, userID, compilationID, projection);
+        const mixTree = await this.getMixedTree(gljLibID, userID, compilationID);
+        const gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
+        return {
+            receiveLibs,
+            shareLibs,
+            gljItems,
+            mixTree,
+            distTypeData: gljDistTypeCache,
+        };
+    }
+
     getGljTypes (gljLibId, callback){
         gljClassModel.find({"repositoryId": gljLibId},function(err,data){
             if(data.length) {
@@ -159,7 +208,6 @@ class GljDao {
                 catch (err) {
                     cb(err);
                 }
-
             },
             function (cb) {
                 if (!userId || !compilationId) {
@@ -189,6 +237,20 @@ class GljDao {
 
     }
 
+    async getGLJItemsSync(stdGljLibId, userId, compilationId, projection) {
+        const rst = { stdGljs: [], complementaryGljs: [] };
+        const task = [
+            stdGljModel.find({ repositoryId: stdGljLibId }, projection).lean(),
+            complementaryGljModel.find({ userId: userId, compilationId: compilationId }, '-_id').lean()
+        ];
+        const [ stdRst, cptRst ] = await Promise.all(task);
+        this.sortToNumber(stdRst);
+        rst.stdGljs = stdRst;
+        this.sortToNumber(cptRst);
+        rst.complementaryGljs = cptRst;
+        return rst;
+    }
+
     async getStdItems (stdGljLibId, projection, callback) {
         try {
             let stdItems = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();

+ 2 - 1
modules/complementary_glj_lib/routes/routes.js

@@ -11,13 +11,14 @@ let gljController = new GljController();
 let compleRationController = new CompleRationController();
 
 module.exports = function (app) {
-    app.get('/complementaryGlj', gljController.init, gljController.redirectGlj);
+    app.get('/complementaryGlj/:userID', gljController.init, gljController.redirectGlj);
 
    /* router.post("/updateRationBasePrc",gljController.init, gljController.updateRationBasePrc);//更新定额单价
     router.post("/getRationGljIds", gljController.init, gljController.getRationGljIds);
     router.post("/createNewGljTypeNode", gljController.init, gljController.createNewGljTypeNode);
     router.post("/updateGljNodes", gljController.init, gljController.updateGljNodes);
     router.post("/deleteGljNodes", gljController.init, gljController.deleteGljNodes);*/
+    router.post("/initData", gljController.init, gljController.prepareInitData);
     router.post("/getGljDistType", gljController.init, gljController.getGljDistType);
     router.post("/getGljTree", gljController.init, gljController.getGljTree);
     router.post("/getGljItems", gljController.init, gljController.getGljItems);

+ 4 - 2
modules/complementary_ration_lib/controllers/compleRationController.js

@@ -90,7 +90,8 @@ class CompleRationController extends BaseController{
             rations = [];
         try {
             if (data.type === libType.complementary) {
-                rations = await compleRationDao.getCompleRationBySection(req.session.sessionUser.id, data.sectionId);
+                const userID = data.owner || req.session.sessionUser.id;
+                rations = await compleRationDao.getCompleRationBySection(userID, data.sectionId);
             }  else {
                 rations = await compleRationDao.getRationGljItemsBySection(data.sectionId);
             }
@@ -102,7 +103,8 @@ class CompleRationController extends BaseController{
 
     async getCoeList(req, res){
         try{
-            let coeList = await coeFacade.getComplementaryCoes(req.session.sessionUser.id, req.session.sessionCompilation._id);
+            const { userID } = JSON.parse(req.body.data);
+            let coeList = await coeFacade.getComplementaryCoes(userID, req.session.sessionCompilation._id);
             callback(req, res, 0, 'success', coeList);
         } catch (err) {
             callback(req, res, 1, err, null);

+ 2 - 1
modules/complementary_ration_lib/controllers/compleSectionTreeController.js

@@ -20,7 +20,8 @@ class CompleSectionTreeController extends BaseController{
 
     getRationTree(req, res){
         let data = JSON.parse(req.body.data);
-        sectionTreeDao.getRationTree(req.session.sessionUser.id, req.session.sessionCompilation._id, data.rationRepId, data.type, function (err, data) {
+        const userID = data.owner || req.session.sessionUser.id;
+        sectionTreeDao.getRationTree(userID, req.session.sessionCompilation._id, data.rationRepId, data.type, function (err, data) {
             callback(req, res, err, '', data);
         });
     }

+ 31 - 15
modules/complementary_ration_lib/controllers/compleViewController.js

@@ -9,6 +9,8 @@ let config = require("../../../config/config.js");
 let compleViewModel = new CompleViewModel();
 const CompleRationDao = require('../models/compleRationModel');
 const compleRationDao = new CompleRationDao();
+const pmFacade = require('../../pm/facade/pm_facade');
+const { ShareLibType } = require('../../../public/common_constants');
 let callback = function (req, res, err, msg, data) {
     res.json({error: err, message: msg, data: data});
 };
@@ -38,13 +40,25 @@ async function getGljLibId(sessionCompilation) {
 
 class CompleViewController extends BaseController{
     async redirectRation(req, res){
+        const { userID } = req.params;
+        const sessionUserID = req.session.sessionUser.id;
+        const compilationID = req.session.sessionCompilation._id;
+        if (userID !== sessionUserID) {
+            const receiveList = await pmFacade.getReceiveLibList(sessionUserID, compilationID, ShareLibType.RATION_LIB);
+            const isValid = !!receiveList.find(user => String(user._id) === userID);
+            if (!isValid) {
+                return res.redirect(`/complementaryRation/ration/${sessionUserID}`);
+            }
+        }
         const gljLibId = await getGljLibId(req.session.sessionCompilation);
-        const redirectGlj = `/complementaryRation/glj`;
-        const redirectCoe = `/complementaryRation/coe`;
-        const redirectInstallation = `/complementaryRation/installation`;
+        const redirectGlj = `/complementaryRation/glj/${userID}`;
+        const redirectCoe = `/complementaryRation/coe/${userID}`;
+        const redirectInstallation = `/complementaryRation/installation/${userID}`;
         const priceProperties = req.session.sessionCompilation.priceProperties || [];
+        const isReadOnly = userID !== sessionUserID;
         res.render('building_saas/complementary_ration_lib/html/dinge.html', {
-            userID: req.session.sessionUser.id,
+            isReadOnly,
+            userID: sessionUserID,
             redirectGlj: redirectGlj,
             redirectCoe: redirectCoe,
             redirectInstallation: redirectInstallation,
@@ -58,10 +72,11 @@ class CompleViewController extends BaseController{
 
     async prepareInitData (req, res) {
         try {
+            const { userID } = req.query;
+            const sessionUserID = req.session.sessionUser.id;
             const gljLibId = await getGljLibId(req.session.sessionCompilation);
-            const userId = req.session.sessionUser.id;
             const compilationId = req.session.sessionCompilation._id;
-            const initData = await compleRationDao.prepareInitData(userId, compilationId, gljLibId);
+            const initData = await compleRationDao.prepareInitData(sessionUserID, userID, compilationId, gljLibId);
             res.json({error: 0, message: 'success', data: initData});
         } catch (err) {
             res.json({error: 1, message: 'fail', data: null});
@@ -69,10 +84,11 @@ class CompleViewController extends BaseController{
     }
 
     async redirectGljList(req, res){
+        const { userID } = req.params;
         const gljLibId = await getGljLibId(req.session.sessionCompilation);
-        const redirectRation = `/complementaryRation/ration`;
-        const redirectCoe = `/complementaryRation/coe`;
-        const redirectInstallation = `/complementaryRation/installation`;
+        const redirectRation = `/complementaryRation/ration/${userID}`;
+        const redirectCoe = `/complementaryRation/coe/${userID}`;
+        const redirectInstallation = `/complementaryRation/installation/${userID}`;
         res.render('building_saas/complementary_ration_lib/html/gongliao.html', {
             userID: req.session.sessionUser.id,
             redirectRation: redirectRation,
@@ -87,9 +103,9 @@ class CompleViewController extends BaseController{
 
     async redirectCoeList(req, res){
         const gljLibId = await getGljLibId(req.session.sessionCompilation);
-        const redirectRation = `/complementaryRation/ration`;
-        const redirectGlj = `/complementaryRation/glj`;
-        const redirectInstallation = `/complementaryRation/installation`;
+        const redirectRation = `/complementaryRation/ration/${userID}`;
+        const redirectGlj = `/complementaryRation/glj/${userID}`;
+        const redirectInstallation = `/complementaryRation/installation/${userID}`;
         res.render('building_saas/complementary_ration_lib/html/fuzhu.html', {
             userID: req.session.sessionUser.id,
             redirectRation: redirectRation,
@@ -104,9 +120,9 @@ class CompleViewController extends BaseController{
 
     redirectInstallation(req, res){
         const repId = req.query.repository;
-        const redirectRation = `/complementaryRation/ration`;
-        const redirectGlj = `/complementaryRation/glj?repository=${repId}`;
-        const redirectCoe = `/complementaryRation/coe?repository=${repId}`;
+        const redirectRation = `/complementaryRation/ration/${userID}`;
+        const redirectGlj = `/complementaryRation/glj?repository=${repId}/${userID}`;
+        const redirectCoe = `/complementaryRation/coe?repository=${repId}/${userID}`;
         res.render('building_saas/complementary_ration_lib/html/anzhuang.html', {
             userID: req.session.sessionUser.id,
             redirectRation: redirectRation,

+ 14 - 3
modules/complementary_ration_lib/models/compleRationModel.js

@@ -19,6 +19,8 @@ const gljDao = new GljDao();
 let counter = require('../../../public/counter/counter');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
 let gljUtil = require('../../../public/gljUtil');
+const { ShareLibType } = require('../../../public/common_constants');
+const pmFacade = require('../../pm/facade/pm_facade');
 
 class CompleRatoinDao {
     async updateRation(userID, compilationId, updateData, callback){
@@ -99,7 +101,8 @@ class CompleRatoinDao {
                     }
                 }
                 else if(ids[i].type === 'complementary'){
-                    let compleGlj = await complementaryGljModel.find({userId: userID, ID: ids[i].id, deleteInfo: null});
+                    const owner = ids[i].fromUser || userID;
+                    let compleGlj = await complementaryGljModel.find({userId: owner, ID: ids[i].id, deleteInfo: null});
                     if(compleGlj.length > 0){
                         compleGlj[0]._doc.type = 'complementary';
                         rst.push(compleGlj[0]);
@@ -148,6 +151,7 @@ class CompleRatoinDao {
                 comGljIds = [],
                 stdGljs = [],
                 comGljs = [];
+            const users = [userId];
             let gljAmtMapping = {};
             for(let rationGlj of ration.rationGljList){
                 gljAmtMapping[rationGlj.gljId] = rationGlj.consumeAmt;
@@ -155,6 +159,9 @@ class CompleRatoinDao {
                     stdGljIds.push(rationGlj.gljId);
                 }
                 else {
+                    if (rationGlj.fromUser && !users.includes(rationGlj.fromUser)) {
+                        users.push(rationGlj.fromUser);
+                    }
                     comGljIds.push(rationGlj.gljId);
                 }
             }
@@ -162,7 +169,7 @@ class CompleRatoinDao {
                 stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}}).lean();
             }
             if(comGljIds.length > 0) {
-                comGljs = await complementaryGljModel.find({userId: userId, ID: {$in: comGljIds}}).lean();
+                comGljs = await complementaryGljModel.find({userId: { $in: users }, ID: {$in: comGljIds}}).lean();
             }
             let gljDatas = gljUtil.sortRationGLJ(stdGljs.concat(comGljs),true);
             if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
@@ -523,7 +530,9 @@ class CompleRatoinDao {
         return codes;
     }
 
-    async prepareInitData (userId, compilationId, gljLibId) {
+    async prepareInitData (sessionUserID, userId, compilationId, gljLibId) {
+        const receiveLibs = await pmFacade.getReceiveLibList(sessionUserID, compilationId, ShareLibType.RATION_LIB);
+        const shareLibs = sessionUserID === userId ? await pmFacade.getLibShareList(sessionUserID, compilationId, ShareLibType.RATION_LIB) : [];
         const rationsCodes = await this.getCodes(userId, compilationId);
         const gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
         const installationData = await installFacade.getInstallation(userId, compilationId);
@@ -531,6 +540,8 @@ class CompleRatoinDao {
         const mixedTreeData = await gljDao.getMixedTree(gljLibId, userId, compilationId);
         const mixedGLJData = await gljDao.getGLJDataSync(gljLibId, userId, compilationId);
         return {
+            shareLibs,
+            receiveLibs,
             rationsCodes,
             gljDistTypeCache,
             installationData,

+ 27 - 16
modules/complementary_ration_lib/models/searchModel.js

@@ -16,15 +16,19 @@ class SearchDao{
     async getRationItem(userId, compilationId, rationRepIds, code, ID, callback){
         let ration = null;
         let otherLibs=[];
+        const compleLibUsers = [userId];
         try{
-            let firstLib = rationRepIds[0];//优先取第一个
+            let [firstLib] = String(rationRepIds[0]).split('*');//优先取第一个
             for (let l of rationRepIds){
-                if(l != firstLib && l != compleRationLib){
-                    otherLibs.push(l);
+                const [libID, owner] = String(l).split('*');
+                if(libID != firstLib && libID != compleRationLib){
+                    otherLibs.push(libID);
+                } else if (libID === compleRationLib && owner && !compleLibUsers.includes(owner)) {
+                    compleLibUsers.push(owner);
                 }
             }
             if(firstLib == compleRationLib){//说明选中的是补充定额库
-                ration = await this.getCompleRation(userId,compilationId,code,ID);
+                ration = await this.getCompleRation(compleLibUsers,compilationId,code,ID);
             }else {
                 firstLib = parseInt(firstLib);
                 let firstQuery = {rationRepId: firstLib, code: code};
@@ -39,7 +43,7 @@ class SearchDao{
                     stdQuery = {ID: ID};
                 }
                 ration = await this.getStdRation(stdQuery);
-                if(ration == null) ration = await this.getCompleRation(userId,compilationId,code,ID);
+                if(ration == null) ration = await this.getCompleRation(compleLibUsers,compilationId,code,ID);
             }
             if(isDef(ration)){
                 if (ration.type === 'std') {
@@ -65,9 +69,9 @@ class SearchDao{
         }
         return ration;
     }
-    async getCompleRation(userId,compilationId,code,ID){
+    async getCompleRation(users,compilationId,code,ID){
         let ration = null;
-        let compleQuery = {userId: userId, compilationId: compilationId, code: code, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]};
+        let compleQuery = {userId: {$in: users}, compilationId: compilationId, code: code, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]};
         if(ID){
             compleQuery.ID = ID;
         }
@@ -95,14 +99,21 @@ class SearchDao{
         let resultCount = 0,
             rst = {data: [], count: null};
         try{
+            let compleLibUsers = new Set();
+            const stdRationLibs = [];
+            rationRepId.forEach(libItem => {
+                const [libID, owner] = String(libItem).split('*');
+                if (libID === compleRationLib) {
+                    compleLibUsers.add(owner || userId);
+                } else {
+                    stdRationLibs.push(libID);
+                }
+            });
+            compleLibUsers = [...compleLibUsers];
             //是否需要查找补充定额
-            let findCompleRtion = rationRepId.length > 0 && rationRepId.includes(compleRationLib) ? true : false;
-            //剔除补充定额库id
-            if (rationRepId.includes(compleRationLib)) {
-                rationRepId.splice(rationRepId.indexOf(compleRationLib), 1);
-            }
+            let findCompleRtion = compleLibUsers.length > 0;
             let filter = {
-                'rationRepId': {$in: rationRepId},
+                'rationRepId': {$in: stdRationLibs},
                 '$and': [{
                     '$or': [{'code': {'$regex': keyword, $options: '$i'}}, {'name': {'$regex': keyword, $options: '$i'}}]
                 }, {
@@ -110,7 +121,7 @@ class SearchDao{
                 }]
             };
             let compleFilter = {
-                userId: userId,
+                userId: { $in: compleLibUsers },
                 compilationId: compilationId,
                 '$and': [{
                     '$or': [{'code': {'$regex': keyword, $options: '$i'}}, {'name': {'$regex': keyword, $options: '$i'}}]
@@ -120,14 +131,14 @@ class SearchDao{
             };
             //结果数
             if (skip && skip.std === 0 && skip.comple === 0) {
-                resultCount += rationRepId.length === 0 ? 0 : await stdRationModel.find(filter).count();
+                resultCount += stdRationLibs.length === 0 ? 0 : await stdRationModel.find(filter).count();
                 resultCount += findCompleRtion ? await compleRationModel.find(compleFilter).count() : 0;
                 rst.count = resultCount;
             }
             //搜索定额
             let stdGljIds = [],
                 comGljIds = [];
-            let stdRations = rationRepId.length === 0 ? [] : await stdRationModel.find(filter).lean().sort({code: 1}).skip(skip.std).limit(limit);
+            let stdRations = stdRationLibs.length === 0 ? [] : await stdRationModel.find(filter).lean().sort({code: 1}).skip(skip.std).limit(limit);
             for(let i = 0, len = stdRations.length; i < len; i++){
                 stdRations[i].type = 'std';
                 for(let glj of stdRations[i].rationGljList){

+ 4 - 4
modules/complementary_ration_lib/routes/routes.js

@@ -17,10 +17,10 @@ let gljController = new GljController();
 let searchController = new SearchController();
 
 module.exports = function (app) {
-    app.get('/complementaryRation/ration', compleViewController.init, compleViewController.redirectRation);
-    app.get('/complementaryRation/glj', compleViewController.init, compleViewController.redirectGljList);
-    app.get('/complementaryRation/coe', compleViewController.init, compleViewController.redirectCoeList);
-    app.get('/complementaryRation/installation', compleViewController.init, compleViewController.redirectInstallation);
+    app.get('/complementaryRation/ration/:userID', compleViewController.init, compleViewController.redirectRation);
+    app.get('/complementaryRation/glj/:userID', compleViewController.init, compleViewController.redirectGljList);
+    app.get('/complementaryRation/coe/:userID', compleViewController.init, compleViewController.redirectCoeList);
+    app.get('/complementaryRation/installation/:userID', compleViewController.init, compleViewController.redirectInstallation);
     router.get('/initData', compleViewController.init, compleViewController.prepareInitData);
 
     router.post('/getRationLib', compleViewController.init, compleViewController.getRationLib);

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

@@ -12,16 +12,9 @@ let raiton_facade = require("../facade/ration_facade");
 let stdBillsModel = mongoose.model('std_bills_lib_bills');
 let stdBillJobsModel = mongoose.model('std_bills_lib_jobContent');
 let stdBillCharacterModel = mongoose.model('std_bills_lib_itemCharacter');
-const { fixedFlag } = require('../../../public/common_constants');
+const { fixedFlag, billType, rationType } = require('../../../public/common_constants');
 let LZString = require('lz-string');
 const uuidV1 = require('uuid/v1');
-const billType ={
-    DXFY:1,//大项费用
-    FB:2,//分部
-    FX:3,//分项
-    BILL:4,//清单
-    BX:5//补项
-};
 //上传的09表、广联达表
 const uploadType = {lj: 'lj', gld: 'gld'};
 // 上传控件
@@ -196,7 +189,7 @@ module.exports = {
     downloadExample: function(request, response) {
         try {
             //导入类型(09表、广联达)对应的示例文件名
-            const uploadTypeMap = { lj: '分部分项工程项目清单计价表.xlsx', gld: '算量示例-清单汇总表.xlsx' };
+            const uploadTypeMap = { general: '示例1-分部分项工程项目清单.xlsx', lj: '示例2-分部分项工程项目清单计价表.xlsx', gld: '算量示例-清单汇总表.xlsx' };
             const type = request.query.type;
             const fileName = uploadTypeMap[type];
             const filePath = `./public/static/${fileName}`;
@@ -317,11 +310,28 @@ async function importSheet(position, excelBills, userID, projectID, stdData){
         let deleteDatas = await billsData.deepDeleteBill([fixedBill], userID);
         //新增清单数据
         await billsData.importBills(excelBills);
+        // 如果导入招标、控制价文件、每个分项底下自动生成一条空定额
+        const rations = [];
+        excelBills.forEach(bills => {
+            if (bills.type === billType.FX) {
+                const emptyRation = {
+                    projectID: bills.projectID,
+                    ID: uuidV1(),
+                    billsItemID: bills.ID,
+                    serialNo: 1,
+                    type: rationType.ration,
+                };
+                rations.push(emptyRation);
+            }
+        });
+        if (rations.length) {
+            await ration_model.model.insertMany(rations);
+        }
         //返回数据以更新前端
         if(insertFixedBill){
             excelBills.push(insertFixedBill);
         }
-        return {fixedBill: fixedBill, insert: {bill: excelBills, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
+        return {fixedBill: fixedBill, insert: {bill: excelBills, ration: rations}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
 }
 
 

+ 12 - 0
modules/main/controllers/ration_controller.js

@@ -9,6 +9,9 @@ let bill_facade = require('../facade/bill_facade');
 let project_facade = require("../facade/project_facade");
 let logger = require("../../../logs/log_helper").logger;
 const GLJController = require("../../glj/controllers/glj_controller");
+const pmFacade = require('../../pm/facade/pm_facade');
+const { ShareLibType } = require('../../../public/common_constants');
+const compleRationLib = 'compleRationLib';
 let controller = {
     insertGLJAsRation:async function (req){
         let data = req.body.data;
@@ -19,6 +22,14 @@ let controller = {
         let data = req.body.data;
         data = JSON.parse(data);
         let userID = req.session.sessionUser.id;
+        const compilationID = req.session.sessionCompilation._id;
+        if (data.libIDs && !data.libIDs.includes(compleRationLib)) {
+            const rationLibUsers = await pmFacade.getReceiveLibList(userID, compilationID, ShareLibType.RATION_LIB);
+            data.libIDs.push(compleRationLib);
+            rationLibUsers.forEach(user => {
+                data.libIDs.push(`${compleRationLib}*${user._id}`);
+            });
+        }
         return await ration_facade.replaceRations(userID,data,req.session.sessionCompilation);
     },
     addNewRation:async function(req) {
@@ -27,6 +38,7 @@ let controller = {
             data = JSON.stringify(data);
         }
         data = JSON.parse(data);
+        data.sessionUserID = req.session.sessionUser.id;
         let result = await ration_facade.addNewRation(data,req.session.sessionCompilation);
         //合并取项目工料机数据的情求,用于刷新项目工料机数据,当有添加、替换项目工料机的情况,才需要刷新
        /* if(result.ration_gljs && result.ration_gljs.length > 0 && data.newData){

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

@@ -57,6 +57,9 @@ async function addNewRation(data,compilation) {
     if(query){
         let searchDao = new SearchDao();
         stdRation = await searchDao.getRationItem(query.userID, compilation._id, [query.rationRepId],query.code, query.ID);
+        if (stdRation && data.sessionUserID !== query.userID) {
+          stdRation.owner = query.userID;
+        }
         //data.newData.code = query.code;
     }
     let stdRationTime = +new Date();
@@ -138,6 +141,10 @@ async function  updateSerialNo(serialNoUpdate){
 async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入新的定额
     let startTime = +new Date();
     if(std){
+        if (std.owner) {
+          // 别人分享的定额
+          newData.fromUser = std.owner;
+        }
         newData.code = std.code;
         newData.name = std.name;
         newData.caption = std.caption;
@@ -181,6 +188,9 @@ async function replaceRations(userID,data,compilation) {
     let recodes = [];
     for(let recode of data.nodeInfo){
         let stdRation = await searchDao.getRationItem(userID,compilation._id,data.libIDs,recode.newCode, null);
+        if (stdRation && stdRation.userId && stdRation.userId !== userID) {
+          stdRation.owner = stdRation.userId;
+        }
         let newRecode = await replaceRation(recode,stdRation,data.defaultLibID,data.projectID,data.calQuantity,compilation,data.cleanzmhs);
         if(newRecode){
             recodes.push(newRecode);
@@ -433,12 +443,16 @@ async function addRationGLJ(std,newRation,compilation) {
     }
     let ext = getExtendData(property,compilation);
     let first = +new Date();
+    const users = [std.userId];
     if(std.hasOwnProperty('rationGljList') && std.rationGljList.length > 0){
         let stdGLJID =[];//标准工料机ID数组
         let cptGLJID=[];//补充工料机ID数组
             //let stdGLJID = _.map(std.rationGljList,'gljId');
         for(let tem_g  of std.rationGljList){
              if(tem_g.type == 'complementary'){
+               if (tem_g.fromUser && !users.includes(tem_g.fromUser)) {
+                 users.push(tem_g.fromUser);
+               }
                 cptGLJID.push(tem_g.gljId);
              }else {
                 stdGLJID.push(tem_g.gljId);
@@ -446,7 +460,7 @@ async function addRationGLJ(std,newRation,compilation) {
         }
        let stdGLJList = stdGLJID.length > 0 ? await std_glj_lib_gljList_model.find({'ID':{'$in':stdGLJID}}):[];//速度优化-------先一次性取出所有的工料机列表
        let stdGLJMap = _.indexBy(stdGLJList, 'ID');
-       let cptGLJList =  cptGLJID.length > 0 ? await complementary_glj_model.find({'userId':std.userId,'ID':{'$in':cptGLJID}}):[];
+       let cptGLJList =  cptGLJID.length > 0 ? await complementary_glj_model.find({'userId':{$in: users},'ID':{'$in':cptGLJID}}):[];
        let cptGLJMap = _.indexBy(cptGLJList, 'ID');
         let stdGLJMapTime = +new Date();
         console.log("找到工料机映射表时间-------------------------------"+(stdGLJMapTime - first));
@@ -462,6 +476,9 @@ async function addRationGLJ(std,newRation,compilation) {
           newGLJ.glj_repository_id = std.rationRepId;
           let std_glj = null;
           if(sub.type == 'complementary'){//有可能来自标准工料机库或补充工料机库
+            if (sub.fromUser) {
+              newGLJ.fromUser = sub.fromUser;
+            }
               std_glj = cptGLJMap[sub.gljId];
               newGLJ.from = 'cpt';
           }else {
@@ -958,6 +975,10 @@ async function updateCoeAdjust(data,compilation) {
 async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,calQuantity,cleanzmh=false) {
     // insertNewRation
     let ration ={};
+    if (std.owner) {
+      // 别人分享的定额
+      ration.fromUser = std.owner;
+    }
     ration.code = std.code;
     ration.name = std.name;
     ration.caption = std.caption;

+ 79 - 4
modules/pm/controllers/pm_controller.js

@@ -634,6 +634,81 @@ module.exports = {
             callback(req, res, 1, err.message, null);
         }
     },
+    getReceiveLibList: async function (req, res) {
+        try {
+            const { libType } = JSON.parse(req.body.data);
+            const userID = req.session.sessionUser.id;
+            const compilationID = req.session.sessionCompilation._id;
+            const list = await pm_facade.getReceiveLibList(userID, compilationID, libType);
+            callback(req, res, 0, 'success', list);
+        } catch (err) {
+            callback(req, res, 1, err.message, null);
+        }
+    },
+    getInitialShareLibData: async function (req, res) {
+        try {
+            const { libType, count } = JSON.parse(req.body.data);
+            const userID = req.session.sessionUser.id;
+            const compilationID = req.session.sessionCompilation._id;
+            // 最近分享
+            const recentUsers = await pm_facade.getRecentShareList(userID, count);
+            // 联系人
+            const contacts = await userModelObj.getContacts(userID);
+            // 分享过的人
+            const sharedUsers = await pm_facade.getLibShareList(userID, compilationID, libType, count);
+            callback(req, res, 0, 'success', { recentUsers, contacts, sharedUsers });
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err.message, null);
+        }
+    },
+    shareLib: async function (req, res) {
+        try {
+            const { type, libType, shareData, count } = JSON.parse(req.body.data);
+            const owner = req.session.sessionUser.id;
+            const compilationID = req.session.sessionCompilation._id;
+            const shareDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
+            shareData.forEach(item => item.shareDate = shareDate);
+            const task = [];
+            // 是否只是单纯的更新分享选项,如果不是,需要重新获取最近分享和联系人
+            let isSimpleUpdate = true;
+            if (type === 'create') {
+                isSimpleUpdate = false;
+                // 生成分享记录
+                const docs = shareData.map(item => ({
+                    compilationID,
+                    owner,
+                    libType,
+                    ID: uuidV1(),
+                    receiver: item.receiver,
+                    allowCooperate: false,
+                    shareDate: item.shareDate,
+                    updateDate: item.shareDate
+                }));
+                task.push(pm_facade.addShareList(docs, true));
+                // 分享即互相添加为联系人
+                task.push(userModelObj.addContact(docs[0].owner, docs[0].receiver));
+            } else if (type === 'cancel') { // 取消分享
+                const cancelReceivers = shareData.map(item => item.receiver);
+                task.push(pm_facade.deleteShareList({ owner, libType, compilationID, receiver: { $in: cancelReceivers } }, true));
+            }
+            await Promise.all(task);
+            const rst = {};
+            if (!isSimpleUpdate) {
+                const rstTask = [
+                    pm_facade.getRecentShareList(owner, count),
+                    userModelObj.getContacts(owner)
+                ];
+                const [recentUsers, contacts] = await Promise.all(rstTask);
+                Object.assign(rst, { recentUsers, contacts });
+            }
+            callback(req, res, 0, 'success', rst);
+        } catch (err) {
+            console.log(err);
+            logger.info(err);
+            callback(req, res, 1, err, null);
+        }
+    },
     share: async function (req, res) {
         try {
             const data = JSON.parse(req.body.data);
@@ -696,15 +771,15 @@ module.exports = {
                 task.push(pm_facade.deleteShareList({ projectID, receiver: { $in: cancelReceivers } }));
             }
             await Promise.all(task);
-            const rstTask = [
-                pm_facade.getRecentShareList(userID, count),
-                userModelObj.getContacts(userID)
-            ];
             // 获取需要广播推送的单位工程
             // shareData数组的形式是以前需求需要,现在的需求下,shareData数组必定只有一个元素
             const emitTenders = await pm_facade.getShareInfoAfterChangePermission(permissionType, shareData[0].userID, projectID);
             let rst = { emitTenders };
             if (!isSimpleUpdate) {
+                const rstTask = [
+                    pm_facade.getRecentShareList(userID, count),
+                    userModelObj.getContacts(userID)
+                ];
                 const [recentUsers, contacts] = await Promise.all(rstTask);
                 Object.assign(rst, { recentUsers, contacts });
             }

+ 67 - 9
modules/pm/facade/pm_facade.js

@@ -17,6 +17,8 @@ module.exports={
     getShareInfoMap,
     getRecentShareList,
     getProjectShareList,
+    getReceiveLibList,
+    getLibShareList,
     getUnreadShareListByCompilation,
     getShareTip,
     getShareState,
@@ -103,6 +105,7 @@ let featureLibModel =  mongoose.model("std_project_feature_lib");
 let importLogsModel = mongoose.model("import_logs");
 const overHeightLibModel = mongoose.model('std_over_height_lib');
 const shareListModel = mongoose.model('share_list');
+const shareLibModel = mongoose.model('share_libs');
 let welcomeModel = mongoose.model("welcome_setting");
 
 let scMathUtil = require('../../../public/scMathUtil').getUtil();
@@ -211,19 +214,23 @@ async function prepareShareList() {
 
 
 // 获取分享列表
-async function getShareList(query) {
-    return await shareListModel.find(query, '-_id').lean();
+async function getShareList(query, isShareLib = false) {
+    const model = isShareLib ? shareLibModel : shareListModel;
+    return await model.find(query, '-_id').lean();
 }
 
-async function addShareList(docs) {
-    await shareListModel.insertMany(docs);
+async function addShareList(docs, isShareLib = false) {
+    const model = isShareLib ? shareLibModel : shareListModel;
+    await model.insertMany(docs);
 }
 
-async function deleteShareList(query) {
-    await shareListModel.deleteMany(query);
+async function deleteShareList(query, isShareLib = false) {
+    const model = isShareLib ? shareLibModel : shareListModel;
+    await model.deleteMany(query);
 }
 
-async function updateShareList(updateData) {
+async function updateShareList(updateData, isShareLib = false) {
+    const model = isShareLib ? shareLibModel : shareListModel;
     const bulks = updateData.map(item => (
         {
             updateOne: {
@@ -233,7 +240,7 @@ async function updateShareList(updateData) {
         }
     ));
     if (bulks.length) {
-        await shareListModel.bulkWrite(bulks);
+        await model.bulkWrite(bulks);
     }
 }
 
@@ -259,7 +266,10 @@ async function getShareInfoMap(projectIDs, shareList = null) {
 
 // 获取最近分享(只算分享出去的记录)
 async function getRecentShareList(userID, count) {
-    const shareList = await getShareList({owner: userID});
+    // 合并分享的项目和库
+    const shareProjectList = await getShareList({owner: userID});
+    const shareLibList = await getShareList({owner: userID}, true);
+    const shareList = [...shareProjectList, ...shareLibList];
     shareList.sort((a, b) => Date.parse(b.shareDate) - Date.parse(a.shareDate));
     const set = new Set();
     for(const item of shareList) {
@@ -275,6 +285,7 @@ async function getRecentShareList(userID, count) {
     return users;
 }
 
+
 // 获取某项目的分享记录
 async function getProjectShareList(projectID, limit = null) {
     const sharedList = limit 
@@ -301,6 +312,51 @@ async function getProjectShareList(projectID, limit = null) {
     return users;
 }
 
+async function getReceiveLibList(receiver, compilationID, libType) {
+    const sharedList = await shareLibModel.find({ receiver, compilationID, libType }).lean().sort({ shareDate: -1 });
+    const userIDs = [];
+    const userMap = {};
+    sharedList.forEach((item, index) => {
+        userIDs.push(item.owner);
+        userMap[item.owner] = item;
+        userMap[item.owner].index = index;
+    });
+    const userObjectIDs = userIDs.map(userID => mongoose.Types.ObjectId(userID));
+    const users = await userModel.find({ _id: { $in: userObjectIDs } }, 'real_name').lean();
+    users.forEach(user => {
+        const matched = userMap[user._id];
+        if (matched) {
+            user.index = matched.index;
+        }
+    });
+    users.sort((a, b) => a.index - b.index);
+    return users;
+}
+
+// 获取某库的分享记录
+async function getLibShareList(owner, compilationID, libType, limit = null) {
+    const sharedList = limit 
+        ? await shareLibModel.find({ owner, compilationID, libType }).lean().sort({ shareDate: -1 }).limit(limit)
+        : await shareLibModel.find({ owner, compilationID, libType }).lean().sort({ shareDate: -1 });
+    const userIDs = [];
+    const userMap = {};
+    sharedList.forEach((item, index) => {
+        userIDs.push(item.receiver);
+        userMap[item.receiver] = item;
+        userMap[item.receiver].index = index;
+    });
+    const userObjectIDs = userIDs.map(userID => mongoose.Types.ObjectId(userID));
+    const users = await userModel.find({_id: {$in: userObjectIDs}}, 'real_name mobile company').lean();
+    users.forEach(user => {
+        const matched = userMap[user._id];
+        if (matched) {
+            user.index = matched.index;
+        }
+    });
+    users.sort((a, b) => a.index - b.index);
+    return users;
+}
+
 // 获取某用户某费用定额的未读的被分享记录
 async function getUnreadShareListByCompilation(userID, compilationID, isGetCount) {
     const fields = isGetCount ? '-_id projectID ' : '-_id';
@@ -2058,6 +2114,8 @@ async function importProject(importObj, userID, compilationID, overWriteUrl) {
     }
     //给单位工程设置一些数据
     async function setupTender(data) {
+        // 将项目标记为通过接口导入的项目
+        data.importedByInterface = true;
         if (!data.property.decimal) {     
             //小数位数 需要修改,所以深拷贝
             data.property.decimal = JSON.parse(JSON.stringify(defaultDecimal));

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

@@ -58,9 +58,12 @@ module.exports = function (app) {
     pmRouter.post('/recGC', pmController.recGC);
     pmRouter.post('/delGC', pmController.delGC);
     //share
+    pmRouter.post('/getReceiveLibList', pmController.getReceiveLibList);
     pmRouter.post('/getProjectShareInfo', pmController.projectShareInfo);
     pmRouter.post('/getInitialShareData', pmController.getInitialShareData);
+    pmRouter.post('/getInitialShareLibData', pmController.getInitialShareLibData);
     pmRouter.post('/share', pmController.share);
+    pmRouter.post('/shareLib', pmController.shareLib);
     pmRouter.post('/receiveProjects', pmController.receiveProjects);
     pmRouter.post('/changeFile', pmController.changeFile);
     pmRouter.post('/exportProject', pmController.exportProject);

+ 38 - 9
modules/ration_glj/controllers/ration_glj_controller.js

@@ -5,7 +5,8 @@ let mongoose = require("mongoose")
 let ration_glj_facade = require('../facade/ration_glj_facade')
 const EngineeringLibModel = require("../../users/models/engineering_lib_model");
 let logger = require("../../../logs/log_helper").logger;
-const { COMPILATION, COMPLEMENTARY_LIB } = require('../../../public/common_constants');
+const { COMPILATION, COMPLEMENTARY_LIB, ShareLibType } = require('../../../public/common_constants');
+const pmFacade = require('../../pm/facade/pm_facade');
 
 module.exports={
     createRationGLJ:createRationGLJ,
@@ -67,12 +68,18 @@ async function getGLJDataPaging(req, res) {
         const compilationId = req.session.sessionCompilation._id;
         const userID = req.session.sessionUser.id;
         let libData = null;
-        if (condition.gljLibID !== COMPLEMENTARY_LIB) {
+        const cptLibReg = new RegExp(COMPLEMENTARY_LIB);
+        if (!cptLibReg.test(condition.gljLibID)) {
             condition.gljLibID = +condition.gljLibID;
         }
         if (condition.initLibs) {
             libData = await ration_glj_facade.getLibOptionsForCompilation(compilationId);
-            libData.push({ name: '补充工料机', gljLibId: COMPLEMENTARY_LIB });
+            libData.push({ name: '我的补充人材机库', gljLibId: COMPLEMENTARY_LIB });
+            // 设置被分享的人材机库
+            const receiveData = await pmFacade.getReceiveLibList(userID, compilationId, ShareLibType.GLJ_LIB);
+            receiveData.forEach(user => {
+                libData.push({ name: `${user.real_name}的补充人材机库`, gljLibId: `${COMPLEMENTARY_LIB}*${user._id}` });
+            });
             if (condition.gljLibID) { // 替换人材机初始化会触发此条件(初始化库且定位了库)
                 const orgDefalutLib = libData.find(lib => lib.isDefault);
                 const newDefaultLib = libData.find(lib => lib.gljLibId === condition.gljLibID);
@@ -85,9 +92,16 @@ async function getGLJDataPaging(req, res) {
         if (!condition.gljLibID && libData) {
             condition.gljLibID = (libData.find(lib => lib.isDefault) || {}).gljLibId;
         }
-        const info = condition.gljLibID === COMPLEMENTARY_LIB
+        let info;
+        if (cptLibReg.test(condition.gljLibID)) {
+            const owner = condition.gljLibID.split('*')[1] || userID;
+            info = { gljLibId: null, userID: owner, compilationId  };
+        } else {
+            info = { gljLibId: condition.gljLibID, userID: null, compilationId: null };
+        }
+        /* const info = condition.gljLibID === COMPLEMENTARY_LIB
             ? { gljLibId: null, userID, compilationId  }
-            : { gljLibId: condition.gljLibID, userID: null, compilationId: null };
+            : { gljLibId: condition.gljLibID, userID: null, compilationId: null }; */
         result.data = await ration_glj_facade.getGLJDataPaging(info, condition);
         if (req.session.sessionCompilation.priceProperties) {
             result.data.priceProperties = req.session.sessionCompilation.priceProperties
@@ -133,15 +147,23 @@ async function getGLJData(req, res) {
     try {
         let libData = null;
         let { engineerID, gljLibId, isInitial } = req.params;
-        if (gljLibId !== COMPLEMENTARY_LIB) {
+        const userID = req.session.sessionUser.id;
+        const compilationId = req.session.sessionCompilation._id;
+        const cptLibReg = new RegExp(COMPLEMENTARY_LIB);
+        if (!cptLibReg.test(gljLibId)) {
             gljLibId = +gljLibId;
         }
         isInitial = JSON.parse(isInitial);
         if (!gljLibId || isInitial) { // 替换人材机的话,可能存在gljLibID,但是是初始化的操作
             libData = engineerID === COMPILATION 
-            ? await ration_glj_facade.getLibOptionsForCompilation(req.session.sessionCompilation._id)
+            ? await ration_glj_facade.getLibOptionsForCompilation(compilationId)
             : await ration_glj_facade.getLibOptions(engineerID);
             libData.push({ name: '补充工料机', gljLibId: COMPLEMENTARY_LIB });
+            // 设置被分享的人材机库
+            const receiveData = await pmFacade.getReceiveLibList(userID, compilationId, ShareLibType.GLJ_LIB);
+            receiveData.forEach(user => {
+                libData.push({ name: `${user.real_name}的补充人材机库`, gljLibId: `${COMPLEMENTARY_LIB}*${user._id}` });
+            });
             if (gljLibId) { // 替换人材机初始化会触发此条件
                 const orgDefalutLib = libData.find(lib => lib.isDefault);
                 const newDefaultLib = libData.find(lib => lib.gljLibId === gljLibId);
@@ -155,9 +177,16 @@ async function getGLJData(req, res) {
         if (!gljLibId && libData) {
             gljLibId = (libData.find(lib => lib.isDefault) || {}).gljLibId;
         }
-        const info = gljLibId === COMPLEMENTARY_LIB 
+        let info;
+        if (cptLibReg.test(gljLibId)) {
+            const owner = gljLibId.split('*')[1] || userID;
+            info = { gljLibId: null, userID: owner, compilationId  };
+        } else {
+            info = { gljLibId, userID: null, compilationId: null };
+        }
+/*         const info = gljLibId === COMPLEMENTARY_LIB 
             ? { gljLibId: null, userID: req.session.sessionUser.id, compilationId: req.session.sessionCompilation._id }
-            : { gljLibId, userID: null, compilationId: null };
+            : { gljLibId, userID: null, compilationId: null }; */
         info.skipGLJ = skipGLJ;
         ration_glj_facade.getGLJData(info,function (err,datas) {
             if(err){

+ 5 - 1
modules/ration_glj/facade/ration_glj_facade.js

@@ -188,6 +188,9 @@ function createNewRecord(ration_glj) {
     newRecoed.repositoryId = ration_glj.repositoryId;
     newRecoed.projectGLJID = ration_glj.projectGLJID;
     newRecoed.adjCoe = ration_glj.adjCoe;
+    if (ration_glj.fromUser) {
+        newRecoed.fromUser = ration_glj.fromUser;
+    }
     return newRecoed
 }
 
@@ -698,7 +701,8 @@ function getGLJSearchInfo(ration_glj) {
         supply_quantity:0,
         supply:0,
         materialIndexCoe:ration_glj.materialIndexCoe,
-        from: ration_glj.from ? ration_glj.from : 'std'//std:标准工料机库, cpt:补充工料机库
+        from: ration_glj.from ? ration_glj.from : 'std',//std:标准工料机库, cpt:补充工料机库
+        fromUser: ration_glj.fromUser,
     };
     let glj_type_object = glj_type_util.getStdGljTypeCacheObj();
     let type = glj_type_object.getItemById(data.type);

+ 42 - 0
modules/users/controllers/cld_controller.js

@@ -231,6 +231,16 @@ class CLDController {
         }
     }
 
+    async provinceList(request, response) {
+        let userModel = new UserModel();
+        let responseData = {
+            error: 0,
+            msg: '',
+            data: userModel.province,
+        };
+        response.json(responseData);
+    }
+
     /**
      * 获取用户列表分页
      * @param request
@@ -295,6 +305,10 @@ class CLDController {
             // 获取用户列表
             userList = await userModel.getList(condition, page, pageSize, sort);
             await online_facade.setOnlineTimes(userList,condition);
+            userList = JSON.parse(unescape(escape(JSON.stringify(userList))));
+            for (const userData of userList) {
+                userData.province = userData.province !== -1 ? userModel.province[userData.province] : '';
+            }
             response.json({error: 0, msg: 'success', data: {
                     userInfo: userList,
                     pageData: pageData,
@@ -322,6 +336,34 @@ class CLDController {
         }
         response.json(responseData);
     }
+
+    async getRejectUsers(request, response) {
+        let userModel = new UserModel();
+        let total = 0;
+        let pageSize = 12;
+        let pageData = {};
+        let userList = [];
+        try {
+            const condition = {
+                user_type: 'normal',
+                is_cld: {$exists:true, $ne: 0}
+            };
+            // 获取用户总数
+            total = await userModel.count(condition);
+            // 分页数据
+            let page = request.query.page === undefined ? 1 : request.query.page;
+            pageSize = request.query.pageSize === undefined ? 12 : parseInt(request.query.pageSize);
+            userList = await userModel.getList(condition, page, pageSize, {is_cld:-1});
+            pageData = {
+                current: page,
+                total: total,
+                queryData: response.locals.urlQuery
+            };
+            response.json({error: 0, msg: 'success', data: { userList: userList, pageData: pageData }});
+        } catch (error) {
+            response.json({error: 1, msg: error});
+        }
+    }
 }
 
 module.exports = CLDController;

+ 8 - 1
modules/users/models/user_model.js

@@ -493,6 +493,13 @@ class UserModel extends BaseModel {
             condition.online_times = this.getOnlineTimestamp(onlineTimes);
         }
 
+        // 企业所在地区
+        let province = request.query.province;
+        province = province !== '' && province !== undefined ? parseInt(province) - 1 : -1;
+        if (province !== -1) {
+            condition.province = province;
+        }
+
         let keyword = request.query.keyword;
         if (keyword !== '' && keyword !== undefined) {
             condition.$or = [{real_name : {$regex: keyword}},{email : {$regex: keyword}},{mobile : {$regex: keyword}},{qq : {$regex: keyword}},{company : {$regex: keyword}}];
@@ -551,7 +558,7 @@ class UserModel extends BaseModel {
             default :
                 break;
         }
-        return startTime === '' ? '' : {'$gte': startTime};
+        return startTime === 0 ? '' : {'$gte': startTime};
     }
 
     /**

+ 4 - 0
modules/users/routes/cld_route.js

@@ -30,5 +30,9 @@ module.exports = function (app) {
 
     router.post('/getUserOnlineInfo', cldController.getUserOnlineInfo);
 
+    router.get('/getRejectUsers', cldController.getRejectUsers);
+
+    router.get('/provinceList', cldController.provinceList);
+
     app.use('/cld',router)
 };

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 921 - 883
package-lock.json


+ 2 - 2
package.json

@@ -34,8 +34,8 @@
     "gulp-load-plugins": "^0.10.0",
     "gulp-plumber": "^1.0.1",
     "gulp-size": "^1.2.1",
-    "gulp-uglify": "^1.1.0",
-    "gulp-uglify-es": "^0.1.3",
+    "gulp-uglify": "^3.0.2",
+    "gulp-uglify-es": "^2.0.0",
     "iconv-lite": "^0.5.1",
     "ioredis": "^3.1.4",
     "jszip": "^3.1.3",

+ 9 - 1
public/common_constants.js

@@ -176,6 +176,13 @@
         SHARE_CANCEL: 2,
     };
 
+    // 分享库类型
+    const ShareLibType = {
+        RATION_LIB: 1,
+        GLJ_LIB: 2,
+        BLOCK_LIB: 3,
+    };
+
     return {
         fixedFlag,
         billType,
@@ -191,6 +198,7 @@
         StorageKey,
         SharePermissionChangeType,
         PageTarget,
-        BlankType
+        BlankType,
+        ShareLibType,
     };
 });

BIN
public/static/示例1-分部分项工程项目清单.xlsx


public/static/分部分项工程项目清单计价表.xlsx → public/static/示例2-分部分项工程项目清单计价表.xlsx


+ 4 - 1
public/web/id_tree.js

@@ -738,11 +738,14 @@ var idTree = {
             } else {
                 this.roots.splice(node.siblingIndex(), 1);
             }
+         
+            if(node.preSibling) node.preSibling.setNextSibling(node.nextSibling);//后兄弟设置为前兄弟的后兄弟 
+           
             if(node.children.length>0){
                 if(node.preSibling){//子项变成前兄弟的子项
                     for(let c of node.children){
                         node.preSibling.addChild(c);
-                    }
+                    } 
                 }else if(node.nextSibling){//没有前兄弟,有后兄弟
                     let oldChild = node.parent.children;
                     node.parent.children = [];

+ 63 - 14
public/web/sheet/sheet_common.js

@@ -499,9 +499,9 @@ var sheetCommonObj = {
         return c
     },
     // 无法勾选的复选框
-    getReadOnlyCheckBox () {
+    getReadOnlyCheckBox (threeState = false) {
         function ReadOnlyCheckBox() {}
-        ReadOnlyCheckBox.prototype = new GC.Spread.Sheets.CellTypes.CheckBox();
+        ReadOnlyCheckBox.prototype = this.getCheckBox(threeState);
         ReadOnlyCheckBox.prototype.processMouseUp = function () {
             return;
         };
@@ -685,7 +685,6 @@ var sheetCommonObj = {
         }
         //sheet.setCellType(row, col,this.getSelectButton(header.headerWidth),GC.Spread.Sheets.SheetArea.viewport);
     },
-
     getCusButtonCellType: function (callback, readOnly = false, ostyle) {
         var ns = GC.Spread.Sheets;
 
@@ -701,10 +700,12 @@ var sheetCommonObj = {
                     var imageWidth = 25;
                     var imageX = x + w - imageWidth - imageMagin, imageY = y + h / 2 - imageHeight / 2;
                     ctx.save();
+                   /* 2020-12-16 这里原先是还原背景色的,发现不需要了,先观察 
                     if (style.backColor) {
                         ctx.fillStyle = style.backColor;
                         ctx.fillRect(x, y, w, h);
-                    }
+                    } 
+                    */
                     ctx.drawImage(image, imageX, imageY, imageWidth, imageHeight);
                     ctx.beginPath();
                     ctx.arc(imageX + imageWidth / 2, imageY + imageHeight / 2, 1, 0, 360, false);
@@ -990,13 +991,13 @@ var sheetCommonObj = {
         };
         return new ComboCellForActiveCell();
     },
-    getTipsCombo:function (forLocked,tips,setting,node) {
-        let getTipsCombo = function () {
+    getTipsCell: function (baseCell, cellPrototype, tips, setting, node) {
+        let getTipsCell = function () {
             this.clickCom=false;
         };
-        getTipsCombo.prototype = sheetCommonObj.getDynamicCombo(forLocked);
+        getTipsCell.prototype = baseCell;
         if(tips && tips !=""){
-            getTipsCombo.prototype.processMouseEnter = function(hitinfo){
+            getTipsCell.prototype.processMouseEnter = function(hitinfo){
                 if(this.clickCom == true){ //点击了下拉框的三角形,则不用再显示悬浮框了
                     this.clickCom = false;
                     return;
@@ -1004,23 +1005,43 @@ var sheetCommonObj = {
                 let text =  typeof tips == 'function'?tips(node):tips;
                 TREE_SHEET_HELPER.delayShowTips(hitinfo,setting,text);
             };
-            getTipsCombo.prototype.processMouseLeave = function (hitinfo) {
+            getTipsCell.prototype.processMouseLeave = function (hitinfo) {
                 TREE_SHEET_HELPER.hideTipsDiv();
             };
-            getTipsCombo.prototype.processMouseDown = function (hitinfo){
+            getTipsCell.prototype.processMouseDown = function (hitinfo){
                 if(hitinfo.isReservedLocation == true){//这里是点击了下拉框的三角形才会有这个属性
                     TREE_SHEET_HELPER.hideTipsDiv();
                     this.clickCom = true;
                 }
-                GC.Spread.Sheets.CellTypes.ComboBox.prototype.processMouseDown.apply(this, arguments);
+                cellPrototype.processMouseDown.apply(this, arguments);
             };
 
-            getTipsCombo.prototype.updateEditor = function (editorContext, cellStyle, cellRect, context){
+            getTipsCell.prototype.updateEditor = function (editorContext, cellStyle, cellRect, context){
                 TREE_SHEET_HELPER.hideTipsDiv();
-                GC.Spread.Sheets.CellTypes.ComboBox.prototype.updateEditor.apply(this, arguments);
+                cellPrototype.updateEditor.apply(this, arguments);
             };
         }
-        return new getTipsCombo();
+        return new getTipsCell();
+    },
+    getTipsText: function (tips, setting, node) {
+        function baseTextCell() {}
+        baseTextCell.prototype = new GC.Spread.Sheets.CellTypes.Text();
+        baseTextCell.prototype.getHitInfo =  function (x, y, cellStyle, cellRect, context) {
+            return {
+                x: x,
+                y: y,
+                row: context.row,
+                col: context.col,
+                cellStyle: cellStyle,
+                cellRect: cellRect,
+                sheetArea: context.sheetArea
+            };
+        };
+        return this.getTipsCell(new baseTextCell(), GC.Spread.Sheets.CellTypes.Text.prototype, tips, setting, node);
+    },
+    getTipsCombo:function (forLocked,tips,setting,node) {
+        const baseCell = sheetCommonObj.getDynamicCombo(forLocked);
+        return this.getTipsCell(baseCell, GC.Spread.Sheets.CellTypes.ComboBox.prototype, tips, setting, node);
     },
     // paintFunc,需要追加到paint方法中的自定义paint方法
     getTreeNodeCellType:function (datas,row,parentMap,paintFunc) {// 2018-09-26  不用spreadjs默认的树结构,自定义控件
@@ -1497,6 +1518,34 @@ var sheetCommonObj = {
         }
         return result;
     },
+    disableSpread(spread) {
+        spread.unbind(GC.Spread.Sheets.Events.ButtonClicked);
+        let sheetCount = spread.getSheetCount();
+        for(let i = 0; i < sheetCount; i++){
+            let sheet = spread.getSheet(i);
+            sheet.unbind(GC.Spread.Sheets.Events.ButtonClicked);
+            sheet.unbind(GC.Spread.Sheets.Events.EditStarting);
+            sheet.unbind(GC.Spread.Sheets.Events.EditEnded);
+            sheet.unbind(GC.Spread.Sheets.Events.RangeChanged);
+            sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanging);
+            sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanged);
+            sheet.unbind(GC.Spread.Sheets.Events.CellDoubleClick);
+            sheet.unbind(GC.Spread.Sheets.Events.CellClick);
+            sheet.unbind(GC.Spread.Sheets.Events.ValueChanged);
+            sheet.suspendPaint();
+            sheet.suspendEvent();
+            sheet.options.isProtected = true;
+            let rowCount = sheet.getRowCount();
+            let colCount = sheet.getColumnCount();
+            for(let row = 0; row < rowCount; row++){
+                for(let col = 0; col < colCount; col++){
+                    sheet.getCell(row, col).locked(true);
+                }
+            }
+            sheet.resumePaint();
+            sheet.resumeEvent();
+        }
+    },
     // 延迟一段时间刷新表格,因为有的弹窗里面有表格,马上刷新可能会造成,弹窗界面还未完全显示完就完成了表格刷新,导致表格显示不完整
     refreshWorkbookDelDefer(workbook, time) {
         if (workbook) {

+ 9 - 3
public/web/tree_sheet/tree_sheet_helper.js

@@ -166,6 +166,8 @@ var TREE_SHEET_HELPER = {
                             tag = node.data.adjustState?node.data.adjustState:'';
                         }
                         if(tag!=null) sheet.setTag(iRow, iCol,tag);
+                    } else if (colSetting) {
+
                     }
                     // 单元格字体颜色
                     const foreColorFunc = MainTreeCol.foreColor[colSetting.data.field];
@@ -176,8 +178,7 @@ var TREE_SHEET_HELPER = {
                 if(colSetting.visible == false) return;//隐藏列不做其它操作
                 if (colSetting.data.getText && Object.prototype.toString.apply(colSetting.data.getText) === "[object Function]") {
                     cell.value(colSetting.data.getText(node));
-                }else if((colSetting.data.field=="mainBills"||(colSetting.data.field=="outPutMaxPrice" && $("#fileKind").val() != '1'))&&MainTreeCol.mainBillsEnable(node)){//主要清单有三种状态,所以直接显示就好,不走最后的逻辑
-                   //outPutMaxPrice 对于投标项目 即 fileKind = 1 时只读,不进这个逻辑
+                }else if((colSetting.data.field=="mainBills"||(['outPutMaxPrice', 'outPutLimitPrice'].includes(colSetting.data.field)))&&MainTreeCol.mainBillsEnable(node)){//主要清单有三种状态,所以直接显示就好,不走最后的逻辑
                     cell.value(node.data[colSetting.data.field]===undefined?false:node.data[colSetting.data.field]);
                 } else {
                     cell.value(getFieldText2());
@@ -190,6 +191,9 @@ var TREE_SHEET_HELPER = {
                 if(colSetting.data.autoHeight == true){
                     colSetting.setAutoHeight(cell,node);
                 }
+                //主界面分部名称点击弹窗    
+                if(sheet.name()=="mainSheet" && colSetting.data.field=="name") MainTreeCol.setNameCusButton(cell,node);   
+
                 if(colSetting.editChecking&&colSetting.editChecking(node,colSetting.data.field)){
                     cell.locked(true);
                 }else if (colSetting.readOnly) {
@@ -312,6 +316,8 @@ var TREE_SHEET_HELPER = {
         let halfExpandLength = 3;
     
         let isRationNode = sheet.name()=="mainSheet" && initNode.sourceType == ModuleNames.ration&&initNode.data.type == rationType.ration;
+
+
         let questionImg = document.getElementById('question_pic'),
             questionImgWidth = 16,
             questionImgHeight = 16;
@@ -329,6 +335,7 @@ var TREE_SHEET_HELPER = {
             } else {
                 ctx.clearRect(x, y, w, h);
             }
+
             // ������(x1, y1)���(��, ��), (x2, y2)�յ�(��, ��), ��ɫ
             let drawLine = function (canvas, x1, y1, x2, y2, color) {
                 ctx.save();
@@ -437,7 +444,6 @@ var TREE_SHEET_HELPER = {
                 let centerY = Math.floor((y + (y + h)) / 2);
                 ctx.drawImage(questionImg, centerX + 3, centerY - 7, questionImgWidth,questionImgHeight);
             }
-
         };
         TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
             return {

+ 11 - 4
web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html

@@ -27,6 +27,11 @@
     <script type="text/javascript">
         let priceProperties = JSON.parse('<%- priceProperties %>');
         let consumeAmtProperties = JSON.parse('<%- consumeAmtProperties %>');
+        let userId = "<%= userID%>";
+        const userID = "<%= userID%>";
+        let compilationId = "<%= compilationId%>";
+        let stdGljLibId = "<%= gljLibId%>";//用户当前编办下的标准工料机库ID,目前认为一个编办只有一个标准工料机库
+        const isReadOnly = JSON.parse('<%= isReadOnly %>');
     </script>
 </head>
 <body>
@@ -150,6 +155,7 @@
             </div>
         </div>
     </div>
+    <%include ../../../common/components/share/index.html %>
     <!-- JS. -->
     <script src="/lib/jquery-contextmenu/jquery.contextMenu.min.js"></script>
     <script src="/lib/jquery-contextmenu/jquery.ui.position.js"></script>
@@ -160,6 +166,9 @@
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.exedit.js"></script>
     <!--inject:js-->
     <script src="/web/building_saas/js/global.js"></script>
+    <script src="/lib/pinyinjs/pinyin_dict_firstletter.js"></script>
+    <script src="/lib/pinyinjs/pinyinUtil.js"></script>
+    <script src="/web/common/components/share/index.js"></script>
     <!-- zTree -->
     <script type="text/javascript" src="/public/web/common_ajax.js"></script>
     <script type="text/javascript" src="/public/web/treeDataHelper.js"></script>
@@ -170,6 +179,7 @@
     <script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_controller.js"></script>
     <script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_helper.js"></script>
     <script type="text/javascript" src="/public/web/ration_glj_units.js"></script>
+    <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/init.js"></script>
     <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/glj.js"></script>
     <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/gljClassTree.js"></script>
     <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/gljComponent.js"></script>
@@ -182,9 +192,6 @@
     <!--endinject-->
     <script type="text/javascript" src="<%= overWriteUrl %>"></script>
     <SCRIPT type="text/javascript">
-        let userId = "<%= userID%>";
-        let compilationId = "<%= compilationId%>";
-        let stdGljLibId = "<%= gljLibId%>";//用户当前编办下的标准工料机库ID,目前认为一个编办只有一个标准工料机库
         let gljSetting = {
             view: {
                 //addHoverDom: gljTypeTreeOprObj.addHoverDom,
@@ -262,7 +269,7 @@
             $('#componentTreeDiv').height($(window).height() - 300);
             $("#componentSheet").height($("#componentTreeDiv").height() - 15);
             $("#componentSheet").width($('#modalCon').width() * 0.63); */
-            pageOprObj.initPage($("#GLJListSheet")[0], $('#gljComponentSheet')[0], $("#componentSheet")[0]);
+            //pageOprObj.initPage($("#GLJListSheet")[0], $('#gljComponentSheet')[0], $("#componentSheet")[0]);
         });
         //组成物弹出窗大小设置
         /* $(window).resize(function () {

+ 12 - 6
web/building_saas/complementary_glj_lib/js/glj.js

@@ -297,15 +297,18 @@ let repositoryGljObj = {
             }
         });
         distTypeTree.distTypesArr.forEach(function (distTypeObj) {
-            if (distTypeObj.data.fullName !== '材料' && distTypeObj.data.fullName !== '机械') {
-                distTypeTree.comboDatas.push({ text: distTypeObj.data.fullName, value: distTypeObj.data.ID });
+            if(allowGljType.includes(distTypeObj.data.ID)){
+                distTypeTree.comboDatas.push({text: distTypeObj.data.fullName, value: distTypeObj.data.ID});
             }
+            /* if (distTypeObj.data.fullName !== '材料' && distTypeObj.data.fullName !== '机械') {
+                distTypeTree.comboDatas.push({ text: distTypeObj.data.fullName, value: distTypeObj.data.ID });
+            } */
         });
         return distTypeTree;
     },
     getGljDistType: function (callback) {
         let me = this;
-        CommonAjax.post('complementartGlj/api/getGljDistType', {}, function (rstData) {
+        CommonAjax.post('/complementartGlj/api/getGljDistType', {}, function (rstData) {
             if (callback) {
                 me.distTypeTree = me.getComboData(rstData);
                 console.log(me.distTypeTree);
@@ -315,7 +318,7 @@ let repositoryGljObj = {
     },
     getGljTree: function (gljLibId, callback) {
         let me = this;
-        CommonAjax.post('complementartGlj/api/getGljTree', { gljLibId: gljLibId }, function (rstData) {
+        CommonAjax.post('/complementartGlj/api/getGljTree', { gljLibId: gljLibId }, function (rstData) {
             zTreeHelper.createTree(rstData, gljSetting, "repositoryTree", me);
             zTreeHelper.createTree(rstData, componentSetting, "componentTree", componentOprObj);
             if (rstData && rstData.length > 0) {
@@ -461,6 +464,9 @@ let repositoryGljObj = {
     buildSheet: function (container) {
         let me = repositoryGljObj;
         me.workBook = sheetOpr.buildSheet(container, me.setting, 30);
+        if (isReadOnly) {
+            sheetCommonObj.disableSpread(me.workBook);
+        }
         sheetCommonObj.spreadDefaultStyle(me.workBook);
         me.repositoryGljDelOpr();
         me.onContextmenuOpr();
@@ -1052,7 +1058,7 @@ let repositoryGljObj = {
                             "delete": {
                                 name: "删除",
                                 disabled: function () {
-                                    return !(me.currentCache && me.currentCache[target.row]);
+                                    return isReadOnly || !(me.currentCache && me.currentCache[target.row]);
                                 },
                                 icon: "fa-remove",
                                 callback: function (key, opt) {
@@ -1468,7 +1474,7 @@ let repositoryGljObj = {
         if (addArr.length > 0) {
             me.saveInString(addArr);
         }
-        let url = 'complementartGlj/api/mixUpdateGljItems';
+        let url = '/complementartGlj/api/mixUpdateGljItems';
         let post = { updateItems: updateArr, addItems: addArr, removeIds: removeIds };
         let scCaller = function (rstData) {
             me.updateCache(addArr, updateArr, removeIds, rstData);

+ 1 - 1
web/building_saas/complementary_glj_lib/js/gljClassTree.js

@@ -149,7 +149,7 @@ let gljClassTreeObj = {
     },
 
     gljClassTreeAjax: function (postData, scFunc, errFunc) {
-        CommonAjax.post('api/updateNodes', {updateData: postData, lastOpr: userAccount}, scFunc, errFunc);
+        CommonAjax.post('/complementartGlj/api/updateNodes', {updateData: postData, lastOpr: userAccount}, scFunc, errFunc);
     },
     //模仿默认点击
     initSelection: function (node) {

+ 6 - 4
web/building_saas/complementary_glj_lib/js/gljComponent.js

@@ -100,7 +100,9 @@ let gljComponentOprObj = {
         me.workBook.getSheet(0).setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
         me.workBook.getSheet(0).setFormatter(-1, 0, "@", GC.Spread.Sheets.SheetArea.viewport);
         sheetOpr.cleanData(me.workBook.getSheet(0), me.setting, -1);
-
+        if (isReadOnly) {
+            sheetCommonObj.disableSpread(me.workBook);
+        }
         me.onContextmenuOpr();//右键菜单
         me.gljComponentDelOpr();
         me.workBook.getSheet(0).bind(GC.Spread.Sheets.Events.EditStarting, me.onCellEditStart);
@@ -228,7 +230,7 @@ let gljComponentOprObj = {
                         callback: function () { },
                         items: {
                             "insert": {
-                                name: "插入", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
+                                name: "插入", disabled: isReadOnly || insertDis, icon: "fa-sign-in", callback: function (key, opt) {
                                     let oprFunc = function () {
                                         //默认radio所有工料机
                                         co.initRadio();
@@ -246,7 +248,7 @@ let gljComponentOprObj = {
                                 }
                             },
                             "delete": {
-                                name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
+                                name: "删除", disabled: isReadOnly || delDis, icon: "fa-remove", callback: function (key, opt) {
                                     //删除
                                     let deleteObj = that.currentComponent[target.row];
                                     let gljComponent = that.currentGlj.component;
@@ -518,7 +520,7 @@ let gljComponentOprObj = {
         that.saveInString(updateArr);
         $.ajax({
             type: 'post',
-            url: 'complementartGlj/api/updateComponent',
+            url: '/complementartGlj/api/updateComponent',
             data: { "userId": pageOprObj.userId, "updateArr": JSON.stringify(updateArr) },
             dataType: 'json',
             success: function (result) {

+ 124 - 0
web/building_saas/complementary_glj_lib/js/init.js

@@ -0,0 +1,124 @@
+const GLJ_INIT = (() => {
+  const params = location.href.split('/');
+  const curUserID = params[params.length - 1];
+  console.log('curUserID', curUserID);
+
+  // 头部按钮相关
+  function initHeaderTools(receiveList, shareList) {
+    // 设置下拉项
+    const selectHtml = `
+    <select class="form-control  form-control-sm" id="glj-libs">
+    </select>`;
+    // 设置分享按钮
+    const shareHtml = `
+    <span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" data-original-title="">
+        <a id="init-share" href="javascript:;" class="btn btn-xs btn-primary ${isReadOnly ? 'disabled' : ''}"><i class="fa fa-share-alt"></i> 分享</a>
+    </span>`;
+    const html = `
+        <div style="display: flex; align-items: center">
+            ${selectHtml}
+            ${shareHtml}
+        </div>
+    `
+    $('#fullpath').html(html);
+    initShareTip(shareList);
+    $('[data-toggle="tooltip"]').tooltip();
+    initLibOptions(receiveList);
+    // 分享给
+    SHARE_TO.handleEventListener();
+    $('#init-share').click(() => {
+      SHARE_TO.initModal(SHARE_TO.Mode.GLJ_LIB);
+      $('#share-tip').tooltip('hide');
+    });
+
+    // 库下拉
+    function initShareTip(shareList) {
+      if (shareList.length) {
+        const owners = shareList.map(owner => owner.real_name);
+        const tips = `已分享给 ${owners.join(' ')}`;
+        $('#share-tip').attr('data-original-title', tips);
+      }
+    }
+
+    // 库下拉
+    function initLibOptions(receiveList) {
+      const libList = [
+        `<option value="${userID}">我的补充人材机库</option>`
+      ];
+      const options = receiveList.map(owner => `<option value=${owner._id} ${curUserID === owner._id ? 'selected' : ''}>${owner.real_name}的补充人材机库</option>`);
+      libList.push(...options);
+      $('#glj-libs').html(libList.join(''))
+    }
+
+    $('#glj-libs').change(function () {
+      const userID = $(this).val();
+      window.location.href = `/complementaryGlj/${userID}`;
+    });
+  }
+
+  $(document).ready(() => {
+    $.bootstrapLoading.start();
+    const projection = {
+      _id: 0,
+      ID: 1,
+      code: 1,
+      unit: 1,
+      name: 1,
+      priceProperty: 1,
+      basePrice: 1
+    };
+    CommonAjax.post('/complementartGlj/api/initData', { projection, userID: curUserID }, res => {
+        if (res.error) {
+            alert('数据初始化失败,请重试。');
+            setTimeout(() => {
+                window.location.href = '/'
+            }, 1200);
+        } else {
+          const { receiveLibs, shareLibs, gljItems, mixTree, distTypeData } = res;
+          initHeaderTools(receiveLibs, shareLibs);
+          pageOprObj.stdGljLibId = stdGljLibId;
+          repositoryGljObj.currentRepositoryId = stdGljLibId;
+          pageOprObj.userId = userId;
+          pageOprObj.compilationId = compilationId;
+          //生成人材机表格列头
+          repositoryGljObj.setting.header = repositoryGljObj.initHeaders(priceProperties);
+          //生成列映射
+          sheetCommonObj.initColMapping(repositoryGljObj, repositoryGljObj.setting.header);
+          //生成单价列下标数组
+          repositoryGljObj.initPriceCols(priceProperties, repositoryGljObj.colMapping);
+          repositoryGljObj.buildSheet($("#GLJListSheet")[0]);
+          gljComponentOprObj.buildSheet($('#gljComponentSheet')[0]);
+          componentOprObj.buildSheet($("#componentSheet")[0]);
+          // distTypeTree
+          repositoryGljObj.distTypeTree = repositoryGljObj.getComboData(distTypeData);
+          // tree
+          gljClassTreeObj.treeData = mixTree;
+          if (mixTree.comple && mixTree.comple.length > 0) {
+            gljClassTreeObj.gljCurTypeId = mixTree.comple[0].ID;
+          }
+          gljClassTreeObj.buildSheet();
+          gljClassTreeObj.initTree(mixTree.comple);
+          gljClassTreeObj.cache = gljClassTreeObj.tree.items;
+          repositoryGljObj.updateParentNodeIds(gljClassTreeObj.cache, repositoryGljObj);
+          gljClassTreeObj.initController(gljClassTreeObj.tree, gljClassTreeObj.sheet, gljClassTreeObj.setting.sheet);
+          gljClassTreeObj.controller.showTreeData();
+          gljClassTreeObj.sheet.setFormatter(-1, 0, '@');
+          // gljItems
+          repositoryGljObj.stdGljList = gljItems.stdGljs;
+          // 兼容多单价情况
+          for (const sGlj of repositoryGljObj.stdGljList) {
+              if (sGlj.priceProperty && typeof sGlj.priceProperty.price1 !== 'undefined') {
+                  sGlj.basePrice = sGlj.priceProperty.price1;
+              }
+          }
+          repositoryGljObj.sortGlj(repositoryGljObj.stdGljList);
+          repositoryGljObj.setProp('isStd', true, repositoryGljObj.stdGljList);
+          repositoryGljObj.complementaryGljList = gljItems.complementaryGljs;
+          repositoryGljObj.sortGlj(repositoryGljObj.complementaryGljList);
+          gljClassTreeObj.initSelection(gljClassTreeObj.tree.selected);
+        }
+        $.bootstrapLoading.end();
+    });
+  });
+
+})();

+ 5 - 0
web/building_saas/complementary_ration_lib/html/dinge.html

@@ -56,6 +56,7 @@
         }
     </style>
     <script>
+        const isReadOnly = JSON.parse('<%= isReadOnly %>');
         const gljLibId = '<%= gljLibId %>';
         const userID = '<%=userID %>';
         let priceProperties = JSON.parse('<%- priceProperties %>');
@@ -604,6 +605,7 @@
         </div>
     </div>
 </div>
+<%include ../../../common/components/share/index.html %>
 <!-- JS. -->
 <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
 <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
@@ -613,6 +615,9 @@
 <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.ui.position.js"></script>
 <script type="text/javascript" src="/lib/ztree/jquery.ztree.core.js"></script>
 <script type="text/javascript" src="/web/building_saas/complementary_ration_lib/js/global.js"></script>
+<script src="/lib/pinyinjs/pinyin_dict_firstletter.js"></script>
+<script src="/lib/pinyinjs/pinyinUtil.js"></script>
+<script src="/web/common/components/share/index.js"></script>
 <script type="text/javascript" src="/public/web/id_tree.js"></script>
 <script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_controller.js"></script>
 <script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_helper.js"></script>

+ 9 - 2
web/building_saas/complementary_ration_lib/js/coe.js

@@ -173,6 +173,10 @@ var pageObj = {
         let me = this;
         coeOprObj.buildSheet($('#mainSpread')[0]);
         gljAdjOprObj.buildSheet($('#contentSpread')[0]);
+        if (isReadOnly) {
+            sheetCommonObj.disableSpread(coeOprObj.workBook);
+            sheetCommonObj.disableSpread(gljAdjOprObj.workBook);
+        }
         coeOprObj.getCoeList();
         //gljAdjOprObj.getGljItemsOcc();
 
@@ -452,9 +456,12 @@ let coeOprObj = {
     },
     getCoeList: function () {
         let me = coeOprObj;
+        const params = location.href.split('/');
+        const curUserID = params[params.length - 1];
         $.ajax({
             type: 'post',
-            url: 'api/getCoeList',
+            url: '/complementaryRation/api/getCoeList',
+            data: {data: JSON.stringify({userID: curUserID})},
             dataType: 'json',
             timeout:20000,
             success: function (result) {
@@ -475,7 +482,7 @@ let coeOprObj = {
         let me = coeOprObj;
         $.ajax({
             type:"POST",
-            url:"api/saveCoeList",
+            url:"/complementaryRation/api/saveCoeList",
             data: {data: JSON.stringify({addArr: addArr, updateArr: updateArr, deleteArr: deleteArr})},
             dataType:"json",
             timeout:5000,

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

@@ -247,7 +247,8 @@ let gljSelOprObj = {
         let me = gljSelOprObj;
         //选择改变,数据重新筛选显示
         me.showGljList = [];
-        if (gljLib === commonConstants.COMPLEMENTARY_LIB) {
+        const cptLibReg = new RegExp(commonConstants.COMPLEMENTARY_LIB);
+        if (cptLibReg.test(gljLib)) {
             me.setShowGljList(me.complementaryGljList);
         } else {
             me.setShowGljList(me.stdGljList);

+ 71 - 2
web/building_saas/complementary_ration_lib/js/init.js

@@ -8,9 +8,65 @@
  * @version
  */
 const initialization = (() => {
+    const params = location.href.split('/');
+    const curUserID = params[params.length - 1];
+
+    // 头部按钮相关
+    function initHeaderTools(receiveList, shareList) {
+        // 设置下拉项
+        const selectHtml = `
+        <select class="form-control  form-control-sm" id="ration-libs">
+        </select>`;
+        // 设置分享按钮
+        const shareHtml = `
+        <span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" data-original-title="">
+            <a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a>
+        </span>`;
+        const html = `
+            <div style="display: flex; align-items: center">
+                ${selectHtml}
+                ${shareHtml}
+            </div>
+        `
+        $('#fullpath').html(html);
+        initShareTip(shareList);
+        $('[data-toggle="tooltip"]').tooltip();
+        initLibOptions(receiveList);
+        // 分享给
+        SHARE_TO.handleEventListener();
+        $('#init-share').click(() => {
+            SHARE_TO.initModal(SHARE_TO.Mode.RATION_LIB);
+            $('#share-tip').tooltip('hide');
+        });
+
+        // 定额库下拉
+        function initShareTip(shareList) {
+            if (shareList.length) {
+                const owners = shareList.map(owner => owner.real_name);
+                const tips = `已分享给 ${owners.join(' ')}`;
+                $('#share-tip').attr('data-original-title', tips);
+            }
+        }
+
+        // 定额库下拉
+        function initLibOptions(receiveList) {
+            const libList = [
+                `<option value="${userID}">我的补充定额库</option>`
+            ];
+            const options = receiveList.map(owner => `<option value=${owner._id} ${curUserID === owner._id ? 'selected' : ''}>${owner.real_name}的补充定额库</option>`);
+            libList.push(...options);
+            $('#ration-libs').html(libList.join(''))
+        }
+
+        $('#ration-libs').change(function () {
+            const userID = $(this).val();
+            window.location.href = `/complementaryRation/ration/${userID}`;
+        });
+    }
+
     $(document).ready(function () {
         $.bootstrapLoading.start();
-        CommonAjax.get('/complementaryRation/api/initData', {}, res => {
+        CommonAjax.get('/complementaryRation/api/initData', { userID: curUserID }, res => {
             if (res.error) {
                 alert('数据初始化失败,请重试。');
                 setTimeout(() => {
@@ -18,6 +74,7 @@ const initialization = (() => {
                 }, 1200);
             } else {
                 const data = res.data;
+                initHeaderTools(data.receiveLibs, data.shareLibs);
                 pageOprObj.rationTreeData = data.rationTreeData;
                 pageOprObj.mixedTreeData = data.mixedTreeData;
                 pageOprObj.mixedGLJData = data.mixedGLJData;
@@ -28,6 +85,7 @@ const initialization = (() => {
                 var rdSpread = sheetCommonObj.createSpread($("#rdSpread")[0], 4);
                 rdSpread.options.allowUserDragFill = false;
                 rdSpread.options.allowUserDragDrop = false;
+                rdSpread.options.allowExtendPasteRange = true;
                 sheetCommonObj.spreadDefaultStyle(rdSpread);
                 rationGLJOprObj.buildSheet(rdSpread.getSheet(0));
                 rationAssistOprObj.buildSheet(rdSpread.getSheet(1));
@@ -42,7 +100,18 @@ const initialization = (() => {
                 sheetCommonObj.bindEscKey(rdSpread, rdSpreadEscSheets);
                 gljAdjOprObj.gljList = data.mixedGLJData.stdGljs.concat(data.mixedGLJData.complementaryGljs);
                 pageOprObj.initPage();
-
+                if (isReadOnly) {
+                    sheetCommonObj.disableSpread(sectionTreeObj.workBook);
+                    sheetCommonObj.disableSpread(rationOprObj.workBook);
+                    sheetCommonObj.disableSpread(rdSpread);
+                    $('#tree_Insert').addClass('disabled');
+                    $('#tree_remove').addClass('disabled');
+                    $('#tree_upLevel').addClass('disabled');
+                    $('#tree_downLevel').addClass('disabled');
+                    $('#tree_downMove').addClass('disabled');
+                    $('#tree_upMove').addClass('disabled');
+                    $('#init-share').addClass('disabled');
+                }
                 $("#linkGLJ").click(function(){
                     rationGLJOprObj.bindRationGljDelOpr();
                     rdSpread.setActiveSheetIndex(0);

+ 5 - 5
web/building_saas/complementary_ration_lib/js/installation.js

@@ -339,7 +339,7 @@ let feeItemObj = {
     },
     getInstallation: function (callback) {
         let me = this;
-        CommonAjax.post('api/getInstallation', {}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/getInstallation', {}, function (rstData) {
             me.cache = rstData;
             if(callback){
                 callback(rstData);
@@ -348,7 +348,7 @@ let feeItemObj = {
     },
     updateFeeItem: function (updateData, callback) {
         let me = this;
-        CommonAjax.post('api/updateFeeItem', {updateData: updateData}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/updateFeeItem', {updateData: updateData}, function (rstData) {
             if(callback){
                 callback();
             }
@@ -577,7 +577,7 @@ let sectionObj = {
         me.workBook.commandManager().setShortcutKey('sectionDel', GC.Spread.Commands.Key.del, false, false, false, false);
     },
     updateSection: function (updateData, callback) {
-        CommonAjax.post('api/updateInstallSection', {updateData: updateData}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/updateInstallSection', {updateData: updateData}, function (rstData) {
             if(callback){
                 callback();
             }
@@ -1140,7 +1140,7 @@ let batchSectionObj = {
     },
     getSectionTree: function () {
         let me = this;
-        let url = 'api/getRationTree';
+        let url = '/complementaryRation/api/getRationTree';
         //type:0补充定额库,1标准定额库
         let postData = {type: 0};
         let sucFunc = function (rstData) {
@@ -1258,7 +1258,7 @@ let batchSectionObj = {
     },
     //inst: 定额安装费用, rationSection: 定额章节树IDs
     batchUpdate: function (feeItemId, sectionId, rationSection) {
-        CommonAjax.post('api/batchUpdateInst', {inst: {feeItemId: feeItemId, sectionId: sectionId}, rationSection: rationSection}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/batchUpdateInst', {inst: {feeItemId: feeItemId, sectionId: sectionId}, rationSection: rationSection}, function (rstData) {
             $('#sectionTreeModal').modal('hide');
         });
     }

+ 2 - 2
web/building_saas/complementary_ration_lib/js/jobContent.js

@@ -384,7 +384,7 @@ let jobContentOprObj = {
         let me = jobContentOprObj;
         $.ajax({
             type: 'post',
-            url: 'api/updateJobContent',
+            url: '/complementaryRation/api/updateJobContent',
             data: {lastOpr: userAccount, repId: pageOprObj.rationLibId, updateArr: JSON.stringify(updateArr)},
             dataType: 'json',
             success: function (result) {
@@ -399,7 +399,7 @@ let jobContentOprObj = {
         let me = jobContentOprObj;
         $.ajax({
             type: 'post',
-            url: 'api/updateSituation',
+            url: '/complementaryRation/api/updateSituation',
             data: {lastOpr: userAccount, repId: pageOprObj.rationLibId, nodeId: nodeId, situation: situation},
             dataType: 'json',
             success: function (result) {

+ 12 - 6
web/building_saas/complementary_ration_lib/js/ration.js

@@ -617,7 +617,7 @@ let rationOprObj = {
     mixUpdateRequest: function(updateArr, addArr, removeIds, callback) {
         let me = rationOprObj;
         me.saveInString(updateArr);
-        CommonAjax.post('api/mixUpdateRationItems', {sectionId: me.currentSectionId, updateItems: updateArr, addItems: addArr, removeIds: removeIds}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/mixUpdateRationItems', {sectionId: me.currentSectionId, updateItems: updateArr, addItems: addArr, removeIds: removeIds}, function (rstData) {
             let cacheSection = me.updateCache(addArr, updateArr, removeIds, rstData);
             me.sortByCode(cacheSection);
             /*cacheSection.sort(function(a, b){
@@ -673,26 +673,32 @@ let rationOprObj = {
                 //annotation
                 //annotationOprObj.rationAnnotationOpr(me.currentRations["_SEC_ID_" + sectionID]);
                 me.showRationItems(sectionID);
-                sectionTreeObj.removeBtn.removeClass('disabled');
+                if (!isReadOnly) {
+                    sectionTreeObj.removeBtn.removeClass('disabled');
+                }
                 if(callback){
                     callback();
                 }
             } else {
-                CommonAjax.post('api/getRationItems', {sectionId: sectionID}, function (rstData) {
+                CommonAjax.post('/complementaryRation/api/getRationItems', {sectionId: sectionID}, function (rstData) {
                     me.currentRations["_SEC_ID_" + sectionID] = rstData;
                     me.sortByCode(me.currentRations["_SEC_ID_" + sectionID]);
                     //job--
                     /*jobContentOprObj.currentRationItems = me.currentRations["_SEC_ID_" + sectionID];
                     jobContentOprObj.rationJobContentOpr(me.currentRations["_SEC_ID_" + sectionID]);*/
                     //annotation
-                    //annotationOprObj.rationAnnotationOpr(me.currentRations["_SEC_ID_" + sectionID]);
+                //annotationOprObj.rationAnnotationOpr(me.currentRations["_SEC_ID_" + sectionID]);
                     me.showRationItems(sectionID);
-                    sectionTreeObj.removeBtn.removeClass('disabled');
+                    if (!isReadOnly) {
+                        sectionTreeObj.removeBtn.removeClass('disabled');
+                    }
                     if(callback) {
                         callback();
                     }
                 }, function () {
-                    sectionTreeObj.removeBtn.removeClass('disabled');
+                    if (!isReadOnly) {
+                        sectionTreeObj.removeBtn.removeClass('disabled');
+                    }
                 });
             }
         }

+ 3 - 1
web/building_saas/complementary_ration_lib/js/ration_assist.js

@@ -172,7 +172,9 @@ var rationAssistOprObj = {
         me.ration = ration;
 
         sheetCommonObj.cleanData(me.sheet, me.setting, -1);
-        sheetCommonObj.unShieldAllCells(me.sheet);
+        if (!isReadOnly) {
+            sheetCommonObj.unShieldAllCells(me.sheet);
+        }
 
         if (ration == undefined || ration.rationAssList == undefined ||
             ration.rationAssList.length == 0){

+ 2 - 2
web/building_saas/complementary_ration_lib/js/ration_coe.js

@@ -235,7 +235,7 @@ var rationCoeOprObj = {
             }else{
                 $.ajax({
                     type:"POST",
-                    url:"api/getCoeItemsByNos",
+                    url:"/complementaryRation/api/getCoeItemsByNos",
                     data: {"data": JSON.stringify({"coeNos": temp})},
                     dataType:"json",
                     cache:false,
@@ -292,7 +292,7 @@ var rationCoeOprObj = {
                 var data = {"coeIDs": coeIDs};
                 $.ajax({
                     type:"POST",
-                    url:"api/getCoeItemsByIDs",
+                    url:"/complementaryRation/api/getCoeItemsByIDs",
                     data: {"data": JSON.stringify(data)},
                     dataType:"json",
                     cache:false,

+ 15 - 4
web/building_saas/complementary_ration_lib/js/ration_glj.js

@@ -64,7 +64,7 @@ var rationGLJOprObj = {
         let me = this;
         $.ajax({
             type: 'post',
-            url: "api/getGljDistType",
+            url: "/complementaryRation/api/getGljDistType",
             dataType: 'json',
             success: function (result) {
                 if (!result.error && callback) {
@@ -425,7 +425,7 @@ var rationGLJOprObj = {
     },
     addGljItems: function (codes, repId, args) {
         let me = this;
-        CommonAjax.post('api/getGljItemsByCodes', { gljCodes: codes, rationRepId: repId }, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/getGljItemsByCodes', { gljCodes: codes, rationRepId: repId }, function (rstData) {
             if (rstData.length > 0) {
                 if (priceProperties && priceProperties.length > 0) {
                     let priceField = priceProperties[0].price.dataCode;
@@ -580,7 +580,12 @@ var rationGLJOprObj = {
         if (me.currentRationItem && me.cache["_GLJ_" + me.currentRationItem.ID]) {
             var cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID];
             for (var i = 0; i < cacheArr.length; i++) {
-                rst.push({ gljId: cacheArr[i].gljId, consumeAmt: cacheArr[i].consumeAmt, type: cacheArr[i].type });
+                const newItem = { gljId: cacheArr[i].gljId, consumeAmt: cacheArr[i].consumeAmt, type: cacheArr[i].type };
+                if (cacheArr[i].userId && cacheArr[i].userId !== userID) {
+                    // 是分享人材机
+                    newItem.fromUser = cacheArr[i].userId;
+                }
+                rst.push(newItem);
             }
         }
         return rst;
@@ -597,6 +602,9 @@ var rationGLJOprObj = {
         rst.unit = repGlj.unit;
         rst.basePrice = repGlj.basePrice;
         rst.gljType = repGlj.gljType;
+        if (rItem.fromUser) {
+            rst.userId = rItem.fromUser;
+        }
         return rst;
     },
     getGljItems: function (rationItem, callback) {
@@ -610,9 +618,12 @@ var rationGLJOprObj = {
                 let idObj = Object.create(null);
                 idObj.type = rationGljList[i].type;
                 idObj.id = rationGljList[i].gljId;
+                if (rationGljList[i].fromUser) {
+                    idObj.fromUser = rationGljList[i].fromUser;
+                }
                 gljIds.push(idObj);
             }
-            CommonAjax.post('api/getGljItemsByIds', { ids: gljIds }, function (rstData) {
+            CommonAjax.post('/complementaryRation/api/getGljItemsByIds', { ids: gljIds }, function (rstData) {
                 sheetCommonObj.cleanSheet(me.sheet, me.setting, -1);
                 if (priceProperties && priceProperties.length > 0) {
                     let priceField = priceProperties[0].price.dataCode;

+ 9 - 9
web/building_saas/complementary_ration_lib/js/repository_glj.js

@@ -81,7 +81,7 @@ repositoryGljObj = {
     },
     getGljDistType: function (callback) {
         let me = this;
-        CommonAjax.post('api/getGljDistType', {}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/getGljDistType', {}, function (rstData) {
             me.distTypeTree = me.getComboData(rstData);
             me.workBook.getSheet(0).getCell(-1, 5, GC.Spread.Sheets.SheetArea.viewport).value(me.distTypeTree.comboDatas[0].text);
             if(callback){
@@ -91,7 +91,7 @@ repositoryGljObj = {
     },
     getGljTree: function(gljLibId, callback) {
         var me = this;
-        CommonAjax.post('api/getGljTree', {gljLibId: gljLibId}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/getGljTree', {gljLibId: gljLibId}, function (rstData) {
             zTreeHelper.createTree(rstData, gljSetting, "repositoryTree", me);
             if (rstData && rstData.length > 0) {
                 me.gljCurTypeId = rstData[0].ID;
@@ -103,7 +103,7 @@ repositoryGljObj = {
     },
     getGljItems: function(gljLibId) {
         var me = this;
-        CommonAjax.post('api/getGljItems', {stdGljLibId: gljLibId}, function (rstData) {
+        CommonAjax.post('/complementaryRation/api/getGljItems', {stdGljLibId: gljLibId}, function (rstData) {
             me.gljList = rstData.stdGljs.concat(rstData.complementaryGljs);
             me.workBook.getSheet(0).setRowCount(rstData.length);
             me.sortGlj();
@@ -620,7 +620,7 @@ repositoryGljObj = {
     updateRationBasePrcRq: function (basePrcArr) {
         $.ajax({
             type: 'post',
-            url: 'api/updateRationBasePrc',
+            url: '/complementaryRation/api/updateRationBasePrc',
             data:{data: JSON.stringify({repId: pageOprObj.rationLibId, lastOpr: userAccount, basePrcArr: basePrcArr})},
             dataType: 'json',
             success: function (result) {
@@ -634,7 +634,7 @@ repositoryGljObj = {
         let me = repositoryGljObj;
         $.ajax({
             type: 'post',
-            url: 'api/getRationGljIds',
+            url: '/complementaryRation/api/getRationGljIds',
             data: {data: JSON.stringify({repId: repId})},
             dataType: 'json',
             success: function(result){
@@ -648,7 +648,7 @@ repositoryGljObj = {
         var me = repositoryGljObj;
         $.ajax({
             type:"POST",
-            url:"api/mixUpdateGljItems",
+            url:"/complementaryRation/api/mixUpdateGljItems",
             data:{"repositoryId": me.currentRepositoryId, "lastOpr": userAccount, "updateItems": JSON.stringify(updateArr), "addItems": JSON.stringify(addArr), "removeIds": JSON.stringify(removeIds)},
             dataType:"json",
             cache:false,
@@ -806,7 +806,7 @@ var gljTypeTreeOprObj = {
         }
         $.ajax({
             type:"POST",
-            url:"api/deleteGljNodes",
+            url:"/complementaryRation/api/deleteGljNodes",
             data:{"repId": pageOprObj.rationLibId, "lastOpr": userAccount, "nodes": JSON.stringify(nodeIds), "preNodeId": preNodeId, "preNodeNextId": treeNode.NextSiblingID},
             dataType:"json",
             cache:false,
@@ -845,7 +845,7 @@ var gljTypeTreeOprObj = {
             }
             $.ajax({
                 type:"POST",
-                url:"api/updateGljNodes",
+                url:"/complementaryRation/api/updateGljNodes",
                 data:{"repId": pageOprObj.rationLibId, "lastOpr": userAccount, "nodes": JSON.stringify(reqData)},
                 dataType:"json",
                 cache:false,
@@ -876,7 +876,7 @@ var gljTypeTreeOprObj = {
     addNewNode : function(rawNode, lastNodeId, callback){
         $.ajax({
             type:"POST",
-            url:"api/createNewGljTypeNode",
+            url:"/complementaryRation/api/createNewGljTypeNode",
             data:{"lastOpr": userAccount, "repositoryId": repositoryGljObj.currentRepositoryId,"lastNodeId": lastNodeId, "rawNodeData": JSON.stringify(rawNode)},
             dataType:"json",
             cache:false,

+ 5 - 2
web/building_saas/complementary_ration_lib/js/section_tree.js

@@ -22,7 +22,7 @@ let pageOprObj = {
         annotationOprObj.bindAllEvents($('#fzTxtareaAll'));
     },
     getRationLibInfo: function (rationLibId, callback) {
-        CommonAjax.post('api/getRationLib', {rationRepId: rationLibId}, callback);
+        CommonAjax.post('/complementaryRation/api/getRationLib', {rationRepId: rationLibId}, callback);
     },
 };
 
@@ -239,6 +239,9 @@ let sectionTreeObj = {
     },
     
     refreshBtn: function (selected) {
+        if (isReadOnly) {
+            return;
+        }
         let me = this;
         me.insertBtn.removeClass('disabled');
         me.removeBtn.removeClass('disabled');
@@ -526,7 +529,7 @@ let sectionTreeObj = {
         return updateObj;
     },
     sectionTreeAjax: function (postData, scFunc, errFunc) {
-        CommonAjax.post('api/updateRationSection', {updateData: postData}, scFunc, errFunc);
+        CommonAjax.post('/complementaryRation/api/updateRationSection', {updateData: postData}, scFunc, errFunc);
     },
     initTools: function (node) {
         if(this.isDef(node)){

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

@@ -513,4 +513,18 @@ margin-right: 100px !important;
 
 .fee_detail_height{
   height:270px
+}
+
+.limit-price-input {
+  display: inline-block;
+  width: 80px;
+}
+
+.inline-wrapper {
+  display: flex;
+  justify-content: space-between;
+}
+
+.inline-wrapper .inline-tools {
+  display: inline-block;
 }

+ 1 - 1
web/building_saas/fee_rates/fee_rate.html

@@ -17,7 +17,7 @@
             </div>
         </div>
         <div class="col-lg-4 p-0">
-            <div class="form-inline py-1" style="margin-top: 5px"><input type="checkbox" id="cascadeSet" checked >  <label class="mx-2" >统一设置相同参数</label></div>
+            <div class="form-inline py-1" style="margin-top: 5px; display:none"><input type="checkbox" id="cascadeSet" checked >  <label class="mx-2" >统一设置相同参数</label></div>
         </div>
     </div>
 </div>

+ 47 - 5
web/building_saas/main/html/main.html

@@ -100,7 +100,7 @@
                 <a id="importDropDown" class="dropdown-toggle" href="#"><i class="fa fa-cloud-upload"></i></a>
                 <div class="dropdown-menu">
                   <a id="uploadLj" class="dropdown-item" href="#import" data-toggle="modal"
-                    data-target="#import">导入报表Excel清单</a>
+                    data-target="#import">导入Excel清单</a>
                   <a id="uploadGld" class="dropdown-item" href="#import" data-toggle="modal"
                     data-target="#import">导入广联达算量Excel清单</a>
                 </div>
@@ -1114,6 +1114,19 @@
                         </label>
                       </div>
                     </fieldset>
+                    <% if (!projectData.importedByInterface) { %>
+                    <fieldset class="form-group">
+                      <h5 >清单限价</h5>
+                      <div class="mt-1">
+                        <span data-toggle="tooltip" data-placement="bottom" data-original-title="最高限价=清单综合单价*(1+X%)">最高限价 </span><input id="max-price-rate" data-limit="max" class="form-control form-control-sm limit-price-input" value="0" type="text"> %
+                      </div>
+                      <% if (compilationName !== '重庆定额(2018)') { %>
+                        <div class="mt-1">
+                          <span data-toggle="tooltip" data-placement="bottom" data-original-title="最低限价=清单综合单价*(1-X%)">最低限价 </span><input id="min-price-rate" data-limit="min" class="form-control form-control-sm limit-price-input" value="0" type="text"> %
+                        </div>
+                      <% } %>
+                    </fieldset>
+                    <% } %>
                   </div>
                 </div>
                 <!--清单工程精度-->
@@ -2001,10 +2014,15 @@
           </div>
           <div id="uploadSheets"></div>
         </div>
-        <div class="modal-footer">
-          <button style="margin-right: 215px;" type="button" class="btn btn-primary" id="uploadExample">下载示例</button>
-          <a href="javascript:void(0);" class="btn btn-primary" id="importConfirm">确定导入</a>
-          <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+        <div class="modal-footer inline-wrapper">
+          <div class="inline-tools">
+            <button type="button" class="btn btn-primary" id="uploadExample-general">下载示例1</button>
+            <button type="button" class="btn btn-primary" id="uploadExample">下载示例2</button>
+          </div>
+          <div class="inline-tools">
+            <a href="javascript:void(0);" class="btn btn-primary" id="importConfirm">确定导入</a>
+            <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+          </div>
         </div>
       </div>
     </div>
@@ -2676,6 +2694,29 @@
     </div>
   </div>
 
+  
+<!--弹出 选择分部窗口-->
+<div class="modal fade" id="selectFBDiv" data-backdrop="static">
+  <div class="modal-dialog" role="document">
+      <div class="modal-content">
+          <div class="modal-header">
+              <h5 class="modal-title"> 选择分部</h5>
+              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                  <span aria-hidden="true">&times;</span>
+              </button>
+          </div>
+          <div class="modal-body" style="height: 450px;padding: 0px">
+              <div class="ovf-hidden full-h" id="selectFBSheet" style="height: 100%;"></div>
+          </div>
+          <div class="modal-footer">
+              <button type="button" class="btn btn-primary" data-dismiss="modal" id="selectFBConfirm">确定</button>
+              <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+          </div>
+      </div>
+  </div>
+</div>
+
+
 
   <%include ../../../common/components/share/index.html %>
 
@@ -2843,6 +2884,7 @@
   <script type="text/javascript" src="/web/building_saas/main/js/views/fee_rate_view.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/views/quantity_edit_view.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/views/sub_fee_rate_views.js"></script>
+  <script type="text/javascript" src="/web/building_saas/main/js/views/select_FB_view.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/views/calc_base_view.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_labour_coe_view.js"></script>
   <script type="text/javascript" src="/web/building_saas/main/js/views/locate_view.js"></script>

+ 6 - 2
web/building_saas/main/js/controllers/block_controller.js

@@ -4,7 +4,6 @@
 let BlockController = {
     datas:[],
     disabelCopyRightClick:function(){
-        console.log("right clicked")
         if (projectReadOnly) {
             return true;
         }
@@ -302,7 +301,7 @@ let BlockController = {
      * @param selected
      * @param position next/pre/sub
      */
-    confirmPaste:function (blockData,selected,position) {
+    confirmPaste:function (blockData,selected,position,callback) {
         let me = this;
         let project = projectObj.project;
         let Bills = project.Bills;
@@ -376,6 +375,11 @@ let BlockController = {
              //主材设备工料机插入主树
              project.ration_glj.addToMainTree(result.ration_gljs);
              //更新计算程序模板,并进行重新计算
+             if(callback){
+                callback(rationNodes,calcNodes);
+                return;
+            }
+
              project.calcProgram.calcNodesAndSave(calcNodes,async function () {
                  installationFeeObj.calcInstallationFee();
                  await OVER_HEIGHT.reCalcOverHeightFee();

+ 40 - 2
web/building_saas/main/js/controllers/project_controller.js

@@ -216,9 +216,9 @@ ProjectController = {
         if (!project || !sheetController) { return null; }
         this.addSpecialBill(project, sheetController,null, project.mainTree.selected.nextSibling,true,billType.DXFY);
     },
-    addFB:function(project, sheetController) {//添加分部
+    addFB:function(project, sheetController,node) {//添加分部
         if (!project || !sheetController) { return null; }
-        let selected = project.mainTree.selected;
+        let selected =node|| project.mainTree.selected;
         if(selected.parent==null&&isFlag(selected.data)&&selected.data.flagsIndex.fixed.flag==fixedFlag.SUB_ENGINERRING){//选中的是分部分项,则插入做为最后一个子项
             return this.addSpecialBill(project, sheetController,selected, null,true,billType.FB);
         }
@@ -227,6 +227,44 @@ ProjectController = {
         }
 
     },
+    addFXParent:async function(node){
+        let datas = [];
+        //let parent = node.parent.parent;
+        let newBills = {
+            'ID':uuid.v1(),
+            'isAdd':1,
+            'type':billType.FB,
+            'projectID':node.data.projectID
+        }
+        if(node.preSibling){//有前兄弟 在当前分项和前兄弟中间新增一行“分部”空行,空行作为分项的父项,原父项变为空行的前兄弟。
+            let changeParent = false;
+            newBills.ParentID = node.parent.parent?node.parent.parent.getID():-1;
+            newBills.NextSiblingID = node.parent.data.NextSiblingID;
+            datas.push({type:ModuleNames.bills,data:newBills,action:"add",preSiblingID:node.parent.data.ID});
+            datas.push({type:ModuleNames.bills,data:{'ID':node.parent.getID(),'NextSiblingID':newBills.ID}})
+            for(let c of node.parent.children){
+                if(c.data.ID == node.data.ID) changeParent = true
+                if(changeParent) datas.push({type:ModuleNames.bills,data:{'ID':c.getID(),'ParentID':newBills.ID}})
+            }
+
+        }else{//没有前兄弟,在当前分项和父项中间新增一行“分部”空行,空行作为分项的父项,原父项变为空行的父项。当前分项/补项的所有兄弟都跟着移动。
+            newBills.ParentID = node.parent.getID();
+            newBills.NextSiblingID = -1;
+            datas.push({type:ModuleNames.bills,data:newBills,action:"add"});
+            for(let c of node.parent.children){
+                datas.push({type:ModuleNames.bills,data:{'ID':c.getID(),'ParentID':newBills.ID}})
+            }
+        }
+        if(datas.length > 0){
+             let nodes = await projectObj.project.syncUpdateNodesAndRefresh(datas);
+             //重新计算
+            cbTools.refreshFormulaNodes();
+            projectObj.project.calcProgram.calcNodesAndSave(nodes);
+            projectObj.mainController.setTreeSelected(projectObj.project.mainTree.getNodeByID(newBills.ID));
+        }
+
+    },
+
     addFX:function(project, sheetController) {//添加分项
         if (!project || !sheetController) { return null; }
         let selected = project.mainTree.selected;

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

@@ -524,6 +524,7 @@ var cacheTree = {
                 } else {
                     this.roots.splice(node.siblingIndex(), 1);
                 }
+                if(node.preSibling) node.preSibling.setNextSibling(node.nextSibling);//后兄弟设置为前兄弟的后兄弟 
                 if(node.children.length>0){
                     if(node.preSibling){//子项变成前兄弟的子项
                         for(let c of node.children){

+ 15 - 2
web/building_saas/main/js/models/calc_program.js

@@ -1037,10 +1037,23 @@ let calcTools = {
         const totalFee = this.getFee(node, feeField);
         const maxPrice = node.data.maxPrice;
         // 最高限价有值才对比
-        if (!commonUtil.isDef(maxPrice)) {
+        if (!commonUtil.isNumber(maxPrice)) {
             return false;
         }
-        return totalFee > maxPrice;
+        return totalFee > +maxPrice;
+    },
+    // 清单价格是否小于最低限价
+    unitFeeLTMinPrice: function (node, feeField) {
+        if (!this.isBill(node)) {
+            return false;
+        }
+        const totalFee = this.getFee(node, feeField);
+        const minPrice = node.data.minPrice;
+        // 最低限价有值才对比
+        if (!commonUtil.isNumber(minPrice)) {
+            return false;
+        }
+        return totalFee < +minPrice;
     },
     
     getTenderCalcType: function () {

+ 17 - 12
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -829,27 +829,32 @@ const XML_EXPORT_BASE = (() => {
      */
     function softCheck() {
         const tenderDetailMap = _cache.tenderDetailMap;
-        // 检查清单综合单价是否大于最高限价,大于则提示
-        function checkMaxPrice(tenderDetail) {
-            return tenderDetail.mainTree.items
-                .filter(node => calcTools.unitFeeGTMaxPrice(node, 'common.unitFee'))
-                .map(node => {
-                    const code = node.data.code || '';
-                    const name = node.data.name || '';
-                    return `第${node.serialNo()}行“${code + name}”,清单综合单价 > 最高限价`;
-                });
+        // 检查清单综合单价
+        function checkPrice(tenderDetail) {
+            const rst = [];
+            tenderDetail.mainTree.items.forEach(node => {
+                const code = node.data.code || '';
+                const name = node.data.name || '';
+                if (calcTools.unitFeeGTMaxPrice(node, 'common.unitFee')) {
+                    rst.push(`第${node.serialNo()}行“${code + name}”,清单综合单价 > 最高限价`);
+                }
+                if (calcTools.unitFeeLTMinPrice(node, 'common.unitFee')) {
+                    rst.push(`第${node.serialNo()}行“${code + name}”,清单综合单价 < 最低限价`);
+                }
+            });
+            return rst;
         }
         const infos = [];
         // 按照获取顺序serialNo(根据树结构)排序
         Object.values(tenderDetailMap)
             .sort((a, b) => a.serialNo - b.serialNo)
             .forEach(tenderDetail => {
-                const maxPriceInfos = checkMaxPrice(tenderDetail);
-                if (!maxPriceInfos.length) {
+                const priceInfos = checkPrice(tenderDetail);
+                if (!priceInfos.length) {
                     return;
                 }
                 infos.push(`<span style="font-weight: bold">单位工程“${tenderDetail.projectInfo.name}”下:</span>`);
-                infos.push(...maxPriceInfos);
+                infos.push(...priceInfos);
             });
         return infos;
     }

+ 25 - 0
web/building_saas/main/js/models/fee_rate.js

@@ -53,6 +53,31 @@ var FeeRate = {
             var rates = this.getActivateFeeRate().rates;
             return _.find(rates,{'ID':ID})
         };
+        FeeRate.prototype.getAllSubRates = function (trates) {
+
+            let rates = trates ? trates : this.getActivateFeeRate().rates;
+            let subRates = [],
+              nameMap = {},
+              temRates = [];
+            for (let r of rates) {
+              if (gljUtil.isDef(r.subFeeRate)) {
+                for (let p of r.subFeeRate.recodes) {
+                  if (nameMap[p.name] == true) continue;
+                  nameMap[p.name] = true;
+                  subRates.push(p);
+                  if (p.subList && p.subList.length > 0) {
+                    for (let sub of p.subList) {
+                      sub.isSub = true;
+                      sub.ParentID = p.ID;
+                      subRates.push(sub);
+                    }
+                  }
+                }
+              }
+            }
+            return _.sortByAll(subRates, ['ID']) //要保证树节点的ID子项是紧跟着父项的,不然的话显示会有问题
+        };
+
         FeeRate.prototype.getSubViewData= function(item) {
             var datas = [];
             if(item.hasOwnProperty('subFeeRate')&&item.subFeeRate!=undefined){

+ 56 - 11
web/building_saas/main/js/models/project.js

@@ -1,3 +1,4 @@
+
 /**
  * Created by Mai on 2017/4/1.
  */
@@ -431,7 +432,7 @@ var PROJECT = {
                 type:node.sourceType,
                 data:{ID:node.data.ID}
             };
-            setData(data.data,newval,fieldName);
+            setData(data.data,newval,fieldName, node);
             datas.push(data);
             setChildren(node,newval,datas);//同步设置所有子项
             if(needSetParent) setParent(node,newval,datas);//设置父节点
@@ -463,7 +464,7 @@ var PROJECT = {
                         };
                         //有基数计算的子项值清空
                         let val = fieldName == "lockUnitPrice" && c.data.calcBase && c.data.calcBase != ""?null:newval;
-                        setData(data.data,val,fieldName);
+                        setData(data.data,val,fieldName, c);
                         datas.push(data);
                         setChildren(c,newValue,datas)
                     }
@@ -484,14 +485,26 @@ var PROJECT = {
                         type:cnode.parent.sourceType,
                         data:{ID:cnode.parent.data.ID}
                     };
-                    setData(data.data,pvalue,fieldName);
+                    setData(data.data,pvalue,fieldName, cnode.parent);
                     datas.push(data);
                     setParent(cnode.parent,pvalue,datas);
                 }
             }
-            function setData(data,avalue,fieldName) {
+            function setData(data,avalue,fieldName, node) {
                 data[fieldName] = avalue;
-                if(fieldName == "outPutMaxPrice") data.maxPrice = null;
+                // 二次修改项目属性上下限设置,前端的最高、最低限价不重算,要去掉输出限价列的√,再重新勾选才计算。因此输出最高限价、输出限价时,需要给节点的maxPrice、minPrice设置一个算好的值
+                if (['outPutLimitPrice', 'outPutMaxPrice'].includes(fieldName)) {
+                    if (avalue) {
+                        const unitFee = node.data && node.data.feesIndex && node.data.feesIndex.common && node.data.feesIndex.common.unitFee || 0;
+                        const maxPriceRate = projectObj.project.property.maxPriceRate || 0;
+                        data.maxPrice = scMathUtil.roundForObj(unitFee * (1 + maxPriceRate * 0.01), decimalObj.bills.unitPrice) || null;
+                        const minPriceRate = projectObj.project.property.minPriceRate || 0;
+                        data.minPrice = scMathUtil.roundForObj(unitFee * (1 - minPriceRate * 0.01), decimalObj.bills.unitPrice) || null;
+                    } else {
+                        data.maxPrice = null;
+                        data.minPrice = null;
+                    }
+                }
             }
         };
         project.prototype.updateNodesAndRefresh=function (datas,callback) {
@@ -542,10 +555,19 @@ var PROJECT = {
             })
         };
 
+        project.prototype.updateParentNode = function(node,parentID,addNodeDatas){
+            addNodeDatas.push({type:node.sourceType,data:node.data,parentID:parentID});
+            for(let c of node.children){
+                this.updateParentNode(c,node.getID(),addNodeDatas);
+            }
+            
+        };
+
         project.prototype.updateNodesCache =function (datas) {
             let refreshNode = [];
             let reclacQuantity = false;
             let deleteNode=[],addNodeDatas=[];
+           // let changeParentIDMap = {}; 确定刷新无错后删除
             for(let d of datas){
                 let temObj = null;
                 if(d.type == ModuleNames.bills || d.type == ModuleNames.ration){//如果是树节点类型,直接取树节点更新
@@ -567,6 +589,12 @@ var PROJECT = {
                                 temObj = temNode.data;
                                 if(gljUtil.isDef(d.data.quantity))reclacQuantity = true;
                                 refreshNode.push(temNode);
+                                if(d.data.ParentID){//更新了父节点-相当于删了再添加
+                                    //changeParentIDMap[temNode.data.ParentID] = true;
+                                   deleteNode.push(temNode);
+                                   this.updateParentNode(temNode,d.data.ParentID,addNodeDatas);
+                                } 
+
                             }
                         }
                     }
@@ -590,22 +618,38 @@ var PROJECT = {
 
             //对树节点的操作并删除、添加清单、删除添加定额、删除对应的定额工料机缓存
             TREE_SHEET_HELPER.massOperationSheet(projectObj.mainController.sheet, function () {
-                deleteTreeNodes(deleteNode);
-                addNewNodes(addNodeDatas,refreshNode);
+                let rationsDatas=[];
+                let billsDatas=[];
+                for(let a of addNodeDatas){
+                    if(a.type == ModuleNames.bills)  billsDatas.push(a);
+                    if(a.type == ModuleNames.ration)  rationsDatas.push(a);
+                }
+                deleteTreeNodes(deleteNode);     
+                addNewNodes(billsDatas,refreshNode);
+                addNewNodes(rationsDatas,refreshNode);
             });
 
+        /*     for(let pID in changeParentIDMap){
+              let pnode =  this.mainTree.getNodeByID(pID);
+              getNextRefreshNode(pnode,refreshNode);
+            } */
 
             if(reclacQuantity) this.projectGLJ.calcQuantity();
             return refreshNode;
 
 
-
+         /*    function getNextRefreshNode(node,refreshNode){
+                if(node.nextSibling){
+                    refreshNode.push(node.nextSibling)
+                    getNextRefreshNode(node.nextSibling,refreshNode)
+                } 
+            }; */
 
             function deleteTreeNodes(deleteNodes) {
                 let controller = projectObj.mainController, project = projectObj.project;
                 let Bill = project.Bills, Ration = project.Ration;
                 for(let rd of deleteNodes){
-                    controller.sheet.deleteRows(rd.serialNo(),1);
+                    controller.sheet.deleteRows(rd.serialNo(),rd.posterityCount() + 1);
                     controller.tree.delete(rd);
                     if(rd.sourceType == Bill.getSourceType()) Bill.tree.delete(rd.source);
                 }
@@ -616,11 +660,12 @@ var PROJECT = {
                 let newAddNode = [];
                 for(let nr of newDatas){
                     let nextID = -1;
+                    let parentID = nr.parentID || (nr.data && nr.data.ParentID || -1);
                     let preNode = projectObj.project.mainTree.getNodeByID(nr.preSiblingID);
                     if(preNode) nextID = preNode.getNextSiblingID();
-                    let newNode = projectObj.project.mainTree.insert(nr.parentID, nextID, nr.data.ID);
+                    let newNode = projectObj.project.mainTree.insert(parentID, nextID, nr.data.ID);
                     if(nr.type == ModuleNames.bills){
-                        let newSource = Bill.tree.insertByData(nr.data, nr.ParentID, nextID, true);
+                        let newSource = Bill.tree.insertByData(nr.data, parentID, nextID, true);
                         newNode.source = newSource;
                     }else {
                         newNode.source = nr.data;

+ 5 - 3
web/building_saas/main/js/models/ration.js

@@ -539,10 +539,11 @@ var Ration = {
                 updateBillsOprRation();
             }
         };
-        ration.prototype.insertVolumePrice = function(type){
+        ration.prototype.insertVolumePrice = function(type,ext){
+            let isEmpty = ext?false:true;
             this.addNewRation(null,rationType.volumePrice,function (newNode) {//插入人工不需要自动定位到编号列
                 projectObj.selectColAndFocus(newNode,null);
-            },true,type);
+            },isEmpty,type,true,ext);
         };
         // 已经有数据,更新前端缓存及节点,不进行通信
         ration.prototype.addNewDataSimply = function (newData) {
@@ -570,7 +571,7 @@ var Ration = {
                 projectObj.project.ration_glj.deleteByRation(node.data);
             });
         };
-        ration.prototype.addNewRation = function (itemQuery,rationType,callback=null,isEmpty=false,priceType,needCalcAndSave=true) {//priceType 是量价类型
+        ration.prototype.addNewRation = function (itemQuery,rationType,callback=null,isEmpty=false,priceType,needCalcAndSave=true,ext) {//priceType 是量价类型
             console.log('addNewRation');
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
@@ -617,6 +618,7 @@ var Ration = {
                     let billsNode = project.mainTree.getNodeByID(billItemID);
                     needInstall = project.Bills.isFBFX(billsNode);//在分部分项插入的定额才需要定额安装增加费
                 }
+                if(ext) gljUtil.setProperty(newData,ext);
                 $.bootstrapLoading.start();
                 CommonAjax.post("/ration/addNewRation",{projectID:me.project.ID(),itemQuery:itemQuery,newData:newData,defaultLibID: rationLibObj.getDefaultStdRationLibID(),calQuantity:calQuantity,brUpdate:brUpdate,needInstall:needInstall},function (data) {
                     //更新缓存

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

@@ -753,6 +753,9 @@ let ration_glj = {
                 const glj = allGLJ.find(item => gljOprObj.getIndex(item, gljLibKeyArray) === selKey);
                 if (glj) {
                     const rationGLJ = me.getAddDataByStd(glj, ration.ID, ration.billsItemID, ration.projectID);
+                    if (glj.userId) {
+                        rationGLJ.fromUser = glj.userId;
+                    }
                     gljList.push(rationGLJ);
                 }
             });
@@ -840,6 +843,9 @@ let ration_glj = {
             oldData. materialIndexCoe =  glj.materialIndexCoe;
             if (glj.hasOwnProperty("compilationId")) {
                 oldData.from = "cpt";
+                if (glj.userId) {
+                    oldData.fromUser = glj.userId;
+                }
                 if (glj.code.indexOf('-') != -1) {//这条工料机是用户通过修改包称、规格、型号等保存到补充工料机库的
                     oldData.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
                 }
@@ -909,6 +915,9 @@ let ration_glj = {
             };
             if (glj.hasOwnProperty("compilationId")) {
                 doc.from = "cpt";
+                if (glj.userId) {
+                    doc.fromUser = glj.userId;
+                }
                 if (glj.code.indexOf('-') != -1) {//这条工料机是用户通过修改包称、规格、型号等保存到补充工料机库的
                     doc.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
                 }

+ 29 - 24
web/building_saas/main/js/views/block_lib.js

@@ -588,30 +588,35 @@ var blockLibObj = {
         $("#div_cloneOptions").modal({show: true});
     },
     oneToOneClone: function (projectNode, block, options) {
-        let canClone = true;
-        if (options.checkCode)
-            canClone = canClone && (projectNode.data.code.substr(0, 9) == block.data.code.substr(0, 9));
-        if (options.checkName)
-            canClone = canClone && (projectNode.data.name == block.data.name);
-        if (options.checkUnit)
-            canClone = canClone && (projectNode.data.unit == block.data.unit);
-        if (!canClone) return;
-        
-        if (options.overwriteRations)
-            projectObj.project.Bills.deleteChildren(projectNode);
-        /*  这里封装成伟城的块文件格式,可直接使用伟城的“粘贴块”接口。
-            但这里结构要作出调整:忽略叶子清单层,直接从定额开始(跟粘贴块有区别),始终强制在叶子清单下插入定额。
-            该操作前提:当前块文件的全部数据已从后台取到前台。  */
-        let vBlock_WC = {
-            compilationID: block.data.compilationID,
-            copyTime: block.data.copyTime,
-            firstNodeType: 1,           // 强制改成1 (因为是从清单下的定额开始。清单自身的还是保留,暂不使用使用)。
-            isFBFX: block.data.isFBFX,
-            zeroQuantity: options.zeroQuantity,
-            datas: block.data.children       // rations
-        };
-        vBlock_WC = JSON.parse(JSON.stringify(vBlock_WC));
-        BlockController.confirmPaste(vBlock_WC, projectNode, 'sub');
+        return new Promise(function (resolve, reject) {        
+            let canClone = true;
+            if (options.checkCode)
+                canClone = canClone && (projectNode.data.code.substr(0, 9) == block.data.code.substr(0, 9));
+            if (options.checkName)
+                canClone = canClone && (projectNode.data.name == block.data.name);
+            if (options.checkUnit)
+                canClone = canClone && (projectNode.data.unit == block.data.unit);
+            if (!canClone) return resolve([[], []]);
+            
+            if (options.overwriteRations)
+                projectObj.project.Bills.deleteChildren(projectNode);
+            /*  这里封装成伟城的块文件格式,可直接使用伟城的“粘贴块”接口。
+                但这里结构要作出调整:忽略叶子清单层,直接从定额开始(跟粘贴块有区别),始终强制在叶子清单下插入定额。
+                该操作前提:当前块文件的全部数据已从后台取到前台。  */
+            let vBlock_WC = {
+                compilationID: block.data.compilationID,
+                copyTime: block.data.copyTime,
+                firstNodeType: 1,           // 强制改成1 (因为是从清单下的定额开始。清单自身的还是保留,暂不使用使用)。
+                isFBFX: block.data.isFBFX,
+                zeroQuantity: options.zeroQuantity,
+                datas: block.data.children       // rations
+            };
+            vBlock_WC = JSON.parse(JSON.stringify(vBlock_WC));
+            BlockController.confirmPaste(vBlock_WC, projectNode, 'sub',function(r, c){
+                resolve([r, c])
+            });
+        })
+
     },
     checkShow: async function () {   // 这里需要处理异步:模板库装载完再弹出位置选择窗。
         if (!$("#kmbk").is(":visible")){  // 如果还没显示

+ 2 - 2
web/building_saas/main/js/views/calc_program_manage.js

@@ -97,8 +97,8 @@ let calcProgramManage = {
             if(me.detailSetting.view.lockColumns){
                 me.detailSetting.view.lockColumns = null;
             }
-            disableSpread(me.mainSpread);
-            disableSpread(me.detailSpread);
+            sheetCommonObj.disableSpread(me.mainSpread);
+            sheetCommonObj.disableSpread(me.detailSpread);
         }
     },
     getStdCalcProgramFiles: function(){

+ 2 - 2
web/building_saas/main/js/views/config_material_view.js

@@ -77,8 +77,8 @@ let configMaterialObj = {
         materialAdjustObj.initPriceCoeSheet();
         //打开别人分享的项目,只读
         if(projectReadOnly){
-            disableSpread(this.configSpread);
-            disableSpread(this.relatedSpread);
+            sheetCommonObj.disableSpread(this.configSpread);
+            sheetCommonObj.disableSpread(this.relatedSpread);
         }else {
             this.initRightClick();
         }

+ 105 - 2
web/building_saas/main/js/views/fee_rate_view.js

@@ -567,7 +567,7 @@ var feeRateObject={
             sheetCommonObj.lockCells(this.mainFeeRateSheet , this.mainFeeRateSetting);
         }
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onMainFeeRateSheetValueChange);
-        this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.onMainFeeRateSelectChanged);
+        //this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.onMainFeeRateSelectChanged);
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onMainFeeRateRangeChanged);
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.EditStarting, function (e,args) {
             let me =feeRateObject;
@@ -580,7 +580,7 @@ var feeRateObject={
         disableRightMenu("divFee",this.mainFeeRateSpread,this.rightClickCallback);
         //打开他人分享的项目、只读
         if(projectReadOnly){
-            disableSpread(this.mainFeeRateSpread);
+            sheetCommonObj.disableSpread(this.mainFeeRateSpread);
         }
     },
     rightClickCallback:function (row) {
@@ -713,6 +713,108 @@ var feeRateObject={
             projectObj.project.FeeRate.batchUpdateFeeRate([{rateIndex:selected.row,rate:item}],feeRateObject.activateFeeRate);
         }
     },
+    setRateFromSub:function (subRate,value,subList,editText,feeRateCoe) {//editText  下拉框选择的值,对于如“19.00”这样下拉选择项,value会变成19,所以在valueMap里找的时候要用editText去查找
+        let me = feeRateObject,feeRate =  projectObj.project.FeeRate;
+        let rates = feeRate.getActivateFeeRate().rates;
+        let updateDatas = [];
+        //对于有子节点的记录和可输入的记录,只支持一个参数(因为还要内插法取费率值),如果要支持两个以上的参数,valueMap要再改下~~~~
+        for(let r of rates){
+            if(gljUtil.isDef(r.subFeeRate)){
+                let valueArray=[];//用来存参数的值 //这里和养护的不一样,这里下拉框是{text:value}的形式,那里是直接下匹配
+                let temP =null;//这个用来存储需要内插法记算的时候,同时,树节点的子节点改变也计在这里, 这样,改变temP的值,会体现在保存subRecode
+                let match = false;
+                let temR = _.cloneDeep(r);//临时存储
+                for(let p of temR.subFeeRate.recodes){
+                    if(p.name == subRate.name){
+                        temP = p;
+                        match = true;
+                    }
+                    let setValue = false;
+                    for(let o of p.optionList){
+                        if(match == true) {//匹配上了,改对应的selected 为 true
+                            if(o.value == value){//这里和养护的不一样,这里下拉框是{text:value}的形式,那里是直接下匹配
+                                o.selected = true;
+                                valueArray.push(value);
+                                setValue = true;
+                            }else {
+                                o.selected = false;
+                            }
+                        }else {//没有匹配上的,直接取值就可以(这个是为多参数时用的,但是目前来看,不支持多参数,比较麻烦)
+                            if(o.selected == true)  valueArray.push(o.value);
+                        }
+                    }
+                    if(match==true && setValue ==false) valueArray.push(value);//如果匹配上了,但是setValue为false,说明没有满足和选项,要用内插法或超出倍数算值
+                }
+                if(match == true){//说明要改rate的值,先从valueMap中去查
+                    if(subList && temP.subList && temP.subList.length > 0){
+                        temP.subList = subList;// 设置树节点的子节点的值
+                    }
+                    let doc = {"subFeeRate":temR.subFeeRate};
+                    let valueKey = valueArray.join('-');
+                    let valueMaps = r.subFeeRate.valueMaps;
+                    if(me.feeRateSpecialHandle){//不同编办特殊处理
+                        let sf = me.feeRateSpecialHandle(subRate,value);
+                        if(!_.isEmpty(sf)){
+                            valueKey = sf.valueKey;
+                            temP.value = sf.value;
+                        }
+                    }
+                    let rate = _.find(valueMaps,{"ID":valueKey});
+                    if(isDef(rate)) {//找到了,直接改费率值
+                        doc.rate = rate.value;
+                    } else {//没找到,用内插法或步长算值
+                        let ltRate = null;//
+                        let gtRate = null;
+                        temP.value = scMathUtil.roundForObj(value,getDecimal("feeRate")) ;
+                        for(let v of valueMaps){
+                           if(parseFloat(v.ID)<parseFloat(value)){
+                               ltRate = v;
+                           }else if( gtRate==null && parseFloat(v.ID)>parseFloat(value)){
+                               gtRate = v;
+                           }
+                        }
+                        if(gljUtil.isDef(ltRate)&&gljUtil.isDef(gtRate)){//已经找到前后的值了
+                            let step = scMathUtil.roundForObj(gtRate.value - ltRate.value,getDecimal("process"));
+                            let total =  parseFloat(gtRate.ID) - parseFloat(ltRate.ID)
+                            doc.rate =getRateByStep(ltRate.value,total,parseFloat(value) - parseFloat(ltRate.ID),step);
+                        }else if(gljUtil.isDef(ltRate)&&gtRate == null){//说明是超出了选项的最大值
+                            let share = parseFloat(value) - parseFloat(ltRate.ID);//超出了多少
+                            doc.rate = getRateByStep(ltRate.value,temP.step,share,temP.amount)
+                        }else if(gljUtil.isDef(gtRate)&&ltRate==null){//说明是只有一个选项,且这个选项比输入的值大
+                            let step =  parseFloat(gtRate.value)- 0;
+                            doc.rate = getRateByStep(0,gtRate.value,value,step)
+                        }
+                    }
+                    if(isDef(doc.rate)){
+                        if(feeRateCoe) doc.rate = scMathUtil.roundForObj(doc.rate * feeRateCoe,getDecimal("feeRate"));    //如果是从乘系数过来的,乘以系数。
+                        updateDatas.push({rateID:r.ID,doc:doc});
+                    }
+                }
+            }
+        }
+       if(updateDatas.length > 0){
+           $.bootstrapLoading.start();
+           feeRate.updateFeeRatesByIDs(updateDatas,async function () {
+               let feerateInfo = [];
+               for(let u of updateDatas){
+                   feerateInfo.push({rateID:u.rateID,value:u.doc["rate"]});
+               }
+               subRateObject.showSubRateData();
+               me.showMainFeeRateData();
+               if(feerateInfo.length > 0){
+                   await feeRate.onFeeRatesChange(feerateInfo);
+               }else {
+                   $.bootstrapLoading.end();
+               }
+           })
+       }
+       function getRateByStep(ltValue,total,share,step) { //min值 ,计算值的总区间, 区间中占比,步长--后端重选标准也要用到
+           let p = scMathUtil.roundForObj(share/total,getDecimal("process"));
+           let a = scMathUtil.roundForObj(step * p,getDecimal("process")) ;
+           return scMathUtil.roundForObj(ltValue + a,getDecimal("feeRate"))
+       }
+   },
+
     cascadeSetRates:function(selectedItem,sourceRow,mapID,selectMap){
         let items=[];
         items.push({rateIndex:sourceRow,rate:selectedItem});
@@ -1048,6 +1150,7 @@ $(function(){
         let me = feeRateObject;
         $(e.relatedTarget.hash).removeClass('active');
         if(me.mainFeeRateSpread == null)  me.initFeeRateSpread(0);
+        subRateObject.initSubRateSpread();
         me.showMainFeeRateData();
         me.loadPageContent();
     });

+ 7 - 5
web/building_saas/main/js/views/glj_view.js

@@ -78,7 +78,7 @@ var gljOprObj = {
                         me.gljCurTypeId = treeNode.ID;
                         /* me.filterLibGLJSheetData();
                          me.showLibGLJSheetData();*/
-                        me.loadPageData(gljOprObj.gljLibSheet, true, 0);
+                        me.loadPageData(gljOprObj.gljLibSheet, true, 0, $('#glj-lib-select').val());
                         //gljOprObj.initSelection({row: me.gljLibSheet.getActiveRowIndex()});
                     }
                 } else {
@@ -338,8 +338,8 @@ var gljOprObj = {
             me.loadPageData(args.sheet, false, curRecord.length);
         }
     },
-    loadPageData: function (sheet, reset, index) {
-        let condition = this.getPagingCondition(false, reset, false, index, false, null);
+    loadPageData: function (sheet, reset, index, gljLibID = null) {
+        let condition = this.getPagingCondition(false, reset, false, index, false, gljLibID);
         let getPagingFun =  typeof unitPriceObj != 'undefined' ? unitPriceObj.getGLJDataPaging:projectObj.project.ration_glj.getGLJDataPaging;
         getPagingFun(condition, function (data) {
             sheetCommonObj.appendData(sheet, condition.index, 0, gljOprObj.gljLibSheetSetting, data);
@@ -1179,6 +1179,7 @@ var gljOprObj = {
             // 搜索文本
             search: ''
         };
+        const cptLibReg = new RegExp(commonConstants.COMPLEMENTARY_LIB);
         // 触发入口(添加、替换、批量替换)
         const actionType = $('#actionType').val();
         const replaceActions = ['replace', 'm_replace'];
@@ -1201,12 +1202,13 @@ var gljOprObj = {
         } else {
             if (condition.replace) {
                 const selected = gljOprObj.sheetData[gljContextMenu.selectedRow];
-                condition.gljLibID =  selected.from === 'cpt' || !selected.repositoryId ? commonConstants.COMPLEMENTARY_LIB : selected.repositoryId;
+                const cptLibID = selected.fromUser && selected.fromUser !== userID ? `${commonConstants.COMPLEMENTARY_LIB}*${selected.fromUser}` : commonConstants.COMPLEMENTARY_LIB;
+                condition.gljLibID =  selected.from === 'cpt' || !selected.repositoryId ? cptLibID : selected.repositoryId;
             } else {
                 condition.gljLibID = initLibs ? null : $('#glj-lib-select').val();
             }
         }
-        if (condition.gljLibID === commonConstants.COMPLEMENTARY_LIB) {
+        if (cptLibReg.test(condition.gljLibID)) {
             condition.type = this.pagingType.complementaryGLJs;
         }
         if (init) {

+ 173 - 14
web/building_saas/main/js/views/importBills.js

@@ -50,10 +50,13 @@ const importBills = (function(){
 
     //上传类型
     const uploadType = {
+        general: 'general',
         lj: 'lj',
         gld: 'gld',
     };
 
+    let curFileType = uploadType.general;
+
     //设置导入表内容(选择导入位置)
     //@param {Object}workBook
     function setImportSheetsInfo(sheets){
@@ -131,14 +134,14 @@ const importBills = (function(){
     }
 
     //提取excel表头列名与列下标映射
-    //@
     function getColMapping(dataTable){
         //获取表头
         function getHeadRow(dataTable){
+            const headTexts = [colText.serialNo[0], ...colText.code];
             for(let rowIdx in dataTable ){
                 for(let colIdx in dataTable[rowIdx]){
                     let cellData = dataTable[rowIdx][colIdx]['value'];
-                    if(cellData && _deESC(cellData) === colText.serialNo[0]){
+                    if(cellData && headTexts.includes(_deESC(cellData))){
                         return dataTable[rowIdx];
                     }
                 }
@@ -194,8 +197,16 @@ const importBills = (function(){
         return colMapping;
     }
 
+    // 根据列设置判断从excel表上传界面,上传的具体类型是什么:自定义通用表和09表使用了同一个按钮,因此在获取列设置的时候,还要再区分general和lj
+    function getExactlyFileType(colMapping) {
+        // 只需要区分自定义通用表和09表
+        return !_isDef(colMapping.serialNo) && !_isDef(colMapping.money)
+            ? uploadType.general
+            : uploadType.lj;
+    }
+
     //是否是有效的表头列格式,只要含有各表需要的列就行,不严格控制多少列
-    function isValidSheet(colMapping, fileType){
+    function isValidSheet(colMapping){
         function hasField(field, all){
             for(let i of all){
                 if(field === i){
@@ -205,11 +216,13 @@ const importBills = (function(){
             return false;
         }
         let needFields;
-        if(fileType === uploadType.lj){
+        if (curFileType === uploadType.general) {
+            // 自定义通用表:项目编码	项目名称	项目特征	计量单位	工程量
+            needFields = ['code', 'name', 'itemCharacterText', 'unit', 'quantity'];
+        } else if(curFileType === uploadType.lj){
             //09表:序号、项目编码、项目名称、项目特征、计量单位、工程量、金额
             needFields = ['serialNo', 'code', 'name', 'money'];
-        }
-        else {
+        } else {
             //广联达表:序号、项目编码、项目名称、项目特征、计量单位、工程量、工程量明细、费用明细
             needFields = ['serialNo', 'code', 'name', 'itemCharacterText', 'unit', 'quantity', 'quantityDetail', 'feeDetail'];
         }
@@ -225,8 +238,9 @@ const importBills = (function(){
     //获取要无效和有效导入表
     //@param {Array}importSheetInfo 勾选要导入的表
     function getImportSheets(sheets, sheetsInfo, fileType){
+        curFileType = fileType; // 自定义通用表和09表使用了同一个按钮,因此在获取列设置的时候,还要再区分general和lj
+        let isGetType = false;
         let rst = {invalidSheets: [], validSheets: {fbfx: [], jscsxm: [], zzcsxm: []}};
-
         for(let sheetInfo of sheetsInfo){
             let sheet = getSheetByIndex(sheets, sheetInfo.index);
             if(!sheet){
@@ -237,12 +251,17 @@ const importBills = (function(){
                 rst.invalidSheets.push(sheet.name);
                 continue;
             }
-            //获取表的列设置确定导入的格式是否合法(09、广联达)
+            //获取表的列设置确定导入的格式是否合法(通用excel、09、广联达)
             let colMapping = getColMapping(sheet.data.dataTable);
-            if(!isValidSheet(colMapping, fileType)){
+            // 以第一个有效的sheet作为类型基准
+            if (!isGetType && fileType === uploadType.lj) {
+                curFileType = getExactlyFileType(colMapping);
+            }
+            if(!isValidSheet(colMapping)){
                 rst.invalidSheets.push(sheet.name);
                 continue;
             }
+            isGetType = true;
             //合法的表
             sheet.colMapping = colMapping;
             //将合法的表按导入位置分类当做一个表来处理
@@ -269,7 +288,9 @@ const importBills = (function(){
         let withingD = false;
         let validData = [];
         function isHead(rData){
-            return rData[colMapping.serialNo] && _deESC(rData[colMapping.serialNo]['value']) === colText.serialNo[0];
+            return curFileType === uploadType.general
+                ? rData[colMapping.code] && colText.code.includes(_deESC(rData[colMapping.code]['value']))
+                : rData[colMapping.serialNo] && _deESC(rData[colMapping.serialNo]['value']) === colText.serialNo[0];
         }
         function isTail(rowData){
             for(let colIdx in rowData){
@@ -306,6 +327,147 @@ const importBills = (function(){
         let preRootID = -1,
             preLeafID = -1,
             preID = -1;
+        //父节点
+        function isRoot(rData){
+            //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
+            const code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+            const unit = rData[colMapping.unit] ? _deESC(rData[colMapping.unit]['value']) : '';
+            if (curFileType === uploadType.general) {
+                // 通用表:项目编码非12位或者无单位的
+                return !_isDef(code) || !/\w{12}/.test(code) || !_isDef(unit);
+            } else {
+                // 09、广联达表:1.无序号 2有编码
+                const serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+                return !_isDef(serialNo) && _isDef(code);
+            }
+        }
+        //子节点
+        function isLeaf(rData){
+            if (curFileType === uploadType.general) {
+                // 通用表:项目编码12位且有单位的
+                const code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+                const unit = rData[colMapping.unit] ? _deESC(rData[colMapping.unit]['value']) : '';
+                return _isDef(code) && /\w{12}/.test(code) && _isDef(unit);
+            } else {
+                // 09、广联达表:有序号
+                const serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+                return _isDef(serialNo);
+            }
+        }
+        //续数据:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
+        function isExtend(preData, rData){
+            if (curFileType === uploadType.general) {
+                // 通用表:1. 前数据有效 2.无编码 3.无单位 4.有名称或特征
+                let code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+                let unit = rData[colMapping.unit] ? _deESC(rData[colMapping.unit]['value']) : '';
+                let name = rData[colMapping.name] ? _deESC(rData[colMapping.name]['value']) : '';
+                let itemCharacterText = rData[colMapping.itemCharacterText] ? _deESC(rData[colMapping.itemCharacterText]['value']) : '';
+                return _isDef(preData) && (isRoot(preData) || isLeaf(preData)) && !_isDef(unit) && !_isDef(code) && (_isDef(name) || _isDef(itemCharacterText));
+            } else {
+                // 09、广联达表:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
+                let serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+                let code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+                let name = rData[colMapping.name] ? _deESC(rData[colMapping.name]['value']) : '';
+                let itemCharacterText = rData[colMapping.itemCharacterText] ? _deESC(rData[colMapping.itemCharacterText]['value']) : '';
+                return _isDef(preData) && (isRoot(preData) || isLeaf(preData)) && !_isDef(serialNo) && !_isDef(code) && (_isDef(name) || _isDef(itemCharacterText));
+            }
+        }
+        function getBillType(rData, flag){
+            if(flag === fixedFlag.CONSTRUCTION_TECH || flag === fixedFlag.CONSTRUCTION_ORGANIZATION){
+                return billType.BILL;
+            }
+            else if(flag === fixedFlag.SUB_ENGINERRING){
+                return isLeaf(rData) ? billType.FX : billType.FB;
+            }
+            return null;
+        }
+        let preData = null;
+        for(let r = 0; r < validData.length; r++){
+            let rData = validData[r];
+            if(flag == fixedFlag.CONSTRUCTION_TECH && rData[colMapping.name] && rData[colMapping.name]['value'] === '施工技术措施项目'
+                || flag == fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] && rData[colMapping.name]['value'] === '施工组织措施项目'){
+                continue;
+            }
+            //过滤无效数据
+            if(!isRoot(rData) && !isLeaf(rData) && !isExtend(preData, rData)){
+                continue;
+            }
+            if(isExtend(preData, rData)){
+                let preBill = billIdx[preID];
+                if(preBill){
+                    //合并续数据
+                    preBill.code += rData[colMapping.code] && rData[colMapping.code]['value'] && _isDef(_deESC(rData[colMapping.code]['value'])) ? rData[colMapping.code]['value'] : '';
+                    preBill.name += rData[colMapping.name] && rData[colMapping.name]['value'] && _isDef(_deESC(rData[colMapping.name]['value'])) ? rData[colMapping.name]['value'] : '';
+                    preBill.itemCharacterText += rData[colMapping.itemCharacterText] && rData[colMapping.itemCharacterText]['value'] && _isDef(_deESC(rData[colMapping.itemCharacterText]['value']))
+                        ? '\n' + _deNR(rData[colMapping.itemCharacterText]['value']) : '';
+                    preBill.unit += rData[colMapping.unit] && rData[colMapping.unit]['value'] && _isDef(_deESC(rData[colMapping.unit]['value'])) ? rData[colMapping.unit]['value'] : '';
+                    preBill.quantity += rData[colMapping.quantity] && rData[colMapping.quantity]['value'] && _isDef(_deESC(rData[colMapping.quantity]['value'])) ? rData[colMapping.quantity]['value'] : '';
+                }
+            } else {
+                let newID = uuid.v1();
+                let pID = -1;
+                let preBill = null;
+                let preRoot = null,
+                    preLeaf = null;
+                let nodeType = 'root';//后端以此标记来设置ParentID
+                let preSerialBill = billIdx[preID];
+                if(isRoot(rData)){
+                    //pID = 'fixedBillID';
+                    preBill = billIdx[preRootID];
+                    preRoot = billIdx[preRootID];
+                } else if(isLeaf(rData)){
+                    nodeType = 'leaf';
+                    //pID = preRootID !== -1 ? preRootID : fixedBill.ID;
+                    preBill = billIdx[preLeafID];
+                    preLeaf = billIdx[preLeafID];
+                }
+                //set bill data
+                billIdx[newID] = {
+                    nodeType: nodeType,
+                    ID: newID, ParentID: pID, NextSiblingID: -1,
+                    code: rData[colMapping.code] && rData[colMapping.code]['value'] ? _deESC(rData[colMapping.code]['value']) : '',
+                    name: rData[colMapping.name] && rData[colMapping.name]['value'] ? _deESC(rData[colMapping.name]['value']) : '',
+                    itemCharacterText: rData[colMapping.itemCharacterText] && rData[colMapping.itemCharacterText]['value'] ? _deNR(rData[colMapping.itemCharacterText]['value']) : '',
+                    itemCharacter: [],
+                    jobContentText: '',
+                    jobContent: [],
+                    programID: null,
+                    unit: rData[colMapping.unit] && rData[colMapping.unit]['value'] ? _deESC(rData[colMapping.unit]['value']) : '',
+                    quantity: rData[colMapping.quantity] && rData[colMapping.quantity]['value'] ? _deESC(rData[colMapping.quantity]['value']) : '',
+                    quantityEXP: rData[colMapping.quantity] && rData[colMapping.quantity]['value'] ? _deESC(rData[colMapping.quantity]['value']) : '',
+                    //安全文明
+                    flags: flag === fixedFlag.CONSTRUCTION_ORGANIZATION && (rData[colMapping.name] && (rData[colMapping.name]['value'] === '安全文明施工专项费用' || rData[colMapping.name]['value'] === '安全文明施工费')) ?
+                        [{fieldName: 'fixed', flag: fixedFlag.SAFETY_CONSTRUCTION}] : [],
+                    fees: [],
+                    projectID: projectID,
+                    type: getBillType(rData, flag)};
+                //update preBill NextSibling
+                if(nodeType === 'root' && preRoot){
+                    preRoot.NextSiblingID = newID;
+                } else if(nodeType === 'leaf' && preLeaf && preSerialBill && preSerialBill.nodeType === preLeaf.nodeType){
+                    preLeaf.NextSiblingID = newID;
+                }
+               /* if(preBill){
+                    preBill.NextSiblingID = newID;
+                }*/
+                //set new preID
+                preID = newID;
+                preRootID = isRoot(rData) ? newID : preRootID;
+                preLeafID = isLeaf(rData) ? newID : preLeafID;
+            }
+            preData = rData;
+        }
+        for(let i in billIdx){
+            rst.push(billIdx[i]);
+        }
+        return rst;
+    }
+    /* function parseToBillData(validData, colMapping, flag, projectID){
+        let rst = [];
+        let billIdx = {};
+        let preRootID = -1,
+            preLeafID = -1,
+            preID = -1;
         //父节点:1.无序号 2有编码
         function isRoot(rData){
             //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
@@ -404,9 +566,6 @@ const importBills = (function(){
                 else if(nodeType === 'leaf' && preLeaf && preSerialBill && preSerialBill.nodeType === preLeaf.nodeType){
                     preLeaf.NextSiblingID = newID;
                 }
-               /* if(preBill){
-                    preBill.NextSiblingID = newID;
-                }*/
                 //set new preID
                 preID = newID;
                 preRootID = isRoot(rData) ? newID : preRootID;
@@ -418,7 +577,7 @@ const importBills = (function(){
             rst.push(billIdx[i]);
         }
         return rst;
-    }
+    } */
 
     function getImportData(validSheets, projectID){
         let rst = {fbfx: [], jscsxm: [], zzcsxm: []};

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

@@ -108,7 +108,7 @@ let indexObj= {
         }
        // disableRightMenu("glj_from_sheet",this.spread);
         if(projectReadOnly){
-            disableSpread(this.spread);
+            sheetCommonObj.disableSpread(this.spread);
         }
         sheetCommonObj.refreshWorkbookDelDefer(this.spread, 100);
     },

+ 2 - 2
web/building_saas/main/js/views/item_increase_fee_view.js

@@ -50,7 +50,7 @@ let itemIncreaseFeeObj = {
         this.settingSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onItemValueChange);
         this.settingSheet.name('itemIncreaseFee_sheet');
         if(projectReadOnly){
-            disableSpread(this.settingSpread);
+            sheetCommonObj.disableSpread(this.settingSpread);
         }
     },
     initScopeSpread:function () {
@@ -62,7 +62,7 @@ let itemIncreaseFeeObj = {
         this.scopeSpread.bind(GC.Spread.Sheets.Events.ButtonClicked, this.onScopeCheckBoxClick);
         this.scopeSheet.name('scopeSheet');
         if(projectReadOnly){
-            disableSpread(this.scopeSpread);
+            sheetCommonObj.disableSpread(this.scopeSpread);
         }
     },
     getSelectedItem:function () {

+ 65 - 15
web/building_saas/main/js/views/main_tree_col.js

@@ -69,12 +69,21 @@ let MainTreeCol = {
             return node.data.feeRate;
         },
         maxPrice:function (node) {
-            if(node.data.outPutMaxPrice == true){
-                if(node.data.maxPrice === null&&node.data.feesIndex &&node.data.feesIndex.common) return node.data.feesIndex.common.unitFee?node.data.feesIndex.common.unitFee:"";
+            if(node.data.outPutMaxPrice == true || node.data.outPutLimitPrice == true){
+                if(node.data.maxPrice === null&&node.data.feesIndex &&node.data.feesIndex.common) {
+                    return node.data.feesIndex.common.unitFee?node.data.feesIndex.common.unitFee:""
+                };
                 return node.data.maxPrice?node.data.maxPrice:"";
             }
             return "";
-        }
+        },
+        minPrice:function (node) {
+            if(node.data.outPutLimitPrice == true){
+                if(node.data.minPrice === null&&node.data.feesIndex &&node.data.feesIndex.common) return node.data.feesIndex.common.unitFee?node.data.feesIndex.common.unitFee:"";
+                return node.data.minPrice?node.data.minPrice:"";
+            }
+            return "";
+        },
     },
     readOnly: {
         // Vincent, 2018-01-09
@@ -308,10 +317,16 @@ let MainTreeCol = {
         },
         maxPrice:function (node) {
             // 对于投标项目只读
-            if(+$('#fileKind').val() === _fileKind.tender) {
+            /* if(+$('#fileKind').val() === _fileKind.tender) {
                 return true;
-            }
-            return node.data.outPutMaxPrice !== true;
+            } */
+            const hasOutPutMaxPriceCol = !!projectObj.project.projSetting.main_tree_col.cols.find(item => item.data.field === 'outPutMaxPrice');
+            const hasOutPutLitmitPriceCol = !!projectObj.project.projSetting.main_tree_col.cols.find(item => item.data.field === 'outPutLitmitPrice');
+            return projectObj.project.projectInfo.importedByInterface || (hasOutPutMaxPriceCol && (node.data.outPutMaxPrice === undefined || node.data.outPutMaxPrice === false)) || (hasOutPutLitmitPriceCol && (node.data.outPutLimitPrice === undefined || node.data.outPutLimitPrice === false));
+        },
+        minPrice: function (node) {
+            const hasOutPutLitmitPriceCol = !!projectObj.project.projSetting.main_tree_col.cols.find(item => item.data.field === 'outPutLitmitPrice');
+            return projectObj.project.projectInfo.importedByInterface || (hasOutPutLitmitPriceCol && (node.data.outPutLimitPrice === undefined || node.data.outPutLimitPrice === false));
         },
         // 超高降效
         forOverHeight: function (node) {
@@ -427,7 +442,11 @@ let MainTreeCol = {
             return sheetCommonObj.getCusButtonCellType(projectObj.onCommonTotalFeeButtonClick,readOnly) //projectObj.getCommonTotalFeeCellType();
         },
         mainBills:function (node) {
-            if(MainTreeCol.mainBillsEnable(node)) return sheetCommonObj.getCheckBox(true);
+            if(MainTreeCol.mainBillsEnable(node)) {
+                return projectObj.project.projectInfo.property.lockBills 
+                    ? sheetCommonObj.getReadOnlyCheckBox()
+                    : sheetCommonObj.getCheckBox(true)
+            };
       },
       lockUnitPrice: function (node) {
           //仅未使用基数计算的清单有效
@@ -444,11 +463,16 @@ let MainTreeCol = {
         },
         outPutMaxPrice:function (node) {
             if(MainTreeCol.mainBillsEnable(node)) {
-                // 投标项目,复选框不可改变
-                const checkBox = +$('#fileKind').val() === _fileKind.tender
+                return projectObj.project.projectInfo.importedByInterface || projectObj.project.projectInfo.property.lockBills
                     ? sheetCommonObj.getReadOnlyCheckBox()
-                    : sheetCommonObj.getCheckBox(true);
-                return checkBox;
+                    : sheetCommonObj.getCheckBox();
+            }
+        },
+        outPutLimitPrice:function (node) {
+            if(MainTreeCol.mainBillsEnable(node)) {
+                return projectObj.project.projectInfo.importedByInterface || projectObj.project.projectInfo.property.lockBills
+                ? sheetCommonObj.getReadOnlyCheckBox()
+                : sheetCommonObj.getCheckBox();
             }
         },
         overHeight: function (node) {
@@ -464,7 +488,21 @@ let MainTreeCol = {
             dynamicCombo._maxDropDownItems = 10;
             dynamicCombo.items(items);
             return dynamicCombo;
-        }
+        },
+        /* maxPrice: function (node, setting) {
+            const tips = () => {
+                const maxPriceRate = projectObj.project.property.maxPriceRate || 0;
+                return node.data.maxPrice ? `最高限价=清单综合单价*(1+${maxPriceRate}%)` : '';
+            };
+            return sheetCommonObj.getTipsText(tips, setting, node);
+        },
+        minPrice: function (node, setting) {
+            const tips = () => {
+                const minPriceRate = projectObj.project.property.minPriceRate || 0;
+                return  node.data.minPrice ? `最低限价=清单综合单价*(1-${minPriceRate}%)` : '';
+            };
+            return sheetCommonObj.getTipsText(tips, setting, node);
+        } */
     },
     mainBillsEnable:function (node) {
         let Bills = projectObj.project.Bills;
@@ -582,10 +620,19 @@ let MainTreeCol = {
     },
     // 字体颜色w
     foreColor: {
-        // 清单综合单价>最高限价时,标红显示
+        // 清单综合单价>最高限价 清单综合单价<最低限价 时,标红显示
         'feesIndex.common.unitFee': function (node) {
             const color = 'red';
-            return calcTools.unitFeeGTMaxPrice(node, 'common.unitFee') ? color : null;
+            return calcTools.unitFeeGTMaxPrice(node, 'common.unitFee') || calcTools.unitFeeLTMinPrice(node, 'common.unitFee') ? color : null;
+        }
+    },
+    setNameCusButton:function(cell,node){
+        let isFBNode = node.sourceType == ModuleNames.bills&&node.data.type == billType.FB;
+        if(isFBNode){
+           let CELL =sheetCommonObj.getCusButtonCellType(()=>{
+            $('#selectFBDiv').modal('show');
+            })
+            cell.cellType(CELL); 
         }
     }
 };
@@ -755,7 +802,7 @@ $('#poj-set').on('shown.bs.modal', function (e) {
     if (!colSettingObj.settingSpread) {
         colSettingObj.initSettingSpread();
         if(projectReadOnly){
-            disableSpread(colSettingObj.settingSpread);
+            sheetCommonObj.disableSpread(colSettingObj.settingSpread);
         }
     }
     if($('#tab_display_setting').hasClass('active')){
@@ -767,6 +814,9 @@ $('#poj-set').on('shown.bs.modal', function (e) {
             }
         });
     }
+    // 清单限价
+    $('#max-price-rate') && $('#max-price-rate').val(projectObj.project.property.maxPriceRate || 0);
+    $('#min-price-rate') && $('#min-price-rate').val(projectObj.project.property.minPriceRate || 0);
 });
 
 $('#poj-set').on('hidden.bs.modal', function (e) {

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

@@ -61,7 +61,7 @@ let mbzm_obj={
         this.spread.bind(GC.Spread.Sheets.Events.ButtonClicked, installationFeeObj.onPositionButtonClick);//共用一个位置选择器
         this.sheet.name('ration_template');
         if(projectReadOnly){
-            disableSpread(this.spread);
+            sheetCommonObj.disableSpread(this.spread);
         }
     },
     refresh:function () {

+ 4 - 4
web/building_saas/main/js/views/project_glj_view.js

@@ -208,7 +208,7 @@ let projectGljObject = {
     this.getInfoPriceOptions();
 
     if (projectReadOnly) {
-      disableSpread(this.infoPriceSpread);
+      sheetCommonObj.disableSpread(this.infoPriceSpread);
     } else {
       this.initInfoPriceRightClick();
     }
@@ -224,7 +224,7 @@ let projectGljObject = {
     //this.infoClassSheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.infoClassSelectionChange);
     this.infoClassSheet.bind(GC.Spread.Sheets.Events.CellClick,this.infoClassClick);
     if (projectReadOnly) {
-      disableSpread(this.infoClassSpread);
+      sheetCommonObj.disableSpread(this.infoClassSpread);
     } 
   },
   initSpreads: function () {
@@ -247,7 +247,7 @@ let projectGljObject = {
       if (this.projectGljSetting.view.lockColumns) {
         this.projectGljSetting.view.lockColumns = null;
       }
-      disableSpread(this.projectGljSpread);
+      sheetCommonObj.disableSpread(this.projectGljSpread);
     }
   },
   initProjectGljSheet: function () {
@@ -282,7 +282,7 @@ let projectGljObject = {
       if (this.mixRatioSetting.view.lockColumns) {
         this.mixRatioSetting.view.lockColumns = null;
       }
-      disableSpread(this.mixRatioSpread);
+      sheetCommonObj.disableSpread(this.mixRatioSpread);
     }
   },
   initRelatedRationSheet: function () {

+ 2 - 1
web/building_saas/main/js/views/project_info.js

@@ -76,6 +76,7 @@ var projectInfoObj = {
     },
     showProjectInfo: function (data) {
         if (data) {
+            $('#tzCharacterText').prop('readonly', projectObj.project.projectInfo.property.lockBills);
             // 清单子界面的模块:清单指引、清单精灵
             for (let billsGuidanceLib of data.engineeringInfo.billsGuidance_lib) {
                 BillsSub.switchModule(billsGuidanceLib.type);
@@ -88,7 +89,7 @@ var projectInfoObj = {
             $('#fullpath').html(this.getFullPathHtml(data));
             // 分享给
             $('#init-share').click(() => {
-                SHARE_TO.initModal(projectObj.project.ID());
+                SHARE_TO.initModal(SHARE_TO.Mode.PROJECT, projectObj.project.ID());
                 $('#share-tip').tooltip('hide');
             });
             SHARE_TO.handleEventListener();

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

@@ -523,7 +523,7 @@ $(document).ready(function () {
         basicInfoView.buildSheet();
         basicInfoView.showData(basicInfoView.datas);
         if(projectReadOnly){
-            disableSpread(basicInfoView.workBook);
+            sheetCommonObj.disableSpread(basicInfoView.workBook);
         }
     });
 

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

@@ -353,7 +353,7 @@ $(document).ready(function () {
         billsDecimalView.buildSheet();
         billsDecimalView.showData(billsDecimalView.cache);
         if(projectReadOnly){
-            disableSpread(billsDecimalView.workBook);
+            sheetCommonObj.disableSpread(billsDecimalView.workBook);
         }
     });
 

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

@@ -51,7 +51,7 @@ let labourCoeView = {
         sheet.resumeEvent();
         sheet.resumePaint();
         if(projectReadOnly){
-            disableSpread(me.spread);
+            sheetCommonObj.disableSpread(me.spread);
         }
     },
     loadCrossData(datas){          // 交叉表:树结构转换二维表显示,行列转换

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

@@ -506,7 +506,7 @@ $(document).ready(function () {
         projFeatureView.buildSheet();
         projFeatureView.showData(projFeatureView.datas);
         if (projectReadOnly) {
-            disableSpread(projFeatureView.workBook);
+            sheetCommonObj.disableSpread(projFeatureView.workBook);
         }
     });
 

+ 124 - 44
web/building_saas/main/js/views/project_view.js

@@ -550,7 +550,7 @@ var projectObj = {
                 project.calcProgram.calcAndSave(node);
             } else {
                 if (node.sourceType === project.Bills.getSourceType()) {
-                    if(fieldName == "maxPrice"){
+                    if(fieldName === "maxPrice" || fieldName === 'minPrice'){
                         if(value == null){
                             value = "";
                         }else {
@@ -646,7 +646,7 @@ var projectObj = {
             if(node.data.type != rationType.ration) return ;//只有定额类型才需要自动展开,其它都不用 
             let libID = node.data.libID;
             if (node.data.from === 'cpt') {
-                libID = rationLibObj.compleRationLibId;
+                libID = node.data.fromUser ? `${rationLibObj.compleRationLibId}*${node.data.fromUser}` : rationLibObj.compleRationLibId;
             }
             if(!rationLibObj.tree){
                 if(isDef(libID)) sessionStorage.setItem('stdRationLib', libID);
@@ -1032,7 +1032,7 @@ var projectObj = {
                 let mainTabFocus = sessionStorage.getItem('mainTab') ? sessionStorage.getItem('mainTab') : '#tab_zaojiashu';
                 $(`${mainTabFocus}`).click();
                 if(projectReadOnly){
-                    disableSpread(that.mainSpread);
+                    sheetCommonObj.disableSpread(that.mainSpread);
                 }
 
                 // 检查旧项目是否有调价数据,没有则自动生成 ----- 有种情况不行,如下:
@@ -1273,7 +1273,7 @@ var projectObj = {
                     newData.push({ itemQuery: null, rationType: rationType.ration });
                 }
                 await project.Ration.addMultiRation(newData);
-                projectObj.setActiveCell('quantity', true);
+                projectObj.setActiveCell('code', true);
             } catch (err) {
                 console.log(err);
                 if (!$('hintBox_form').is(':visible')) {
@@ -1322,6 +1322,14 @@ var projectObj = {
                             if(selected.data.type==billType.FB){
                                 return false;
                             }
+                            if(selected.data.type==billType.FX ||  selected.data.type==billType.BX){// 当前定位在分项(包括补项),
+                                if(selected.preSibling){//判断分项有无前兄弟?
+                                    //有,则判断父项的类型是什么,是分部,右键有效显示,点击则按②执行。
+                                   return selected.parent.data.type!==billType.FB
+                                }
+                                return false;// 无,右键有效显示,
+                            }
+
                             if(isFlag(selected.data)&&selected.data.flagsIndex.fixed.flag==fixedFlag.SUB_ENGINERRING){//焦点行是分部分项工程
                                 if(selected.children.length>0){
                                     return selected.children[0].data.type==billType.FX ||selected.children[0].data.type==billType.BX;//焦点行是分部分项工程,且子项是分项或补项
@@ -1333,8 +1341,15 @@ var projectObj = {
                         return true;//除了清单,其它类型都只读
                     },
                     callback: function (key, opt) {
-                        ProjectController.addFB(project, controller);
-                        projectObj.selectColAndFocus(project.mainTree.selected);
+                        let selected = project.mainTree.selected;
+                        if(selected.data.type==billType.FX || selected.data.type==billType.BX){
+                             //添加成分项的父亲
+                             ProjectController.addFXParent(selected);   
+                        }else{//正常添加分部
+                            ProjectController.addFB(project, controller);
+                            projectObj.selectColAndFocus(project.mainTree.selected);
+                        }
+                       
                     },
                     visible: function(key, opt){
                         if(project.mainTree.selected){
@@ -1500,6 +1515,24 @@ var projectObj = {
                         return true;
                     }
                 },
+                "insertMainMaterial": {
+                    name: "按清单名称插入主材",//插入量价不需要自动定位到编号列
+                    icon: 'fa-sign-in',
+                    disabled: function () {
+                        if (projectReadOnly) {
+                            return true;
+                        }
+                        return !project.Ration.canAdd(project.mainTree.selected);
+                    },
+                    callback: function (key, opt) {
+                        //名称取清单名称,单位取清单单位
+                        let selected = project.mainTree.selected;
+                        let billNode = selected;
+                        if(selected.sourceType==project.Ration.getSourceType()) billNode = selected.parent;
+                        let ext = {name:billNode.data.name,unit:billNode.data.unit};
+                        project.Ration.insertVolumePrice(gljType.MAIN_MATERIAL,ext);
+                    },
+                },
                 "spr2": '--------',
                 "calc_installation_fee": {
                     name: "计取安装费用",
@@ -2022,7 +2055,7 @@ var projectObj = {
             projectObj.onIsEstimateClick(node,info);
         }else if(fieldName == "evaluationProject"){
             projectObj.onEvaluationProjectClic(node,info);
-        }else if(fieldName == "mainBills"||fieldName == "outPutMaxPrice"||fieldName=="areaIncreaseFee"||fieldName == "lockUnitPrice"){
+        }else if(fieldName == "mainBills"||fieldName == "outPutMaxPrice"||fieldName=="outPutLimitPrice"||fieldName=="areaIncreaseFee"||fieldName == "lockUnitPrice"){
             projectObj.onCasCadeButtonClick(node,info,fieldName);
         }
     },
@@ -2389,6 +2422,7 @@ $("a[name='lockBills']").click(function () {//点击锁定/解锁清单
         controller.refreshTreeNode(nodes);
         projectObj.mainController.setTreeSelected(selected);//触发树节点选中事件
         projectObj.loadLockBillsButton();
+        $('#tzCharacterText').prop('readonly', lockBills);
         if(!projectReadOnly){
             pageCCOprObj.refreshRuleTools(lockBills);
         }
@@ -2724,6 +2758,15 @@ $('#property_ok').click(function () {
         projectObj.project.property.projectFeature = saveData;
         properties['property.projectFeature'] = saveData;
     }
+    // 清单限价
+    const maxPriceRate = $('#max-price-rate') && +$('#max-price-rate').val();
+    if (commonUtil.isNumber(maxPriceRate) && maxPriceRate !== projectObj.project.property.maxPriceRate) {
+        properties['property.maxPriceRate'] = maxPriceRate;
+    }
+    const minPriceRate = $('#min-price-rate') && +$('#min-price-rate').val();
+    if (commonUtil.isNumber(minPriceRate) && minPriceRate !== projectObj.project.property.minPriceRate) {
+        properties['property.minPriceRate'] = minPriceRate;
+    }
     //清单工程量精度
     let newBillsDecimalDatas = billsDecimalView.toBillsDecimalDatas(billsDecimalView.cache);
     if(billsDecimalView.toUpdate(billsQuanDecimal.datas, newBillsDecimalDatas)){
@@ -2826,6 +2869,12 @@ $('#property_ok').click(function () {
                 window.location.href = '/main?project=' + projectID;
             }
             else{
+                if(mixDatas.properties.hasOwnProperty('property.maxPriceRate')){
+                    projectObj.project.property.maxPriceRate = maxPriceRate;
+                }
+                if(mixDatas.properties.hasOwnProperty('property.minPriceRate')){
+                    projectObj.project.property.minPriceRate = minPriceRate;
+                }
                 if(mixDatas.properties.hasOwnProperty('property.basicInformation')){
                     basicInfoView.orgDatas = basicInfoView.toViewDatas(mixDatas.properties['property.basicInformation']);
                 }
@@ -3026,14 +3075,18 @@ function canInsertRationNode(selected) {//判断是否能插入定额、量价
 }
 
 //导入类型(09表、广联达)
-const uploadType = {lj: 'lj', gld: 'gld'};
+const uploadType = {general: 'general', lj: 'lj', gld: 'gld'};
 let fileType = uploadType.lj;
 
 $('#uploadLj').click(function () {
+    $('#uploadExample-general').show();
+    $('#uploadExample').text('下载示例2');
     fileType = uploadType.lj;
 });
 $('#uploadGld').click(function () {
-   fileType = uploadType.gld;
+    $('#uploadExample-general').hide();
+    $('#uploadExample').text('下载示例');
+    fileType = uploadType.gld;
 });
 
 let importJson = null;
@@ -3147,6 +3200,7 @@ $('#importConfirm').click(function () {
         });
     }
     catch (err){
+        console.log(err);
         showUploadAlert(false, err);
         $(me).removeClass('disabled');
         if($.bootstrapLoading.isLoading()){
@@ -3236,6 +3290,18 @@ function doAfterImportPosition(positionData){
         }
         ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes);
     }
+    if (positionData.insert.ration.length) {
+        const rationNodes = [];
+        positionData.insert.ration.forEach(ration => {
+            projectObj.project.Ration.datas.push(ration);
+            const rationNode = projectObj.project.mainTree.insert(ration.billsItemID, -1, ration.ID);
+            rationNode.source = ration;
+            rationNode.sourceType = projectObj.project.Ration.getSourceType();
+            rationNode.data = ration;
+            rationNodes.push(rationNode);
+        });
+        ProjectController.syncDisplayNewNodes(projectObj.mainController, rationNodes);
+    }
 }
 //导入后更新操作
 function doAfterImport(resData){
@@ -3257,8 +3323,11 @@ function doAfterImport(resData){
     }
 }
 //下载导入清单示例文件
+$('#uploadExample-general').click(function () {
+    window.location.href = `/bills/downloadExamp?type=${uploadType.general}`;
+});
 $('#uploadExample').click(function () {
-    window.location.href = `/bills/downloadExamp?type=${fileType}`;
+    window.location.href = `/bills/downloadExamp?type=${fileType === uploadType.lj ? uploadType.lj : uploadType.gld}`;
 });
 
 $(function () {
@@ -3388,7 +3457,7 @@ $(function () {
         $("#div_createBlocks").modal("hide");
     });
 
-    $("#btn_block_clone_ok").click(function () {
+    $("#btn_block_clone_ok").click(async function () {
         let projectNode = projectObj.project.mainTree.selected;
         let block = blockLibObj.mainTree.selected;
         let options = {};
@@ -3397,16 +3466,20 @@ $(function () {
         if ($('#clone_option_unit').prop("checked")) options.checkUnit = true;
         if ($('#clone_option_zeroQuantity').prop("checked")) options.zeroQuantity = true;
         if ($('#clone_option_cover').prop("checked")) options.overwriteRations = true;
-
+        
+        let nodes = [];
+        let calcNodes = [];
         switch (blockLibObj.cloneType) {
             case 1:
-                blockLibObj.oneToOneClone(projectNode, block, options);
+                nodes = await blockLibObj.oneToOneClone(projectNode, block, options);
+                calcNodes = calcNodes.concat(nodes[1]);
                 break;
             case 2:
                 let bills = calcTools.getLeafBills(projectNode);
                 for (let bill of bills){
                     calcTools.forceSelect(bill);
-                    blockLibObj.oneToOneClone(bill, block, options);
+                    nodes = await blockLibObj.oneToOneClone(bill, block, options);
+                    calcNodes = calcNodes.concat(nodes[1]);
                 };
                 break;
             case 3:
@@ -3415,11 +3488,23 @@ $(function () {
                 for (let mtmBill of mtmBills){
                     for (let mtmBlock of mtmBlocks){
                         calcTools.forceSelect(mtmBill);
-                        blockLibObj.oneToOneClone(mtmBill, mtmBlock, options);
+                        nodes = await blockLibObj.oneToOneClone(mtmBill, mtmBlock, options);
+                        calcNodes = calcNodes.concat(nodes[1]);
                     };
                 };
                 break;
         }
+        if(calcNodes.length > 0){
+            // projectObj.project.calcProgram.calcNodesAndSave(calcNodes, function () {
+            //     installationFeeObj.calcInstallationFee();
+            // });
+
+            projectObj.project.calcProgram.calcNodesAndSave(calcNodes, async function () {
+                installationFeeObj.calcInstallationFee();
+                await OVER_HEIGHT.reCalcOverHeightFee();
+                await itemIncreaseFeeObj.calcItemIncreaseFeeByNodes(nodes[0]);
+            });
+        }
     });
 
 
@@ -3454,6 +3539,8 @@ function disableTools(){
     $('#poj-settings-4').find('input').prop('disabled', 'disabled');
     //小数位数
     $('#poj-settings-decimal').find('input').prop('disabled', 'disabled');
+    // 清单限价
+    $('.limit-price-input').prop('disabled', 'disabled');
     //人工单价调整
     $('#std_labour_coe_files').prop('disabled', 'disabled');
     //呈现选项
@@ -3515,35 +3602,6 @@ function disableTools(){
     $('#cleanTender').prop('disabled', 'disabled');
     $('#calcTender').prop('disabled', 'disabled');
 }
-//项目只读,表格只读
-function disableSpread(spread){
-    spread.unbind(GC.Spread.Sheets.Events.ButtonClicked);
-    let sheetCount = spread.getSheetCount();
-    for(let i = 0; i < sheetCount; i++){
-        let sheet = spread.getSheet(i);
-        sheet.unbind(GC.Spread.Sheets.Events.ButtonClicked);
-        sheet.unbind(GC.Spread.Sheets.Events.EditStarting);
-        sheet.unbind(GC.Spread.Sheets.Events.EditEnded);
-        sheet.unbind(GC.Spread.Sheets.Events.RangeChanged);
-        sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanging);
-        sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanged);
-        sheet.unbind(GC.Spread.Sheets.Events.CellDoubleClick);
-        sheet.unbind(GC.Spread.Sheets.Events.CellClick);
-        sheet.unbind(GC.Spread.Sheets.Events.ValueChanged);
-        sheet.suspendPaint();
-        sheet.suspendEvent();
-        sheet.options.isProtected = true;
-        let rowCount = sheet.getRowCount();
-        let colCount = sheet.getColumnCount();
-        for(let row = 0; row < rowCount; row++){
-            for(let col = 0; col < colCount; col++){
-                sheet.getCell(row, col).locked(true);
-            }
-        }
-        sheet.resumePaint();
-        sheet.resumeEvent();
-    }
-}
 //根据触发改变计算基数、费率弹出框
 function changeCalcBaseFeeRate(toggle) {
     if(toggle === 'calcBase'){
@@ -3681,6 +3739,28 @@ $('#locate-other').click(() => {
     fastLocate(FastLocateType.OTHER);
 });
 
+// 清单限价
+$('.limit-price-input').bind('input', function () {
+    const limitType = $(this).data('limit');
+    const orgVal = limitType === 'max'
+        ? (projectObj.project.property.maxPriceRate || 0)
+        : (projectObj.project.property.minPriceRate || 0);
+    try {
+        const val = $(this).val();
+        if (isNaN(val)) {
+            throw '只能输入两位小数数值!';
+        }
+        const splits = val.split('.');
+        const decimalDigits = splits[1] && splits[1].length || 0;
+        if (decimalDigits > 2) {
+            const roundVal = scMathUtil.roundForObj(val, 2);
+            $(this).val(roundVal);
+        }
+    } catch (err) {
+        alert(err);
+        $(this).val(orgVal);
+    }
+});
 
 //导出接口
 ExportView.exportListener();

+ 89 - 0
web/building_saas/main/js/views/select_FB_view.js

@@ -0,0 +1,89 @@
+let selectFBObject = { 
+    spread:null,
+    datas:[],
+    setting:{
+        header: [
+            {headerName: "编号", headerWidth: 180, dataCode: "code", dataType: "String"},
+            {headerName: "名称", headerWidth: 210, dataCode: "name", dataType: "String"},
+            {headerName: "单位", headerWidth: 50, dataCode: "unit", dataType: "String", hAlign: "center"},
+        ],
+        view:{ lockColumns: ["name","code","unit"]}
+    },
+    initSpread:function(){
+        if(!this.spread){
+            this.spread = SheetDataHelper.createNewSpread($("#selectFBSheet")[0]);
+            sheetCommonObj.spreadDefaultStyle(this.spread);
+            let sheet = this.spread.getSheet(0);
+            sheetCommonObj.initSheet(sheet,this.setting,0);
+              /*   sheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onSelectionChange);
+                sheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onEngineerInfoValueChange);
+                sheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onEngineerInfoRangeChange);
+                sheet.name('engineerInfo'); */
+               
+          
+
+            if(projectReadOnly){
+                sheetCommonObj.disableSpread(this.spread);
+            }
+        }else{
+            this.spread.repaint();
+        }
+        
+    },
+    getSelectedData:function(){
+        let sel = this.spread.getSheet(0).getSelections()[0];
+        if(sel && gljUtil.isDef(sel.row)){
+            return this.datas[sel.row]
+        }
+        return null;
+    },
+    showData:function(){
+        let me = selectFBObject;
+        billsGuidance.initBillsLibs(()=>{
+            if(me.datas.length==0){
+                for(let r of billsGuidance.bills.tree.roots){
+                    selectFBObject.setDatas(r);
+                }
+            }
+            sheetCommonObj.showTreeData(me.spread.getSheet(0), me.setting, me.datas);
+        });
+    },
+    setDatas:function(node){
+        if(node.children.length > 0){//过滤叶子节点
+            let nodeData = node.data;
+            let d = {
+                ID:nodeData.ID,
+                ParentID:nodeData.ParentID,
+                code:nodeData.code,
+                name:nodeData.name,
+                unit:nodeData.unit
+            }
+            this.datas.push(d);
+            for(let c of node.children){
+                this.setDatas(c)
+            }
+        }
+    }
+}
+$(function () {
+
+    $('#selectFBDiv').on('shown.bs.modal', function (e) {
+        selectFBObject.initSpread();
+        selectFBObject.showData();
+    })
+    $("#selectFBConfirm").click(async ()=>{
+        let data = selectFBObject.getSelectedData();
+        if(!data) return;
+        let selected = projectObj.project.mainTree.selected;
+        let datas = [{
+            type:'bills',
+            data:{
+                ID:selected.data.ID,
+                name:data.name,
+                code:data.code,
+                unit:data.unit
+            }
+          }]
+          await projectObj.project.syncUpdateNodesAndRefresh(datas);
+    })
+})

+ 14 - 3
web/building_saas/main/js/views/std_billsGuidance_lib.js

@@ -383,7 +383,7 @@ const billsGuidance = (function () {
     }
     //初始选择清单指引库
     //@param {Number}libID @return {void}
-    function libInitSel(libID){
+    function libInitSel(libID,rcallback){
         //获取清单
         $.bootstrapLoading.start();
         CommonAjax.post('/billsGuidance/api/getLibWithBills', {libID: libID, isGuidanceLib: false}, function(rstData){
@@ -393,6 +393,7 @@ const billsGuidance = (function () {
                 if(doAfterLoadGuidance){
                     doAfterLoadGuidance();
                 }
+                if(rcallback)rcallback();
                 $.bootstrapLoading.end();
             };
             //获取清单库中的工作内容和项目特征
@@ -403,7 +404,7 @@ const billsGuidance = (function () {
     }
     //初始化清单指引库
     //@param {Array}libDats @return {void}
-    function initLibs(libDatas){
+    function initLibs(libDatas,callback){
         libSel.empty();
         if(!libDatas){
             return;
@@ -417,7 +418,7 @@ const billsGuidance = (function () {
             libSel.append(opt);
         }
         //初始默认选择
-        libInitSel(libSel.select().val());
+        libInitSel(libSel.select().val(),callback);
     }
     //初始化视图
     //@param {void} @return {void}
@@ -452,6 +453,15 @@ const billsGuidance = (function () {
             TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.roots, billsSheet, true);
         });
     }
+
+    function initBillsLibs(callback){
+        if(libSel.children().length === 0 && !$(this).hasClass('disabled')){
+            initLibs(projectObj.project.projectInfo.engineeringInfo.bill_lib,callback);
+        }else{
+            callback();
+        }
+    }
+    
     //各按钮监听事件
     //@return {void}
     function bindBtn(){
@@ -577,6 +587,7 @@ const billsGuidance = (function () {
         bindBtn,
         refreshWorkBook,
         locateAtBills,
+        initBillsLibs,
         bills
     };
 })();

+ 17 - 7
web/building_saas/main/js/views/std_ration_lib.js

@@ -46,7 +46,7 @@ var rationLibObj = {
             this.resultSpread.refresh();
         }
     },
-    loadStdRationLibs: function () {
+    loadStdRationLibs: async function () {
         let select = $('#stdRationLibSelect');
         select.empty();
         let ration_lib = projectObj.project.projectInfo.engineeringInfo.ration_lib;
@@ -55,6 +55,9 @@ var rationLibObj = {
             id: rationLibObj.compleRationLibId,
             name: '我的补充定额'
         });
+        const receiveList = await ajaxPost('/pm/api/getReceiveLibList', { user_id: userID, libType: commonConstants.ShareLibType.RATION_LIB });
+        const otherCompleLibs = receiveList.map(user => ({ name: `${user.real_name}的补充定额库`, isDefault: false, id: `${rationLibObj.compleRationLibId}*${user._id}`}));
+        ration_lib.push(...otherCompleLibs);
         let selectedRationLib = sessionStorage.getItem('stdRationLib');
         ration_lib.forEach(function (data) {
             let option = $('<option>').val(data.id).text(data.name);
@@ -104,8 +107,9 @@ var rationLibObj = {
         }
         return false;
     },
-    loadStdRation: function (rationLibID) {
+    loadStdRation: function (rationLibIDVal) {
         $.bootstrapLoading.start();
+        const [rationLibID, owner] = rationLibIDVal.split('*');
         rationLibObj.curLibType = rationLibID === rationLibObj.compleRationLibId ? rationLibObj.libType.complementary : rationLibObj.libType.std;
         var that = this;
         var showRationChapterTree = function (datas) {
@@ -139,7 +143,7 @@ var rationLibObj = {
             };
         };
         //type: 0-补充库 1-标准库
-        CommonAjax.post('/complementaryRation/api/getRationTree', {userId: userID, rationRepId: rationLibID, type: rationLibObj.curLibType}, function (datas) {
+        CommonAjax.post('/complementaryRation/api/getRationTree', {owner, userId: userID, rationRepId: rationLibID, type: rationLibObj.curLibType}, function (datas) {
             showRationChapterTree(datas);
             if(that.doAfterGetRationTree){
                 that.doAfterGetRationTree();
@@ -323,7 +327,8 @@ var rationLibObj = {
             }
         } */
         if (sectionID) {
-            CommonAjax.post('/complementaryRation/api/getRationGljItemsBySection', {user_Id: userID, sectionId: sectionID, type: me.curLibType}, function (datas) {
+            const [, owner] = $('#stdRationLibSelect').val().split('*');
+            CommonAjax.post('/complementaryRation/api/getRationGljItemsBySection', {user_Id: userID, sectionId: sectionID, type: me.curLibType, owner}, function (datas) {
                 const sectionItem = rationLibObj.tree.findNode(sectionID);
                 simplifyName(sectionItem, datas);
                 showDatas(datas, rationLibObj.sectionRationsSetting);
@@ -341,7 +346,8 @@ var rationLibObj = {
     onRationSpreadCellDoubleClick: function (sender, args) {
         var select = $('#stdRationLibSelect'), rationCode = args.sheet.getText(args.row, 0);
         if (rationCode !== '' && projectObj.project.Ration.canAdd(projectObj.project.mainTree.selected)) {
-            let query = {userID: userID, rationRepId: select.val(), code: rationCode};
+            const [rationLibID, owner] = select.val().split('*');
+            let query = {userID: owner || userID, rationRepId: rationLibID, code: rationCode};
             //搜索结果全部定额中双击添加定额、有可能同名不同库,更新查询的库ID
             if (rationLibObj.resultCache && rationLibObj.resultCache[args.row]) {
                 query.rationRepId = rationLibObj.resultCache[args.row].type === 'std' ? rationLibObj.resultCache[args.row].rationRepId : rationLibObj.compleRationLibId;
@@ -638,11 +644,15 @@ if($('#stdRationChapter').height() === 0 || $('#stdSectionRations').height() ===
     $('#stdSectionRations').height(270);
 }
 
-$('#stdRationTab').bind('click', function () {
+$('#stdRationTab').bind('click', async function () {
     var select = $('#stdRationLibSelect');
     rationLibObj.checkSpread();
     if (select[0].options.length === 0) {
-        rationLibObj.loadStdRationLibs();
+        try {
+            await rationLibObj.loadStdRationLibs();
+        } catch (err) {
+            alert(err);
+        }
         rationLibObj.loadStdRationContextMenu();
     };
 });

+ 89 - 4
web/building_saas/main/js/views/sub_fee_rate_views.js

@@ -92,28 +92,64 @@ var subRateObject={
             //this.subRateSheet.bind(GC.Spread.Sheets.Events.ValueChanged, me.onSheetValueChange);
             this.subRateSheet.name('subRateSheet');
         }
+      /* 20201209 改成和公路一样 
         subRateObject.datas = projectObj.project.FeeRate.getSubViewData(item);
-        subRateObject.valueMap=projectObj.project.FeeRate.getValueMap(item);
+        subRateObject.valueMap=projectObj.project.FeeRate.getValueMap(item); 
+        
+        */
         subRateObject.showSubRateData();
         disableRightMenu("subRate",this.subRateSpread);
         if(projectReadOnly){
             if(this.subRateSetting.view.lockColumns){
                 this.subRateSetting.view.lockColumns = null;
             }
-            disableSpread(this.subRateSpread);
+            sheetCommonObj.disableSpread(this.subRateSpread);
         }
     },
+  /*  20201209 改成和公路一样 
+   showSubRateData:function () {
+        this.subRateSheet.setRowCount(0);
+        sheetCommonObj.showData(this.subRateSheet, this.subRateSetting, this.datas);
+        this.subRateSheet.setRowCount(this.datas.length);
+        for(let row =0; row < this.datas.length;row++){
+            this.setComboOptionCell(row,1,this.datas[row],this.subRateSheet);
+        }
+    }, */
+
     showSubRateData:function () {
+        let preSelections = this.subRateSheet.getSelections();
+        this.datas = projectObj.project.FeeRate.getAllSubRates();
         this.subRateSheet.setRowCount(0);
         sheetCommonObj.showData(this.subRateSheet, this.subRateSetting, this.datas);
         this.subRateSheet.setRowCount(this.datas.length);
+        let parentMap=_.groupBy(this.datas, 'ParentID');
+        let visibleMap = {};
+        this.subRateSheet.suspendPaint();
+        this.subRateSheet.suspendEvent();
         for(let row =0; row < this.datas.length;row++){
             this.setComboOptionCell(row,1,this.datas[row],this.subRateSheet);
+            this.setTreeNodeCellType(this.datas,row,parentMap,visibleMap,this.subRateSheet);
+        }
+        this.subRateSheet.resumeEvent();
+        this.subRateSheet.resumePaint();
+        if(preSelections){//定位光标到之前的位置
+            this.subRateSheet.setSelection(preSelections[0].row,preSelections[0].col,preSelections[0].rowCount,preSelections[0].colCount);
         }
     },
     onSubRateSelectChanged:function (e,info) {
         info.sheet.repaint();
     },
+    setTreeNodeCellType:function (datas,row,parentMap,visibleMap,sheet) {
+        delete parentMap.undefined;//去掉无用节点
+        let treeNode = sheetCommonObj.getTreeNodeCellType(datas,row,parentMap);
+        treeNode.treeNodeType = false;
+        if((datas[row].subList && datas[row].subList.length >0)||datas[row].isSub == true ){
+            treeNode.treeNodeType = true;
+            visibleMap[datas[row].ID] = treeNode.collapsed;
+        }
+        sheet.setCellType(row, 0, treeNode, GC.Spread.Sheets.SheetArea.viewport);
+        if(visibleMap[datas[row].ParentID]) sheet.getRange(row , -1, 1, -1).visible(!visibleMap[datas[row].ParentID]);//显示或隐藏
+    },
     setComboOptionCell:function(row,col,subRate,sheet){
         let options=[];
         for(let op of subRate.optionList){
@@ -153,7 +189,9 @@ var subRateObject={
             feeRateObject.updateBySelect(rate,selectMap,mapID);
         }
     },
-    onSubRateValueChange:function (e,info) {
+/*   
+    20201209 改成和公路一样 
+onSubRateValueChange:function (e,info) {
         console.info(info);
         let me = subRateObject, selectValueList=[],selectMap={};
         if(me.datas&&me.datas.length>0){
@@ -171,7 +209,54 @@ var subRateObject={
             feeRateObject.updateBySelect(rate,selectMap,mapID);
         }
     },
-
+ */
+    onSubRateValueChange:function (e,info,feeRateCoe) {
+        let me = subRateObject, subList=null;
+        let value = info.newValue;
+        let subRate = me.datas[info.row];
+        if(subRate.editable == true || subRate.isSub == true){//是可编辑的,要检查数据类型
+            let checkResult = scMathUtil.isNumOrFormula(value);
+            if(checkResult!=null && !isNaN(checkResult)){
+                value = scMathUtil.roundForObj(checkResult,getDecimal("feeRate"));
+            }else {
+                alert('当前输入的数据类型不正确,请重新输入。');
+                return me.showSubRateData();
+            }
+        }
+        if(subRate.isSub == true){//是树节点的子节点,计算父节点的值
+            let parentRate = _.find(me.datas,{"ID":subRate.ParentID});//找到父节点
+            if(parentRate){
+                let sum = 0;
+                subList = _.cloneDeep(parentRate.subList);
+                for(let s of subList){//按比例计算
+                    if(s.ID == subRate.ID) s.value = value;//当前项要用新输入的值
+                    let v_a = scMathUtil.roundForObj(s.value * s.amount,getDecimal("process"))
+                    sum = scMathUtil.roundForObj(sum + v_a,getDecimal("feeRate"));
+                }
+                value = sum;
+                subRate = parentRate;//把subRate 指向parentRate
+            }
+        } else if(subRate.subList && subRate.subList.length > 0){//输入树节结的父结点,子结点都等于父节点的值
+            subList = _.cloneDeep(subRate.subList);
+            for(let t of subList){
+                t.value = value
+            }
+        }
+        if(me.timeStamp = null){
+            me.timeStamp =  +new Date()
+            feeRateObject.setRateFromSub(subRate,value,subList,info.newValue,feeRateCoe);
+        }else {
+            let now = +new Date();
+            if(now - me.timeStamp < 500){
+                setTimeout(function () {
+                    feeRateObject.setRateFromSub(subRate,value,subList,info.newValue,feeRateCoe);
+                },500)
+            }else {
+                feeRateObject.setRateFromSub(subRate,value,subList,info.newValue,feeRateCoe);
+            }
+            me.timeStamp = now;
+        }
+    },
     destorySpreadView:function () {
         if(this.views){
             this.views.destroy();

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

@@ -60,9 +60,9 @@ let subObj = {
         sheetCommonObj.bindEscKey(subSpread, subEscSheets);
 
         if(projectReadOnly){
-            disableSpread(subSpread);
-            disableSpread(contentOprObj.workBook);
-            disableSpread(characterOprObj.workBook);
+            sheetCommonObj.disableSpread(subSpread);
+            sheetCommonObj.disableSpread(contentOprObj.workBook);
+            sheetCommonObj.disableSpread(characterOprObj.workBook);
         }
     },
     initNavItem:function (node) {

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

@@ -59,6 +59,14 @@ let tender_obj={
                 newNode.source = mainNode.source;
                 newNode.sourceType = mainNode.sourceType;
                 newNode.mainNode = mainNode;
+
+                // 只显示到叶子清单层
+                if (calcTools.isLeafBill(newNode))
+                    newNode.expanded = false;
+
+                if (calcTools.isRationCategory(newNode))
+                    newNode.visible = false;
+                    
                 if (mainNode.children.length > 0) {
                     for (let c of mainNode.children) {
                         createTenderNode(c, newNode, null);

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

@@ -146,7 +146,7 @@ let zmhs_obj = {
         }
         this.coeSheetData = coeList;
         if(projectReadOnly){
-            disableSpread(zmhs_obj.coeSpread);
+            sheetCommonObj.disableSpread(zmhs_obj.coeSpread);
         }
         if(preSelections){//定位光标到之前的位置
             this.coeSheet.setSelection(preSelections[0].row,preSelections[0].col,preSelections[0].rowCount,preSelections[0].colCount);
@@ -167,7 +167,7 @@ let zmhs_obj = {
         sheetCommonObj.showData(this.cusSheet, this.cusSetting,cusList);
         this.cusSheet.getRange(cusList.length,-1,this.cusSheet.getRowCount()-cusList.length, -1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
         if(projectReadOnly){
-            disableSpread(zmhs_obj.cusSpread);
+            sheetCommonObj.disableSpread(zmhs_obj.cusSpread);
         }
     },
 
@@ -194,7 +194,7 @@ let zmhs_obj = {
         this.assSheet.resumePaint();
         this.assSheet.resumeEvent();
         if(projectReadOnly){
-            disableSpread(this.assSpread);
+            sheetCommonObj.disableSpread(this.assSpread);
         }
     },
     getAssList:function (node) {

+ 13 - 5
web/building_saas/pm/js/pm_newMain.js

@@ -363,7 +363,7 @@ const projTreeObj = {
             callback: function (key, opt) {
                 const selected = projTreeObj.tree.selected;
                 if (selected && selected.data.ID) {
-                    SHARE_TO.initModal(selected.data.ID);
+                    SHARE_TO.initModal(SHARE_TO.Mode.PROJECT, selected.data.ID);
                 }
             }
         },
@@ -3573,9 +3573,12 @@ function setFileOptions(projID){
                 return false;
             }
             let unitFileHtml = '';
-            for(let tmp of response.data) {
+            response.data.forEach((uf, index) => {
+                unitFileHtml += `<option value=${uf.id} ${index === 0 ? 'selected' : ''}>${uf.name}</option>`
+            });
+            /* for(let tmp of response.data) {
                 unitFileHtml += '<option value="'+ tmp.id +'">'+ tmp.name +'</option>';
-            }
+            } */
             $("#unit-price").children("option").first().after(unitFileHtml);
         });
         // 获取费率文件数据
@@ -3586,10 +3589,15 @@ function setFileOptions(projID){
             if (response.data.length <= 0) {
                 return false;
             }
-            for(let tmp of response.data) {
+            /* for(let tmp of response.data) {
                 let option =  $("<option>").val(tmp.ID).text(tmp.name);
                 $("#tender-fee-rate").append(option);
-            }
+            } */
+            let feeRateFileHtml = '';
+            response.data.forEach((ff, index) => {
+                feeRateFileHtml += `<option value=${ff.ID} ${index === 0 ? 'selected' : ''}>${ff.name}</option>`
+            });
+            $("#tender-fee-rate").children("option").first().after(feeRateFileHtml);
         });
     }
 }

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

@@ -1,5 +1,5 @@
 <div class="modal fade" id="share" data-backdrop="static">
-    <div class="modal-dialog modal-lg" role="document">
+    <div class="modal-dialog modal-lg" role="document" style="max-width: 800px;">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title">分享给...</h5>

+ 121 - 34
web/common/components/share/index.js

@@ -7,7 +7,23 @@ const SHARE_TO = (() => {
         CANCEL: 'cancel',
     };
 
-    const { SharePermissionChangeType: PermissionType, PageTarget } = commonConstants;
+    const { SharePermissionChangeType: PermissionType, PageTarget, ShareLibType } = commonConstants;
+
+
+    const Mode = {
+        PROJECT: 1,
+        RATION_LIB: 2,
+        GLJ_LIB: 3,
+    };
+
+    const ModeToLibType = {
+        [Mode.RATION_LIB]: ShareLibType.RATION_LIB,
+        [Mode.GLJ_LIB]: ShareLibType.GLJ_LIB,
+    };
+
+    // 当前模式
+    let curMode = Mode.PROJECT;
+
 
     // 当前分享的项目ID
     let curProjectID;
@@ -25,9 +41,13 @@ const SHARE_TO = (() => {
     const rencentCount = 5;
 
     // 获取初始数据:1.最近分享人 2.联系人 3.已分享人
-    async function getInitalData(projectID) {
+    async function getInitialData(projectID) {
         return await ajaxPost('/pm/api/getInitialShareData', { user_id: userID, count: rencentCount, projectID }, false);
+    }
 
+    // 获取分享库的初始数据
+    async function getInitialLibData(libType) {
+        return await ajaxPost('/pm/api/getInitialShareLibData', { user_id: userID, count: rencentCount, libType }, false);
     }
 
     // 获取头像视图html
@@ -122,16 +142,16 @@ const SHARE_TO = (() => {
                         <div class="col-5">${company}</div>
                         <div class="col ml-auto p-0">
                             <div class="d-flex justify-content-end">
-                                <div>
-                                    <div class="custom-control custom-checkbox">
-                                        <input type="checkbox" class="custom-control-input allow-copy" id="${copyLabelFor}" data-user="${user._id}" ${user.allowCopy ? 'checked' : ''}>
-                                        <label class="custom-control-label" for="${copyLabelFor}">允许拷贝</label>
-                                    </div>
-                                    <div class="custom-control custom-checkbox">
-                                        <input type="checkbox" class="custom-control-input allow-edit" id="${editLabelFor}" data-user="${user._id}" ${user.allowCooperate ? 'checked' : ''}>
-                                        <label class="custom-control-label" for="${editLabelFor}">允许编辑</label>
-                                    </div>
-                                </div>
+                            ${curMode === Mode.PROJECT ? `<div>
+                            <div class="custom-control custom-checkbox">
+                                <input type="checkbox" class="custom-control-input allow-copy" id="${copyLabelFor}" data-user="${user._id}" ${user.allowCopy ? 'checked' : ''}>
+                                <label class="custom-control-label" for="${copyLabelFor}">允许拷贝</label>
+                            </div>
+                            <div class="custom-control custom-checkbox">
+                                <input type="checkbox" class="custom-control-input allow-edit" id="${editLabelFor}" data-user="${user._id}" ${user.allowCooperate ? 'checked' : ''}>
+                                <label class="custom-control-label" for="${editLabelFor}">允许编辑</label>
+                            </div>
+                        </div>` : ''}
                                 <div class="ml-3 d-flex align-items-center">
                                     <button class="btn btn-sm btn-outline-danger cancel-share" data-user="${user._id}">取消分享</button>
                                 </div>
@@ -153,7 +173,11 @@ const SHARE_TO = (() => {
         });
         // 取消分享
         $('#shared-list .cancel-share').click(function () {
-            handleShareAction.call(this, ShareType.CANCEL);
+            if (curMode === Mode.PROJECT) {
+                handleShareAction.call(this, ShareType.CANCEL);
+            } else {
+                handleShareLibAction.call(this, ShareType.CANCEL, ModeToLibType[curMode]);
+            }
         });
     }
 
@@ -177,16 +201,16 @@ const SHARE_TO = (() => {
                             <div class="col-5">${company}</div>
                             <div class="col ml-auto p-0">
                                     <div class="d-flex justify-content-end">
-                                        <div>
-                                            <div class="custom-control custom-checkbox">
-                                                <input type="checkbox" class="custom-control-input" id="allow-copy" checked="">
-                                                <label class="custom-control-label" for="allow-copy">允许拷贝</label>
-                                            </div>
-                                            <div class="custom-control custom-checkbox">
-                                                <input type="checkbox" class="custom-control-input" id="allow-edit">
-                                                <label class="custom-control-label" for="allow-edit">允许编辑</label>
-                                            </div>
+                                        ${curMode === Mode.PROJECT ? `<div>
+                                        <div class="custom-control custom-checkbox">
+                                            <input type="checkbox" class="custom-control-input" id="allow-copy" checked="">
+                                            <label class="custom-control-label" for="allow-copy">允许拷贝</label>
                                         </div>
+                                        <div class="custom-control custom-checkbox">
+                                            <input type="checkbox" class="custom-control-input" id="allow-edit">
+                                            <label class="custom-control-label" for="allow-edit">允许编辑</label>
+                                        </div>
+                                    </div>` : ''}
                                         <div class="ml-3 d-flex align-items-center"><button class="btn btn-sm btn-primary" id="share-to" data-user="${user._id}">分享给Ta</button></div>
                                     </div>
                                 </div>
@@ -204,7 +228,11 @@ const SHARE_TO = (() => {
             });
             // 分享给事件
             $('#share-to').click(function () {
-                handleShareAction.call(this, ShareType.CREATE, user);
+                if (curMode === Mode.PROJECT) {
+                    handleShareAction.call(this, ShareType.CREATE, user);
+                } else {
+                    handleShareLibAction.call(this, ShareType.CREATE, ModeToLibType[curMode], user);
+                }
             });
         }
     }
@@ -219,6 +247,52 @@ const SHARE_TO = (() => {
         }
     }
 
+    async function handleShareLibAction(shareType, libType, user) {
+        try {
+            $.bootstrapLoading.start();
+            const receiver = $(this).data('user');
+            let shareData;
+            if (shareType === ShareType.CREATE) {
+                shareData = [{ receiver }];
+            } else if (shareType === ShareType.CANCEL) {
+                shareData = [{ receiver, isCancel: true }];
+            }
+            const postData = {
+                type: shareType,
+                user_id: userID,
+                count: rencentCount,
+                libType,
+                shareData
+            };
+            const rst = await ajaxPost('/pm/api/shareLib', postData);
+            // 请求成功后刷新视图
+            if (shareType === ShareType.CREATE || shareType === ShareType.CANCEL) {
+                if (shareType === ShareType.CREATE) {
+                    curSharedUsers.unshift(user);
+                    $('#share-phone').val('');
+                    initSearchResultView();
+                } else {
+                    curSharedUsers = curSharedUsers.filter(user => user._id !== receiver);
+                }
+                if (Array.isArray(rst.recentUsers)) {
+                    initRecentView(rst.recentUsers);
+                }
+                if (Array.isArray(rst.contacts)) {
+                    initContactsView(rst.contacts)
+                }
+                initSharedView(curSharedUsers);
+                refreshShareTip(curSharedUsers);
+                refreshTreeView();
+            }
+        } catch (err) {
+            console.log(err);
+            alert(`${String(err)} 请重试。`);
+            initSharedView(curSharedUsers);
+        } finally {
+            $.bootstrapLoading.end();
+        }
+    }
+
     // 添加分享、编辑分享、取消分享的动作
     async function handleShareAction(shareType, user) {
         try {
@@ -470,25 +544,37 @@ const SHARE_TO = (() => {
     }
 
     // 初始化分享给的页面
-    async function initModal(projectID) {
+    // mode: 模式,分享项目、分享库
+    async function initModal(mode, projectID) {
         try {
-            curProjectID = projectID;
+            curMode = mode;
             $.bootstrapLoading.start();
             // 恢复
             $('#share-phone').val('');
             initSearchResultView();
             $('#share-hint').text('');
-            const { isFree, sharedUsers, recentUsers, contacts } = await getInitalData(projectID);
-            if (isFree) {
-                hintBox.versionBox('此功能仅在专业版中提供,免费版可选择单位工程进行分享。');
+            let sharedUsers = [];
+            let recentUsers = [];
+            let contacts = [];
+            let data;
+            if (mode === Mode.PROJECT) {
+                curProjectID = projectID;
+                data = await getInitialData(projectID);
+                if (data.isFree) {
+                    return hintBox.versionBox('此功能仅在专业版中提供,免费版可选择单位工程进行分享。');
+                }
             } else {
-                curSharedUsers = sharedUsers;
-                initSharedView(sharedUsers);
-                initRecentView(recentUsers);
-                initContactsView(contacts);
-                setTimeout(() => $('#share-phone').focus(), 200);
-                $('#share').modal('show');
+                data = await getInitialLibData(ModeToLibType[curMode]);
             }
+            sharedUsers = data.sharedUsers;
+            recentUsers = data.recentUsers;
+            contacts = data.contacts;
+            curSharedUsers = sharedUsers;
+            initSharedView(sharedUsers);
+            initRecentView(recentUsers);
+            initContactsView(contacts);
+            setTimeout(() => $('#share-phone').focus(), 200);
+            $('#share').modal('show');
         } catch (err) {
             console.log(err);
             alert(err);
@@ -595,6 +681,7 @@ const SHARE_TO = (() => {
     }
 
     return {
+        Mode,
         initModal,
         handleEventListener,
         permissionChangeListener,

+ 2 - 2
web/common/html/header.html

@@ -66,8 +66,8 @@
             <li class="nav-item dropdown">
                 <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-wrench" data-placement="bottom"></i> 工具</a>
                 <div class="dropdown-menu dropdown-menu-right">
-                    <a id="compleRationLib" class="dropdown-item compleRationLib" href="/complementaryRation/ration" target="_blank">定额库编辑器</a>
-                    <a id="compleGljLib" class="dropdown-item compleGljLib" href="/complementaryGlj" target="_">人材机库编辑器</a>
+                    <a id="compleRationLib" class="dropdown-item compleRationLib" href=<%- "/complementaryRation/ration/" + userID %> target="_blank">定额库编辑器</a>
+                    <a id="compleGljLib" class="dropdown-item compleGljLib" href=<%- "/complementaryGlj/" + userID %> target="_blank">人材机库编辑器</a>
                 </div>
             </li>
             <% } %>

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

@@ -1289,7 +1289,7 @@ const XMLStandard = (function () {
                 decimal = detail.projectInfo.property.decimal;
             // 处理最高限价,输出最高限价时,若最高限价为空,则取综合单价,否则取最高限价
             let maxPrice = 0;
-            if (exportKind === _config.EXPORT_KIND.Control && node.data.outPutMaxPrice) {
+            if (exportKind === _config.EXPORT_KIND.Control && (node.data.outPutMaxPrice || node.data.outPutLimitPrice)) {
                 maxPrice = _util.isDef(node.data.maxPrice)
                     ? node.data.maxPrice
                     : _util.getFee(node.data.fees, 'common.unitFee')

+ 12 - 0
web/over_write/js/chongqing_2018_import.js

@@ -278,6 +278,7 @@ const importXML = (() => {
             //不为0才输出
             if (maxPrice && maxPrice !== '0') {
                 obj.outPutMaxPrice = true;
+                obj.outPutLimitPrice = true;
                 obj.maxPrice = maxPrice;
             }
         }
@@ -1491,6 +1492,17 @@ const importXML = (() => {
             delete bills.items;
             delete bills.rations;
             rst.bills.push(bills);
+            // 如果导入招标、控制价文件、每个分项底下自动生成一条空定额
+            if (importFileKind !== FileKind.tender && bills.type === billType.FX) {
+                const emptyRation = {
+                    projectID: tenderData.ID,
+                    ID: uuid.v1(),
+                    billsItemID: bills.ID,
+                    serialNo: 1,
+                    type: rationType.ration,
+                };
+                rst.ration.push(emptyRation);
+            }
         });
         return rst;
         function handleRation(billsData) {

+ 30 - 0
web/over_write/js/guangdong_2018.js

@@ -9,6 +9,36 @@
  */
 let isGD2018 = true;
 
+//允许使用的工料机类型:人工、普通材料、其他材料费、混凝土、砂浆、配合比、商品混凝土、商品砂浆
+//机械台班、机上人工、机械组成物、主材、设备、企业管理费
+if(typeof allowGljType !== 'undefined'){
+    allowGljType = [1, 201, 202, 203, 204, 205, 206, 207, 301, 302, 303, 4,5, 6];
+}
+if(typeof allowComponent !== 'undefined'){
+    //允许含有组成物的工料机类型:混凝土、砂浆、配合比、机械台班、主材
+    allowComponent = [202, 203, 204, 301, 4];
+}
+if(typeof componentType !== 'undefined'){
+    //可以作为组成物的工料机类型:普通材料、机械组成物、机上人工、主材
+    componentType = [201, 302, 303, 4];
+}
+if(typeof machineAllowComponent !== 'undefined'){
+    //允许含有组成物的机械工料机类型:机械台班器仪表
+    machineAllowComponent = [301];
+}
+if(typeof machineComponent !== 'undefined'){
+    //可以作为机械工料机组成物的工料机类型:机械组成物、机上人工
+    machineComponent = [302, 303];
+}
+if(typeof materialAllowComponent !== 'undefined'){
+    //允许含有组成物的材料工料机类型:混凝土、砂浆、配合比
+    materialAllowComponent = [202, 203, 204];
+}
+if(typeof materialComponent !== 'undefined'){
+    //可以作为材料工料机组成物的工料机类型:普通材料
+    materialComponent = [201];
+}
+
 function overwriteRationCalcBases (){
     if (typeof rationCalcBases == 'undefined') return;
     for (let key in rationCalcBases) delete rationCalcBases[key];

+ 35 - 22
web/over_write/js/guangdong_2018_import.js

@@ -394,6 +394,7 @@ const importXML = (() => {
             const maxPrice = getValue(workElementSrc, ['_PriceHigh']);
             //不为0才输出
             if (maxPrice && maxPrice !== '0') {
+                bills.outPutLimitPrice = true;
                 bills.outPutMaxPrice = true;
                 bills.maxPrice = maxPrice;
             }
@@ -1519,29 +1520,41 @@ const importXML = (() => {
             rationQuantityDetails: [],
         };
         billsData
-            .filter(bills => bills.rations && bills.rations.length)
             .forEach(bills => {
-                bills.rations.forEach(ration => {
-                    // TODO 取费专业不知如何取,暂时取单位工程取费专业(导出的文件没有可用的取费专业字段,因此需要后端匹配标准定额)
-                    // 如果标准定额有取费专业,则会替换掉这里设置的取费专业
-                    ration.programID = tenderData.property.projectEngineering;
-                    ration.ID = uuid.v1();
-                    ration.projectID = tenderData.ID;
-                    ration.billsItemID = bills.ID;
-                    ration.contain = getContain(bills, ration);
-                    // 工程量明细
-                    transformQuantityDetails(tenderData.ID, ration.quantityDetails, ration.ID, 'rationID');
-                    ration.quantityEXP = getQuantityEXP(bills, ration);
-                    if (ration.quantityDetails) {
-                        rst.rationQuantityDetails.push(...ration.quantityDetails);
-                    }
-                    rst.ration.push(ration);
-                    // 定额人材机
-                    rst.rationGLJ.push(...transformRationGLJs(bills, ration));
-                    // 定额系数
-                    rst.rationCoe.push(generateRationCoe(ration));
-                });
-            });
+                if (bills.rations && bills.rations.length) {
+                    bills.rations.forEach(ration => {
+                        // TODO 取费专业不知如何取,暂时取单位工程取费专业(导出的文件没有可用的取费专业字段,因此需要后端匹配标准定额)
+                        // 如果标准定额有取费专业,则会替换掉这里设置的取费专业
+                        ration.programID = tenderData.property.projectEngineering;
+                        ration.ID = uuid.v1();
+                        ration.projectID = tenderData.ID;
+                        ration.billsItemID = bills.ID;
+                        ration.contain = getContain(bills, ration);
+                        // 工程量明细
+                        transformQuantityDetails(tenderData.ID, ration.quantityDetails, ration.ID, 'rationID');
+                        ration.quantityEXP = getQuantityEXP(bills, ration);
+                        if (ration.quantityDetails) {
+                            rst.rationQuantityDetails.push(...ration.quantityDetails);
+                        }
+                        rst.ration.push(ration);
+                        // 定额人材机
+                        rst.rationGLJ.push(...transformRationGLJs(bills, ration));
+                        // 定额系数
+                        rst.rationCoe.push(generateRationCoe(ration));
+                    });
+                } else if (importFileKind !== FileKind.tender && bills.type === billType.FX) {
+                    // 如果导入招标、控制价文件、每个分项底下自动生成一条空定额
+                    const emptyRation = {
+                        projectID: tenderData.ID,
+                        ID: uuid.v1(),
+                        billsItemID: bills.ID,
+                        serialNo: 1,
+                        type: rationType.ration,
+                        jobContentText: bills.jobContent && bills.jobContent[0] && bills.jobContent[0].content || ''
+                    };
+                    rst.ration.push(emptyRation);
+                }
+            })
         return rst;
 
         // 含量:定额工程量/清单工程量

+ 30 - 0
web/over_write/js/jiangxi_2017.js

@@ -4,6 +4,36 @@
 
 let isJX2017 = true;
 
+//允许使用的工料机类型:人工、普通材料、其他材料费、混凝土、砂浆、配合比、商品混凝土、商品砂浆
+//机械台班、机上人工、机械组成物、主材、设备
+if(typeof allowGljType !== 'undefined'){
+    allowGljType = [1, 201, 202, 203, 204, 205, 206, 207, 301, 302, 303, 4, 5];
+}
+if(typeof allowComponent !== 'undefined'){
+    //允许含有组成物的工料机类型:混凝土、砂浆、配合比、机械台班、主材
+    allowComponent = [202, 203, 204, 301, 4];
+}
+if(typeof componentType !== 'undefined'){
+    //可以作为组成物的工料机类型:普通材料、机械组成物、机上人工、主材
+    componentType = [201, 302, 303, 4];
+}
+if(typeof machineAllowComponent !== 'undefined'){
+    //允许含有组成物的机械工料机类型:机械台班器仪表
+    machineAllowComponent = [301];
+}
+if(typeof machineComponent !== 'undefined'){
+    //可以作为机械工料机组成物的工料机类型:机械组成物、机上人工
+    machineComponent = [302, 303];
+}
+if(typeof materialAllowComponent !== 'undefined'){
+    //允许含有组成物的材料工料机类型:混凝土、砂浆、配合比
+    materialAllowComponent = [202, 203, 204];
+}
+if(typeof materialComponent !== 'undefined'){
+    //可以作为材料工料机组成物的工料机类型:普通材料
+    materialComponent = [201];
+}
+
 // 一般计税取不含税市场价、不含税定额价。简易计税取含税市场价、含税定额价。打开项目时,4个价格根据计税类型只载入其二,所以这里可不作区分。
 function overwriteRationCalcBases (taxType){
     if (typeof rationCalcBases == 'undefined') return;

+ 29 - 0
web/over_write/js/neimenggu_2017.js

@@ -2,6 +2,35 @@
 
 // CSL, 2019-01-18
 
+//允许使用的工料机类型:人工、普通材料、混凝土、砂浆、配合比、商品混凝土、商品砂浆、其他材料费、机械台班、机械组成物、机上人工、主材、设备、企业管理费、利润
+if(typeof allowGljType !== 'undefined'){
+    allowGljType = [1, 201, 202, 203, 204, 205, 206, 207, 301, 302, 303, 4, 5, 6, 7];
+}
+if(typeof allowComponent !== 'undefined'){
+    //允许含有组成物的工料机类型:混凝土、砂浆、配合比、机械台班、主材
+    allowComponent = [202, 203, 204, 301, 4];
+}
+if(typeof componentType !== 'undefined'){
+    //可以作为组成物的工料机类型:普通材料、机械组成物、机上人工、主材
+    componentType = [201, 302, 303, 4];
+}
+if(typeof machineAllowComponent !== 'undefined'){
+    //允许含有组成物的机械工料机类型:机械台班
+    machineAllowComponent = [301];
+}
+if(typeof machineComponent !== 'undefined'){
+    //可以作为机械工料机组成物的工料机类型:机械组成物、机上人工
+    machineComponent = [302, 303];
+}
+if(typeof materialAllowComponent !== 'undefined'){
+    //允许含有组成物的材料工料机类型:混凝土、砂浆、配合比
+    materialAllowComponent = [202, 203, 204];
+}
+if(typeof materialComponent !== 'undefined'){
+    //可以作为材料工料机组成物的工料机类型:普通材料
+    materialComponent = [201];
+}
+
 (function overwriteFeeTypes() {
     if (typeof cpFeeTypes == 'undefined') return;
     cpFeeTypes = [

+ 14 - 14
web/users/html/index.html

@@ -80,7 +80,7 @@
                      </div>
                      <!-- Heading -->
                      <h2>
-                     真云版免安装
+                        跨平台免安装
                      </h2>
                   </div>
                   <!--End Box -->
@@ -123,7 +123,7 @@
                         <div class="media-body pl-4">
                            <!-- Heading -->
                            <h3 class="mb-5">
-                           真云版、免安装
+                             跨平台、免安装
                            </h3>
                            <!-- Text -->
                            <p class="mb-5 lead">
@@ -217,6 +217,18 @@
                   <!-- Box -->
                   <div class="box bg-box text-center">
                      <div class="icon text-primary">
+                        <i class="fas fa-archway"></i>
+                     </div>
+                     <h3>
+                     市政
+                     </h3>
+                  </div>
+                  <!--End Box -->
+               </div>
+               <div class="col-12 col-md-4 col-lg-3 mt-30">
+                  <!-- Box -->
+                  <div class="box bg-box text-center">
+                     <div class="icon text-primary">
                         <i class="fas fa-building"></i>
                      </div>
                      <h3>
@@ -297,18 +309,6 @@
                   </div>
                   <!--End Box -->
                </div>
-               <div class="col-12 col-md-4 col-lg-3 mt-30">
-                  <!-- Box -->
-                  <div class="box bg-box text-center">
-                     <div class="icon text-primary">
-                        <i class="fas fa-tools"></i>
-                     </div>
-                     <h3>
-                     房屋修缮
-                     </h3>
-                  </div>
-                  <!--End Box -->
-               </div>
             </div>
          </div>
       </section>