Browse Source

Merge branch 'master' of http://smartcost.f3322.net:3000/SmartCost/ConstructionCost

Conflicts:
	package.json
zhangweicheng 8 years ago
parent
commit
9e82a4cfa8
42 changed files with 2376 additions and 142 deletions
  1. 9 0
      modules/main/controllers/bills_controller.js
  2. 22 0
      modules/main/models/bills.js
  3. 21 1
      modules/main/models/bills_sub_schemas.js
  4. 1 0
      modules/main/models/proj_counter.js
  5. 1 0
      modules/main/routes/bills_route.js
  6. 11 10
      modules/pm/controllers/new_proj_controller.js
  7. 32 1
      modules/pm/controllers/pm_controller.js
  8. 2 2
      modules/pm/models/project_model.js
  9. 5 1
      modules/pm/models/project_schema.js
  10. 75 0
      modules/pm/models/templates/bills_template_model.js
  11. 35 0
      modules/pm/models/templates/schemas/bills_template.js
  12. 1 19
      modules/pm/routes/pm_route.js
  13. 80 13
      modules/reports/controllers/rpt_controller.js
  14. 4 0
      modules/reports/routes/report_router.js
  15. 4 1
      modules/reports/rpt_component/jpc_ex.js
  16. 230 0
      modules/reports/util/canvas_util.js
  17. BIN
      modules/reports/util/pdf_base_files/Smart-italic.ttf
  18. BIN
      modules/reports/util/pdf_base_files/Smart.ttf
  19. BIN
      modules/reports/util/pdf_base_files/hwxsb.ttf
  20. 49 0
      modules/reports/util/pdf_base_files/rpt_pdf_consts.js
  21. 59 0
      modules/reports/util/pdfkit_test.js
  22. 144 17
      modules/reports/util/rpt_excel_util.js
  23. 202 0
      modules/reports/util/rpt_pdf_util.js
  24. 97 0
      modules/users/models/engineering_lib_model.js
  25. 12 17
      modules/users/models/schema/compilation.js
  26. 47 0
      modules/users/models/schema/engineering_lib.js
  27. 132 3
      public/calc_util.js
  28. 7 0
      public/fsUtil.js
  29. 3 0
      public/web/rpt_value_define.js
  30. 2 2
      public/web/sheet/sheet_common.js
  31. 13 0
      public/web/tree_sheet/tree_sheet_helper.js
  32. 209 0
      test/calculation/test_analyzer.js
  33. 6 5
      test/tmp_data/bills_grid_setting.js
  34. 34 29
      web/building_saas/complementary_glj_lib/js/components.js
  35. 2 2
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  36. 16 0
      web/building_saas/main/html/main.html
  37. 695 0
      web/building_saas/main/js/views/character_content_view.js
  38. 9 1
      web/building_saas/main/js/views/main_tree_col.js
  39. 29 3
      web/building_saas/main/js/views/project_view.js
  40. 42 1
      web/building_saas/main/js/views/sub_view.js
  41. 1 1
      web/building_saas/pm/html/project-management.html
  42. 33 13
      web/building_saas/pm/js/pm_main.js

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

@@ -39,6 +39,15 @@ module.exports = {
                 callback(req, res, err, message, null);
                 callback(req, res, err, message, null);
             }
             }
         });
         });
+    },
+    //zhong 2017-9-1
+    updateCharacterContent: function (req, res) {
+        let findSet = JSON.parse(req.body.findSet),
+            updateObj = JSON.parse(req.body.updateObj),
+            txtObj = JSON.parse(req.body.txtObj);
+        billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
+            callback(req, res, err, message, null);
+        });
     }
     }
 
 
 };
 };

+ 22 - 0
modules/main/models/bills.js

@@ -43,6 +43,12 @@ let billsSchema = new Schema({
     tenderTargetPrice: String, // Decimal
     tenderTargetPrice: String, // Decimal
     tenderTargetUnitPrice: String, // Decimal
     tenderTargetUnitPrice: String, // Decimal
     tenderTargetUnitPrice: String, // Decimal
     tenderTargetUnitPrice: String, // Decimal
+    //工作内容//zhong 2017-8-31
+    jobContentText: String, //清单工作内容列显示文本, 减少第一次拉数据时的循环次数
+    jobContent: [subSchema.jobContentSchema],
+    //项目特征
+    itemCharacterText: String,//清单项目特征列显示文本
+    itemCharacter: [subSchema.itemCharacterSchema],
     // 费用字段
     // 费用字段
     fees: [subSchema.feesSchema],
     fees: [subSchema.feesSchema],
     // 标记字段
     // 标记字段
@@ -108,6 +114,22 @@ class billsModel extends baseModel {
             callback(0, '', {lowID: lowID, highID: highID});
             callback(0, '', {lowID: lowID, highID: highID});
         });
         });
     };
     };
+    //zhong 2017-9-1
+    updateCharacterContent(findSet, updateObj, txtObj, callback) {
+        let updateSet = {};
+        updateSet[updateObj.field] = updateObj.updateArr;
+        if(txtObj && typeof txtObj !== 'undefined'){
+            updateSet[txtObj.field] = txtObj.text;
+        }
+       bills.update(findSet, updateSet, function (err) {
+           if(err){
+               callback(1, '更新失败');
+           }
+           else{
+               callback(0, '更新成功');
+           }
+       });
+    };
 };
 };
 
 
 module.exports = new billsModel();
 module.exports = new billsModel();

+ 21 - 1
modules/main/models/bills_sub_schemas.js

@@ -25,4 +25,24 @@ var changesSchema = new Schema({
     deleteGLJ:String
     deleteGLJ:String
 });
 });
 
 
-module.exports = {feesSchema: feesSchema, flagsSchema: flagsSchema};
+//add by Zhong2017-8-30
+//清单工作内容字段
+let jobContentSchema = new Schema({
+    content: String, //工作内容
+    serialNo: Number, //排序用
+    isChecked: Boolean //是否勾选(输出)
+}, {_id: false});
+//特征值字段
+let eigenvalueSchema = new Schema({
+    value: String,
+    isSelected: Boolean //判断哪个特征值被选中
+}, {_id: false});
+//项目特征字段
+let itemCharacterSchema = new Schema({
+    character: String, //特征
+    serialNo: Number, //排序用
+    eigenvalue: [eigenvalueSchema],//特征值
+    isChecked: Boolean //是否勾选(输出)
+}, {_id: false});
+
+module.exports = {feesSchema: feesSchema, flagsSchema: flagsSchema, jobContentSchema: jobContentSchema, itemCharacterSchema: itemCharacterSchema};

+ 1 - 0
modules/main/models/proj_counter.js

@@ -2,6 +2,7 @@
  * Created by Mai on 2017/6/13.
  * Created by Mai on 2017/6/13.
  */
  */
 
 
+let mongoose = require('mongoose');
 let baseModel = require('./base_model');
 let baseModel = require('./base_model');
 
 
 class projCounter extends baseModel {
 class projCounter extends baseModel {

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

@@ -9,6 +9,7 @@ module.exports = function (app) {
     billsRouter.post('/getData', billsController.getData);
     billsRouter.post('/getData', billsController.getData);
     billsRouter.post('/getItemTemplate', billsController.getItemTemplate);
     billsRouter.post('/getItemTemplate', billsController.getItemTemplate);
     billsRouter.post('/allocIDs', billsController.allocIDs);
     billsRouter.post('/allocIDs', billsController.allocIDs);
+    billsRouter.post('/updateCharacterContent', billsController.updateCharacterContent)//特征及内容更新 zhong 2017-9-1
     app.use('/bills', billsRouter);
     app.use('/bills', billsRouter);
 };
 };
 
 

+ 11 - 10
modules/pm/controllers/new_proj_controller.js

@@ -2,23 +2,24 @@
  * Created by Mai on 2017/4/24.
  * Created by Mai on 2017/4/24.
  */
  */
 
 
-let BillsTemplateData = require('../../templates/models/bills_template');
 let billsData = require('../../main/models/bills');
 let billsData = require('../../main/models/bills');
 let projCounter = require('../../main/models/proj_counter');
 let projCounter = require('../../main/models/proj_counter');
 let async = require('async');
 let async = require('async');
 
 
+import BillsTemplateModel from "../models/templates/bills_template_model";
+
 module.exports = {
 module.exports = {
-    copyTemplateData: function (tempType, newProjID, callback) {
+    copyTemplateData: async function (valuationId, newProjID, callback) {
         async.parallel([
         async.parallel([
-            function (cb) {
-                BillsTemplateData.getTemplate(tempType).then(function (templates) {
-                    let datas = [];
-                    templates.forEach(function (template) {
-                        template._doc.projectID = newProjID;
-                        datas.push(template._doc);
-                    })
-                    return billsData.insertData(datas, cb);
+            async function (cb) {
+                // 获取清单模板数据
+                let billsTemplateModel = new BillsTemplateModel();
+                let templateData = JSON.stringify(await billsTemplateModel.getTemplateDataForNewProj(valuationId));
+                let billsDatas = JSON.parse(templateData);
+                billsDatas.forEach(function (template) {
+                    template.projectID = newProjID;
                 });
                 });
+                billsData.insertData(billsDatas, callback);
             },
             },
             function (cb) {
             function (cb) {
                 projCounter.insertData({"projectID": newProjID}, cb);
                 projCounter.insertData({"projectID": newProjID}, cb);

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

@@ -3,6 +3,8 @@
  */
  */
 let ProjectsData = require('../models/project_model').project;
 let ProjectsData = require('../models/project_model').project;
 let projType = require('../models/project_model').projType;
 let projType = require('../models/project_model').projType;
+const engineering = require("../../common/const/engineering");
+let EngineeringLibModel = require("../../users/models/engineering_lib_model");
 
 
 //统一回调函数
 //统一回调函数
 let callback = function(req, res, err, message, data){
 let callback = function(req, res, err, message, data){
@@ -10,7 +12,6 @@ let callback = function(req, res, err, message, data){
 };
 };
 
 
 
 
-
 module.exports = {
 module.exports = {
     checkRight: function (req, res) {
     checkRight: function (req, res) {
         let data = JSON.parse(req.body.data);
         let data = JSON.parse(req.body.data);
@@ -91,5 +92,35 @@ module.exports = {
         ProjectsData.getNewProjectID(data.count, function (err, message, data) {
         ProjectsData.getNewProjectID(data.count, function (err, message, data) {
             callback(req, res, err, message, data);
             callback(req, res, err, message, data);
         });
         });
+    },
+    // 项目管理首页
+    index: async function(request, response) {
+        // 获取编办信息
+        let sessionCompilation = request.session.sessionCompilation;
+
+
+        // 清单计价
+        let billValuation = sessionCompilation.bill_valuation !== undefined ?
+            sessionCompilation.bill_valuation : [];
+
+        // 获取标准库数据
+        let engineeringLibModel = new EngineeringLibModel();
+        billValuation = await engineeringLibModel.getLib(billValuation);
+
+        // 定额计价
+        let rationValuation = sessionCompilation.ration_valuation !== undefined ?
+            sessionCompilation.ration_valuation : [];
+        rationValuation = await engineeringLibModel.getLib(rationValuation);
+
+        let renderData = {
+            userAccount: request.session.userAccount,
+            userID: request.session.sessionUser.ssoId,
+            compilationData: sessionCompilation,
+            billValuation: JSON.stringify(billValuation),
+            rationValuation: JSON.stringify(rationValuation),
+            engineeringList: JSON.stringify(engineering.List)
+        };
+
+        response.render('building_saas/pm/html/project-management.html', renderData);
     }
     }
 }
 }

+ 2 - 2
modules/pm/models/project_model.js

@@ -37,7 +37,7 @@ ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
 }
 }
 
 
 ProjectsDAO.prototype.updateUserProjects = function(userId, datas, callback){
 ProjectsDAO.prototype.updateUserProjects = function(userId, datas, callback){
-    let data, project, updateLength = 0, hasError = false, deleteInfo = null, tempType = 1, i, newProject;
+    let data, project, updateLength = 0, hasError = false, deleteInfo = null, valuationId = "599256ba700b1b340c03805e", i, newProject;
     let updateAll = function (err) {
     let updateAll = function (err) {
             if (!err){
             if (!err){
                 updateLength += 1;
                 updateLength += 1;
@@ -61,7 +61,7 @@ ProjectsDAO.prototype.updateUserProjects = function(userId, datas, callback){
                 newProject = new Projects(data.updateData);
                 newProject = new Projects(data.updateData);
                 newProject.save(function (err, result) {
                 newProject.save(function (err, result) {
                     if (!err && result._doc.projType === projectType.tender) {
                     if (!err && result._doc.projType === projectType.tender) {
-                        newProjController.copyTemplateData(tempType, newProject.ID, updateAll);
+                        newProjController.copyTemplateData(valuationId, newProject.ID, updateAll);
                     } else {
                     } else {
                         updateAll(err);
                         updateAll(err);
                     }
                     }

+ 5 - 1
modules/pm/models/project_schema.js

@@ -17,7 +17,11 @@ let ProjectSchema = new Schema({
     "recentDateTime": Date,
     "recentDateTime": Date,
     "createDateTime": Date,
     "createDateTime": Date,
     "deleteInfo": deleteSchema,
     "deleteInfo": deleteSchema,
-    'fullFolder': Array
+    'fullFolder': Array,
+    "property": {
+        type: Schema.Types.Mixed,
+        default: {}
+    }
 });
 });
 
 
 module.exports = mongoose.model(collectionName, ProjectSchema);
 module.exports = mongoose.model(collectionName, ProjectSchema);

+ 75 - 0
modules/pm/models/templates/bills_template_model.js

@@ -0,0 +1,75 @@
+/**
+ * Created by Mai on 2017/4/14.
+ * 清单模板,新建项目使用
+ */
+import BaseModel from "../../../common/base/base_model";
+import BillsTemplateSchema from "./schemas/bills_template";
+
+class BillsTemplateModel extends BaseModel {
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = BillsTemplateSchema;
+        parent.init();
+    }
+
+    /**
+     * 获取计价类别对应的清单模板
+     * @param valuationId
+     * @returns {*}
+     */
+    async getTemplateData (valuationId) {
+        // 筛选字段
+        let field = {_id: 1, valuationId: 1, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let data = await this.findDataByCondition({valuationId: valuationId}, field, false);
+
+        return data === null ? [] : data;
+    }
+
+    /**
+     * 新建项目时,获取计价类别对应的清单模板
+     * @param valuationId
+     * @returns {*}
+     */
+    async getTemplateDataForNewProj (valuationId) {
+        // 筛选字段
+        let field = {_id: 0, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let data = await this.findDataByCondition({valuationId: valuationId}, field, false);
+
+        return data === null ? [] : data;
+    }
+
+    async updateTemplate (valuationId, datas) {
+        try {
+            for (let data of datas) {
+                data.data.valuationId = valuationId;
+                let condition = {valuationId: valuationId, ID: data.data.ID}, result;
+                if (data.type === 'update') {
+                    result = await this.db.update(condition, data.data);
+                    if (result === undefined || result.ok ===undefined || !result.ok) {
+                        throw '更新数据错误';
+                    }
+                } else if (data.type === 'new') {
+                    result = await this.db.create(data.data);
+                    if (!result) {
+                        throw '新增数据错误';
+                    }
+                } else if (data.type === 'delete') {
+                    result = await this.db.delete(condition);
+                    if (result === undefined || result.ok ===undefined || !result.ok) {
+                        throw '删除数据错误';
+                    }
+                }
+            }
+            return true;
+        } catch (error) {
+            console.log(error);
+        }
+    }
+};
+
+export default BillsTemplateModel;

+ 35 - 0
modules/pm/models/templates/schemas/bills_template.js

@@ -0,0 +1,35 @@
+/**
+ * Created by Mai on 2017/8/17.
+ */
+import mongoose from "mongoose";
+let Schema = mongoose.Schema;
+
+let collectionName = 'temp_bills';
+
+// 标记字段
+let flagsSchema = new Schema({
+    fieldName: String,
+    flag: Number
+});
+let BillsTemplateSchema = {
+    // 树结构所需ID
+    ID: Number,
+    ParentID: Number,
+    NextSiblingID: Number,
+    // 编号
+    code: String,
+    // 名称
+    name: String,
+    // 单位
+    unit: String,
+    // 标记
+    flags:{
+        type: [flagsSchema],
+        default: []
+    },
+    // 所属计价ID
+    valuationId: String
+};
+
+let model = mongoose.model(collectionName, new Schema(BillsTemplateSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 1 - 19
modules/pm/routes/pm_route.js

@@ -5,28 +5,10 @@
 
 
 let express = require('express');
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
 let pmController = require('./../controllers/pm_controller');
-const engineering = require("../../common/const/engineering");
 
 
 module.exports = function (app) {
 module.exports = function (app) {
 
 
-    app.get('/pm', function(req, res){
-        // 获取编办信息
-        let sessionCompilation = req.session.sessionCompilation;
-        // 清单计价
-        let billValuation = sessionCompilation.bill_valuation !== undefined ? sessionCompilation.bill_valuation : [];
-
-        // 定额计价
-        let rationValuation = sessionCompilation.ration_valuation !== undefined ? sessionCompilation.ration_valuation : [];
-        let renderData = {
-            userAccount: req.session.userAccount,
-            userID: req.session.sessionUser.ssoId,
-            compilationData: sessionCompilation,
-            billValuation: JSON.stringify(billValuation),
-            rationValuation: JSON.stringify(rationValuation),
-            engineeringList: JSON.stringify(engineering.List)
-        };
-        res.render('building_saas/pm/html/project-management.html', renderData);
-    });
+    app.get('/pm', pmController.index);
 
 
     let pmRouter = express.Router();
     let pmRouter = express.Router();
 
 

+ 80 - 13
modules/reports/controllers/rpt_controller.js

@@ -3,6 +3,7 @@
  */
  */
 
 
 let mongoose = require('mongoose');
 let mongoose = require('mongoose');
+let async = require('async');
 
 
 let JV = require('../rpt_component/jpc_value_define');
 let JV = require('../rpt_component/jpc_value_define');
 
 
@@ -13,6 +14,7 @@ let demoTemplateFacade = require('../facade/rpt_tpl_data_demo_facade');
 let JpcEx = require('../rpt_component/jpc_ex');
 let JpcEx = require('../rpt_component/jpc_ex');
 let rptUtil = require("../util/rpt_util");
 let rptUtil = require("../util/rpt_util");
 let rpt_xl_util = require('../util/rpt_excel_util');
 let rpt_xl_util = require('../util/rpt_excel_util');
+let rpt_pdf_util = require('../util/rpt_pdf_util');
 let fs = require('fs');
 let fs = require('fs');
 let strUtil = require('../../../public/stringUtil');
 let strUtil = require('../../../public/stringUtil');
 
 
@@ -27,7 +29,7 @@ let callback = function(req, res, err, data){
     }
     }
 };
 };
 
 
-function getAllPagesCommonOrg(req, res, rpt_id, pageSize, cb) {
+function getAllPagesCommonOrg(rpt_id, pageSize, cb) {
     let rptTpl = null;
     let rptTpl = null;
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
         rptTpl = rst;
         rptTpl = rst;
@@ -35,10 +37,12 @@ function getAllPagesCommonOrg(req, res, rpt_id, pageSize, cb) {
             if (rptTpl.ID_KEY) {
             if (rptTpl.ID_KEY) {
                 return demoTemplateFacade.getDemoData(rptTpl.ID_KEY);
                 return demoTemplateFacade.getDemoData(rptTpl.ID_KEY);
             } else {
             } else {
-                callback(req, res, 'No report template data were found!', null);
+                //callback(req, res, 'No report template data were found!', null);
+                cb('No report template data were found!', null);
             }
             }
         } else {
         } else {
-            callback(req, res, 'No report template was found!', null);
+            //callback(req, res, 'No report template was found!', null);
+            cb('No report template was found!', null);
         }
         }
     }).then(function(tplData){
     }).then(function(tplData){
             if (tplData) {
             if (tplData) {
@@ -50,18 +54,20 @@ function getAllPagesCommonOrg(req, res, rpt_id, pageSize, cb) {
                 let maxPages = printCom.totalPages;
                 let maxPages = printCom.totalPages;
                 let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
                 let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
                 if (pageRst) {
                 if (pageRst) {
-                    cb(pageRst);
+                    cb(null, pageRst);
                 } else {
                 } else {
-                    callback(req, res, "Have errors while on going...", null);
+                    //callback(req, res, "Have errors while on going...", null);
+                    cb('Have errors while on going...', null);
                 }
                 }
             } else {
             } else {
-                callback(req, res, 'No report data were found!', null);
+                //callback(req, res, 'No report data were found!', null);
+                cb('No report data were found!', null);
             }
             }
         }
         }
     );
     );
 };
 };
 
 
-function getAllPagesCommon(req, res, rpt_id, pageSize, cb) {
+function getAllPagesCommon(rpt_id, pageSize, cb) {
     let rptTpl = null;
     let rptTpl = null;
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
         rptTpl = rst;
         rptTpl = rst;
@@ -95,10 +101,10 @@ function getAllPagesCommon(req, res, rpt_id, pageSize, cb) {
                 //return demoTemplateData.findOne({"Data_Key": rptTpl.ID_KEY}).exec();
                 //return demoTemplateData.findOne({"Data_Key": rptTpl.ID_KEY}).exec();
 
 
             } else {
             } else {
-                callback(req, res, 'No report template data were found!', null);
+                cb('No report template data were found!', null);
             }
             }
         } else {
         } else {
-            callback(req, res, 'No report template was found!', null);
+            cb('No report template was found!', null);
         }
         }
     })
     })
 };
 };
@@ -107,9 +113,9 @@ module.exports = {
     getReportAllPages: function(req, res){
     getReportAllPages: function(req, res){
         let rpt_id = req.body.ID;
         let rpt_id = req.body.ID;
         let pageSize = req.body.pageSize;
         let pageSize = req.body.pageSize;
-        getAllPagesCommonOrg(req, res, rpt_id, pageSize, function(pageRst){
+        getAllPagesCommonOrg(rpt_id, pageSize, function(err, pageRst){
             //fs.writeFileSync('D:/GitHome/ConstructionOperation/tmp/testRpt.js', JSON.stringify(pageRst));
             //fs.writeFileSync('D:/GitHome/ConstructionOperation/tmp/testRpt.js', JSON.stringify(pageRst));
-            callback(req, res, null, pageRst);
+            callback(req, res, err, pageRst);
         })
         })
     },
     },
     getExcel: function(req, res) {
     getExcel: function(req, res) {
@@ -118,8 +124,8 @@ module.exports = {
             rptName = req.params.rptName,
             rptName = req.params.rptName,
             isOneSheet = req.params.isOneSheet;
             isOneSheet = req.params.isOneSheet;
         ;
         ;
-        getAllPagesCommonOrg(req, res, rpt_id, pageSize, function(pageRst){
-            rpt_xl_util.exportExcel(pageRst, pageSize, rptName, isOneSheet, function(newName){
+        getAllPagesCommonOrg(rpt_id, pageSize, function(err, pageRst){
+            rpt_xl_util.exportExcel(pageRst, pageSize, rptName, isOneSheet, null, function(newName){
                 res.setHeader('Content-Type', 'application/vnd.openxmlformats');
                 res.setHeader('Content-Type', 'application/vnd.openxmlformats');
                 res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".xlsx");
                 res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".xlsx");
                 let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + newName + '.xlsx');
                 let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + newName + '.xlsx');
@@ -131,5 +137,66 @@ module.exports = {
                 });
                 });
             });
             });
         })
         })
+    },
+    getExcelInOneBook: function(req, res) {
+        let rpt_ids = req.params.ids.split(','),
+            pageSize = req.params.size,
+            rptName = req.params.rptName;
+        ;
+        let parallelFucs = [];
+        for (let id of rpt_ids) {
+            parallelFucs.push((function (rpt_id) {
+                return function (cb) {
+                    getAllPagesCommonOrg(rpt_id, pageSize, function (err, pageRst) {
+                        if(err){
+                            cb(err);
+                        }
+                        else{
+                            cb(null, pageRst);
+                        }
+                    })
+                }
+            })(parseInt(id)));
+        }
+        async.parallel(parallelFucs, function (err, pageRstArray) {
+            if (err) {
+                callback(req, res, '数据有误', null);
+            } else {
+                rpt_xl_util.exportExcelInOneBook(pageRstArray, pageSize, rptName, function(tmpFilePath){
+                    res.setHeader('Content-Type', 'application/vnd.openxmlformats');
+                    res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".xlsx");
+                    let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + tmpFilePath + '.xlsx');
+                    filestream.on('data', function(chunk) {
+                        res.write(chunk);
+                    });
+                    filestream.on('end', function() {
+                        res.end();
+                    });
+                });
+                //callback(req, res, false, '', {compilation: rst, gljLibs: gljLibsRst});
+            }
+        })
+    },
+    getPDF:function (req, res) {
+        let rpt_id = req.params.id,
+            pageSize = req.params.size,
+            rptName = req.params.rptName;
+
+        getAllPagesCommonOrg(rpt_id, pageSize, function(err, pageRst){
+            rpt_pdf_util.export_pdf_file(pageRst, pageSize, rptName,function (newName) {
+                res.setHeader('Content-Type', 'application/vnd.openxmlformats');
+                res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".pdf");
+
+                let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + newName + '.pdf');
+                filestream.on('data', function(chunk) {
+                    res.write(chunk);
+                });
+                filestream.on('end', function() {
+                    res.end();
+                });
+            })
+
+        })
+
     }
     }
 };
 };

+ 4 - 0
modules/reports/routes/report_router.js

@@ -18,6 +18,10 @@ module.exports =function (app) {
         }
         }
     });
     });
     rptRouter.post('/getReport', reportController.getReportAllPages);
     rptRouter.post('/getReport', reportController.getReportAllPages);
+
     rptRouter.get('/getExcel/:id/:size/:rptName/:isOneSheet', reportController.getExcel);
     rptRouter.get('/getExcel/:id/:size/:rptName/:isOneSheet', reportController.getExcel);
+    rptRouter.get('/getExcelInOneBook/:ids/:size/:rptName', reportController.getExcelInOneBook);
+
+    rptRouter.get('/getPDF/:id/:size/:rptName', reportController.getPDF);//2/A4/07-1表
     app.use("/report_api", rptRouter);
     app.use("/report_api", rptRouter);
 }
 }

+ 4 - 1
modules/reports/rpt_component/jpc_ex.js

@@ -169,7 +169,10 @@ JpcExSrv.prototype.createNew = function(){
             rst[JV.NODE_CONTROL_COLLECTION] = private_buildDftControls(rptTpl, (defProperties == null)?null:defProperties.ctrls);
             rst[JV.NODE_CONTROL_COLLECTION] = private_buildDftControls(rptTpl, (defProperties == null)?null:defProperties.ctrls);
             rst[JV.NODE_STYLE_COLLECTION] = private_buildDftStyles(rptTpl, (defProperties == null)?null:defProperties.styles);
             rst[JV.NODE_STYLE_COLLECTION] = private_buildDftStyles(rptTpl, (defProperties == null)?null:defProperties.styles);
             rst[JV.NODE_FONT_COLLECTION] = private_buildDftFonts(rptTpl, (defProperties == null)?null:defProperties.fonts);
             rst[JV.NODE_FONT_COLLECTION] = private_buildDftFonts(rptTpl, (defProperties == null)?null:defProperties.fonts);
-            rst[JV.NODE_PAGE_INFO] = JpcCommonHelper.getPageSize(rptTpl);
+            rst[JV.NODE_PAGE_INFO] = {};
+            rst[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MAIN_INFO_RPT_NAME];
+            rst[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE] = JpcCommonHelper.getPageSize(rptTpl);
+            rst[JV.NODE_PAGE_INFO][JV.NODE_MARGINS] = rptTpl[JV.NODE_MAIN_INFO][JV.NODE_MARGINS];
             rst.items = [];
             rst.items = [];
             let bands = JpcBand.createNew(rptTpl, defProperties);
             let bands = JpcBand.createNew(rptTpl, defProperties);
             try {
             try {

+ 230 - 0
modules/reports/util/canvas_util.js

@@ -0,0 +1,230 @@
+/**
+ * Created by chen on 2017/7/27.
+ */
+let can = require('canvas');
+let canvas = new can(1200, 900,'pdf'), fs = require('fs');
+let reportO = require('./reportOj');
+//,ctx = canvas.getContext('2d')
+/*var y = 80, x = 50;
+ctx.font = '22px Helvetica';
+ctx.fillText('node-canvas pdf', x, y);
+ctx.font = '10px Arial';
+ctx.fillText('Just a quick example of PDFs with node-canvas lonso', x, y += 20);
+ctx.globalAlpha = .5;
+ctx.fillRect(x, y += 20, 10, 10);
+ctx.fillRect(x += 20, y, 10, 10);
+ctx.fillRect(x += 20, y, 10, 10);*/
+//fs.writeFile('out.pdf', canvas.toBuffer());
+
+
+
+let JpcCanvasOutput = {
+    offsetX: 10,
+    offsetY: 10,
+    cleanCanvas: function (canvas) {
+        var ctx = canvas.getContext("2d");
+        ctx.save();
+        ctx.fillStyle="white";
+        ctx.clearRect(0,0, canvas.width, canvas.height);
+        ctx.restore();
+    },
+    drawToCanvas : function(pageObj, canvas, pageIdx) {
+        var me = this;
+        var ctx = canvas.getContext("2d");
+
+        function private_setupAreaH(area, type, fontAngle, dftFontHeight, outputPoint) {
+            var lType = type;
+            if (type != "left" && type != "right" && type != "center") lType = "left";
+            switch (lType) {
+                case "left":
+                    if (fontAngle == JV.VERTICAL_ANGLE) {
+                        outputPoint[1] = 1 * area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+                    } else if (fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[1] = 1 * area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+                    } else outputPoint[0] = 1 * area[JV.IDX_LEFT] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+                    ctx.textAlign="start";
+                    break;
+                case "right":
+                    if (fontAngle == JV.VERTICAL_ANGLE) {
+                        outputPoint[1] = 1 * area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+                    } else if (fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[1] = 1 * area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+                    } else outputPoint[0] = 1 * area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+                    ctx.textAlign="end";
+                    break;
+                case "center":
+                    if (fontAngle == JV.VERTICAL_ANGLE || fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[1] = (1 * area[JV.IDX_TOP] + 1 * area[JV.IDX_BOTTOM]) / 2;
+                    } else outputPoint[0] = (1 * area[JV.IDX_LEFT] + 1 * area[JV.IDX_RIGHT]) / 2;
+                    ctx.textAlign="center";
+                    break;
+            }
+        }
+        function private_setupAreaV(area, type, fontAngle, dftFontHeight, outputPoint) {
+            var lType = type;
+            if (type != "top" && type != "bottom" && type != "center") lType = "top";
+            switch (lType) {
+                case "top":
+                    if (fontAngle == JV.VERTICAL_ANGLE) {
+                        outputPoint[0] = 1 * area[JV.IDX_RIGHT] - dftFontHeight - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                    } else if (fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[0] = 1 * area[JV.IDX_LEFT] + dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                    } else outputPoint[1] = 1 * area[JV.IDX_TOP] + dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                    break;
+                case "bottom":
+                    if (fontAngle == JV.VERTICAL_ANGLE) {
+                        outputPoint[0] = 1 * area[JV.IDX_LEFT] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM];
+                    } else if (fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[0] = 1 * area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM];
+                    } else outputPoint[1] = 1 * area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM];
+                    break;
+                case "center":
+                    if (fontAngle == JV.VERTICAL_ANGLE) {
+                        outputPoint[0] = (1 * area[JV.IDX_LEFT] + 1 * area[JV.IDX_RIGHT] - dftFontHeight) / 2;
+                    } else if (fontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                        outputPoint[0] = (1 * area[JV.IDX_LEFT] + 1 * area[JV.IDX_RIGHT] + dftFontHeight) / 2;
+                    } else outputPoint[1] = (1 * area[JV.IDX_TOP] + 1 * area[JV.IDX_BOTTOM] + dftFontHeight) / 2;
+                    break;
+            }
+        }
+        function private_drawText(val, area, font, control) {
+            var dftFontHeight = 12;
+            var output = [];
+            if (font) {
+                dftFontHeight = 1 * font[JV.FONT_PROPS[1]];
+                var dftOthers = "";
+                var dftFontBold = font[JV.FONT_PROPS[3]];
+                if (dftFontBold && dftFontBold == 'T') {
+                    dftOthers = "bold " + dftOthers ;
+                }
+                var dftFontItalic = font[JV.FONT_PROPS[4]];
+                if (dftFontItalic && dftFontItalic == 'T') {
+                    dftOthers = dftOthers + "italic ";
+                }
+                ctx.font = dftOthers + dftFontHeight + "px " + font[JV.PROP_NAME];
+            }
+            if (control) {
+                private_setupAreaH(area, control.Horizon, font.FontAngle, dftFontHeight, output);
+                private_setupAreaV(area, control.Vertical, font.FontAngle, dftFontHeight, output);
+            } else {
+                private_setupAreaH(area, "left", font.FontAngle, dftFontHeight, output);
+                private_setupAreaV(area, "bottom", font.FontAngle, dftFontHeight, output);
+            }
+            var w = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+            if (font.FontAngle != "0") {
+                w = area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] - area[JV.IDX_TOP] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+            }
+            ctx.save();
+            ctx.translate(output[0], output[1]);
+            if (font.FontAngle == JV.VERTICAL_ANGLE) {
+                ctx.rotate(Math.PI/2);
+            } else if (font.FontAngle == JV.ANTI_VERTICAL_ANGLE) {
+                ctx.rotate(-Math.PI/2);
+            }
+            if (w >= ctx.measureText(val).width) {
+                ctx.fillText(val, 0, 0);
+            } else {
+                while (true) {
+                    dftFontHeight--;
+                    ctx.font = "" + dftFontHeight + "px " + font[JV.PROP_NAME];
+                    if (w >=  ctx.measureText(val).width || dftFontHeight < 6) {
+                        ctx.fillText(val, 0, 0);
+                        break;
+                    }
+                }
+            }
+            ctx.restore();
+        }
+        function private_drawCellText(cell, fonts, controls) {
+            if (cell[JV.PROP_VALUE]) {
+                var values = ("" + cell[JV.PROP_VALUE]).split('|');
+                var font = fonts[cell[JV.PROP_FONT]];
+                var control = controls[cell[JV.PROP_CONTROL]];
+                var height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
+                var area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + me.offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + me.offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + me.offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + me.offsetY];
+                for (var i = 0; i < values.length; i++) {
+                    area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + i * (height / values.length) + me.offsetY;
+                    area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (i + 1) * (height / values.length) + me.offsetY;
+                    private_drawText(values[i], area, font, control);
+                }
+            }
+        }
+        function private_drawLine(cell, ctx, style, styleBorderDest, startP, destP, mergedBand, styles) {
+            ctx.beginPath();
+            var destStyle = style;
+            if (mergedBand) {
+                if (mergedBand[styleBorderDest] == cell[JV.PROP_AREA][styleBorderDest]) {
+                    destStyle = styles[mergedBand[JV.PROP_STYLE][JV.PROP_ID]];
+                }
+            }
+            ctx.moveTo(cell[JV.PROP_AREA][startP[0]] + me.offsetX, cell[JV.PROP_AREA][startP[1]] + me.offsetY);
+            if (destStyle[styleBorderDest] && destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT] != 0) {
+                ctx.lineWidth = 1.0 * destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT];
+                ctx.strokeStyle = destStyle[styleBorderDest][JV.PROP_COLOR];
+                ctx.lineTo(cell[JV.PROP_AREA][destP[0]] + me.offsetX, cell[JV.PROP_AREA][destP[1]] + me.offsetY);
+            }
+            ctx.stroke();
+        }
+        function private_drawCell(cell, fonts, styles, controls, mergedBand) {
+            ctx.save();
+            ctx.translate(0.5,0.5);
+            var style = styles[cell[JV.PROP_STYLE]];
+            if (style) {
+                private_drawLine(cell, ctx, style, JV.PROP_TOP, [JV.PROP_LEFT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_TOP], mergedBand, styles);
+                private_drawLine(cell, ctx, style, JV.PROP_RIGHT, [JV.PROP_RIGHT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_BOTTOM], mergedBand, styles);
+                private_drawLine(cell, ctx, style, JV.PROP_BOTTOM, [JV.PROP_RIGHT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_BOTTOM], mergedBand, styles);
+                private_drawLine(cell, ctx, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles);
+            }
+            private_drawCellText(cell, fonts, controls);
+            ctx.restore();
+        }
+
+        for(var p=1;p<50;p++){
+            pageIdx=p;
+            if (pageObj && pageObj.items.length > 0 && canvas && pageObj.items.length >= pageIdx) {
+                var page = pageObj.items[pageIdx - 1],
+                    fonts = pageObj[JV.NODE_FONT_COLLECTION],
+                    styles = pageObj[JV.NODE_STYLE_COLLECTION],
+                    controls = pageObj[JV.NODE_CONTROL_COLLECTION],
+                    mergedBand = pageObj[JV.BAND_PROP_MERGE_BAND];
+                for (var j = 0; j < page.cells.length; j++) {
+                    var cell = page.cells[j];
+                    private_drawCell(cell, fonts, styles, controls, mergedBand);
+                }
+            }
+            ctx.addPage();
+        }
+
+        fs.writeFile('report.pdf', canvas.toBuffer());
+    },
+    drawPageBorder: function(rptTpl, canvas, resolution) {
+        var me = this;
+        var size = rptTpl[JV.NODE_PAGE_INFO].slice(0);
+        size[0] = Math.round(resolution[0] * size[0]);
+        size[1] = Math.round(resolution[0] * size[1]);
+
+        var ctx = canvas.getContext("2d");
+        ctx.save();
+        ctx.beginPath();
+        ctx.translate(0.5,0.5);
+        ctx.lineWidth = 1;
+        ctx.moveTo(me.offsetX, me.offsetY);
+        ctx.lineTo(size[0] + me.offsetX, me.offsetY);
+        ctx.lineTo(size[0] + me.offsetX, size[1] + me.offsetY);
+        ctx.lineTo(me.offsetX, size[1] + me.offsetY);
+        ctx.lineTo(me.offsetX, me.offsetY);
+        ctx.stroke();
+        ctx.restore();
+
+        ctx.fillStyle="black";
+        ctx.fillRect(size[0] + me.offsetX,10 + me.offsetY,10,size[1]);
+        ctx.fillRect(10 + me.offsetX,size[1] + me.offsetY,size[0],10);
+    }
+}
+
+
+let rpt_oj = reportO.reportObj;
+
+
+
+JpcCanvasOutput.drawToCanvas(rpt_oj,canvas,1);

BIN
modules/reports/util/pdf_base_files/Smart-italic.ttf


BIN
modules/reports/util/pdf_base_files/Smart.ttf


BIN
modules/reports/util/pdf_base_files/hwxsb.ttf


+ 49 - 0
modules/reports/util/pdf_base_files/rpt_pdf_consts.js

@@ -0,0 +1,49 @@
+/**
+ * Created by zhang on 2017/8/14.
+ */
+module.exports={
+    JV:{
+        NODE_MAIN_INFO: "主信息",
+        NODE_PAGE_INFO: "打印页面_信息",
+        NODE_MARGINS: "页边距",
+
+        NODE_FONT_COLLECTION: "font_collection",
+        NODE_STYLE_COLLECTION: "style_collection",
+        NODE_CONTROL_COLLECTION: "control_collection",
+
+        BAND_PROP_MERGE_BAND: "MergeBand",
+
+        PROP_NAME: "Name",
+        PROP_VALUE: "Value",
+        PROP_FONT: "font",
+        PROP_CONTROL: "control",
+        PROP_STYLE: "style",
+        PROP_AREA: "area",
+        PROP_ID: "ID",
+        PROP_LEFT: "Left",
+        PROP_RIGHT: "Right",
+        PROP_TOP: "Top",
+        PROP_BOTTOM: "Bottom",
+
+        IDX_LEFT: 0,
+        IDX_TOP: 1,
+        IDX_RIGHT: 2,
+        IDX_BOTTOM: 3,
+
+        CONTROL_PROPS: ["Shrink", "ShowZero", "Horizon", "Vertical", "Wrap"],
+        BORDER_STYLE_PROPS: ["LineWeight", "DashStyle", "Color"],
+        PROP_LINE_WEIGHT: "LineWeight",
+        PROP_DASH_STYLE: "DashStyle",
+        PROP_COLOR: "Color",
+        FONT_PROPS: ["Name", "FontHeight", "FontColor", "FontBold", "FontItalic", "FontUnderline", "FontStrikeOut", "FontAngle"],
+
+        OUTPUT_OFFSET: [2,1,2,3],
+        OFFSET_IDX_LEFT: 0,
+        OFFSET_IDX_RIGHT: 1,
+        OFFSET_IDX_TOP: 2,
+        OFFSET_IDX_BOTTOM: 3,
+
+        VERTICAL_ANGLE: "90",
+        ANTI_VERTICAL_ANGLE: "-90"
+    }
+}

+ 59 - 0
modules/reports/util/pdfkit_test.js

@@ -0,0 +1,59 @@
+/**
+ * Created by chen on 2017/7/28.
+ */
+
+
+var pdf = require('pdfkit');
+var fs = require('fs');
+var doc = new pdf({autoFirstPage: false});
+var text = '测试,he';
+
+doc.pipe(fs.createWriteStream('测试.pdf'));
+doc.font('simsunB.ttf');
+
+for(var p=0;p<50;p++){
+    doc.addPage({size:[1200,900]});
+
+    doc.rect(30,113,76,609).lineWidth(0.5).strokeColor('black').stroke();
+    doc.text('报表报表报表',42,717,{ height: 100, width: 500,align: 'left'});
+
+
+   /* let width = 70;
+    let height=20;
+    let j = 100;
+    let sum = 0;
+    for(i=0;i<400;i++){
+        if(i%15==0){
+            j+=height;
+            sum = 0;
+        }
+        sum =sum+width;
+        let  x = sum;
+        let y  = j;
+        doc.rect(x,y,width,height).lineWidth(0.5).strokeColor('black').stroke();
+      /!*  doc.lineWidth(1.5);
+        doc.moveTo(x,y).lineTo(x,y+height);
+        doc.lineTo(x+width,y+height);
+        doc.lineTo(x+width,y);
+        doc.lineTo(x,y)
+        doc.strokeColor('black').stroke();*!/
+
+
+        doc.fontSize(12);
+        doc.text(text,x,5+y,{
+            height: height,
+            width: width,
+            align: 'right'});
+
+        // doc.text('报表文件',100,100);
+    }*/
+    doc.save();
+  /*  doc.fontSize(20);
+    doc.rotate(-20,{origin:[400, 105]})
+    doc.text('报表报表报表',300,110,{ height: 100, width: 500,align: 'left'});*/
+    doc.restore();
+}
+
+
+
+doc.end();

+ 144 - 17
modules/reports/util/rpt_excel_util.js

@@ -7,6 +7,7 @@ let JSZip = require("jszip");
 let strUtil = require('../../../public/stringUtil');
 let strUtil = require('../../../public/stringUtil');
 let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let DPI = jpcCmnHelper.getScreenDPI()[0];
 let DPI = jpcCmnHelper.getScreenDPI()[0];
+let fsUtil = require('../../../public/fsUtil');
 const dftHeadXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
 const dftHeadXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
 
 
 function writeContentTypes(sheets, isSinglePage) {
 function writeContentTypes(sheets, isSinglePage) {
@@ -331,6 +332,7 @@ function writeSheets(pageData, paperSize, sharedStrList, stylesObj, isSinglePage
 function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
 function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
     let rst = [], xPos = [], yPos = [], yMultiPos = [], headerStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
     let rst = [], xPos = [], yPos = [], yMultiPos = [], headerStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
     let cacheBorderCell = {};
     let cacheBorderCell = {};
+    let currentPageMergePos = null;
     let private_pre_analyze_pos = function(){
     let private_pre_analyze_pos = function(){
         let cell, pos;
         let cell, pos;
         let self_analyze_sheet_pos = function (theShtData, theXPos, theYPos) {
         let self_analyze_sheet_pos = function (theShtData, theXPos, theYPos) {
@@ -446,22 +448,22 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         borderLineWidths.push(border[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]);
         borderLineWidths.push(border[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]);
         borderLineWidths.push(border[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]);
         borderLineWidths.push(border[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]);
         if (sheetBorder[JV.PROP_LEFT] && sheetBorder[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT]) {
         if (sheetBorder[JV.PROP_LEFT] && sheetBorder[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT]) {
-            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_LEFT));
+            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_LEFT, true));
         } else {
         } else {
             sheetBorderLineWidths.push(0);
             sheetBorderLineWidths.push(0);
         }
         }
         if (sheetBorder[JV.PROP_RIGHT] && sheetBorder[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT]) {
         if (sheetBorder[JV.PROP_RIGHT] && sheetBorder[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT]) {
-            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_RIGHT));
+            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_RIGHT, true));
         } else {
         } else {
             sheetBorderLineWidths.push(0);
             sheetBorderLineWidths.push(0);
         }
         }
         if (sheetBorder[JV.PROP_TOP] && sheetBorder[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]) {
         if (sheetBorder[JV.PROP_TOP] && sheetBorder[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]) {
-            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_TOP));
+            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_TOP, false));
         } else {
         } else {
             sheetBorderLineWidths.push(0);
             sheetBorderLineWidths.push(0);
         }
         }
         if (sheetBorder[JV.PROP_BOTTOM] && sheetBorder[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]) {
         if (sheetBorder[JV.PROP_BOTTOM] && sheetBorder[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]) {
-            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_BOTTOM));
+            sheetBorderLineWidths.push(private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_BOTTOM, false));
         } else {
         } else {
             sheetBorderLineWidths.push(0);
             sheetBorderLineWidths.push(0);
         }
         }
@@ -473,14 +475,45 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         }
         }
         return rst;
         return rst;
     };
     };
-    let private_chkAndGetMergeLine = function(cell, sheetBorder, borderStr) {
+    let private_chkAndGetMergeLine = function(cell, sheetBorder, borderStr, needFurtherChk) {
         let rst = 0, mergeBorder = pageData[JV.BAND_PROP_MERGE_BAND];
         let rst = 0, mergeBorder = pageData[JV.BAND_PROP_MERGE_BAND];
-        if (sheetBorder[borderStr] && sheetBorder[borderStr][JV.PROP_LINE_WEIGHT] != undefined) {
+        if (sheetBorder[borderStr] && sheetBorder[borderStr][JV.PROP_LINE_WEIGHT] !== undefined) {
             rst = sheetBorder[borderStr][JV.PROP_LINE_WEIGHT];
             rst = sheetBorder[borderStr][JV.PROP_LINE_WEIGHT];
         }
         }
-        if (cell[JV.PROP_AREA][borderStr] == mergeBorder[borderStr]) {
-            let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
-            rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
+        if (currentPageMergePos) {
+            let side = currentPageMergePos[borderStr];
+            if (side.indexOf(cell[JV.PROP_AREA][borderStr]) >= 0) {
+                if (needFurtherChk) {
+                    let topSide = currentPageMergePos[JV.PROP_TOP];
+                    let bottomSide = currentPageMergePos[JV.PROP_BOTTOM];
+                    for (let i = 0; i < topSide.length; i++) {
+                        if (cell[JV.PROP_AREA][JV.PROP_TOP] >= topSide[i]) {
+                            if (cell[JV.PROP_AREA][JV.PROP_BOTTOM] <= bottomSide[i]) {
+                                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                                rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
+                                break;
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                } else {
+                    let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                    rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
+                }
+            }
+        } else {
+            if (cell[JV.PROP_AREA][borderStr] === mergeBorder[borderStr]) {
+                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                if (needFurtherChk) {
+                    if (cell[JV.PROP_AREA][JV.PROP_TOP] >= mergeBorder[JV.PROP_TOP] &&
+                        cell[JV.PROP_AREA][JV.PROP_BOTTOM] <= mergeBorder[JV.PROP_BOTTOM]) {
+                        rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
+                    }
+                } else {
+                    rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
+                }
+            }
         }
         }
         return parseInt(rst);
         return parseInt(rst);
     };
     };
@@ -503,10 +536,10 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         }
         }
         let sheetBorder = pageData[JV.NODE_STYLE_COLLECTION][cell.style];
         let sheetBorder = pageData[JV.NODE_STYLE_COLLECTION][cell.style];
         let mergedBorder = private_getIniBorder();
         let mergedBorder = private_getIniBorder();
-        mergedBorder[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_LEFT);
-        mergedBorder[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_RIGHT);
-        mergedBorder[JV.PROP_TOP][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_TOP);
-        mergedBorder[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_BOTTOM);
+        mergedBorder[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_LEFT, true);
+        mergedBorder[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_RIGHT, true);
+        mergedBorder[JV.PROP_TOP][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_TOP, false);
+        mergedBorder[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT] = private_chkAndGetMergeLine(cell, sheetBorder, JV.PROP_BOTTOM, false);
         for (let i = 0; i < stylesObj.borders.length; i++) {
         for (let i = 0; i < stylesObj.borders.length; i++) {
             let border = stylesObj.borders[i];
             let border = stylesObj.borders[i];
             if (private_checkBorder(cell, border, mergedBorder)) {
             if (private_checkBorder(cell, border, mergedBorder)) {
@@ -723,12 +756,14 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         private_cacheMergeBandBorderIdxs();
         private_cacheMergeBandBorderIdxs();
         if (sheetData) {
         if (sheetData) {
             //current sheet data
             //current sheet data
+            currentPageMergePos = sheetData[JV.PAGE_SPECIAL_MERGE_POS];
             self_setDataEx(sheetData, yPos, 0);
             self_setDataEx(sheetData, yPos, 0);
         } else {
         } else {
             //total data in one sheet
             //total data in one sheet
             let cnt = 0;
             let cnt = 0;
             for (let i = 0; i < pageData.items.length; i++) {
             for (let i = 0; i < pageData.items.length; i++) {
                 let shtItemData = pageData.items[i];
                 let shtItemData = pageData.items[i];
+                currentPageMergePos = shtItemData[JV.PAGE_SPECIAL_MERGE_POS];
                 let tmpPos = yMultiPos[i];
                 let tmpPos = yMultiPos[i];
                 cellIdx = 0;
                 cellIdx = 0;
                 self_setDataEx(shtItemData, tmpPos, cnt);
                 self_setDataEx(shtItemData, tmpPos, cnt);
@@ -758,15 +793,30 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
     if (paperSizeIdx >= 0) {
     if (paperSizeIdx >= 0) {
         pStr = 'paperSize="' + JV.PAGES_SIZE_IDX[paperSizeIdx] + '"';
         pStr = 'paperSize="' + JV.PAGES_SIZE_IDX[paperSizeIdx] + '"';
     }
     }
-    let orientationStr = (pageData[JV.NODE_PAGE_INFO][0] > pageData[JV.NODE_PAGE_INFO][1])?'landscape':'portrait';
+    let orientationStr = (pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][0] > pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1])?'landscape':'portrait';
     rst.push('<pageSetup ' + pStr + ' fitToWidth="0" fitToHeight="0" orientation="' + orientationStr + '" />');
     rst.push('<pageSetup ' + pStr + ' fitToWidth="0" fitToHeight="0" orientation="' + orientationStr + '" />');
     rst.push('<headerFooter alignWithMargins="0"/>');
     rst.push('<headerFooter alignWithMargins="0"/>');
     rst.push('</worksheet>');
     rst.push('</worksheet>');
     return rst;
     return rst;
 }
 }
 
 
+function mergeProperties(orgObj, newObj) {
+    let orgPropArr = [], newPropArr = [];
+    for (let p in orgObj) {
+        orgPropArr.push(p);
+    }
+    for (let p in newObj) {
+        newPropArr.push(p);
+    }
+    for (let i = 0; i < newPropArr.length; i++) {
+        if (orgPropArr.indexOf(newPropArr[i]) < 0) {
+            orgObj[newPropArr[i]] = newObj[newPropArr[i]];
+        }
+    }
+}
+
 module.exports = {
 module.exports = {
-    exportExcel: function (pageData, paperSize, fName, options, callback) {
+    exportExcel: function (pageData, paperSize, fName, options, custSheetNames, callback) {
         let rptOptions = ({singlePage: false, fileName: 'report'});
         let rptOptions = ({singlePage: false, fileName: 'report'});
         if (options === 'true') {
         if (options === 'true') {
             rptOptions.singlePage = true;
             rptOptions.singlePage = true;
@@ -776,8 +826,14 @@ module.exports = {
         if (isSinglePage) {
         if (isSinglePage) {
             sheets.push({sheetName: '全部页'});
             sheets.push({sheetName: '全部页'});
         } else {
         } else {
-            for (let i = 0; i < pageData.items.length; i++) {
-                sheets.push({sheetName: '第' + (i + 1) + '页'});
+            if (custSheetNames && custSheetNames.length === pageData.items.length) {
+                for (let i = 0; i < pageData.items.length; i++) {
+                    sheets.push({sheetName: custSheetNames[i]});
+                }
+            } else {
+                for (let i = 0; i < pageData.items.length; i++) {
+                    sheets.push({sheetName: '第' + (i + 1) + '页'});
+                }
             }
             }
         }
         }
         //1.
         //1.
@@ -849,5 +905,76 @@ module.exports = {
             //return zip.generateNodeStream({type:'nodebuffer',streamFiles:true});
             //return zip.generateNodeStream({type:'nodebuffer',streamFiles:true});
             return zip;
             return zip;
         }
         }
+    },
+
+    exportExcelInOneBook: function (pageDataArray, paperSize, fName, callback) {
+        let me = this, newPageData = {};
+        //1. 重新编排一下数据,把一份报表的pageData合并到一起作为一个Sheet输出(需要重新调整数据纵向坐标),多份报表数据就形成多个Sheet
+        //   -- 简单来说,就是重新包装数据
+        try {
+            // 1.1 newPageData外围属性
+            let newContrl = {}, newFont = {}, newStyle = {};
+            for (let i = 0; i < pageDataArray.length; i++) {
+                mergeProperties(newContrl, pageDataArray[i][JV.NODE_CONTROL_COLLECTION]);
+                mergeProperties(newFont, pageDataArray[i][JV.NODE_FONT_COLLECTION]);
+                mergeProperties(newStyle, pageDataArray[i][JV.NODE_STYLE_COLLECTION]);
+            }
+            newPageData[JV.NODE_CONTROL_COLLECTION] = newContrl;
+            newPageData[JV.NODE_FONT_COLLECTION] = newFont;
+            newPageData[JV.NODE_STYLE_COLLECTION] = newStyle;
+            newPageData[JV.NODE_PAGE_INFO] = pageDataArray[0][JV.NODE_PAGE_INFO];
+            newPageData[JV.BAND_PROP_MERGE_BAND] = pageDataArray[0][JV.BAND_PROP_MERGE_BAND];
+
+            // 1.2 重新设置pageDataArray的各个cell的Top/Bottom坐标
+            let sheetNames = [], newPagePos = [];
+            for (let i = 0; i < pageDataArray.length; i++) {
+                let offsetY = 0;
+                let mergeBand = {};
+                mergeBand[JV.PROP_LEFT] = [];
+                mergeBand[JV.PROP_RIGHT] = [];
+                mergeBand[JV.PROP_TOP] = [];
+                mergeBand[JV.PROP_BOTTOM] = [];
+                newPagePos.push(mergeBand);
+                mergeBand[JV.PROP_LEFT].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_LEFT]);
+                mergeBand[JV.PROP_RIGHT].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_RIGHT]);
+                sheetNames.push(pageDataArray[i][JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME]);
+
+                for (let j = 0; j < pageDataArray[i].items.length; j++) {
+                    let maxY = 0, minY = 100000;
+                    mergeBand[JV.PROP_TOP].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_TOP] + offsetY);
+                    mergeBand[JV.PROP_BOTTOM].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_BOTTOM] + offsetY);
+                    for (let k = 0; k < pageDataArray[i].items[j].cells.length; k++) {
+                        if (maxY < pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_BOTTOM]) {
+                            maxY = pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_BOTTOM];
+                        }
+                        if (minY > pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_TOP]) {
+                            minY = pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_TOP];
+                        }
+                        pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_BOTTOM] += offsetY;
+                        pageDataArray[i].items[j].cells[k][JV.PROP_AREA][JV.PROP_TOP] += offsetY;
+                    }
+                    offsetY += (maxY - minY);
+                }
+            }
+            //2. newPageData的items属性
+            newPageData.items = [];
+            for (let i = 0; i < pageDataArray.length; i++) {
+                let pageItem = {};
+                pageItem[JV.PROP_PAGE_SEQ] = i + 1;
+                pageItem[JV.PROP_CELLS] = [];
+                for (let j = 0; j < pageDataArray[i].items.length; j++) {
+                    for (let k = 0; k < pageDataArray[i].items[j].cells.length; k++) {
+                        pageItem[JV.PROP_CELLS].push(pageDataArray[i].items[j].cells[k]);
+                    }
+                }
+                pageItem[JV.PAGE_SPECIAL_MERGE_POS] = newPagePos[i];
+                newPageData.items.push(pageItem);
+            }
+            //3. everything is ok, then call me
+            me.exportExcel(newPageData, paperSize, fName, 'false', sheetNames, callback);
+            fsUtil.wirteObjToFile(newPageData, 'D:/GitHome/ConstructionOperation/tmp/combinedHeader.js');
+        } catch (e) {
+            console.log(e);
+        }
     }
     }
 }
 }

+ 202 - 0
modules/reports/util/rpt_pdf_util.js

@@ -0,0 +1,202 @@
+/**
+ * Created by chen on 2017/8/16.
+ */
+/**
+ * Created by zhang on 2017/8/14.
+ */
+
+const prf_cons = require('./pdf_base_files/rpt_pdf_consts');
+var pdf = require('pdfkit');
+var fs = require('fs');
+let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
+let DPI = jpcCmnHelper.getScreenDPI()[0];
+//let JV = prf_cons.JV;
+let JV = require('../rpt_component/jpc_value_define');
+
+module.exports ={
+    export_pdf_file:export_pdf_file
+}
+
+
+function export_pdf_file (pageData, paperSize, fName, callback) {
+    let offsetX= 10;
+    let offsetY=10;
+    var doc = new pdf({autoFirstPage: false});
+    let newName = '' + (new Date()).valueOf();
+    let stream = doc.pipe(fs.createWriteStream(__dirname.slice(0, __dirname.length - 21) + '/tmp/'+newName+'.pdf'));
+    let pageObj = pageData;
+    // doc.rect(5,5,1190,890).lineWidth(1).strokeColor('black').stroke();//边框
+    let paperSizeIdx = JV.PAGES_SIZE_STR.indexOf(paperSize);
+    let size = JV.PAGES_SIZE[paperSizeIdx];
+
+    if (pageObj && pageObj.items.length > 0 ) {
+        for(let i=0;i<pageObj.items.length;i++){
+            doc.addPage({size:[size[1]*DPI,size[0]*DPI]});
+            var page = pageObj.items[i],
+                fonts = pageObj[JV.NODE_FONT_COLLECTION],
+                styles = pageObj[JV.NODE_STYLE_COLLECTION],
+                controls = pageObj[JV.NODE_CONTROL_COLLECTION],
+                mergedBand = pageObj[JV.BAND_PROP_MERGE_BAND];
+
+            for (var j = 0; j < page.cells.length; j++) {
+                var cell = page.cells[j];
+                private_drawCell(cell, fonts, styles, controls, mergedBand);
+            }
+        }
+    }
+    doc.end();
+    stream.on('finish',function () {
+        console.log(newName + ".pdf was written.");
+        callback(newName);
+    })
+
+
+    function private_drawCell(cell, fonts, styles, controls, mergedBand) {
+        doc.save();
+        //doc.translate(0.5,0.5);
+        var style = styles[cell[JV.PROP_STYLE]];
+        if (style) {
+            private_drawLine(cell, doc, style, JV.PROP_TOP, [JV.PROP_LEFT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_TOP], mergedBand, styles);
+            private_drawLine(cell, doc, style, JV.PROP_RIGHT, [JV.PROP_RIGHT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_BOTTOM], mergedBand, styles);
+            private_drawLine(cell, doc, style, JV.PROP_BOTTOM, [JV.PROP_RIGHT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_BOTTOM], mergedBand, styles);
+            private_drawLine(cell, doc, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles);
+        }
+        private_drawCellText(cell, fonts, controls);
+        doc.restore();
+
+    }
+
+    function private_drawLine(cell, doc, style, styleBorderDest, startP, destP, mergedBand, styles) {
+        //doc.beginPath();
+        var destStyle = style;
+        if (mergedBand) {
+            if (mergedBand[styleBorderDest] == cell[JV.PROP_AREA][styleBorderDest]) {
+                destStyle = styles[mergedBand[JV.PROP_STYLE][JV.PROP_ID]];
+            }
+        }
+        doc.moveTo(cell[JV.PROP_AREA][startP[0]] + offsetX, cell[JV.PROP_AREA][startP[1]] + offsetY);
+        if (destStyle[styleBorderDest] && destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT] != 0) {
+            doc.lineWidth(1.0 * destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT]);
+            doc.lineTo(cell[JV.PROP_AREA][destP[0]] + offsetX, cell[JV.PROP_AREA][destP[1]] + offsetY);
+            doc.strokeColor(destStyle[styleBorderDest][JV.PROP_COLOR]);
+        }
+        doc.stroke();
+    }
+    function private_drawCellText(cell, fonts, controls) {
+        if (cell[JV.PROP_VALUE]) {
+            var values = ("" + cell[JV.PROP_VALUE]).split('|');
+            var font = fonts[cell[JV.PROP_FONT]];
+            var control = controls[cell[JV.PROP_CONTROL]];
+            var height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
+            var area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + offsetY];
+            for (var i = 0; i < values.length; i++) {
+                area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + i * (height / values.length) + offsetY;
+                area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (i + 1) * (height / values.length) + offsetY;
+                private_drawText(values[i], area, font, control);
+            }
+        }
+    }
+
+    function private_drawText(val, area, font, control) {
+        var dftFontHeight = 12;
+        var output = [];
+        if (font) {
+            dftFontHeight = 1 * font[JV.FONT_PROPS[1]];
+            var dftFontBold = font[JV.FONT_PROPS[3]];
+            var dftFontItalic = font[JV.FONT_PROPS[4]];
+            if (dftFontBold && dftFontBold == 'T') {
+                doc.font(__dirname+'/pdf_base_files/hwxsb.ttf');
+            }else if(dftFontItalic && dftFontItalic == 'T'){
+                doc.font(__dirname+'/pdf_base_files/Smart-italic.ttf');
+            }else {
+                doc.font(__dirname+'/pdf_base_files/Smart.ttf');
+            }
+            doc.fontSize(dftFontHeight);
+        }
+        var options={};
+        if (control) {
+            private_setupAreaH(area, control.Horizon, font.FontAngle, dftFontHeight, output,options);
+            private_setupAreaV(area, control.Vertical, font.FontAngle, dftFontHeight, output);
+        } else {
+            private_setupAreaH(area, "left", font.FontAngle, dftFontHeight, output,options);
+            private_setupAreaV(area, "bottom", font.FontAngle, dftFontHeight, output);
+        }
+        var w = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+        if (font.FontAngle != "0") {
+            w = area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] - area[JV.IDX_TOP] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+        }
+        doc.save();
+        //doc.translate(output[0], output[1]);
+        if (w >= doc.widthOfString(val)) {
+            options.width=w;
+            options.height=dftFontHeight;
+        } else {
+            while (true) {
+                dftFontHeight--;
+                doc.fontSize(dftFontHeight);
+                if (w >= doc.widthOfString(val) || dftFontHeight < 6) {
+                    options.width=w;
+                    options.height=dftFontHeight;
+                    break;
+                }
+            }
+        }
+
+        var rotateOptions;
+        if (font.FontAngle != "0") {
+            if (control){
+                rotateOptions=private_setupAreaRotateOption(area,w,control.Vertical,dftFontHeight, output);
+            }else {
+                rotateOptions=private_setupAreaRotateOption(area,w,"bottom",dftFontHeight, output);
+            }
+            doc.rotate(font.FontAngle,rotateOptions);
+        }
+        doc.text(val,output[0], output[1],options);
+        doc.restore();
+    }
+
+    function private_setupAreaH(area, type, fontAngle, dftFontHeight, outputPoint,options) {
+        var lType = type;
+        if (type != "left" && type != "right" && type != "center") lType = "left";
+        options.align=lType;
+        outputPoint[0]=1 * area[JV.IDX_LEFT]+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+    }
+
+    function private_setupAreaV(area, type, fontAngle, dftFontHeight, outputPoint) {
+        var lType = type;
+        if (type != "top" && type != "bottom" && type != "center") lType = "top";
+        switch (lType) {
+            case "top":
+                outputPoint[1] = 1 * area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                break;
+            case "bottom":
+                outputPoint[1] = 1 * area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]-dftFontHeight;
+                break;
+            case "center":
+                outputPoint[1] = 1 * area[JV.IDX_TOP] + (1 * area[JV.IDX_BOTTOM]-1 * area[JV.IDX_TOP]- dftFontHeight) / 2;
+                break;
+        }
+    }
+
+    function private_setupAreaRotateOption(area,w, type="top",dftFontHeight,outputPoint){
+        var x = (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT])/2+area[JV.IDX_LEFT];
+        var y =(area[JV.IDX_BOTTOM] - area[JV.IDX_TOP])/2+ area[JV.IDX_TOP];
+        var rotateOptions = {origin:[x,y]};
+        var h = area[JV.IDX_RIGHT] - area[JV.IDX_LEFT];
+        outputPoint[0]=x-w/2+JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+        var lType = type;
+        switch (lType) {
+            case "top":
+                outputPoint[1] = y- h/2+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                break;
+            case "bottom":
+                outputPoint[1] = y+ h/2-JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]-dftFontHeight;
+                break;
+            case "center":
+                outputPoint[1] = y+dftFontHeight/2;
+                break;
+        }
+        return rotateOptions;
+    }
+
+}

+ 97 - 0
modules/users/models/engineering_lib_model.js

@@ -0,0 +1,97 @@
+/**
+ * 计价规则标准库业务逻辑
+ *
+ * @author CaiAoLin
+ * @date 2017/8/31
+ * @version
+ */
+import BaseModel from "../../common/base/base_model";
+import EngineeringLibSchema from "./schema/engineering_lib";
+
+class EngineeringLibModel extends BaseModel {
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = EngineeringLibSchema;
+        parent.init();
+    }
+
+    /**
+     * 获取标准库数据
+     *
+     * @param {Object} data
+     * @return {Promise}
+     */
+    async getLib(data) {
+        if (data.length <= 0) {
+            return data;
+        }
+
+        let engineeringTemp = {};
+        let engineeringLibIdList = [];
+        // 整理出数据库需要查找的id,一次查找
+        for(let valuation of data) {
+            if (valuation === null) {
+                continue;
+            }
+            for(let engineering of valuation.engineering_list) {
+                if (engineeringTemp[valuation._id] === undefined) {
+                    engineeringTemp[valuation._id] = [engineering.engineering_id];
+                }else {
+                    engineeringTemp[valuation._id].push(engineering.engineering_id);
+                }
+                engineeringLibIdList.push(engineering.engineering_id);
+            }
+        }
+        // console.log(data);
+        // console.log(engineeringTemp);
+
+        // 查找对应的id数据
+        let condition = {_id: {"$in": engineeringLibIdList}};
+        let engineeringLibList = await this.findDataByCondition(condition, null, false, '_id');
+
+        // 组合计价规则id对应专业工程标准库数据
+        let engineeringLib = {};
+        for(let index in engineeringTemp) {
+            if (engineeringTemp[index].length <= 0) {
+                continue;
+            }
+            for(let libId of engineeringTemp[index]) {
+                if (engineeringLibList[libId] === undefined) {
+                    continue;
+                }
+                if (engineeringLib[index] === undefined) {
+                    engineeringLib[index] = [engineeringLibList[libId] ];
+                }else {
+                    engineeringLib[index].push(engineeringLibList[libId] );
+                }
+            }
+        }
+
+        // 替换计价规则数据
+        let valuationData = JSON.parse(JSON.stringify(data));
+        for(let valuation of valuationData) {
+            if (valuation === null) {
+                continue;
+            }
+            for(let engineering of valuation.engineering_list) {
+                if (engineering === null || engineeringLib[valuation._id] === undefined) {
+                    continue;
+                }
+                for (let tmp of engineeringLib[valuation._id]) {
+                    if (tmp._id.toString() === engineering.engineering_id.toString()) {
+                        engineering.lib = tmp;
+                    }
+                }
+            }
+        }
+        return valuationData;
+    }
+
+}
+
+module.exports = EngineeringLibModel;

+ 12 - 17
modules/users/models/schema/compilation.js

@@ -9,27 +9,22 @@ import mongoose from "mongoose";
 
 
 let Schema = mongoose.Schema;
 let Schema = mongoose.Schema;
 let collectionName = 'compilation';
 let collectionName = 'compilation';
-let childrenSchema = new Schema({
-    // 计价名称
-    name: String,
-    // 工程专业
+let engineeringListSchema = new Schema({
+    // 工程专业id
     engineering: {
     engineering: {
-        type: Number,
-        default: ''
-    },
-    // 标准清单
-    bill_lib: {
-        type: Schema.Types.Mixed,
-        default: []
+        type: Number
     },
     },
-    // 定额库
-    ration_lib: {
+    engineering_id: {
         type: Schema.Types.Mixed,
         type: Schema.Types.Mixed,
         default: []
         default: []
-    },
-    // 工料机库
-    glj_lib: {
-        type: Schema.Types.Mixed,
+    }
+}, {_id: false});
+let childrenSchema = new Schema({
+    // 计价名称
+    name: String,
+    // 工程专业列表
+    engineering_list: {
+        type: [engineeringListSchema],
         default: []
         default: []
     },
     },
     // 是否启用
     // 是否启用

+ 47 - 0
modules/users/models/schema/engineering_lib.js

@@ -0,0 +1,47 @@
+/**
+ * 计价规则数据结构
+ *
+ * @author CaiAoLin
+ * @date 2017/8/31
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'engineering_lib';
+let modelSchema = {
+    // 标准清单
+    bill_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    // 定额库
+    ration_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    // 工料机库
+    glj_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    // 列设置
+    main_tree_col: {
+        type: Schema.Types.Mixed,
+        default: {
+            "emptyRows":3,
+            "headRows":0,
+            "treeCol": 0,
+            "headRowHeight":[],
+            "cols":[]
+        }
+    },
+    // 费率标准库
+    fee_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+};
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};
+

+ 132 - 3
public/calc_util.js

@@ -1,7 +1,7 @@
 /**
 /**
  * Created by Tony on 2017/6/21.
  * Created by Tony on 2017/6/21.
- * Modified by CSL, 2017-08-01
- * 引入多套计算程序、费率同步、人工系数同步、改进基数计算、费字段映射等
+ * Modified by CSL, 2017-08-01 引入多套计算程序、费率同步、人工系数同步、改进基数计算、费字段映射等。
+ * added by CSL, 2017-09-01 增加公式解析对象analyzer,用于解析用户修改公式、自定义表达式
  */
  */
 
 
 let executeObj = {
 let executeObj = {
@@ -39,6 +39,133 @@ let executeObj = {
     }
     }
 };
 };
 
 
+let analyzer = {
+    calcTemplate: null,
+
+    standard: function(expr){
+        let str = expr;
+        str = str.replace(/\s/g, "");               // 去空格、去中文空格
+        str = str.replace(/(/g, "(");              // 中文括号"("换成英文括号"("
+        str = str.replace(/)/g, ")");              // 中文括号")"换成英文括号")"
+        str = str.replace(/f/g, "F");               // f换成F
+        return str;
+    },
+
+    analyzeCalcBase: function(expr){
+        // 前提:必须无空格、无特殊符号
+        function getCalcBase(expr){
+            let base = '',
+                iPos1 = -1, iPos2 = -1;
+            for (let i = 0; i < expr.length; i++) {
+                if (expr[i] === '['){
+                    iPos1 = i;
+                }
+                else if (iPos1 != -1 && expr[i]===']'){
+                    iPos2 = i;
+                };
+
+                if (iPos1 != -1 && iPos2 != -1){
+                    base = expr.slice(iPos1, iPos2 + 1);
+                    break;
+                }
+            };
+            return base;
+        };
+        function calcBaseToIDExpr(base){
+            // for test.
+            let id = -1;
+            if (base == '[人工费]'){
+                id = 111;
+            }
+            else if (base == '[材料费]'){
+                id = 222;
+            }
+            else if (base == '[机械费]'){
+                id = 333;
+            }
+            else id = "错误";
+
+            return "@('" + id + "')";
+        };
+
+        while (expr.indexOf('[') > 0) {
+            let base = getCalcBase(expr);
+            let id = calcBaseToIDExpr(base);
+            let baseValue = base.slice(1, -1);   // []会给下面的正则带来干扰,这里去掉
+            var pattBase =new RegExp(baseValue, "g");
+            expr = expr.replace(pattBase, id);
+            expr = expr.replace(/\[@\('/g, "@('");      // [@('
+            expr = expr.replace(/'\)\]/g, "')");        // ')]
+        };
+
+        return expr;
+    },
+
+    analyzeLineRef: function(expr){
+        function isOperator(char){
+            var operator = "+-*/()";
+            return operator.indexOf(char) > -1;
+        };
+        function codeToID(code){
+            return eval(parseFloat(code)+1);
+        };
+        // 前提:必须无空格、无特殊符号、标准大写F
+        function getSection(expr){
+            let section = '',
+                iPos1 = -1, iPos2 = -1;
+            for (let i = 0; i < expr.length; i++) {
+                if (expr[i] === 'F'){
+                    iPos1 = i;
+                }
+                else if (iPos1 != -1 && isOperator(expr[i])){
+                    iPos2 = i;
+                }
+                else if (iPos1 != -1 && i == expr.length - 1){
+                    iPos2 = i + 1;
+
+                };
+                if (iPos1 != -1 && iPos2 != -1){
+                    section = expr.slice(iPos1, iPos2);
+                    break;
+                }
+            };
+            return section;
+        };
+        function sectionToIDExpr(section){
+            if (section){
+                let code = section.slice(1);
+                if (isNaN(code)){
+                    return '错误'      // 这里的返回提示不能加上section,因为会无限循环
+                }
+                else
+                    return "@('" + codeToID(code) + "')";
+            }
+            else return '';
+        };
+
+        while (expr.indexOf('F') > 0) {
+            let sec = getSection(expr);
+            let id = sectionToIDExpr(sec);
+            var pattSec =new RegExp(sec, "g");
+            expr = expr.replace(pattSec, id);
+        };
+        return expr;
+    },
+
+    analyzeUserExpr: function(calcTemplate, calcItem){
+        this.calcTemplate = calcTemplate;
+        let expr = calcItem.dispExpr;
+        // 标准化:处理特殊字符、中文符号、大小写
+        expr = this.standard(expr);
+        calcItem.dispExpr = expr;
+        // 先换掉计算基数
+        expr = this.analyzeCalcBase(expr);
+        // 再换掉行引用
+        expr = this.analyzeLineRef(expr);
+        calcItem.expression = expr;
+    }
+};
+
 class Calculation {
 class Calculation {
     // 先编译公用的基础数据
     // 先编译公用的基础数据
     compilePublics(feeRates, labourCoes, feeTypes, calcBases){
     compilePublics(feeRates, labourCoes, feeTypes, calcBases){
@@ -234,4 +361,6 @@ class Calculation {
             }
             }
         }
         }
     }
     }
-}
+};
+
+//export default analyzer;

+ 7 - 0
public/fsUtil.js

@@ -24,5 +24,12 @@ module.exports = {
                 console.log('Write file: ' + filePath + ' ok!');
                 console.log('Write file: ' + filePath + ' ok!');
             });
             });
         }
         }
+    },
+    wirteObjToFile: function(obj, filePath) {
+        if (obj) {
+            let arr = [];
+            arr.push(JSON.stringify(obj));
+            this.writeArrayToFile(arr, filePath);
+        }
     }
     }
 }
 }

+ 3 - 0
public/web/rpt_value_define.js

@@ -35,6 +35,7 @@ let JV = {
     NODE_MAIN_INFO: "主信息",
     NODE_MAIN_INFO: "主信息",
     NODE_MAIN_INFO_RPT_NAME: "报表名称",
     NODE_MAIN_INFO_RPT_NAME: "报表名称",
     NODE_PAGE_INFO: "打印页面_信息",
     NODE_PAGE_INFO: "打印页面_信息",
+    NODE_PAGE_SIZE: "纸张宽高",
     NODE_MARGINS: "页边距",
     NODE_MARGINS: "页边距",
 
 
     NODE_FONT_COLLECTION: "font_collection",
     NODE_FONT_COLLECTION: "font_collection",
@@ -192,8 +193,10 @@ let JV = {
     OFFSET_IDX_BOTTOM: 3,
     OFFSET_IDX_BOTTOM: 3,
 
 
     PROP_PAGE_SEQ: "page_seq",
     PROP_PAGE_SEQ: "page_seq",
+    PROP_CELLS: "cells",
 
 
     PAGE_SELF_DEFINE: "自定义",
     PAGE_SELF_DEFINE: "自定义",
+    PAGE_SPECIAL_MERGE_POS: "page_merge_pos",
 
 
     PAGES_SIZE_STR: ["A3", "A4", "A5", "B5", "LETTER", "LEGAL", "EXECUTIVE", "16K"],
     PAGES_SIZE_STR: ["A3", "A4", "A5", "B5", "LETTER", "LEGAL", "EXECUTIVE", "16K"],
     PAGES_SIZE_IDX: [8, 9, 11, 13, 1, 5, 7, 93],
     PAGES_SIZE_IDX: [8, 9, 11, 13, 1, 5, 7, 93],

+ 2 - 2
public/web/sheet/sheet_common.js

@@ -141,10 +141,10 @@ var sheetCommonObj = {
         //me.shieldAllCells(sheet);
         //me.shieldAllCells(sheet);
     },
     },
     analyzePasteData: function(setting, pastedInfo) {
     analyzePasteData: function(setting, pastedInfo) {
-        var rst = [], propId = 0, preStrIdx = 0, itemObj = {};
+        var rst = [], propId = pastedInfo.cellRange.col, preStrIdx = 0, itemObj = {};//propId = 0 to proId = pastedInfo.cellRange.col, update by zhong
         for (var i = 0; i < pastedInfo.pasteData.text.length; i++) {
         for (var i = 0; i < pastedInfo.pasteData.text.length; i++) {
             if (pastedInfo.pasteData.text[i] === "\n") {
             if (pastedInfo.pasteData.text[i] === "\n") {
-                propId = 0;
+                propId = pastedInfo.cellRange.col;//propId = 0 to proId = pastedInfo.cellRange.col, update by zhong
                 preStrIdx = i + 1;
                 preStrIdx = i + 1;
                 rst.push(itemObj);
                 rst.push(itemObj);
                 if (i < pastedInfo.pasteData.text.length - 1) {
                 if (i < pastedInfo.pasteData.text.length - 1) {

+ 13 - 0
public/web/tree_sheet/tree_sheet_helper.js

@@ -3,6 +3,16 @@
  */
  */
 
 
 var TREE_SHEET_HELPER = {
 var TREE_SHEET_HELPER = {
+    createNewSpread: function (obj) {
+        var spread = new GC.Spread.Sheets.Workbook(obj, {sheetCount: 1});
+        spread.options.tabStripVisible = false;
+        spread.options.scrollbarMaxAlign = true;
+        spread.options.cutCopyIndicatorVisible = false;
+        spread.options.allowCopyPasteExcelStyle = false;
+        spread.options.allowUserDragDrop = false;
+        spread.getActiveSheet().setRowCount(3);
+        return spread;
+    },
     getSheetCellStyle: function (setting) {
     getSheetCellStyle: function (setting) {
         var style = new GC.Spread.Sheets.Style();
         var style = new GC.Spread.Sheets.Style();
         //style.locked = setting.readOnly ? true : false;
         //style.locked = setting.readOnly ? true : false;
@@ -102,6 +112,9 @@ var TREE_SHEET_HELPER = {
                 } else {
                 } else {
                     cell.value(getFieldText2());
                     cell.value(getFieldText2());
                 }
                 }
+                if (colSetting.data.cellType) {
+                    cell.cellType(colSetting.data.cellType);
+                }
                 if (colSetting.readOnly) {
                 if (colSetting.readOnly) {
                     if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
                     if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
                         cell.locked(colSetting.readOnly(node));
                         cell.locked(colSetting.readOnly(node));

+ 209 - 0
test/calculation/test_analyzer.js

@@ -0,0 +1,209 @@
+/**
+ * Created by CSL on 2017-09-01.
+ */
+var test = require('tape');
+import analyzer from '../../public/calc_util';
+
+test('解析测试', function(t){
+    let calcTemplate = {
+        ID: 1,
+        name: "建筑工程",
+        calcItems: [
+            {
+                ID: "1",
+                code: "1",
+                name: "基价直接工程费",
+                fieldName: "baseDirect",
+                dispExpr: "F2+F5+F6+F10",
+                expression: "@('2') + @('5') + @('6') + @('10')",
+                compiledExpr: "",
+                statement: "基价人工费+基价材料费+基价机械费+未计价材料费"
+            },
+            {
+                ID: "2",
+                code: "1.1",
+                name: "基价人工费",
+                fieldName: "baseLabour",
+                dispExpr: "F3+F4",
+                expression: "@('3') + @('4')",
+                compiledExpr: "",
+                statement: "定额基价人工费+定额人工单价(基价)调整"
+            },
+            {
+                ID: "3",
+                code: "1.1.1",
+                name: "定额基价人工费",
+                fieldName: "rationBaseLabour",
+                dispExpr: "[定额基价人工费]",
+                expression: "base('定额基价人工费').toFixed(2)",
+                compiledExpr: "",
+                statement: "定额基价人工费"
+            },
+            {
+                ID: "4",
+                code: "1.1.2",
+                name: "定额人工单价(基价)调整",
+                fieldName: "rationLabourFixed",
+                dispExpr: "F3*(L-1)",
+                expression: "@('3') * (L-1)",
+                labourCoeID: 22,
+                compiledExpr: "",
+                statement: "定额基价人工费*[定额人工单价(基价)调整系数-1]",
+                memo: "渝建发(2013)51"
+            },
+            {
+                ID: "5",
+                code: "1.2",
+                name: "基价材料费",
+                fieldName: "baseMaterial",
+                dispExpr: "[定额基价材料费]",
+                expression: "base('定额基价材料费')",
+                compiledExpr: "",
+                statement: "定额基价材料费"
+            },
+            {
+                ID: "6",
+                code: "1.3",
+                name: "基价机械费",
+                fieldName: "baseMachine",
+                dispExpr: "F7+F9",
+                expression: "@('7') + @('9')",
+                compiledExpr: "",
+                statement: "定额基价机械费+定额机上人工单价(基价)调整"
+            },
+            {
+                ID: "7",
+                code: "1.3.1",
+                name: "定额基价机械费",
+                fieldName: "rationBaseMachine",
+                dispExpr: "[定额基价机械费]",
+                expression: "base('定额基价机械费')",
+                compiledExpr: "",
+                statement: "定额基价机械费"
+            },
+            {
+                ID: "8",
+                code: "1.3.1.1",
+                name: "其中:定额基价机上人工费",
+                fieldName: "rationBaseMachineLabour",
+                dispExpr: "[定额基价机上人工费]",
+                expression: "base('定额基价机上人工费')",
+                compiledExpr: "",
+                statement: "定额基价机上人工费"
+            },
+            {
+                ID: "9",
+                code: "1.3.2",
+                name: "定额机上人工单价(基价)调整",
+                fieldName: "rationBaseMachineLabourFixed",
+                dispExpr: "F8*(L-1)",
+                expression: "@('8') * (L-1)",
+                labourCoeID: 24,
+                compiledExpr: "",
+                statement: "定额基价机上人工费*[定额机上人工单价(基价)调整系数-1]"
+            },
+            {
+                ID: "10",
+                code: "1.4",
+                name: "未计价材料费",
+                fieldName: "unPriceMaterial",
+                dispExpr: "[主材费]+[设备费]",
+                expression: "base('主材费') + base('设备费')",
+                compiledExpr: "",
+                statement: "主材费+设备费"
+            },
+            {
+                ID: "11",
+                code: "2",
+                name: "企业管理费",
+                fieldName: "manageFee",
+                dispExpr: "F3+F5+F7",
+                feeRateID: 101,
+                expression: "@('3') + @('5') + @('7')",
+                compiledExpr: "",
+                statement: "定额基价人工费",
+                memo: "渝建发[2014]27号"
+            },
+            {
+                ID: "12",
+                code: "3",
+                name: "利润",
+                fieldName: "profit",
+                dispExpr: "F3+F5+F7",
+                feeRateID: 301,
+                expression: "@('3') + @('5') + @('7')",
+                compiledExpr: "",
+                statement: "定额基价人工费"
+            },
+            {
+                ID: "13",
+                code: "4",
+                name: "风险因素",
+                fieldName: "risk",
+                dispExpr: "F3+F5+F7",
+                feeRateID: 701,
+                expression: "@('3') + @('5') + @('7')",
+                compiledExpr: "",
+                statement: "定额基价人工费",
+                memo: "同定额包干费"
+            },
+            {
+                ID: "14",
+                code: "5",
+                name: "人材机价差",
+                fieldName: "lmmDiff",
+                dispExpr: "F15+F16+F17",
+                expression: "@('15') + @('16') + @('17')",
+                compiledExpr: "",
+                statement: "人工费价差+材料费价差+机械费价差"
+            },
+            {
+                ID: "15",
+                code: "5.1",
+                name: "人工费价差",
+                fieldName: "labourDiff",
+                dispExpr: "[人工费价差]",
+                expression: "base('市场价格人工费') - base('定额基价人工费(调整后)')",
+                compiledExpr: "",
+                statement: "市场价格人工费-调整后的定额人工费(基价)"
+            },
+            {
+                ID: "16",
+                code: "5.2",
+                name: "材料费价差",
+                fieldName: "materialDiff",
+                dispExpr: "[材料费价差]",
+                expression: "base('市场价格材料费') - base('定额基价材料费(调整后)')",
+                compiledExpr: "",
+                statement: "市场价格材料费-定额基价材料费"
+            },
+            {
+                ID: "17",
+                code: "5.3",
+                name: "机械费价差",
+                fieldName: "machineDiff",
+                dispExpr: "[机械费价差]",
+                expression: "base('市场价格机械费') - base('定额基价机械费(调整后)')",
+                compiledExpr: "",
+                statement: "市场价格机械费-调整后的定额基价机械费(基价)"
+            },
+            {
+                ID: "18",
+                code: "6",
+                name: "综合单价",
+                fieldName: "common",
+                dispExpr: "F1+F11+F12+F13+F14",
+                expression: "@('1') + @('11') + @('12') + @('13') + @('14')",
+                compiledExpr: "",
+                statement: "基价直接工程费+企业管理费+利润+风险因素+人材机价差"
+            }
+        ]
+    };
+    let calcItem = {dispExpr: "12 +[人工费]*1.2+f4+ (F6+ f10) +F23+[人工费] + f6+[材料费]"};
+    let target = "12+@('111')*1.2+@('5')+(@('7')+@('11'))+@('24')+@('111')+@('7')+@('222')";
+    analyzer.analyzeUserExpr(calcTemplate, calcItem);
+    console.log(calcItem.dispExpr);
+    console.log(calcItem.expression);
+    t.equal(calcItem.expression, target);
+    t.end();
+});

+ 6 - 5
test/tmp_data/bills_grid_setting.js

@@ -102,8 +102,8 @@ var BillsGridSetting ={
             }
             }
         },
         },
         {
         {
-            "width":40,
-            "readOnly":false,
+            "width":60,
+            "readOnly":'readOnly.ration',
             "head":{
             "head":{
                 "titleNames":[
                 "titleNames":[
                     "计量\n单位"
                     "计量\n单位"
@@ -131,7 +131,8 @@ var BillsGridSetting ={
                 "field":"unit",
                 "field":"unit",
                 "vAlign":1,
                 "vAlign":1,
                 "hAlign":1,
                 "hAlign":1,
-                "font":"Arial"
+                "font":"Arial",
+                "cellType": 'cellType.unit'
             }
             }
         },
         },
         {
         {
@@ -158,7 +159,7 @@ var BillsGridSetting ={
                 ]
                 ]
             },
             },
             "data":{
             "data":{
-                "field":"",
+                "field":"itemCharacterText",
                 "vAlign":1,
                 "vAlign":1,
                 "hAlign":0,
                 "hAlign":0,
                 "font":"Arial"
                 "font":"Arial"
@@ -188,7 +189,7 @@ var BillsGridSetting ={
                 ]
                 ]
             },
             },
             "data":{
             "data":{
-                "field":"",
+                "field":"jobContentText",
                 "vAlign":1,
                 "vAlign":1,
                 "hAlign":0,
                 "hAlign":0,
                 "font":"Arial"
                 "font":"Arial"

+ 34 - 29
web/building_saas/complementary_glj_lib/js/components.js

@@ -43,38 +43,43 @@ let componentOprObj = {
         let thisComponent = me.currentCache[args.row];
         let thisComponent = me.currentCache[args.row];
         thisComponent.isChecked = val;
         thisComponent.isChecked = val;
         console.log(val);
         console.log(val);
-        //维护选中组成物列表
-        if(val === true){
-            //解决复选框编辑状态的暂时方法todo
-            let isExist = false;
-            for(let i = 0, len = me.selectedList.length; i < len; i++){
-                if(me.selectedList[i].ID === thisComponent.ID){
-                    isExist = true;
-                    break;
-                }
-            }
-            if(!isExist){
-                me.selectedList.push(thisComponent);
-            }
+        if(args.sheet.isEditing()){
+            args.sheet.endEdit(true);
         }
         }
-        else if(val === false){
-            for(let i = 0, len = me.selectedList.length; i < len; i++){
-                if(me.selectedList[i].ID === thisComponent.ID){
-                    me.selectedList.splice(i, 1);
-                    break;
+        else{
+            //维护选中组成物列表
+            if(val === true){
+                //解决复选框编辑状态的暂时方法todo
+                let isExist = false;
+                for(let i = 0, len = me.selectedList.length; i < len; i++){
+                    if(me.selectedList[i].ID === thisComponent.ID){
+                        isExist = true;
+                        break;
+                    }
+                }
+                if(!isExist){
+                    me.selectedList.push(thisComponent);
                 }
                 }
             }
             }
-            if($("input[name='glj']:checked").val() === 'selectedGljs'){//radio为已选工料机时
-                me.showGljList = [];
-                me.setShowGljList(me.selectedList, false);
-                re.sortGlj(me.showGljList);
-                //重新显示
-                me.showGljItems(me.showGljList, me.gljCurTypeId);
-                if (re.parentNodeIds["_pNodeId_" + me.gljCurTypeId]) {//更新cache
-                    me.currentOprParent = 1;
-                    me.currentCache = me.getParentCache(re.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
-                } else {
-                    me.currentCache = me.getCache();
+            else if(val === false){
+                for(let i = 0, len = me.selectedList.length; i < len; i++){
+                    if(me.selectedList[i].ID === thisComponent.ID){
+                        me.selectedList.splice(i, 1);
+                        break;
+                    }
+                }
+                if($("input[name='glj']:checked").val() === 'selectedGljs'){//radio为已选工料机时
+                    me.showGljList = [];
+                    me.setShowGljList(me.selectedList, false);
+                    re.sortGlj(me.showGljList);
+                    //重新显示
+                    me.showGljItems(me.showGljList, me.gljCurTypeId);
+                    if (re.parentNodeIds["_pNodeId_" + me.gljCurTypeId]) {//更新cache
+                        me.currentOprParent = 1;
+                        me.currentCache = me.getParentCache(re.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
+                    } else {
+                        me.currentCache = me.getCache();
+                    }
                 }
                 }
             }
             }
         }
         }

+ 2 - 2
web/building_saas/complementary_glj_lib/js/gljComponent.js

@@ -83,7 +83,7 @@ let gljComponentOprObj = {
                     return {
                     return {
                         callback: function(){},
                         callback: function(){},
                         items: {
                         items: {
-                            "insert": {name: "插入", disabled: insertDis, callback: function (key, opt) {
+                            "insert": {name: "插入", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
                                 //默认radio所有工料机
                                 //默认radio所有工料机
                                 co.initRadio();
                                 co.initRadio();
                                 co.gljCurTypeId = null;
                                 co.gljCurTypeId = null;
@@ -95,7 +95,7 @@ let gljComponentOprObj = {
                                 //弹出窗口
                                 //弹出窗口
                                 $('#componentBtn').click();
                                 $('#componentBtn').click();
                             }},
                             }},
-                            "delete": {name: "删除", disabled: delDis, callback: function (key, opt) {
+                            "delete": {name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
                                 //删除
                                 //删除
                                 let deleteObj = that.currentComponent[target.row];
                                 let deleteObj = that.currentComponent[target.row];
                                 let gljComponent = that.currentGlj.component;
                                 let gljComponent = that.currentGlj.component;

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

@@ -154,12 +154,27 @@
                                   <li class="nav-item">
                                   <li class="nav-item">
                                       <a class="nav-link" id="linkDESM" data-toggle="tab" href="#subSpread" role="tab">定额说明</a>
                                       <a class="nav-link" id="linkDESM" data-toggle="tab" href="#subSpread" role="tab">定额说明</a>
                                   </li>
                                   </li>
+                                  <li class="nav-item">
+                                      <a class="nav-link" id="linkTZJNR" data-toggle="tab" href="#subSpread" role="tab">特征及内容</a>
+                                  </li>
                               </ul>
                               </ul>
                               <!-- Tab panes -->
                               <!-- Tab panes -->
                               <div class="tab-content">
                               <div class="tab-content">
                                   <div class="tab-pane active" id="gl" role="tabpanel">
                                   <div class="tab-pane active" id="gl" role="tabpanel">
                                       <div class="main-data-bottom ovf-hidden" id="subSpread">
                                       <div class="main-data-bottom ovf-hidden" id="subSpread">
                                       </div>
                                       </div>
+                                      <div id="tzjnrCon" class="row" style="display: none">
+                                          <div class="col-6">
+                                              <div class="row">
+                                                  <div class="main-data-bottom ovf-hidden col-5" id="jobSpread">
+                                                  </div>
+                                                  <div class="main-data-bottom ovf-hidden col-7" id="itemSpread">
+                                                  </div>
+                                              </div>
+                                          </div>
+                                          <div class="col-6">
+                                          </div>
+                                      </div>
                                   </div>
                                   </div>
                               </div>
                               </div>
                           </div>
                           </div>
@@ -547,6 +562,7 @@
     <script type="text/javascript" src="/public/web/rpt_tpl_def.js"></script>
     <script type="text/javascript" src="/public/web/rpt_tpl_def.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/rpt/rpt_main.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/rpt/rpt_main.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/rpt/rpt_cfg_const.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/rpt/rpt_cfg_const.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/views/character_content_view.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/views/glj_view.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/views/glj_view.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/views/sub_view.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/views/sub_view.js"></script>
     <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/fee_rate_view.js"></script>

+ 695 - 0
web/building_saas/main/js/views/character_content_view.js

@@ -0,0 +1,695 @@
+/**
+ * Created by Zhong on 2017/8/31.
+ * 特征及内容
+ */
+//todo:清单库相关联的操作
+let contentOprObj = {
+    workBook: null,
+    currentCache: [],//按照serialNo排序
+    setting: {
+        header: [
+            {headerName:"工作内容",headerWidth:160,dataCode:"content", dataType: "String", hAlign: "center", vAlign: "center"},
+            {headerName:"输出",headerWidth:80,dataCode:"isChecked", cellType:"checkBox", hAlign: "center", vAlign: "center"}
+        ]
+    },
+    buildSheet: function(container) {
+        let me = contentOprObj;
+        me.workBook = sheetCommonObj.buildSheet(container, me.setting, 30);
+        me.workBook.getSheet(0).setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
+        me.workBook.options.allowCopyPasteExcelStyle = false;
+        me.onContextmenuOpr();//右键菜单
+        me.bindEvents(me.workBook);
+    },
+    bindEvents: function (workBook) {
+        let sheet = workBook.getActiveSheet(), me = contentOprObj;
+        const EVENTS = GC.Spread.Sheets.Events;
+        workBook.bind(EVENTS.ButtonClicked, me.onButtonClicked);
+        sheet.bind(EVENTS.EditEnded, me.onEditEnded);
+        sheet.bind(EVENTS.ClipboardPasting, me.onClipboardPasting);
+        sheet.bind(EVENTS.ClipboardPasted, me.onClipboardPasted);
+    },
+    //显示在jobSpread的数据
+    showContentData: function (sheet, setting, datas) {
+        sheetCommonObj.showData(sheet, setting, datas);
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        for(let i = 0, len = datas.length; i < len; i++){
+            sheet.getCell(i, 0).locked(true);
+        }
+        sheet.resumePaint();
+        sheet.suspendEvent();
+    },
+    //显示到清单工作内容列的数据
+    getColData: function (jobsArr) {
+        let me = contentOprObj;
+        let rstStr = "", count = 0;
+        for(let i = 0, len = jobsArr.length; i < len; i++){
+            if(jobsArr[i].isChecked === true){
+                count ++;
+                if(count === 1){
+                    rstStr += "“";
+                }
+                else{
+                    rstStr += " ";
+                }
+                rstStr += count + " " + jobsArr[i].content + "\n";
+            }
+        }
+        if(rstStr.trim().length > 0){
+            let reg = /\n+$/g;
+            let newStr = rstStr.replace(reg, "”");
+            return newStr;
+        }
+        return null;
+    },
+    //新增行
+    addRow: function (sheet) {
+        let checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
+        checkBox.isThreeState = false;
+        sheet.addRows(sheet.getRowCount(), 1);
+        sheet.getCell(sheet.getRowCount() - 1, 1).cellType(checkBox);
+    },
+    upMove: function (rowIdx) {
+        let me = contentOprObj;
+        let thisObj = me.currentCache[rowIdx],
+            preObj = me.currentCache[rowIdx - 1],
+            temp, contentTxt;
+        temp = thisObj.serialNo;
+        thisObj.serialNo = preObj.serialNo;
+        preObj.serialNo = temp;
+        me.sortCache(me.currentCache);
+        me.save();
+    },
+    downMove: function (rowIdx) {
+        let me = contentOprObj;
+        let thisObj = me.currentCache[rowIdx],
+            nextObj = me.currentCache[rowIdx + 1],
+            temp, contentTxt;
+        temp = thisObj.serialNo;
+        thisObj.serialNo = nextObj.serialNo;
+        nextObj.serialNo = temp;
+        me.sortCache(me.currentCache);
+        me.save();
+    },
+    deleteContent: function (rowIdx) {
+        let me = contentOprObj;
+        me.currentCache.splice(rowIdx, 1);
+        me.save();
+    },
+    //更新
+    updateContent: function (job, newContent) {
+        job.content = newContent;
+    },
+    //新增
+    insertContent: function (content) {
+        let me = contentOprObj;
+        let preObj = me.currentCache.length > 0 ?  me.currentCache[me.currentCache.length - 1] : null;
+        let newObj = {content: content, isCheceked: false, serialNo: preObj ? preObj.serialNo + 1 : 1};
+        me.currentCache.push(newObj);
+
+    },
+    save: function () {
+        let me = contentOprObj;
+        let contentTxt = me.getColData(me.currentCache);
+        let txtObj =  {field: 'jobContentText', text: contentTxt ? contentTxt : ''};
+        pageCCOprObj.updateCharacterContent(pageCCOprObj.currentFindSet, {field: 'jobContent', updateArr: me.currentCache}, txtObj, contentOprObj);
+    },
+    onEditEnded: function (sender, args) {
+        let me = contentOprObj;
+        let preObj = me.currentCache.length > 0 ?  me.currentCache[me.currentCache.length - 1] : null;
+        let contentTxt;
+        if(args.editingText && args.editingText.toString().trim().length > 0){
+            //更新
+            if(args.row < me.currentCache.length ){
+                me.updateContent(me.currentCache[args.row], args.editingText);
+            }
+            //新增
+            else{
+                me.insertContent(args.editingText);
+            }
+            //保存
+            me.save();
+        }
+        else{
+            //恢复
+            args.sheet.setValue(args.row, args.col, me.currentCache.length > args.row ? me.currentCache[args.row].content + '' : '');
+        }
+    },
+    //复选框控制输出
+    onButtonClicked: function (sender, args) {
+        let me = contentOprObj, contentTxt;
+        if(args.sheet.isEditing()){
+            args.sheet.endEdit(true);
+        }
+        let isChecked = args.sheet.getValue(args.row, args.col);
+        if(me.currentCache.length > args.row){
+            me.currentCache[args.row].isChecked = isChecked;
+            me.save();
+        }
+        //else的情况就是新增
+        else{
+            //还没数据清空复选框
+            args.sheet.setValue(args.row, args.col, 0);
+        }
+    },
+    //复制粘贴
+    onClipboardPasting: function (sender, args) {
+        if(args.cellRange.colCount > 1 || args.cellRange.col !== 0){
+            args.cancel = true;
+        }
+    },
+    onClipboardPasted: function (sender, args) {
+        let me = contentOprObj;
+        let items = sheetCommonObj.analyzePasteData(me.setting, args);
+        for(let i = 0, len = items.length; i < len; i++){
+            let rowIdx = args.cellRange.row + i;
+            //更新
+            if(rowIdx < me.currentCache.length){
+                me.updateContent(me.currentCache[rowIdx], items[i].content);
+            }
+            //新增
+            else{
+                me.insertContent(items[i].content);
+            }
+        }
+        me.save();
+    },
+    sortCache: function (cacheArr) {
+        cacheArr.sort(function (a, b) {
+            let rst = 0;
+            if(a.serialNo > b.serialNo){
+                rst = 1;
+            }
+            else if(a.serialNo < b.serialNo){
+                rst = -1;
+            }
+            return rst;
+        });
+    },
+    onContextmenuOpr: function () {//右键菜单
+        let me = contentOprObj;
+        $.contextMenu({
+            selector: '#jobSpread',
+            build: function($triggerElement, e){
+                //控制允许右键菜单在哪个位置出现
+                let clientX = e.originalEvent.clientX,
+                 clientY = e.originalEvent.clientY;
+                 let sheet = me.workBook.getSheet(0);
+                 let offset = $("#jobSpread").offset(),
+                 x = clientX - offset.left,
+                 y = clientY - offset.top;
+                 let target = sheet.hitTest(x, y);
+                if(target.hitTestType === 3){//在表格内&& typeof target.row !== 'undefined' && typeof target.col !== 'undefined'
+                    let insertDis = false, delDis = false, upDis = false, downDis = false;
+                    if(typeof target.row !== 'undefined'){
+                        //控制按钮是否可用
+                        sheet.setActiveCell(target.row, target.col);
+                        if(!me.currentCache ||target.row >= me.currentCache.length){//右键定位在有数据的行,删除键才显示可用
+                            delDis = true;
+                            downDis = true;
+                            upDis = true;
+                        }
+                        else{//有数据
+                            if(target.row === me.currentCache.length -1){//定位在最后一行,不可下移
+                                downDis = true;
+                            }
+                            if(target.row === 0){//定位在第一行,不可上移
+                                upDis = true;
+                            }
+                        }
+                    }
+                    else{
+                        delDis = true;
+                        downDis = true;
+                        upDis = true;
+                    }
+                    return {
+                        callback: function(){},
+                        items: {
+                            "insert": {name: "插入", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
+                                //插入空行
+                                me.addRow(sheet);
+                            }},
+                            "delete": {name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
+                                me.deleteContent(target.row);
+                            }},
+                            "upMove": {name: "上移", disabled: upDis, icon: "fa-arrow-up", callback: function (key, opt) {
+                                me.upMove(target.row);
+
+                            }},
+                            "downMove": {name: "下移", disabled: downDis, icon: "fa-arrow-down", callback: function (key, opt) {
+                                me.downMove(target.row);
+                            }}
+                        }
+                    };
+                }
+                else{
+                    return false;
+                }
+            }
+        });
+    }
+};
+
+
+let characterOprObj = {
+    workBook: null,
+    currentCache: [],
+    setting: {
+        header: [
+            {headerName:"项目特征",headerWidth:160,dataCode:"character", dataType: "String", hAlign: "center", vAlign: "center"},
+            {headerName:"特征值",headerWidth:160,dataCode:"eigenvalue", dataType: "String", cellType: "comboBox", hAlign: "center", vAlign: "center"},
+            {headerName:"输出",headerWidth:80,dataCode:"isChecked", cellType:"checkBox", hAlign: "center", vAlign: "center"}
+        ]
+    },
+    buildSheet: function(container) {
+        let me = characterOprObj;
+        me.workBook = sheetCommonObj.buildSheet(container, me.setting, 30);
+        me.workBook.getSheet(0).setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
+        me.workBook.options.allowCopyPasteExcelStyle = false;
+        me.onContextmenuOpr();
+        me.bindEvents(me.workBook);
+    },
+    bindEvents: function (workBook) {
+        let sheet = workBook.getActiveSheet(), me = characterOprObj;
+        const EVENTS = GC.Spread.Sheets.Events;
+        workBook.bind(EVENTS.ButtonClicked, me.onButtonClicked);
+        sheet.bind(EVENTS.EditEnded, me.onEditEnded);
+        sheet.bind(EVENTS.EditStarting, me.onEditStart);
+        sheet.bind(EVENTS.ClipboardPasting, me.onClipboardPasting);
+        sheet.bind(EVENTS.ClipboardPasted, me.onClipboardPasted);
+    },
+    //显示在itemSpread的数据
+    showCharacterData: function (sheet, setting, datas) {
+        let me = characterOprObj;
+        sheetCommonObj.showData(sheet, setting, datas);
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        for(let i = 0, len = datas.length; i < len; i++){
+            let comboObj = me.getComboBox(datas[i]);
+            comboObj.combo.editable(false);//不可编辑
+            sheet.getCell(i, 1).cellType(comboObj.combo).value(typeof comboObj.selectedValue !== 'undefined' ? comboObj.selectedValue : '');
+            sheet.getCell(i, 0).locked(true);
+        }
+        sheet.resumePaint();
+        sheet.suspendEvent();
+    },
+    //获得项目特征特征值comboBox
+    getComboBox: function (characterItem) {
+        let rst = {};
+        let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        let comboItems = [], eigenvalues = characterItem.eigenvalue;
+        for(let i = 0, len = eigenvalues.length; i < len; i++){
+            comboItems.push(eigenvalues[i].value);
+            if(eigenvalues[i].isSelected){
+                rst.selectedValue = eigenvalues[i].value;
+            }
+        }
+        combo.items(comboItems);
+        rst.combo = combo;
+        return rst;
+    },
+    //获得当前行选中的特征值
+    getCurrentSelected: function (item) {
+        let rst = null;
+        for(let i = 0, len = item.eigenvalue.length; i < len; i++){
+            if(item.eigenvalue[i].isSelected){
+                rst = item.eigenvalue[i].value;
+                break;
+            }
+        }
+        return rst;
+    },
+    //显示到清单项目特征列的数据
+    getColData: function (itemsArr) {
+        let me = characterOprObj;
+        let rstStr = "", count = 0;
+        for(let i = 0, len = itemsArr.length; i < len; i++){
+            if(itemsArr[i].isChecked === true){
+                //获取选中的特征值
+                let eigenvalueStr = "";
+                let eigenvalue = itemsArr[i].eigenvalue;
+                for(let j = 0, vLen = eigenvalue.length; j < vLen; j++){
+                    if(eigenvalue[j].isSelected){
+                        eigenvalueStr += eigenvalue[j].value;
+                        break;
+                    }
+                }
+                count ++;
+                if(count === 1){
+                    rstStr += "“";
+                }
+                else{
+                    rstStr += " ";
+                }
+                rstStr += count + " " + itemsArr[i].character + ": " + eigenvalueStr + "\n";
+            }
+        }
+        if(rstStr.trim().length > 0){
+            let reg = /\n+$/g;
+            let newStr = rstStr.replace(reg, "”");
+            return newStr;
+        }
+        return null;
+    },
+    addRow: function (sheet) {
+        let checkBox = new GC.Spread.Sheets.CellTypes.CheckBox(),
+            combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        checkBox.isThreeState = false;
+        combo.editable(true);
+        let rowIdx = sheet.getRowCount();
+        sheet.addRows(rowIdx, 1);
+        sheet.getCell(rowIdx, 1).cellType(combo);
+        sheet.getCell(rowIdx, 2).cellType(checkBox);
+    },
+    upMove: function (rowIdx) {
+        let me = characterOprObj;
+        let thisObj = me.currentCache[rowIdx],
+            preObj = me.currentCache[rowIdx - 1],
+            temp;
+        temp = thisObj.serialNo;
+        thisObj.serialNo = preObj.serialNo;
+        preObj.serialNo = temp;
+        contentOprObj.sortCache(me.currentCache);
+        me.save();
+    },
+    downMove: function (rowIdx) {
+        let me = characterOprObj;
+        let thisObj = me.currentCache[rowIdx],
+            nextObj = me.currentCache[rowIdx + 1],
+            temp;
+        temp = thisObj.serialNo;
+        thisObj.serialNo = nextObj.serialNo;
+        nextObj.serialNo = temp;
+        contentOprObj.sortCache(me.currentCache);
+        me.save();
+    },
+    deleteCharacter: function (rowIdx) {
+        let me = characterOprObj;
+        me.currentCache.splice(rowIdx, 1);
+        me.save();
+    },
+    //取消选择的特征值
+    unsetSelected: function (item) {
+        for(let i = 0, len = item.eigenvalue.length; i < len; i++){
+            if(item.eigenvalue[i].isSelected){
+                item.eigenvalue[i].isSelected = false;
+            }
+        }
+    },
+    //设置选中的特征值
+    setSelected: function (item, value) {
+        for(let i = 0, len = item.eigenvalue.length; i < len; i++){
+            if(item.eigenvalue[i].value === value){
+                item.eigenvalue[i].isSelected = true;
+            }
+        }
+    },
+    //改变选择特征值
+    changeSelected: function (item, value) {
+        let me = characterOprObj;
+        me.unsetSelected(item);
+        me.setSelected(item, value);
+    },
+    insertValue: function (item, value) {
+        let me = characterOprObj;
+        me.unsetSelected(item);
+        let newValue = {value: value, isSelected: true};
+        item.eigenvalue.push(newValue);
+    },
+    updateCharacter: function (item, character, value) {
+        let me = characterOprObj;
+        if(character){
+            item.character = character;
+        }
+        if(value){
+            let isExist = false;
+            for(let i = 0, len = item.eigenvalue.length; i < len; i++){
+                if(item.eigenvalue[i].value === value){
+                    isExist = true;
+                    break;
+                }
+            }
+            //不存在,新增进eigenvalue
+            if(!isExist){
+                //更新selected
+                me.insertValue(item, value);
+            }
+            //存在,选择特征值
+            else{
+                me.changeSelected(item, value);
+            }
+        }
+    },
+    insertCharacter: function (character, value) {
+        let me = characterOprObj;
+        let preObj = me.currentCache.length > 0 ?  me.currentCache[me.currentCache.length - 1] : null;
+        if(character && !value){
+            let newCharacter = {character: character, eigenvalue: [], isChecked: false, serialNo: preObj ? preObj.serialNo + 1 : 1};
+            me.currentCache.push(newCharacter);
+        }
+        else if(!character && value){
+            let newValue = {value: value, isSelected: true};
+            let newCharacter = {character: '', eigenvalue: [newValue], isChecked: false, serialNo: preObj? preObj.serialNo + 1 : 1};
+            me.currentCache.push(newCharacter);
+        }
+        else if(character && value){
+            let newValue = {value: value, isSelected: true};
+            let newCharacter = {character:character , eigenvalue: [newValue], isChecked: false, serialNo: preObj? preObj.serialNo + 1 : 1};
+            me.currentCache.push(newCharacter);
+        }
+    },
+    save: function () {
+        let me = characterOprObj;
+        let characterTxt = me.getColData(me.currentCache);
+        let txtObj =  {field: 'itemCharacterText', text: characterTxt ? characterTxt : ''};
+        pageCCOprObj.updateCharacterContent(pageCCOprObj.currentFindSet, {field: 'itemCharacter', updateArr: me.currentCache}, txtObj, characterOprObj);
+    },
+    onEditStart: function (sender, args) {
+        let me = characterOprObj;
+        if(args.col === 1){//改变选择的特征值
+            me.currentSelectedValue = me.currentCache.length > args.row ?  me.getCurrentSelected(me.currentCache[args.row]) : null;
+        }
+    },
+    onEditEnded: function (sender, args) {
+        let me = characterOprObj, characterTxt;
+        let preObj = me.currentCache.length > 0 ?  me.currentCache[me.currentCache.length - 1] : null;
+        if(args.editingText && args.editingText.toString().trim().length > 0){
+            //更新
+            if(args.row < me.currentCache.length){
+                let thisCha = me.currentCache[args.row];
+                if(args.col === 0){//特征
+                   me.updateCharacter(thisCha, args.editingText, null);
+                }
+                else if(args.col === 1){//特征值
+                    me.updateCharacter(thisCha, null, args.editingText);
+                }
+            }
+            //新增
+            else{
+                if(args.col === 0){//特征
+                    me.insertCharacter(args.editingText, null);
+                }
+                else if(args.col === 1){//特征值
+                    me.insertCharacter(null, args.editingText);
+                }
+            }
+            //保存
+            me.save();
+        }
+        else{//恢复
+            if(args.col === 0){
+                args.sheet.setValue(args.row, args.col, me.currentCache.length > args.row ? me.currentCache[args.row].character + '' : '');
+            }
+            else if(args.col === 1){
+                args.sheet.setValue(args.row, args.col, me.currentSelectedValue ? me.currentSelectedValue + '' : '');
+            }
+        }
+    },
+    onClipboardPasting: function (sender, args) {
+        if(args.cellRange.col + args.colCount - 1 > 1){
+            args.cancel = true;
+        }
+    },
+    onClipboardPasted: function (sender, args) {
+        let me = characterOprObj;
+        let items = sheetCommonObj.analyzePasteData(me.setting, args);
+        for(let i = 0, len = items.length; i < len; i++){
+            //更新
+            let rowIdx = args.cellRange.row + i;
+            if(me.currentCache.length > rowIdx){
+               me.updateCharacter(me.currentCache[rowIdx], items[i].character, items[i].eigenvalue);
+            }
+            //新增
+            else{
+                me.insertCharacter(items[i].character, items[i].eigenvalue);
+            }
+        }
+        me.save();
+    },
+    //复选框控制输出
+    onButtonClicked: function (sender, args) {
+        let me = characterOprObj, characterTxt;
+        if(args.sheet.isEditing()){
+            args.sheet.endEdit(true);
+        }
+        let isChecked = args.sheet.getValue(args.row, args.col);
+        if(me.currentCache.length > args.row){
+            me.refreshColData = true;
+            me.currentCache[args.row].isChecked = isChecked;
+            me.save();
+        }
+        //else的情况就是新增
+        else{
+            args.sheet.setValue(args.row, args.col, 0);
+        }
+    },
+    onContextmenuOpr: function () {//右键菜单
+        let me = characterOprObj;
+        $.contextMenu({
+            selector: '#itemSpread',
+            build: function($triggerElement, e){
+                //控制允许右键菜单在哪个位置出现
+                let clientX = e.originalEvent.clientX,
+                    clientY = e.originalEvent.clientY;
+                let sheet = me.workBook.getSheet(0);
+                let offset = $("#itemSpread").offset(),
+                    x = clientX - offset.left,
+                    y = clientY - offset.top;
+                let target = sheet.hitTest(x, y);
+                if(target.hitTestType === 3){//在表格内 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'
+                    let insertDis = false, delDis = false, upDis = false, downDis = false;
+                    if(typeof target.row !== 'undefined'){
+                        //控制按钮是否可用
+                        sheet.setActiveCell(target.row, target.col);
+                        if(!me.currentCache ||target.row >= me.currentCache.length){//右键定位在有数据的行,删除键才显示可用
+                            delDis = true;
+                            downDis = true;
+                            upDis = true;
+                        }
+                        else{//有数据
+                            if(target.row === me.currentCache.length -1){//定位在最后一行,不可下移
+                                downDis = true;
+                            }
+                            if(target.row === 0){//定位在第一行,不可上移
+                                upDis = true;
+                            }
+                        }
+                    }
+                    else{
+                        delDis = true;
+                        downDis = true;
+                        upDis = true;
+                    }
+                    return {
+                        callback: function(){},
+                        items: {
+                            "insert": {name: "插入", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
+                                me.addRow(sheet);
+                            }},
+                            "delete": {name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
+                                me.deleteCharacter(target.row);
+                            }},
+                            "upMove": {name: "上移", disabled: upDis, icon: "fa-arrow-up", callback: function (key, opt) {
+                                me.upMove(target.row);
+                            }},
+                            "downMove": {name: "下移", disabled: downDis, icon: "fa-arrow-down", callback: function (key, opt) {
+                                me.downMove(target.row);
+                            }}
+                        }
+                    };
+                }
+                else{
+                    return false;
+                }
+            }
+        });
+    }
+};
+
+let pageCCOprObj = {
+    currentFindSet: null,
+    mainActiveCell: null,//mainSpread焦点单元格
+    //设置特征及内容currentCache
+    setCacheAndShow: function (node) {
+        let theCont = contentOprObj, theCha = characterOprObj;
+        theCont.currentCache = node && typeof node.data.jobContent !== 'undefined' ? node.data.jobContent : [];
+        theCha.currentCache = node && typeof node.data.itemCharacter !== 'undefined' ? node.data.itemCharacter : [];
+        this.currentFindSet = node && typeof node.data.ID !== 'undefined' && typeof node.data.projectID ? {ID: node.data.ID, projectID: node.data.projectID} : null;
+        this.showData(theCont.workBook.getSheet(0), theCont.setting, theCont.currentCache);
+        this.showData(theCha.workBook.getSheet(0), theCha.setting, theCha.currentCache);
+    },
+    //contentSpread、itemSpread展示数据用
+    showData: function(sheet, setting, data) {
+        let me = this, ch = GC.Spread.Sheets.SheetArea.viewport;
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        sheet.clear(0, 0, sheet.getRowCount(), sheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+        sheet.setRowCount(data.length);
+        for (let col = 0; col < setting.header.length; col++) {
+            var hAlign = "left", vAlign = "center";
+            if (setting.header[col].hAlign) {
+                hAlign = setting.header[col].hAlign;
+            } else if (setting.header[col].dataType !== "String"){
+                hAlign = "right";
+            }
+            vAlign = setting.header[col].vAlign?setting.header[col].vAlign:vAlign;
+            sheetCommonObj.setAreaAlign(sheet.getRange(-1, col, -1, 1), hAlign, vAlign);
+            if (setting.header[col].formatter) {
+                sheet.setFormatter(-1, col, setting.header[col].formatter, GC.Spread.Sheets.SheetArea.viewport);
+            }
+            for (let row = 0; row < data.length; row++) {
+                sheet.getCell(row, 0).locked(true);//locked
+                let val = data[row][setting.header[col].dataCode];
+                if(setting.header[col].cellType === "checkBox"){
+                    let checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
+                    checkBox.isThreeState(false);
+                    sheet.getCell(row, col).cellType(checkBox).value(val);
+                }
+                else if(setting.header[col].cellType === 'comboBox'){
+                    let comboObj = characterOprObj.getComboBox(data[row]);
+                    comboObj.combo.editable(true);//不可编辑
+                    sheet.getCell(row, 1).cellType(comboObj.combo).value(typeof comboObj.selectedValue !== 'undefined' ? comboObj.selectedValue : '');
+                }
+                else{
+                    sheet.setValue(row, col, val, ch);
+                }
+            }
+        }
+        sheet.resumeEvent();
+        sheet.resumePaint();
+    },
+    clearData: function () {
+        let theCon = contentOprObj, theCha = characterOprObj;
+        sheetCommonObj.cleanSheet(theCon.workBook.getSheet(0), theCon.setting, -1);
+        sheetCommonObj.cleanSheet(theCha.workBook.getSheet(0), theCha.setting, -1);
+    },
+    //更新特征及内容数据
+    updateCharacterContent: function (findSet, updateObj, txtObj, oprObj) {
+        let me = pageCCOprObj, updateCol;
+        if(txtObj){
+            updateCol = txtObj.field === 'itemCharacterText' ? 4 : 5;//更新清单行特征列或内容列
+        }
+        else{
+            updateCol = null;
+        }
+        $.ajax({
+            type: 'post',
+            url: '/bills/updateCharacterContent',
+            dataType: 'json',
+            data: {findSet: JSON.stringify(findSet), updateObj: JSON.stringify(updateObj), txtObj: JSON.stringify(txtObj)},
+            success: function (result) {
+                if(!result.error){
+                    //更新节点数据
+                    let selectedNode = projectObj.mainController.tree.selected;
+                    selectedNode.data[updateObj.field] = updateObj.updateArr;
+                    selectedNode.data[txtObj.field] = txtObj.text;
+                    me.showData(oprObj.workBook.getSheet(0), oprObj.setting, oprObj.currentCache);//刷新特征及内容Spread
+                    if(updateCol){
+                        projectObj.mainSpread.getActiveSheet().setValue(me.mainActiveCell.row, updateCol, txtObj.text + ''); //刷新输出显示
+                    }
+                }
+            }
+        });
+    }
+}

+ 9 - 1
web/building_saas/main/js/views/main_tree_col.js

@@ -43,6 +43,14 @@ let MainTreeCol = {
             return MainTreeCol.readOnly.billsParent && MainTreeCol.readOnly.non_bills;
             return MainTreeCol.readOnly.billsParent && MainTreeCol.readOnly.non_bills;
         }
         }
     },
     },
+    cellType: {
+        unit: function () {
+            let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+            combo.itemHeight(10).items(['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
+                '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀']);
+            return combo;
+        }
+    },
     getEvent: function (eventName) {
     getEvent: function (eventName) {
         let names = eventName.split('.');
         let names = eventName.split('.');
         let event = this;
         let event = this;
@@ -77,4 +85,4 @@ let MainTreeCol = {
                 return '0.##';
                 return '0.##';
         }
         }
     }
     }
-}
+};

+ 29 - 3
web/building_saas/main/js/views/project_view.js

@@ -94,6 +94,10 @@ var projectObj = {
                     if (col.readOnly && Object.prototype.toString.apply(col.readOnly) === "[object String]") {
                     if (col.readOnly && Object.prototype.toString.apply(col.readOnly) === "[object String]") {
                         col.readOnly = MainTreeCol.getEvent(col.readOnly);
                         col.readOnly = MainTreeCol.getEvent(col.readOnly);
                     }
                     }
+                    if (col.data.cellType && Object.prototype.toString.apply(col.data.cellType) === "[object String]") {
+                        let getCellType = MainTreeCol.getEvent(col.data.cellType);
+                        col.data.cellType = getCellType();
+                    }
                     if (col.data.digit && Object.prototype.toString.apply(col.data.digit) === "[object String]") {
                     if (col.data.digit && Object.prototype.toString.apply(col.data.digit) === "[object String]") {
                         col.data.decimal = that.project.getDecimal(col.data.digit);
                         col.data.decimal = that.project.getDecimal(col.data.digit);
                         col.data.formatter = MainTreeCol.getNumberFormatter(col.data.decimal);
                         col.data.formatter = MainTreeCol.getNumberFormatter(col.data.decimal);
@@ -132,11 +136,22 @@ var projectObj = {
                             return false;
                             return false;
                         }
                         }
                     };
                     };
+                    let canDelete = function (node) {
+                        if (selected) {
+                            if (selected.sourceType === that.project.Bills.getSourceType()) {
+                                return !(selected.data.flagsIndex && selected.data.flagsIndex.fixed && selected.data.flagsIndex.fixed.flag > 0);
+                            } else {
+                                return true;
+                            }
+                        } else {
+                            return false;
+                        }
+                    };
                     setButtonValid(canUpLevel(selected), $('#upLevel'));
                     setButtonValid(canUpLevel(selected), $('#upLevel'));
                     setButtonValid(canDownLevel(selected), $('#downLevel'));
                     setButtonValid(canDownLevel(selected), $('#downLevel'));
                     setButtonValid(selected && (selected.depth() > 0) && selected.canUpMove(), $('#upMove'));
                     setButtonValid(selected && (selected.depth() > 0) && selected.canUpMove(), $('#upMove'));
                     setButtonValid(selected && (selected.depth() > 0) && selected.canDownMove(), $('#downMove'));
                     setButtonValid(selected && (selected.depth() > 0) && selected.canDownMove(), $('#downMove'));
-                    setButtonValid(selected, $('#delete'));
+                    setButtonValid(canDelete(selected), $('#delete'));
                 });
                 });
 
 
               /*  if(!projectObj.gljSpreed){
               /*  if(!projectObj.gljSpreed){
@@ -144,6 +159,8 @@ var projectObj = {
                     that.loadGLJSpreadContextMenu();
                     that.loadGLJSpreadContextMenu();
                 }*/
                 }*/
 
 
+                that.project.calcProgram = new CalcProgram(that.project);
+                that.project.calcProgram.compileAllTemps();
 
 
                 that.mainController.bind(TREE_SHEET_CONTROLLER.eventName.treeSelectedChanged, function (node) {
                 that.mainController.bind(TREE_SHEET_CONTROLLER.eventName.treeSelectedChanged, function (node) {
                     gljOprObj.showDataIfRationSelect(node);
                     gljOprObj.showDataIfRationSelect(node);
@@ -156,6 +173,16 @@ var projectObj = {
                             calcProgramObj.clearData();
                             calcProgramObj.clearData();
                         };
                         };
                     };
                     };
+                    //zhong 2017-9-1 特征及内容
+                    if(pageCCOprObj.active){
+                        pageCCOprObj.mainActiveCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];//mainSpread焦点单元格
+                        if(node.sourceType === that.project.Bills.getSourceType()){
+                            pageCCOprObj.setCacheAndShow(node);
+                        }
+                        else{
+                            pageCCOprObj.clearData();
+                        }
+                    }
                 });
                 });
 
 
                 that.mainSpread.bind(GC.Spread.Sheets.Events.EditEnded, that.mainSpreadEditEnded);
                 that.mainSpread.bind(GC.Spread.Sheets.Events.EditEnded, that.mainSpreadEditEnded);
@@ -166,8 +193,7 @@ var projectObj = {
 
 
             }
             }
         });
         });
-        this.project.calcProgram = new CalcProgram(this.project);
-        this.project.calcProgram.compileAllTemps();
+
     },
     },
     loadMainSpreadContextMenu: function () {
     loadMainSpreadContextMenu: function () {
         var project = this.project, spread = this.mainSpread, controller = this.mainController;
         var project = this.project, spread = this.mainSpread, controller = this.mainController;

+ 42 - 1
web/building_saas/main/js/views/sub_view.js

@@ -1,11 +1,17 @@
 /**
 /**
  * Created by CSL on 2017-07-04.
  * Created by CSL on 2017-07-04.
  */
  */
-
+//modified by zhong on 2017-08-30
 // Tab panes 下有多个Spread时,相互之间不能正确显示。改成一个Spread下多个Sheet。
 // Tab panes 下有多个Spread时,相互之间不能正确显示。改成一个Spread下多个Sheet。
 var subSpread = sheetCommonObj.createSpread($("#subSpread")[0], 7);
 var subSpread = sheetCommonObj.createSpread($("#subSpread")[0], 7);
 subSpread.getSheet(4).name('JSCX');
 subSpread.getSheet(4).name('JSCX');
 
 
+//特征及内容spread,解决不能正确显示spread
+$("#jobSpread").width($("#subSpread").width() *0.5* 0.4);
+$("#itemSpread").width($("#subSpread").width() *0.5 - $("#jobSpread").width()-20);
+contentOprObj.buildSheet($("#jobSpread")[0]);
+characterOprObj.buildSheet($("#itemSpread")[0]);
+pageCCOprObj.active = false;
 
 
 // 工料机
 // 工料机
 gljOprObj.initSheet(subSpread.getSheet(0));
 gljOprObj.initSheet(subSpread.getSheet(0));
@@ -27,6 +33,9 @@ SheetDataHelper.protectdSheet(subSpread.getSheet(3));
 
 
 
 
 $("#linkGLJ").click(function(){
 $("#linkGLJ").click(function(){
+    $("#tzjnrCon").hide();//控制显示subSpread,隐藏特征及内容spread
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(0);
     subSpread.setActiveSheetIndex(0);
     $.contextMenu( 'destroy', "#subSpread" );
     $.contextMenu( 'destroy', "#subSpread" );
     gljContextMenu.loadGLJSpreadContextMenu();
     gljContextMenu.loadGLJSpreadContextMenu();
@@ -35,6 +44,9 @@ $("#linkGLJ").click(function(){
 });
 });
 
 
 $("#linkFZDE").click(function(){
 $("#linkFZDE").click(function(){
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(1);
     subSpread.setActiveSheetIndex(1);
     $.contextMenu( 'destroy', "#subSpread" );
     $.contextMenu( 'destroy', "#subSpread" );
     // for test
     // for test
@@ -42,6 +54,9 @@ $("#linkFZDE").click(function(){
 });
 });
 
 
 $("#linkFZTJ").click(function(){
 $("#linkFZTJ").click(function(){
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(2);
     subSpread.setActiveSheetIndex(2);
     $.contextMenu( 'destroy', "#subSpread" );
     $.contextMenu( 'destroy', "#subSpread" );
     // for test
     // for test
@@ -49,6 +64,9 @@ $("#linkFZTJ").click(function(){
 });
 });
 
 
 $("#linkGCLMX").click(function(){
 $("#linkGCLMX").click(function(){
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(3);
     subSpread.setActiveSheetIndex(3);
     $.contextMenu( 'destroy', "#subSpread" );
     $.contextMenu( 'destroy', "#subSpread" );
     gljContextMenu.loadQuantityDetailMenu();
     gljContextMenu.loadQuantityDetailMenu();
@@ -57,6 +75,9 @@ $("#linkGCLMX").click(function(){
 });
 });
 
 
 $("#linkJSCX").click(function(){        // 计算程序
 $("#linkJSCX").click(function(){        // 计算程序
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(4);
     subSpread.setActiveSheetIndex(4);
     calcProgramObj.initSheet(subSpread.getSheet(4));
     calcProgramObj.initSheet(subSpread.getSheet(4));
     let sel = projectObj.mainController.tree.selected;
     let sel = projectObj.mainController.tree.selected;
@@ -69,17 +90,37 @@ $("#linkJSCX").click(function(){        // 计算程序
 });
 });
 
 
 $("#linkFXSM").click(function(){
 $("#linkFXSM").click(function(){
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(5);
     subSpread.setActiveSheetIndex(5);
     // for test
     // for test
     subSpread.getActiveSheet().setValue(0, 0, "分项说明");
     subSpread.getActiveSheet().setValue(0, 0, "分项说明");
 });
 });
 
 
 $("#linkDESM").click(function(){
 $("#linkDESM").click(function(){
+    $("#tzjnrCon").hide();
+    $("#subSpread").show();
+    pageCCOprObj.active = false;
     subSpread.setActiveSheetIndex(6);
     subSpread.setActiveSheetIndex(6);
     // for test
     // for test
     subSpread.getActiveSheet().setValue(0, 0, "定额说明");
     subSpread.getActiveSheet().setValue(0, 0, "定额说明");
 });
 });
 
 
+//特征及内容
+$("#linkTZJNR").click(function () {
+    $("#subSpread").hide();
+    $("#tzjnrCon").show();
+    pageCCOprObj.active = true;
+    let selectedNode = projectObj.mainController.tree.selected;
+    pageCCOprObj.mainActiveCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];
+   // if(selectedNode && selectedNode.sourceType === projectObj.project.Bills.getSourceType()){
+        pageCCOprObj.setCacheAndShow(selectedNode);
+   // }
+    //else{
+       // pageCCOprObj.clearData();
+    //}
+});
 function SubActiveSheetNameIs(sheetName){
 function SubActiveSheetNameIs(sheetName){
     let rst = subSpread.getActiveSheet().name() == sheetName;
     let rst = subSpread.getActiveSheet().name() == sheetName;
     return rst;
     return rst;

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

@@ -336,7 +336,7 @@
                     </div>
                     </div>
                     <div class="form-group">
                     <div class="form-group">
                         <label>费率文件</label>
                         <label>费率文件</label>
-                        <select class="form-control"><option>新建费率文件</option></select>
+                        <select class="form-control" id="tender-fee-rate"><option>请选择费率文件</option></select>
                     </div>
                     </div>
                     <div class="form-group">
                     <div class="form-group">
                         <label>计价方式</label>
                         <label>计价方式</label>

+ 33 - 13
web/building_saas/pm/js/pm_main.js

@@ -9,6 +9,7 @@ let Tree = null;
 let movetoZTree = null;
 let movetoZTree = null;
 let copytoZTree = null;
 let copytoZTree = null;
 let engineering = [];
 let engineering = [];
+let feeRateData = [];
 let projectType = {
 let projectType = {
     folder: 'Folder',
     folder: 'Folder',
     tender: 'Tender',
     tender: 'Tender',
@@ -165,13 +166,14 @@ $(document).ready(function() {
     $("input[name='valuation_type']").click(function() {
     $("input[name='valuation_type']").click(function() {
         let type = $(this).val();
         let type = $(this).val();
         let targetData = type === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
         let targetData = type === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
+
         let html = '<option value="">请选择计划规则</option>';
         let html = '<option value="">请选择计划规则</option>';
 
 
         for(let valuation of targetData) {
         for(let valuation of targetData) {
             if (valuation === null) {
             if (valuation === null) {
                 continue;
                 continue;
             }
             }
-            html += '<option value="'+ valuation._id +'" data-engineering="'+ valuation.engineering +'">'+ valuation.name +'</option>';
+            html += '<option value="'+ valuation._id +'">'+ valuation.name +'</option>';
         }
         }
         $("#valuation").html(html);
         $("#valuation").html(html);
     });
     });
@@ -228,20 +230,26 @@ $(document).ready(function() {
         }
         }
         if (projectInfo !== null) {
         if (projectInfo !== null) {
             let savedProjectData = localStorage.getItem(projectInfo.data.name);
             let savedProjectData = localStorage.getItem(projectInfo.data.name);
+            console.log(savedProjectData);
             savedProjectData = JSON.parse(savedProjectData);
             savedProjectData = JSON.parse(savedProjectData);
             // 填入计价规则
             // 填入计价规则
             let valuationHtml = '<option value="'+ savedProjectData.valuation +'">'+ savedProjectData.valuationName +'</option>';
             let valuationHtml = '<option value="'+ savedProjectData.valuation +'">'+ savedProjectData.valuationName +'</option>';
             $("#tender-valuation").html(valuationHtml);
             $("#tender-valuation").html(valuationHtml);
 
 
             // 填入工程专业
             // 填入工程专业
-            let engineeringString = getEngineeringName(savedProjectData.engineering);
-            let engineeringHtml = '<option value="'+ savedProjectData.engineering +'">'+ engineeringString +'</option>';
+            let engineeringHtml = getEngineeringHtml(savedProjectData.engineeringList);
             $("#tender-engineering").html(engineeringHtml);
             $("#tender-engineering").html(engineeringHtml);
 
 
             $("input[name='tender_valuation_type']").attr('disabled', 'disabled').removeAttr('checked', 'checked');
             $("input[name='tender_valuation_type']").attr('disabled', 'disabled').removeAttr('checked', 'checked');
             $("input[name='tender_valuation_type'][value='"+ savedProjectData.valuationType +"']")
             $("input[name='tender_valuation_type'][value='"+ savedProjectData.valuationType +"']")
                 .attr("checked", "checked").removeAttr('disabled', 'disabled');
                 .attr("checked", "checked").removeAttr('disabled', 'disabled');
 
 
+            // 填入费率文件
+            let feeHtml = '<option>请选择费率文件</option>';
+            // for (let fee of savedProjectData.feeLib) {
+            //     feeHtml += '<option value="'+ fee.id +'">'+ fee.name +'</option>';
+            // }
+            $("#tender-fee-rate").html(feeHtml);
         }
         }
 
 
     });
     });
@@ -523,7 +531,16 @@ function AddProject() {
     }
     }
     let valuationName = $("#valuation").children("option:selected").text();
     let valuationName = $("#valuation").children("option:selected").text();
     let valuationType = $("input[name='valuation_type']:checked").val();
     let valuationType = $("input[name='valuation_type']:checked").val();
-    let engineering = $("#valuation").children("option:selected").data("engineering");
+    let engineeringList = [];
+
+    let valuationData = valuationType === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
+    for(let tmp of valuationData) {
+        if (tmp._id === valuation) {
+            engineeringList = tmp.engineering_list;
+            break;
+        }
+    }
+
     let callback = function() {
     let callback = function() {
         $("#add-project-dialog").modal("hide");
         $("#add-project-dialog").modal("hide");
         // 记录选择后的信息
         // 记录选择后的信息
@@ -531,7 +548,7 @@ function AddProject() {
             valuation: valuation,
             valuation: valuation,
             valuationType: valuationType,
             valuationType: valuationType,
             valuationName: valuationName,
             valuationName: valuationName,
-            engineering: engineering
+            engineeringList: engineeringList,
         };
         };
         localStorage.setItem(name, JSON.stringify(projectInfo));
         localStorage.setItem(name, JSON.stringify(projectInfo));
     };
     };
@@ -875,19 +892,22 @@ function GetTargetTreeNode(zTreeObj) {
 /**
 /**
  * 根据指定id获取对应的工程专业
  * 根据指定id获取对应的工程专业
  *
  *
- * @param {Number} id
+ * @param {Array} engineeringList
  * @return {String}
  * @return {String}
  */
  */
-function getEngineeringName(id) {
-    let result = '';
-    if (engineering.length <= 0) {
+function getEngineeringHtml(engineeringList) {
+    let result = '<option>请选择对应的工程专业</option>';
+    if (engineeringList.length <= 0) {
         return result;
         return result;
     }
     }
-
+    let engineeringObject = {};
     for(let tmp of engineering) {
     for(let tmp of engineering) {
-        if (tmp.value === id) {
-            result = tmp.name;
-            break;
+        engineeringObject[tmp.value] = tmp.name;
+    }
+
+    for(let tmp of engineeringList) {
+        if (engineeringObject[tmp.engineering] !== undefined) {
+            result += '<option value="'+ tmp.engineering_id +'">'+ engineeringObject[tmp.engineering] +'</option>';
         }
         }
     }
     }