Pārlūkot izejas kodu

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

TonyKang 8 gadi atpakaļ
vecāks
revīzija
2addb99f8b
57 mainītis faili ar 1870 papildinājumiem un 326 dzēšanām
  1. 33 14
      modules/common/base/base_controller.js
  2. 21 4
      modules/common/helper/mongoose_helper.js
  3. 17 7
      modules/complementary_glj_lib/controllers/gljController.js
  4. 23 11
      modules/fee_rates/facade/fee_rates_facade.js
  5. 112 3
      modules/glj/controllers/glj_controller.js
  6. 0 1
      modules/glj/models/glj_list_model.js
  7. 48 0
      modules/glj/models/unit_price_model.js
  8. 2 0
      modules/glj/routes/glj_router.js
  9. 6 1
      modules/main/models/ration.js
  10. 3 2
      modules/main/routes/main_route.js
  11. 3 1
      modules/pm/controllers/pm_controller.js
  12. 34 0
      modules/pm/models/project_model.js
  13. 3 2
      modules/pm/routes/pm_route.js
  14. 33 4
      modules/ration_glj/facade/ration_glj_facade.js
  15. 1 5
      modules/ration_glj/models/ration_glj.js
  16. 10 4
      modules/ration_repository/controllers/search_controller.js
  17. 16 0
      modules/ration_repository/models/ration_section_tree.js
  18. 2 0
      modules/users/controllers/boot_controller.js
  19. 126 0
      modules/users/controllers/message_controller.js
  20. 48 2
      modules/users/models/log_model.js
  21. 40 0
      modules/users/models/message_model.js
  22. 9 5
      modules/users/models/schema/log.js
  23. 51 0
      modules/users/models/schema/message.js
  24. 39 0
      modules/users/models/schema/user_message.js
  25. 118 0
      modules/users/models/user_message_model.js
  26. 22 0
      modules/users/routes/message_route.js
  27. 2 1
      public/calc_util.js
  28. 46 5
      test/logs/testlog.js
  29. 6 6
      web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html
  30. 3 11
      web/building_saas/complementary_glj_lib/js/components.js
  31. 2 4
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  32. 11 2
      web/building_saas/complementary_glj_lib/js/sheetOpr.js
  33. 4 1
      web/building_saas/css/main.css
  34. 7 6
      web/building_saas/fee_rates/fee_rate.html
  35. 50 1
      web/building_saas/glj/html/glj_index.html
  36. 183 55
      web/building_saas/glj/js/project_glj.js
  37. 159 0
      web/building_saas/js/message.js
  38. 7 0
      web/building_saas/js/moment.min.js
  39. 104 19
      web/building_saas/main/html/main.html
  40. 23 12
      web/building_saas/main/js/controllers/project_controller.js
  41. 18 17
      web/building_saas/main/js/models/calc_program.js
  42. 8 3
      web/building_saas/main/js/models/fee_rate.js
  43. 2 1
      web/building_saas/main/js/models/project.js
  44. 114 15
      web/building_saas/main/js/models/project_glj.js
  45. 7 1
      web/building_saas/main/js/models/ration.js
  46. 17 16
      web/building_saas/main/js/models/ration_glj.js
  47. 41 3
      web/building_saas/main/js/views/fee_rate_view.js
  48. 39 5
      web/building_saas/main/js/views/glj_view.js
  49. 4 1
      web/building_saas/main/js/views/main_tree_col.js
  50. 87 0
      web/building_saas/main/js/views/project_property_labour_coe_view.js
  51. 2 2
      web/building_saas/main/js/views/project_view.js
  52. 1 0
      web/building_saas/main/js/views/std_bills_lib.js
  53. 7 5
      web/building_saas/main/js/views/sub_view.js
  54. 43 59
      web/building_saas/pm/html/project-management.html
  55. 1 1
      web/building_saas/pm/js/pm_main.js
  56. 51 7
      web/common/html/header.html
  57. 1 1
      web/users/html/user-safe.html

+ 33 - 14
modules/common/base/base_controller.js

@@ -7,6 +7,7 @@
  */
 import Moment from "moment";
 import Url from "url";
+import UserMessageModel from "../../users/models/user_message_model";
 
 class BaseController {
 
@@ -18,17 +19,6 @@ class BaseController {
     title = '';
 
     /**
-     * 构造函数
-     *
-     * @return {void}
-     */
-    constructor() {
-        if (new.target === BaseController) {
-            throw new Error('BaseController不能实例化,只能继承使用。');
-        }
-    }
-
-    /**
      * 初始化函数
      *
      * @param {object} request
@@ -36,16 +26,45 @@ class BaseController {
      * @param {function} next
      * @return {void}
      */
-    init(request, response, next) {
-        response.locals.title = 'test';
+    async init(request, response, next) {
+        // 获取当前控制器和动作名称
+        let urlInfo = Url.parse(request.originalUrl, true);
+        let url = urlInfo.pathname.substr(1);
+        let actionInfo = url.split('/');
+        let controller = 'index';
+        let action = 'index';
+        switch (actionInfo.length) {
+            case 1:
+                controller = actionInfo[0];
+                break;
+            case 2:
+                controller = actionInfo[0];
+                action = actionInfo[1];
+                break;
+            default:
+                controller = actionInfo[0];
+                break;
+        }
+        response.locals.controller = controller;
+        response.locals.action = action;
 
         // moment工具
         response.locals.moment = Moment;
 
         // url相关数据
-        let urlInfo = Url.parse(request.originalUrl, true);
         response.locals.urlQuery = JSON.stringify(urlInfo.query);
 
+        // 获取当前用户数据
+        let sessionUser = request.session.sessionUser;
+
+        // 获取当前用户消息数量
+        let userMessageModel = new UserMessageModel();
+        // 消息处理
+        await userMessageModel.initMessage(sessionUser.id);
+        // 获取未读数据
+        let messageUnreadCount = await userMessageModel.count({user_id: sessionUser.id, is_read: 0, is_delete: 0});
+        response.locals.unreadCount = messageUnreadCount;
+
         next();
     }
 }

+ 21 - 4
modules/common/helper/mongoose_helper.js

@@ -28,10 +28,15 @@ class MongooseHelper {
      *
      * @param {object} conditions
      * @param {object} fields
+     * @param {Object} option
      * @return {Promise}
      */
-    findOne(conditions, fields = null) {
+    findOne(conditions, fields = null, option = null) {
         let self = this;
+        let sort = {};
+        if (option !== null && Object.keys(option).length > 0) {
+            sort = option.sort !== undefined ? option.sort : sort;
+        }
         return new Promise(function (resolve, reject) {
             conditions = self._convertId(conditions);
             self.model.findOne(conditions, fields, function (error, data) {
@@ -40,7 +45,7 @@ class MongooseHelper {
                 } else {
                     resolve(data);
                 }
-            });
+            }).sort(sort);
         });
     }
 
@@ -60,6 +65,7 @@ class MongooseHelper {
             limit = option.pageSize !== undefined ? option.pageSize : limit;
             skip = option.offset !== undefined ? option.offset : skip;
         }
+
         return new Promise(function (resolve, reject) {
             self.model.find(conditions, fields, option, function (error, data) {
                 if (error) {
@@ -76,13 +82,24 @@ class MongooseHelper {
      *
      * @param {object} conditions
      * @param {object} fields
+     * @param {Object} option
      * @param {String|Object} populate
      * @return {Promise}
      */
-    findWithPopulate(conditions, fields = null, populate = '') {
+        findWithPopulate(conditions, fields = null, option, populate = '') {
         let self = this;
+        let limit = 0;
+        let skip = 0;
+        let sort = {};
+        if (option !== null && Object.keys(option).length > 0) {
+            limit = option.pageSize !== undefined ? option.pageSize : limit;
+            skip = option.offset !== undefined ? option.offset : skip;
+            sort = option.sort !== undefined ? option.sort : sort;
+        }
+        conditions = self._convertId(conditions);
         return new Promise(function (resolve, reject) {
-            self.model.find(conditions, fields).populate(populate).exec(function(error, data) {
+            self.model.find(conditions, fields).skip(skip).limit(limit).sort(sort)
+                .populate(populate).exec(function(error, data) {
                 if (error) {
                     reject(error);
                 } else {

+ 17 - 7
modules/complementary_glj_lib/controllers/gljController.js

@@ -4,6 +4,7 @@
 import BaseController from "../../common/base/base_controller";
 import stdgljutil  from "../../../public/cache/std_glj_type_util";
 import GljDao from "../models/gljModel";
+import EngineeringLibModel from "../../users/models/engineering_lib_model";
 
 let gljDao = new GljDao();
 let callback = function(req, res, err, message, data){
@@ -11,18 +12,27 @@ let callback = function(req, res, err, message, data){
 };
 
 class GljController extends BaseController{
-    redirectGlj(req, res){
-        let gljLibId;
-        if(typeof req.session.sessionCompilation.ration_valuation[0].glj_lib[0] !== 'undefined'){
-            gljLibId = req.session.sessionCompilation.ration_valuation[0].glj_lib[0].id;
+    async redirectGlj(req, res){
+        let gljLibId = null, engineeringId, sessionCompilation = req.session.sessionCompilation,
+            rationValuation = sessionCompilation.ration_valuation,
+            billValuation = sessionCompilation.bill_valuation,
+            engineeringLibModel = new EngineeringLibModel();
+        if(rationValuation[0]){
+            let engineeringList = rationValuation[0].engineering_list;
+            engineeringId = engineeringList.length > 0 ? engineeringList[0].engineering_id : null;
+            let engineeringInfo = await engineeringLibModel.getEngineering(engineeringId);
+            gljLibId = engineeringInfo.glj_lib.length > 0 && typeof engineeringInfo.glj_lib !== 'undefined' ? engineeringInfo.glj_lib[0].id : null;
         }
-        else if(typeof req.session.sessionCompilation.bill_valuation[0].glj_lib[0] !== 'undefined'){
-            gljLibId = req.session.sessionCompilation.bill_valuation[0].glj_lib[0].id;
+        else if(billValuation[0]){
+            let engineeringList = billValuation[0].engineering_list;
+            engineeringId = engineeringList.length > 0 ? engineeringList[0].engineering_id : null;
+            let engineeringInfo = await engineeringLibModel.getEngineering(engineeringId);
+            gljLibId = engineeringInfo.glj_lib.length > 0 && typeof engineeringInfo.glj_lib !== 'undefined' ? engineeringInfo.glj_lib[0].id : null;
         }
         res.render('building_saas/complementary_glj_lib/html/tools-gongliaoji.html',{
             userID: req.session.sessionUser.ssoId,
             gljLibId: gljLibId,
-            compilationId: req.session.sessionCompilation._id
+            compilationId: sessionCompilation._id
         });
     }
     getGljDistType (req, res) {

+ 23 - 11
modules/fee_rates/facade/fee_rates_facade.js

@@ -94,7 +94,11 @@ async function creatFeeRateFile(feeRateFile,feeRate,projectID) {//预留projectI
         err:null
     }
     try {
-        await project_feerate_temp.findOneAndUpdate({projectID:projectID},{feeRateFileID:feeRateFile.ID});
+        let feeFile={
+            id:feeRateFile.ID,
+            name:feeRateFile.name
+        }
+        await projectsModel.findOneAndUpdate({ID:projectID},{'property.feeFile':feeFile});
         await feeRateFileModel.create(feeRateFile);
         await  feeRateModel.create(feeRate);
     }catch (err){
@@ -190,7 +194,6 @@ async function getFeeRateData(projectID) {
         //
         let project =  await projectsModel.findOne({ID:projectID});
         if (project&&project.property&&project.property.feeFile){
-            console.info(project.property.feeFile);
             let feeRateData = await feeRateFileModel.findOne({'ID':project.property.feeFile.id,deleteInfo:null});
             if(feeRateData===null){
                 result.datas=[];
@@ -214,7 +217,7 @@ async function getFeeRateData(projectID) {
 }
 
 async function getUsageProjects(feeRateID){
-    let projects = await projectsModel.find({'property.feeFile':feeRateID,'deleteInfo':null});
+    let projects = await projectsModel.find({'property.feeFile.id':feeRateID,'deleteInfo':null});
     return projects;
 }
 
@@ -322,7 +325,9 @@ async function getChangeInfo(jdata){
     //{ rootProjectID: 99, user_id: '76075' }
     let result={};
     let currentProject = await projectsModel.findOne({'ID':data.rootProjectID},['ID','name']); //{projectID:99,name:'建设项目1'};//dummy 数据
-    currentProject._doc.currentOptions=await getFeeRatesByProject(data.rootProjectID);
+    if(currentProject){
+        currentProject._doc.currentOptions=await getFeeRatesByProject(data.rootProjectID);
+    }
     //根据用户ID 找除了当前项目的其它建设项目;
     let others =await projectsModel.find({'$and': [
             {'$or':[{'userID': data.user_id,'projType':'Project', 'deleteInfo': null}, {'userID': data.user_id,'projType':'Project', 'deleteInfo.deleted': {'$in': [null, false]}}]},
@@ -343,12 +348,15 @@ async function getFeeRatesByProject(rootProjectID) {
 
 async function changeFeeRateFileFromCurrent(jdata){
     let data = JSON.parse(jdata);
-    let result = await  project_feerate_temp.findOneAndUpdate({projectID:data.projectID},{feeRateFileID:data.newFeeRateFileID});
-    let feeRateData = await feeRateFileModel.findOne({'ID':data.newFeeRateFileID});
-    let feeRate = await feeRateModel.findOne({ID:feeRateData.feeRateID});
-    feeRateData._doc.rates = feeRate.rates;
+    let newFeeRateFile=data.newFeeRateFile;
+    let result = await  projectsModel.findOneAndUpdate({ID:data.projectID},{'property.feeFile':newFeeRateFile});
+    let feeRateData = await feeRateFileModel.findOne({'ID':newFeeRateFile.id});
+    if(feeRateData!==null){
+        let feeRate = await feeRateModel.findOne({ID:feeRateData.feeRateID});
+        feeRateData._doc.rates = feeRate.rates;
+    }
     //
-    feeRateData._doc.usageProjects=getUsageProjects(feeRateData.ID);
+    feeRateData._doc.usageProjects=await getUsageProjects(feeRateData.ID);
     return feeRateData;
 }
 
@@ -369,8 +377,12 @@ async function changeFeeRateFileFromOthers(jdata) {
     newFeeRateFile.feeRateID =newFeeRate.ID;
     await feeRateModel.create(newFeeRate);
     await feeRateFileModel.create(newFeeRateFile);
-    await project_feerate_temp.findOneAndUpdate({projectID:data.projectID},{feeRateFileID:newFeeRateFile.ID});
+    let feeFile={
+        id:newFeeRateFile.ID,
+        name:data.name
+    }
+    await projectsModel.findOneAndUpdate({ID:data.projectID},{'property.feeFile':feeFile});
     newFeeRateFile.rates=newFeeRate.rates;
-    newFeeRateFile.usageProjects=getUsageProjects(newFeeRateFile.ID);
+    newFeeRateFile.usageProjects=await getUsageProjects(newFeeRateFile.ID);
     return newFeeRateFile;
 }

+ 112 - 3
modules/glj/controllers/glj_controller.js

@@ -10,6 +10,7 @@ import GLJTypeConst from "../../common/const/glj_type_const";
 import GLJListModel from "../models/glj_list_model";
 import UnitPriceModel from "../models/unit_price_model";
 import MixRatioModel from "../models/mix_ratio_model";
+import UnitPriceFileModel from "../models/unit_price_file_model";
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -54,11 +55,12 @@ class GLJController extends BaseController {
             // 获取使用该单价文件的项目数据
             let tenderData = await ProjectModel.getTenderByUnitPriceFileId(unitPriceFileId);
             let usedTenderList = [];
-            let usedUnitPriceName = '';
+            let usedUnitPriceInfo = {};
             if (tenderData !== null) {
                 for (let tmp of tenderData) {
                     usedTenderList.push(tmp.name);
-                    usedUnitPriceName = tmp.property.unitPriceFile.name;
+                    usedUnitPriceInfo.name = tmp.property.unitPriceFile.name;
+                    usedUnitPriceInfo.id = tmp.property.unitPriceFile.id;
                 }
             }
 
@@ -75,7 +77,7 @@ class GLJController extends BaseController {
                 hostname: request.hostname,
                 roomId: unitPriceFileId,
                 GLJTypeConst: JSON.stringify(GLJTypeConst),
-                usedUnitPriceName: usedUnitPriceName
+                usedUnitPriceInfo: usedUnitPriceInfo
             };
         } catch (error) {
             responseData.err = 1;
@@ -243,6 +245,113 @@ class GLJController extends BaseController {
     }
 
     /**
+     * 获取项目与单价文件对应的数据
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async getProjectInfo(request, response) {
+        let projectId = request.body.project_id;
+        projectId = parseInt(projectId);
+        let responseData = {
+            err: 0,
+            data: null
+        };
+        try {
+            let sessionUserData = request.session.sessionUser;
+            // 获取对应用户所有的建设项目数据
+            let projectList = await ProjectModel.getUserProjectData(sessionUserData.ssoId);
+            if (projectList === null) {
+                throw '没有找到对应的项目数据';
+            }
+            // 转换mongoose数据
+            projectList = JSON.stringify(projectList);
+            projectList = JSON.parse(projectList);
+
+            let result = {
+                self: [],
+                other: []
+            };
+            for (let index in projectList) {
+                // 获取对应建设项目下所有的单位工程id
+                let idList = await ProjectModel.getTenderByProjectId(projectList[index].ID);
+                if (idList.length <= 0) {
+                    continue;
+                }
+
+                // 获取对应的单价文件
+                let unitPriceFileModel = new UnitPriceFileModel();
+                let unitPriceFileData = await unitPriceFileModel.getDataByTenderId(idList);
+                projectList[index].unitPriceList = unitPriceFileData;
+
+                // 归类
+                if (idList.indexOf(projectId) >= 0) {
+                    result.self = unitPriceFileData;
+                } else {
+                    result.other.push(projectList[index]);
+                }
+            }
+            responseData.data = result;
+            response.json(responseData);
+
+        } catch (error) {
+            responseData.err = 1;
+            response.json(responseData);
+        }
+    }
+
+    /**
+     * 更改单价文件
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async changeUnitPriceFile(request, response) {
+        let projectId = request.body.project_id;
+        let changeUnitPriceId = request.body.change_id;
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            let currentUnitPriceId = await ProjectModel.getUnitPriceFileId(projectId);
+
+            // 获取即将更改的单价文件信息
+            let unitPriceFileModel = new UnitPriceFileModel();
+            let targetUnitPriceFile = await unitPriceFileModel.findDataByCondition({id: changeUnitPriceId});
+            if (targetUnitPriceFile === null) {
+                throw '没有找到对应的单价文件';
+            }
+
+            // 查找对应单价文件的项目工料机数据
+            let unitPriceModel = new UnitPriceModel();
+            let copyResult = await unitPriceModel.copyNotExist(currentUnitPriceId, changeUnitPriceId);
+            // 复制成功后更改project数据
+            if (!copyResult) {
+                throw '复制数据失败';
+            }
+
+            let changeUnitPriceFileInfo = {
+                id: targetUnitPriceFile.id,
+                name: targetUnitPriceFile.name
+            };
+            let result = ProjectModel.changeUnitPriceFileInfo(projectId, changeUnitPriceFileInfo);
+            if (!result) {
+                throw '切换单价文件失败!';
+            }
+
+        } catch (error) {
+            console.log(error);
+            responseData.err = 1;
+            responseData.msg = error;
+        }
+
+        response.json(responseData);
+    }
+
+    /**
      * 模拟定额插入
      *
      * @param {object} request

+ 0 - 1
modules/glj/models/glj_list_model.js

@@ -9,7 +9,6 @@ import BaseModel from "../../common/base/base_model";
 import {default as GLJSchemas, collectionName as gljCollectionName} from "./schemas/glj";
 import CounterModel from "./counter_model";
 import UnitPriceModel from "./unit_price_model";
-import STDMixRatioModel from "../../common/std/std_mix_ratio_model";
 import UnitPriceFileModel from "./unit_price_file_model";
 import GLJTypeConst from "../../common/const/glj_type_const";
 import RationGLJFacade from "../../ration_glj/facade/ration_glj_facade";

+ 48 - 0
modules/glj/models/unit_price_model.js

@@ -213,6 +213,54 @@ class UnitPriceModel extends BaseModel {
         return result.ok !== undefined && result.ok === 1;
     }
 
+    /**
+     * 复制单价文件数据
+     *
+     * @param {Number} currentUnitPriceId
+     * @param {Number} changeUnitPriceId
+     * @return {Promise}
+     */
+    async copyNotExist(currentUnitPriceId, changeUnitPriceId) {
+        let result = false;
+        // 首先查找原单价文件id下的数据
+        let currentUnitList = await this.findDataByCondition({unit_price_file_id: currentUnitPriceId}, null, false);
+        if (currentUnitList === null) {
+            return result;
+        }
+        // 过滤mongoose格式
+        currentUnitList = JSON.stringify(currentUnitList);
+        currentUnitList = JSON.parse(currentUnitList);
+
+        let codeList = [];
+        for (let tmp of currentUnitList) {
+            if (codeList.indexOf(tmp.code) >= 0) {
+                continue;
+            }
+            codeList.push(tmp.code);
+        }
+
+        // 查找即将更替的单价文件是否存在对应的工料机数据
+        let condition = {unit_price_file_id: changeUnitPriceId, code: {"$in": codeList}};
+        let targetUnitList = await this.findDataByCondition(condition, null, false, 'code');
+
+        // 如果没有重叠的数据则原有的数据都复制一份
+        let insertData = [];
+        for (let tmp of currentUnitList) {
+            if (targetUnitList !== null && targetUnitList[tmp.code] !== undefined) {
+                continue;
+            }
+            // 删除原有id信息
+            delete tmp._id;
+            delete tmp.id;
+            tmp.unit_price_file_id = changeUnitPriceId;
+            insertData.push(tmp);
+        }
+
+        return insertData.length > 0 ? this.add(currentUnitList) : true;
+
+    }
+
+
 }
 
 export default UnitPriceModel;

+ 2 - 0
modules/glj/routes/glj_router.js

@@ -17,6 +17,8 @@ router.post('/getData', gljController.init, gljController.getGljList);
 router.post('/update', gljController.init, gljController.updateData);
 router.post('/get-ratio', gljController.init, gljController.getRatio);
 router.post('/delete-ratio', gljController.init, gljController.deleteMixRatio);
+router.post('/get-project-info', gljController.init, gljController.getProjectInfo);
+router.post('/change-file', gljController.init, gljController.changeUnitPriceFile);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);

+ 6 - 1
modules/main/models/ration.js

@@ -43,13 +43,18 @@ let rationSchema = new Schema({
     adjustState: String,
     content: String,
     rationProjName: String,
+    // 说明
     comments: String,
     // 费用字段
     fees: [subSchema.feesSchema],
     // 标记字段
     flags: [subSchema.flagsSchema],
     deleteInfo: deleteSchema,
-    rationAssList: [rationAssItemSchema]
+    rationAssList: [rationAssItemSchema],
+    // 工作内容
+    content: String,
+    // 计算规则
+    ruleText: String
 });
 
 let ration = db.model("ration", rationSchema, "ration");

+ 3 - 2
modules/main/routes/main_route.js

@@ -3,9 +3,10 @@
  */
 
 
-
+import BaseController from "../../common/base/base_controller";
 module.exports =function (app) {
-    app.get('/main',  function(req, res) {
+    const baseController = new BaseController();
+    app.get('/main', baseController.init, function(req, res) {
         let pm = require('../../pm/controllers/pm_controller');
         pm.checkProjectRight(req.session.sessionUser.ssoId, req.query.project, function (hasRight) {
             if (hasRight) {

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

@@ -107,7 +107,9 @@ module.exports = {
     index: async function(request, response) {
         // 获取编办信息
         let sessionCompilation = request.session.sessionCompilation;
-
+        if (sessionCompilation === undefined) {
+            return response.redirect('/logout');
+        }
 
         // 清单计价
         let billValuation = sessionCompilation.bill_valuation !== undefined ?

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

@@ -299,6 +299,40 @@ ProjectsDAO.prototype.getUnitPriceFileId = async function(projectId) {
     return result;
 };
 
+/**
+ * 获取当前用户的建设项目数据
+ *
+ * @param {Number} userId
+ * @return {Promise}
+ */
+ProjectsDAO.prototype.getUserProjectData = async function(userId) {
+    let projectList = await Projects.find({
+        '$or': [
+            {'userID': userId, 'deleteInfo': null, projType: 'Project'},
+            {'userID': userId, 'deleteInfo.deleted': {'$in': [null, false]}, projType: 'Project'}
+            ]
+    }, {_id: 0, name: 1, ID: 1});
+
+    return projectList;
+};
+
+/**
+ * 更改项目属性中的单价文件
+ *
+ * @param {Number} projectId
+ * @param {Object} changeInfo
+ * @return {Promise}
+ */
+ProjectsDAO.prototype.changeUnitPriceFileInfo = async function(projectId, changeInfo) {
+    projectId = parseInt(projectId);
+    if (isNaN(projectId) || projectId <= 0) {
+        return false;
+    }
+    let result = await Projects.update({ID: projectId}, {"property.unitPriceFile": changeInfo});
+
+    return result.ok === 1;
+};
+
 module.exports ={
     project: new ProjectsDAO(),
     projType: projectType

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

@@ -2,13 +2,14 @@
  * pm: project management
  * Created by Mai on 2017/3/8.
  */
-
+import BaseController from "../../common/base/base_controller";
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
+const baseController = new BaseController();
 
 module.exports = function (app) {
 
-    app.get('/pm', pmController.index);
+    app.get('/pm', baseController.init, pmController.index);
 
     let pmRouter = express.Router();
 

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

@@ -98,7 +98,10 @@ function get_lib_glj_info(ration_glj) {
                 ration_glj.repositoryId = glj.ration_glj
                 getInfoFromProjectGLJ(ration_glj).then(function (info) {
                     if(info){
-                        result.datas.push(info);
+                        let tem={};
+                        tem.newRecode=createNewRecord(info);
+                        tem.showData=info;
+                        result.datas.push(tem);
                         cb(null,result);
                     }else {
                         cb(new Error('get project glj error'),null);
@@ -111,6 +114,26 @@ function get_lib_glj_info(ration_glj) {
     }
 }
 
+
+function createNewRecord(ration_glj) {
+    let newRecoed={};
+    newRecoed.ID=ration_glj.ID;
+    newRecoed.projectID=ration_glj.projectID;
+    newRecoed.GLJID=ration_glj.GLJID;
+    newRecoed.rationID=ration_glj.rationID;
+    newRecoed.rationItemQuantity=ration_glj.rationItemQuantity;
+    newRecoed.quantity=ration_glj.quantity;
+    newRecoed.name = ration_glj.name;
+    newRecoed.code = ration_glj.code;
+    newRecoed.unit = ration_glj.unit;
+    newRecoed.specs = ration_glj.specs;
+    newRecoed.shortName = ration_glj.shortName;
+    newRecoed.type = ration_glj.type;
+    newRecoed.repositoryId = ration_glj.repositoryId;
+    newRecoed.projectGLJID=ration_glj.projectGLJID;
+  return newRecoed
+}
+
 async function getInfoFromProjectGLJ(ration_glj) {
      let data = {
          glj_id: ration_glj.GLJID,
@@ -130,7 +153,8 @@ async function getInfoFromProjectGLJ(ration_glj) {
          let projectGljModel = new GLJListModel();
          let result = await projectGljModel.addList(data);
          ration_glj.marketPrice=result.unit_price.market_price;
-         ration_glj.adjustPrice=result.adjust_price;
+         ration_glj.adjustPrice=result.unit_price.base_price;
+         ration_glj.basePrice=result.unit_price.base_price;
          ration_glj.projectGLJID=result.id;
          ration_glj.isEstimate=result.is_evaluate;
          return ration_glj;
@@ -155,9 +179,11 @@ function create_ration_glj(user_id,datas) {
                 callback(err,results)
             }else {
                 let newRecords =[];
+                let showDatas=[];
                 for (let r of results.datas){
                     if(r){
-                        newRecords.push(r)
+                        newRecords.push(r.newRecode);
+                        showDatas.push(r.showData);
                     }
                 }
                 if(newRecords.length>0){
@@ -168,7 +194,10 @@ function create_ration_glj(user_id,datas) {
                             let returndata ={
                                 updateTpye:commonConsts.UT_CREATE,
                                 moduleName:'ration_glj',
-                                data:newRecords
+                                data:{
+                                    newRecords:newRecords,
+                                    showDatas:showDatas
+                                }
                             }
                             callback(null,returndata)
                         }

+ 1 - 5
modules/ration_glj/models/ration_glj.js

@@ -15,16 +15,12 @@ var ration_glj = new Schema({
     code:String,
     specs:String,
     unit:String,
-    basePrice:Number,
     shortName:String,
     type:Number,
     quantity:Number,
     customQuantity:Number,
     rationItemQuantity:Number,
-    marketPrice:Number,
-    adjustPrice:Number,
-    marketPriceAdjust:Number,
-    isEstimate:Number
+    marketPriceAdjust:Number
 },{versionKey:false});
 
 mongoose.model('ration_glj', ration_glj);

+ 10 - 4
modules/ration_repository/controllers/search_controller.js

@@ -2,17 +2,23 @@
  * Created by Mai on 2017/6/5.
  */
 var rationItem = require('../models/ration_item');
+let rationChapter = require('../models/ration_section_tree');
 var callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
 };
 
 module.exports = {
     getRationItem: function (req, res) {
-        var rId = req.body.rationLibId, code = req.body.code;
-        var rationData = {}
+        let rId = req.body.rationLibId, code = req.body.code;
         rationItem.getRationItem(rId, code).then(function (result) {
-            rationData = result._doc;
-            callback(req, res, null, '', rationData);
+            let rationData = JSON.stringify(result._doc);
+            rationData = JSON.parse(rationData);
+            rationChapter.getRationChapter(rationData.rationRepId, rationData.sectionId).then(function (result) {
+                rationData.chapter = result._doc;
+                callback(req, res, null, '', rationData);
+            }).catch(function (err, message) {
+                callback(req, res, null, '', rationData);
+            });
         }).catch(function (err, message) {
             callback(req, res, err, message, null);
         })

+ 16 - 0
modules/ration_repository/models/ration_section_tree.js

@@ -100,4 +100,20 @@ rationChapterTreeDAO.prototype.updateNodes = function(nodes,callback){
     });
 };
 
+rationChapterTreeDAO.prototype.getRationChapter = function (repId, chapterID, callback) {
+    if (callback) {
+        rationChapterTreeModel.findOne({rationRepId: repId, ID: chapterID}, '-_id').exec()
+            .then(function (result, err) {
+                if (err) {
+                    callback(1, '找不到定额章节' , null);
+                } else {
+                    callback(0, '', result);
+                }
+            });
+        return null;
+    } else {
+        return rationChapterTreeModel.findOne({rationRepId: repId, ID: chapterID}, '-_id').exec();
+    }
+}
+
 module.exports = new rationChapterTreeDAO()

+ 2 - 0
modules/users/controllers/boot_controller.js

@@ -8,6 +8,7 @@
 import BaseController from "../../common/base/base_controller";
 import UserModel from "../models/user_model";
 import CompilationModel from "../models/compilation_model";
+import UserMessageModel from "../models/user_message_model";
 
 class BootController extends BaseController {
 
@@ -31,6 +32,7 @@ class BootController extends BaseController {
 
             request.session.sessionCompilation = compilationData;
         }
+
         // 判断是否已填写信息
         let userData = await userModel.findDataByCondition({_id: sessionUser.id});
         if (userData.company !== '' && userData.real_name !== '') {

+ 126 - 0
modules/users/controllers/message_controller.js

@@ -0,0 +1,126 @@
+/**
+ * 消息通知相关控制器
+ *
+ * @author CaiAoLin
+ * @date 2017/9/25
+ * @version
+ */
+import UserMessageModel from "../models/user_message_model";
+
+class MessageController {
+
+    /**
+     * 初始化函数
+     * @param {object} request
+     * @param {object} response
+     * @param {Object} next
+     * @return {void|Object}
+     */
+    init(request, response, next) {
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            // 所有接口都需要保证访问者是已登录状态
+            let sessionUserData = request.session.sessionUser;
+            if (sessionUserData === undefined || sessionUserData.id === undefined || sessionUserData.id === '') {
+                throw '用户未登录';
+            }
+            next();
+        } catch (error) {
+            responseData.err = 1;
+            responseData.msg = error;
+            return response.json();
+        }
+
+    }
+
+    /**
+     * 获取消息列表数据
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async list(request, response) {
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            let sessionUserData = request.session.sessionUser;
+            let userMessageModel = new UserMessageModel();
+            let messageList = await userMessageModel.getList(sessionUserData.id, 15, {is_read: 1});
+            if (messageList === null) {
+                throw '获取数据失败!';
+            }
+            responseData.data = messageList;
+        } catch (error) {
+            console.log(error);
+            responseData.err = 1;
+            responseData.msg = error;
+        }
+
+        response.json(responseData);
+    }
+
+    /**
+     * 获取详细的消息数据
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async detail(request, response) {
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            let sessionUserData = request.session.sessionUser;
+            let id = request.body.id;
+            let userMessageModel = new UserMessageModel();
+            let messageData = await userMessageModel.getDetail(sessionUserData.id, id);
+            if (messageData === null) {
+                throw '获取数据失败!';
+            }
+            responseData.data = messageData;
+        } catch (error) {
+            console.log(error);
+            responseData.err = 1;
+            responseData.msg = error;
+        }
+
+        response.json(responseData);
+    }
+
+    /**
+     *
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async allRead(request, response) {
+        let responseData = {
+            err: 0,
+            msg: ''
+        };
+        try {
+            let sessionUserData = request.session.sessionUser;
+            let userMessageModel = new UserMessageModel();
+            let result = await userMessageModel.setAllRead(sessionUserData.id);
+            if (result === null) {
+                throw '设置失败!';
+            }
+        } catch (error) {
+            responseData.err = 1;
+            responseData.msg = error;
+        }
+
+        response.json(responseData);
+    }
+}
+
+export default MessageController;

+ 48 - 2
modules/users/models/log_model.js

@@ -9,6 +9,7 @@ import BaseModel from "../../common/base/base_model";
 import LogType from "../../common/const/log_type_const";
 import LogSchema from "./schema/log";
 import UAParser from "ua-parser-js";
+import Request from "request";
 
 class LogModel extends BaseModel {
 
@@ -57,11 +58,13 @@ class LogModel extends BaseModel {
      * @param {Object} request
      * @return {Promise}
      */
-    addLoginLog(userId, request) {
+    async addLoginLog(userId, request) {
         let ip = request.connection.remoteAddress;
         ip = ip.split(':');
         ip = ip[3] === undefined ? '' : ip[3];
 
+        let ipInfo = await this.getIpInfoFromApi(ip);
+
         let userAgentObject = new UAParser(request.headers['user-agent']);
         let osInfo = userAgentObject.getOS();
         let cpuInfo = userAgentObject.getCPU();
@@ -69,7 +72,8 @@ class LogModel extends BaseModel {
         let message = {
             os: osInfo.name + ' ' + osInfo.version + ' ' + cpuInfo.architecture,
             browser: browserInfo.name + ' ' + browserInfo.version,
-            ip: ip
+            ip: ip,
+            ip_info: ipInfo
         };
 
         return this.addLog(userId, LogType.LOGIN_LOG, message);
@@ -99,6 +103,48 @@ class LogModel extends BaseModel {
         return logList
     }
 
+    /**
+     * 获取ip信息
+     *
+     * @param {String} ip
+     * @return {Promise}
+     */
+    async getIpInfoFromApi(ip) {
+        let result = '';
+        if (ip === '') {
+            return result;
+        }
+
+        if (ip === '127.0.0.1') {
+            return '服务器本机访问';
+        }
+
+        let getData = {
+            url: 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip,
+            encoding: 'utf8'
+        };
+        return new Promise(function (resolve, reject) {
+            try {
+                // 请求接口
+                Request.get(getData, function (err, getResponse, body) {
+                    if (err) {
+                        throw '请求错误';
+                    }
+                    if (getResponse.statusCode !== 200) {
+                        throw '获取数据失败!';
+                    }
+                    let responseData = JSON.parse(body);
+                    let ipData = responseData.data !== undefined ? responseData.data : [];
+                    if (ipData.ip === undefined) {
+                        throw '接口数据有误';
+                    }
+                    resolve(ipData.region + ipData.city + ' ' + ipData.isp);
+                });
+            } catch (error) {
+                reject([]);
+            }
+        });
+    }
 }
 
 export default LogModel;

+ 40 - 0
modules/users/models/message_model.js

@@ -0,0 +1,40 @@
+/**
+ * 消息业务逻辑
+ *
+ * @author CaiAoLin
+ * @date 2017/9/21
+ * @version
+ */
+import BaseModel from "../../common/base/base_model";
+import MessageSchema from "./schema/message";
+
+class MessageModel extends BaseModel {
+
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = MessageSchema;
+        parent.init();
+    }
+
+    /**
+     * 获取对应时间点之后的数据
+     *
+     * @param {Number} lastTime
+     * @return {Promise}
+     */
+    async getListByTime(lastTime) {
+        let messageList = await this.findDataByCondition({release_time: {'$gte': lastTime}, message_type: 1, status: 1},
+            null, false);
+        return messageList;
+    }
+
+
+}
+
+export default MessageModel;

+ 9 - 5
modules/users/models/schema/log.js

@@ -9,18 +9,22 @@ import mongoose from "mongoose";
 
 let Schema = mongoose.Schema;
 let collectionName = 'log';
+let messageSchema = new Schema({
+    ip: String,
+    ip_info: String,
+    browser: String,
+    os: String
+}, {_id: false});
 let modelSchema = {
     // 日志类型
     type: {
-        type: Number,
-        index: true
+        type: Number
     },
     // 日志内容
-    message: Schema.Types.Mixed,
+    message: messageSchema,
     // 关联用户id
     user_id: {
-        type: String,
-        index: true
+        type: String
     },
     // 创建时间
     create_time: Number

+ 51 - 0
modules/users/models/schema/message.js

@@ -0,0 +1,51 @@
+/**
+ * 消息通知数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/9/21
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'message';
+let modelSchema = {
+    // 消息标题
+    title: String,
+    // 消息内容
+    content: String,
+    // 消息类型
+    message_type: {
+        type: Number,
+        default: 1
+    },
+    // 消息状态
+    status: {
+        type: Number,
+        default: 0
+    },
+    // 发布时间
+    release_time: {
+        type: Number,
+        default: 0
+    },
+    // 创建时间
+    create_time: Number,
+    // 最后修改时间
+    update_time: {
+        type: Number,
+        default: 0
+    },
+    // 创建者
+    creator: String,
+    // 发布者
+    release_user: String,
+    // 最后修改人
+    last_update: {
+        type: String,
+        default: ''
+    }
+
+};
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 39 - 0
modules/users/models/schema/user_message.js

@@ -0,0 +1,39 @@
+/**
+ * 用户消息数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/9/22
+ * @version
+ */
+let mongoose = require("mongoose");
+let Schema = mongoose.Schema;
+
+// 表名
+let collectionName = 'user_message';
+
+// 表结构
+let schema = {
+    // 用户id
+    user_id: {
+        type: String,
+    },
+    // 是否已读
+    is_read: {
+        type: Number,
+        default: 0
+    },
+    // 是否删除
+    is_delete: {
+        type: Number,
+        default: 0
+    },
+    // 消息关联数据
+    message: {
+        type: Schema.Types.ObjectId,
+        ref: 'message'
+    },
+    // 创建时间
+    create_time: Number,
+};
+
+export default mongoose.model(collectionName, new Schema(schema, {versionKey: false}));

+ 118 - 0
modules/users/models/user_message_model.js

@@ -0,0 +1,118 @@
+/**
+ * 用户消息业务逻辑
+ *
+ * @author CaiAoLin
+ * @date 2017/9/22
+ * @version
+ */
+import BaseModel from "../../common/base/base_model";
+import UserMessageSchema from "./schema/user_message";
+import MessageModel from "./message_model";
+
+class UserMessageModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = UserMessageSchema;
+        parent.init();
+    }
+
+    /**
+     * 初始化用户消息
+     *
+     * @param {String} userId
+     * @return {boolean}
+     */
+    async initMessage(userId) {
+        userId = userId.toString();
+        let result = true;
+        // 获取最后一条消息的信息
+        let option = {pageSize: 1, offset: 0};
+        option.sort = {create_time: -1};
+        let lastMessageData = await this.db.find({user_id: userId}, null, option);
+        lastMessageData = lastMessageData === null || lastMessageData.length <= 0 ? null : lastMessageData[0];
+        let lastTime = lastMessageData === null ? 0 : lastMessageData.create_time;
+
+        // 查找已发布的数据
+        let messageModel = new MessageModel();
+        let messageData = await messageModel.getListByTime(lastTime);
+        if (messageData === null) {
+            return result;
+        }
+
+        // 组合新增数据
+        let insertData = [];
+        let currentTime = new Date().getTime();
+        for(let message of messageData) {
+            let tmp = {
+                message: message._id,
+                user_id: userId,
+                create_time: currentTime
+            };
+            insertData.push(tmp);
+        }
+
+        result = await this.db.create(insertData);
+        return result;
+    }
+
+    /**
+     * 获取列表
+     *
+     * @param {String} userId
+     * @param {Number} pageSize
+     * @param {Object} sort
+     * @return {promise}
+     */
+    async getList(userId, pageSize = 15, sort = {}) {
+        let option = {pageSize: pageSize, offset: 0};
+        option.sort = sort;
+
+        let messageList = await this.db.findWithPopulate({user_id: userId, is_delete: 0}, null, option, 'message');
+        messageList = messageList.length > 0 ? messageList : [];
+
+        return messageList;
+    }
+
+    /**
+     * 获取详细数据
+     *
+     * @param {String} userId
+     * @param {String} id
+     * @return {Promise}
+     */
+    async getDetail(userId, id) {
+        let result = {};
+        let messageList = await this.db.findWithPopulate({user_id: userId, is_delete: 0, _id: id}, null, null, 'message');
+        if (messageList === null) {
+            return result;
+        }
+        result = messageList.length > 0 ? messageList[0] : result;
+        // 如果是未读数据则更改状态
+        if (result.is_read === 0) {
+            let updateData = {is_read: 1};
+            await this.db.update({_id: id}, updateData);
+        }
+
+        return result;
+    }
+
+    /**
+     * 设置全部已读
+     *
+     * @param {String} userId
+     * @return {Promise}
+     */
+    async setAllRead(userId) {
+        let updateData = {is_read: 1};
+        return await this.db.update({user_id: userId}, updateData)
+    }
+
+}
+
+export default UserMessageModel;

+ 22 - 0
modules/users/routes/message_route.js

@@ -0,0 +1,22 @@
+/**
+ * 消息相关路由
+ *
+ * @author CaiAoLin
+ * @date 2017/9/25
+ * @version
+ */
+import Express from "express";
+import MessageController from "../controllers/message_controller";
+
+module.exports = function (app) {
+
+    const router = Express.Router();
+    const messageController = new MessageController();
+
+    // action定义区域
+    router.post('/list', messageController.init, messageController.list);
+    router.post('/detail', messageController.init, messageController.detail);
+    router.post('/read', messageController.init, messageController.allRead);
+
+    app.use('/message',router);
+};

+ 2 - 1
public/calc_util.js

@@ -304,7 +304,8 @@ class Calculation {
                 };
 
                 if (item.feeRateID) {
-                    item.feeRate = me.compiledFeeRates["feeRate_" + item.feeRateID].rate;
+                    var cmf = me.compiledFeeRates["feeRate_" + item.feeRateID];
+                    item.feeRate = cmf?cmf.rate:100;
                 };
 
                 // 字段名映射

+ 46 - 5
test/logs/testlog.js

@@ -1,9 +1,50 @@
 /**
  * Created by chen on 2017/9/13.
  */
-let logger = require("../../logs/log_helper").logger;
 
-logger.info("log info...");
-logger.debug("log debug...");
-logger.warn("log warn...");
-logger.err("log err...");
+console.log(["1", "2", "3"].map(parseInt))
+
+var val = 'smtg';
+console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
+
+function foo() { }
+var oldName = foo.name;
+foo.name = "bar";
+console.log([oldName, foo.name])
+
+
+var name = "The Window";
+var object = {
+    name : "My Object",
+    getNameFunc : function(){
+        return function(){
+            return this.name;
+        };
+    }
+};
+console.log(object.getNameFunc()());
+
+var name = "The Window";
+var object = {
+    name : "My Object",
+    getNameFunc : function(){
+        var that = this;
+        return function(){
+            return that.name;
+        };
+    }
+};
+console.log(object.getNameFunc()());
+
+var User = {
+    count: 1,
+    getCount: function() {
+        return this.count;
+    }
+};
+console.log(User.getCount());
+var func = User.getCount;
+console.log(func());
+
+
+

+ 6 - 6
web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html

@@ -118,24 +118,24 @@
                   <div class="modal-body">
                     <div class="row">
                       <div class="col-4">
-                        <div  class="modal-auto-height">
-                            <div class="print-list">
-                                <div class="form-list" id="componentTreeDiv">
+                        <div  class="modal-auto-height" id="componentTreeDiv" style="overflow: hidden">
+                            <!--<div class="print-list">-->
+                                <div style="width: 100%; height: 100%; overflow: auto">
                                     <ul id="componentTree" class="ztree"></ul>
                                 </div>
-                            </div>
+                            <!--</div>-->
                         </div>
                       </div>
                       <div class="col-8">
                           <div class="row">
-                              <div class="modal-auto-height col-12" id="gljRadios">
+                              <div class="col-12" id="gljRadios">
                                   <input type="radio" class="glj-radio" name="glj" value="allGljs">所有工料机&nbsp;&nbsp;
                                   <input type="radio" class="glj-radio" name="glj" value="stdGljs">标准工料机&nbsp;&nbsp;
                                   <input type="radio" class="glj-radio" name="glj" value="complementaryGljs">补充工料机&nbsp;&nbsp;
                                   <input type="radio" class="glj-radio" name="glj" value="selectedGljs">已选工料机机&nbsp;&nbsp;
                                  <!-- <div class="form-group"><input id="searchGlj" type="text" class="form-control-sm" placeholder="查询工料机"></div>-->
                               </div>
-                              <div class="modal-auto-height col-12"  id="componentSheet">
+                              <div class="modal-auto-height col-12" style="overflow: hidden" id="componentSheet">
                                <!--   <table class="table table-sm table-bordered m-0">
                                       <thead>
                                       <tr><th></th><th>编码</th><th>名称</th><th>规格型号</th><th>计量单位</th><th>单价</th><th>类型</th></tr>

+ 3 - 11
web/building_saas/complementary_glj_lib/js/components.js

@@ -89,7 +89,7 @@ let componentOprObj = {
         let materialArr = [202, 203, 204];//混凝土、砂浆、配合比, 201普通材料
         let that = repositoryGljObj, me = componentOprObj;
             for(let i = 0; i < gljList.length; i++){
-                if(that.currentGlj.gljType === 3 && gljList[i].gljType === 302 ||
+                if(that.currentGlj.gljType === 301 && gljList[i].gljType === 302 ||
                     materialArr.indexOf(that.currentGlj.gljType) !== -1 && gljList[i].gljType === 201){
                     //去除与已添加的组成物重复的条目
                     let isExist = false;
@@ -248,6 +248,8 @@ let componentOprObj = {
             }
             sheetOpr.cleanSheet(me.workBook.getSheet(0), me.setting, -1);
             sheetOpr.showData(me.workBook.getSheet(0), me.setting, cacheSection, re.distTypeTree);
+            me.workBook.getSheet(0).options.isProtected = true;
+            me.workBook.getSheet(0).setRowCount(cacheSection);
             cacheSection = null;
         }
     },
@@ -280,17 +282,7 @@ let componentTypeTreeOprObj = {
             } else {
                 me.currentCache = me.getCache();
             }
-            //切换分类树时,记住当前分类的选择
-            //me.setComponentChecked(me.workBook.getSheet(0));
         }
         me.showGljItems(me.showGljList, gljTypeId);
-        /*sheetOpr.cleanSheet(that.workBook.getSheet(0), that.setting, 5);
-         that.workBook.getSheet(0).getRange(-1, 0 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
-         that.workBook.getSheet(0).getRange(-1, 4 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
-         re.workBook.getSheet(0).getRange(-1, 6 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);*/
-        //that.workBook.getSheet(0).options.isProtected = true;
-
-        //sheetOpr.lockCodeCells(re.workBook.getSheet(0), re.currentCache.length);
-        //re.workBook.getSheet(0).setRowCount(re.currentCache.length);
     }
 }

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

@@ -61,12 +61,10 @@ let gljComponentOprObj = {
             selector: '#gljComponentSheet',
             build: function($triggerElement, e){
                 //控制允许右键菜单在哪个位置出现
-                let clientX = e.originalEvent.clientX,
-                    clientY = e.originalEvent.clientY;
                 let sheet = me.workBook.getSheet(0);
                 let offset = $("#gljComponentSheet").offset(),
-                    x = clientX - offset.left,
-                    y = clientY - offset.top;
+                    x = e.pageX - offset.left,
+                    y = e.pageY - offset.top;
                 let target = sheet.hitTest(x, y);
                 if(target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'){//在表格内
                     sheet.setActiveCell(target.row, target.col);

+ 11 - 2
web/building_saas/complementary_glj_lib/js/sheetOpr.js

@@ -7,7 +7,8 @@ let sheetOpr = {
         var spreadBook = new GC.Spread.Sheets.Workbook(container, { sheetCount: SheetCount });
         spreadBook.options.allowCopyPasteExcelStyle = false;
         spreadBook.options.tabStripVisible = false;
-        spreadBook.options.showHorizontalScrollbar = false;
+        spreadBook.options.scrollbarMaxAlign = true;
+        //spreadBook.options.showHorizontalScrollbar = false;
         spreadBook.options.allowUserDragDrop = false;
         return spreadBook;
     },
@@ -21,6 +22,10 @@ let sheetOpr = {
         sheet.setColumnCount(setting.header.length, spreadNS.SheetArea.viewport);
         sheet.options.colHeaderAutoTextIndex = 1;
         sheet.options.colHeaderAutoText = spreadNS.HeaderAutoText.numbers;
+        sheet.options.protectionOptions = {
+            allowResizeRows: true,
+            allowResizeColumns: true
+        };
         sheet.showRowOutline(false);
         me.buildHeader(sheet, setting);
         if (rowCount > 0) sheet.setRowCount(rowCount);
@@ -32,7 +37,7 @@ let sheetOpr = {
         var me = this;
         var spreadBook = new GC.Spread.Sheets.Workbook(container, { sheetCount: 1 });
         spreadBook.options.tabStripVisible = false;
-        spreadBook.options.showHorizontalScrollbar = false;
+        //spreadBook.options.showHorizontalScrollbar = false;
         spreadBook.options.scrollbarMaxAlign = true;
         spreadBook.options.allowCopyPasteExcelStyle = false;
         spreadBook.options.allowExtendPasteRange = true;
@@ -46,6 +51,10 @@ let sheetOpr = {
         sheet.setColumnCount(setting.header.length, spreadNS.SheetArea.viewport);
         sheet.options.colHeaderAutoTextIndex = 1;
         sheet.options.colHeaderAutoText = spreadNS.HeaderAutoText.numbers;
+        sheet.options.protectionOptions = {
+            allowResizeRows: true,
+            allowResizeColumns: true
+        };
         sheet.showRowOutline(false);
         //setup column header
         me.buildHeader(sheet, setting);

+ 4 - 1
web/building_saas/css/main.css

@@ -244,7 +244,7 @@ body {
     max-width: 500px;
     margin: 150px auto;
 }
-.poj-list, .side-content {
+.poj-list, .side-content ,.form-view{
     overflow: auto;
 }
 .poj-list span.poj-icon {
@@ -305,3 +305,6 @@ body {
   background:#fff;
   border-bottom:1px solid #ddd
 }
+.gc-column-header-cell{
+    text-align: center!important;
+}

+ 7 - 6
web/building_saas/fee_rates/fee_rate.html

@@ -56,17 +56,17 @@
                 </div>
                 <!--从本建设项目中选择-->
                 <div class="form-group" id="fromProject">
-                    <label id="currentProject">建设项目A</label>
-                    <select class="form-control" id="currentOptions"><option>费率1</option><option>费率2</option><option>费率3</option></select>
+                    <label id="currentProject"></label>
+                    <select class="form-control" id="currentOptions"></select>
                 </div>
                 <!--从其他建设项目中复制-->
                 <div id="fromOther" >
                     <div class="form-group">
                         <label>选择建设项目</label>
-                        <select class="form-control" id="otherProject"><option>建设项目B</option><option>建设项目C</option><option>建设项目D</option></select>
+                        <select class="form-control" id="otherProject"></select>
                     </div>
                     <div class="form-group">
-                        <select class="form-control" id="otherFeeRateOption"><option>费率1</option><option>费率2</option><option>费率3</option></select>
+                        <select class="form-control" id="otherFeeRateOption"></select>
                         <small class="form-text text-muted">你选择的费率文件将复制一份至新项目,不会影响原建设项目的费率文件。</small>
                     </div>
                 </div>
@@ -96,7 +96,7 @@
                     <small class="form-text text-danger">重新选择费率标准将重置当前费率文件的所有费率,正在使用当前费率文件的其他单位工程也将受影响。</small>
                 </div>
                 <div class="form-group">
-                    <label>正在使用 费率1 的单位工程</label>
+                    <label>正在使用 <span id="set-use-feeRateName">费率1</span> 的单位工程</label>
                     <ul class="list-unstyled" id="usageProjectList">
 
                     </ul>
@@ -123,6 +123,7 @@
             <div class="modal-body">
                 <div class="form-group">
                     <label>费率文件名称</label>
+                    <input id="valid_name" type="hidden">
                     <input class="form-control" id="copyFeeRateName" value="">
                     <!--默认为复制费率文件名字+副本 2字,用户修改后需判断是否在同项目下存在同名-->
                     <small class="form-text text-danger" id="nameError" style="display: none">已存在同名费率文件。</small>
@@ -130,7 +131,7 @@
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-primary" data-dismiss="modal" id="saveAsConfirm">确定</button>
+                <button type="button" class="btn btn-primary" id="saveAsConfirm">确定</button>
             </div>
         </div>
     </div>

+ 50 - 1
web/building_saas/glj/html/glj_index.html

@@ -1,3 +1,8 @@
+<style type="text/css">
+    .copy{
+        display: none;
+    }
+</style>
 <div class="toolsbar px-1">
     <div class="form-inline py-1">
         <label class="mx-2">当前使用:<span id="used-name"></span>(<a href="#" id="pop-dj" data-original-title="" title=""><span id="used-count">0</span> 单位工程使用</a>)
@@ -45,9 +50,53 @@
         </div>
     </div>
 </div>
+<!--弹出更换-->
+<div class="modal fade" id="change-dj" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">更换单价文件</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label class="custom-control custom-radio">
+                        <input name="change-type" type="radio" class="custom-control-input" checked="checked" value="0">
+                        <span class="custom-control-indicator"></span>
+                        <span class="custom-control-description">从本建设项目中选择</span>
+                    </label>
+                    <label class="custom-control custom-radio">
+                        <input name="change-type" type="radio" class="custom-control-input" value="1">
+                        <span class="custom-control-indicator"></span>
+                        <span class="custom-control-description">从其他建设项目中复制</span>
+                    </label>
+                </div>
+                <!--从本建设项目中选择-->
+                <div class="form-group select option">
+                    <label id="current-project-name"></label>
+                    <select class="form-control" id="self-file"></select>
+                </div>
+                <!--从其他建设项目中复制-->
+                <div class="form-group copy option">
+                    <label>选择建设项目</label>
+                    <select class="form-control" id="other-project"></select>
+                </div>
+                <div class="form-group copy option">
+                    <select class="form-control" id="other-file"></select>
+                    <small class="form-text text-muted">你选择的单价文件将复制一份至新项目,不会影响原建设项目的单价文件。</small>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                <a href="javascript:void(0);" class="btn btn-primary" id="change-file-confirm">确定</a>
+            </div>
+        </div>
+    </div>
+</div>
 <script type="text/javascript" src="/web/building_saas/glj/js/project_glj.js"></script>
 <script type="text/javascript" src="/web/building_saas/glj/js/composition.js"></script>
-<script type="text/javascript" src="/web/building_saas/glj/js/socket.js"></script>
 <script type="text/javascript" src="/web/building_saas/glj/js/common_spread.js"></script>
 <script type="text/javascript" src="/web/building_saas/glj/js/composition_spread.js"></script>
 <script type="text/javascript" src="/web/building_saas/glj/js/project_glj_spread.js"></script>

+ 183 - 55
web/building_saas/glj/js/project_glj.js

@@ -19,75 +19,171 @@ let GLJTypeConst = [];
 // spreadjs载入数据所需
 let jsonData = [];
 let mixRatioConnectData = [];
-
+// 单价文件相关
+let usedUnitPriceInfo = {};
+let otherFileData = {};
 let currentTag = '';
 let isChanging = false;
 $(document).ready(function () {
-    $('#tab_gongliaoji').on('shown.bs.tab', function (e) {
+    $('#tab_gongliaoji').on('show.bs.tab', function (e) {
         init();
     });
 
+    // 单价文件切换弹框
+    $('#change-dj').on('shown.bs.modal', function () {
+        // 获取当前建设项数据
+        let projectName = projectInfoObj.projectInfo.fullFolder !== undefined &&
+            projectInfoObj.projectInfo.fullFolder.length > 0 ? projectInfoObj.projectInfo.fullFolder[0] : '';
+        $("#current-project-name").text(projectName);
+
+        // 获取切换单价文件相关数据
+        $.ajax({
+            url: '/glj/get-project-info',
+            type: 'post',
+            data: {project_id: scUrlUtil.GetQueryString('project')},
+            dataType: 'json',
+            success: function(response) {
+                if (response.err === 1) {
+                    alert('数据传输错误!');
+                    return false;
+                }
+                let data = response.data;
+                // 本项目中的单价文件
+                if (data.self.length > 0) {
+                    let selfFileHtml = '';
+                    for(let tmp of data.self) {
+                        let select = usedUnitPriceInfo === tmp.id ? ' selected="selected"' : '';
+                        selfFileHtml += '<option'+ select +' value="'+ tmp.id +'">'+ tmp.name +'</option>';
+                    }
+                    $("#self-file").html(selfFileHtml);
+                }
+
+                // 其他建设项目数据
+                if (data.other.length > 0) {
+                    let otherProjectHtml = '';
+                    let otherFileHtml = '';
+                    for(let tmp of data.other) {
+                        otherProjectHtml += '<option value="'+ tmp.ID +'">'+ tmp.name +'</option>';
+                        otherFileData[tmp.ID] = tmp.unitPriceList;
+                        if (otherFileHtml !== '') {
+                            continue;
+                        }
+                        for(let unitPrice of tmp.unitPriceList) {
+                            otherFileHtml += '<option value="'+ unitPrice.id +'">'+ unitPrice.name +'</option>';
+                        }
+                    }
+                    $("#other-project").html(otherProjectHtml);
+                    $("#other-file").html(otherFileHtml);
+                }
+            }
+        });
+    });
+
+    // 从其他建设项目中复制 选择建设项目
+    $("#other-project").change(function() {
+        let projectId = $(this).val();
+        if (otherFileData[projectId] === undefined) {
+            return false;
+        }
+        let otherFileHtml = '';
+        for(let unitPrice of otherFileData[projectId]) {
+            otherFileHtml += '<option value="'+ unitPrice.id +'">'+ unitPrice.name +'</option>';
+        }
+        $("#other-file").html(otherFileHtml);
+    });
+
+    // 单价文件选项切换
+    $("input[name='change-type']").change(function() {
+        let type = $(this).val();
+        type = parseInt(type);
+        $("#change-dj .option").hide();
+        if (type === 0) {
+            $(".option.select").show();
+        } else {
+            $(".option.copy").show();
+        }
+    });
+
+    // 单价文件切换确认
+    $("#change-file-confirm").click(function() {
+        if (isChanging) {
+            return false;
+        }
+        let type = $("input[name='change-type']:checked").val();
+        type = parseInt(type);
+        let changeUnitPriceId = 0;
+        if (type === 0) {
+            // 从本项目中选择
+            changeUnitPriceId = $("#self-file").val();
+        } else {
+            // 从其他项目中复制
+            changeUnitPriceId = $("#other-file").val();
+        }
+        $.ajax({
+            url: '/glj/change-file',
+            type: 'post',
+            data: {project_id: scUrlUtil.GetQueryString('project'), change_id: changeUnitPriceId},
+            error: function() {
+                isChanging = false;
+            },
+            beforeSend: function() {
+                isChanging = true;
+            },
+            success: function(response) {
+                isChanging = false;
+                if (response.err === 1) {
+                    let msg = response.msg !== undefined ? response.msg : '未知错误';
+                    alert(msg);
+                    return false;
+                }
+                $('#change-dj').modal("hide");
+            }
+        });
+    });
+
     // 是否主动更改数据
-    // $("#message").on('click', '#load-data', function() {
-    //     $("#notify").slideUp('fast');
-    //     if (changeInfo.length > 0) {
-    //         for (let index in changeInfo) {
-    //             let cell = gljSheet.getCell(changeInfo[index].row, changeInfo[index].col, GC.Spread.Sheets.SheetArea.viewport);
-    //             cell.value(changeInfo[index].newValue);
-    //         }
-    //     }
-    //     changeInfo = [];
-    // });
+    $("#message").on('click', '#load-data', function() {
+        $("#message").html('正在加载...');
+        // 重新加载数据到缓存
+        projectObj.project.projectGLJ.loadData(function() {
+            projectObj.project.projectGLJ.loadCacheData();
+            $("#notify").slideUp('fast');
+        });
+    });
 });
 
 /**
  * 初始化数据
  *
- * @return {void}
+ * @return {void|boolean}
  */
 function init() {
     // 加载工料机数据
-    $.ajax({
-        url: '/glj/getData',
-        type: 'post',
-        dataType: 'json',
-        data: {project_id: scUrlUtil.GetQueryString('project')},
-        error: function() {
-            // alert('数据传输错误');
-        },
-        beforeSend: function() {
-
-        },
-        success: function(response) {
-            if (response.err === 1) {
-                let msg = response.msg !== undefined && response.msg !== '' ? response.msg : '读取工料机数据失败!';
-                alert(msg);
-                return false;
-            }
-            let data = response.data;
-            // 赋值
-            jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
-            mixRatioConnectData = data.mixRatioConnectData !== undefined ? data.mixRatioConnectData : mixRatioConnectData;
-
-            host = data.constData.hostname !== undefined ? data.constData.hostname : '';
-            materialIdList = data.constData.materialIdList !== undefined ? data.constData.materialIdList : materialIdList;
-            roomId = data.constData.roomId !== undefined ? data.constData.roomId : roomId;
-            canNotChangeTypeId = data.constData.ownCompositionTypes !== undefined ?
-                data.constData.ownCompositionTypes : canNotChangeTypeId;
-            GLJTypeConst = data.constData.GLJTypeConst !== undefined ? JSON.parse(data.constData.GLJTypeConst) : GLJTypeConst;
-
-            let usedTenderList = data.usedTenderList !== undefined ? data.usedTenderList : [];
-            let usedUnitFileName = data.constData.usedUnitPriceName !== undefined ?
-                data.constData.usedUnitPriceName : '';
-            // 存入缓存
-            projectObj.project.projectGLJ.datas = jsonData;
-            console.log(projectObj.project);
-
-            spreadInit();
-            unitPriceFileInit(usedUnitFileName, usedTenderList);
-        }
-    });
+    let data = projectObj.project.projectGLJ === null ? null : projectObj.project.projectGLJ.datas;
+    if (data === null) {
+        return false;
+    }
+    // 赋值
+    jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
+    mixRatioConnectData = data.mixRatioConnectData !== undefined ? data.mixRatioConnectData : mixRatioConnectData;
+
+    host = data.constData.hostname !== undefined ? data.constData.hostname : '';
+    materialIdList = data.constData.materialIdList !== undefined ? data.constData.materialIdList : materialIdList;
+    roomId = data.constData.roomId !== undefined ? data.constData.roomId : roomId;
+    canNotChangeTypeId = data.constData.ownCompositionTypes !== undefined ?
+        data.constData.ownCompositionTypes : canNotChangeTypeId;
+    GLJTypeConst = data.constData.GLJTypeConst !== undefined ? JSON.parse(data.constData.GLJTypeConst) : GLJTypeConst;
 
+    let usedTenderList = data.usedTenderList !== undefined ? data.usedTenderList : [];
+    usedUnitPriceInfo = data.constData.usedUnitPriceInfo !== undefined ?
+        data.constData.usedUnitPriceInfo : {};
+
+    // 连接socket服务器
+    socketInit();
+
+    unitPriceFileInit(usedUnitPriceInfo.name, usedTenderList);
+
+    setTimeout(spreadInit, 1);
 }
 
 /**
@@ -158,13 +254,45 @@ function unitPriceFileInit(name, data) {
  * @return {void}
  */
 function successTrigger(field, info) {
+    let updateData = {};
     switch (field) {
         case 'unit_price.market_price':
             // 计算价格
-            projectGLJSpread.priceCalculate(info);
+            updateData = projectGLJSpread.priceCalculate(info);
 
             // 触发websocket通知
             socket.emit('dataNotify', JSON.stringify(info));
+            console.log(info);
             break;
     }
-}
+    // 重新加载数据到缓存
+    projectObj.project.projectGLJ.loadData();
+
+    // 更新定额工料机
+    gljOprObj.refreshView();
+}
+
+/**
+ * socket.io相关初始化
+ *
+ * @return {void}
+ */
+function socketInit() {
+    if (socket === null) {
+        socket = io('http://'+ host +':3300');
+        socket.on('connect', function () {
+            socket.emit('join', roomId);
+            console.log('单价文件同步连接成功');
+        });
+    }
+
+    // 接收到改变
+    socket.on('dataChange', function(data) {
+        data = JSON.parse(data);
+        if (data.newValue === undefined) {
+            return false;
+        }
+        $("#message").html('市场单位已被修改,<a href="javascript:void(0);" id="load-data">点击加载</a>');
+        $("#notify").slideDown('fast');
+    });
+}

+ 159 - 0
web/building_saas/js/message.js

@@ -0,0 +1,159 @@
+/**
+ * 消息通知相关js
+ *
+ * @author CaiAoLin
+ * @date 2017/9/25
+ * @version
+ */
+let isLoading = false;
+$(document).ready(function() {
+
+    // 消息框弹出
+    $("#msg").on("shown.bs.modal", function() {
+        let loadingElement = $("#message-loading");
+        $.ajax({
+            url: '/message/list',
+            type: 'post',
+            data: '',
+            dataType: 'json',
+            error: function() {
+                loadingElement.text('加载数据失败');
+            },
+            beforeSend: function() {
+                loadingElement.text('正在加载数据').show();
+            },
+            success: function(response) {
+                loadingElement.text('').hide();
+                $("#message-list").show();
+                if (response.err === 1 || response.err === undefined) {
+                    let msg = response.msg !== undefined ? response.msg : '读取数据失败!';
+                    alert(msg);
+                    return false;
+                }
+                let listData = response.data;
+                if (listData.length <= 0) {
+                    return false;
+                }
+                let listHtml = '';
+                for(let tmp of listData) {
+                    let unreadHtml = tmp.is_read === 0 ? '<i class="fa fa-circle-o text-danger unread" title="未读"></i> ' : '';
+                    let dateString = moment(tmp.message.release_time).format('YYYY-MM-DD');
+                    let dateTimeString = moment(tmp.message.release_time).format('YYYY-MM-DD HH:mm:ss');
+                    listHtml += '<tr>' +
+                        '<td>'+ unreadHtml +'<a href="javascript:void(0);" class="detail" data-id="'+ tmp._id +'">'+ tmp.message.title +'</a></td>' +
+                        '<td><span title="'+ dateTimeString +'">'+ dateString +'</span></td>';
+                }
+
+                $("#message-list tbody").html(listHtml);
+            }
+        });
+    });
+
+    // 获取详细消息
+    $("#msg").on("click", "a.detail", function() {
+        let self = $(this);
+        let id = self.data("id");
+        if (id === undefined || id === '' || isLoading) {
+            return false;
+        }
+        let isUnread = self.prev().is(".unread");
+        let loadingElement = $("#message-loading");
+        let listElement = $("#message-list");
+        $.ajax({
+            url: '/message/detail',
+            type: 'post',
+            data: 'id=' + id,
+            dataType: 'json',
+            error: function() {
+                isLoading = false;
+                listElement.show();
+                loadingElement.text('').hide();
+            },
+            beforeSend: function() {
+                isLoading = true;
+                listElement.hide();
+                loadingElement.text('正在加载详细内容').show();
+            },
+            success: function(response) {
+                isLoading = false;
+                loadingElement.text('').hide();
+                let messageData = response.data;
+                if (response.err === 1 || response.err === undefined || messageData === undefined) {
+                    let msg = response.msg !== undefined ? response.msg : '读取数据失败!';
+                    listElement.show();
+                    loadingElement.text('').hide();
+                    alert(msg);
+                    return false;
+                }
+                // 移除未读标记
+                if (isUnread) {
+                    self.prev('i').remove();
+                    changeNumByStep(-1);
+                }
+                $("#message-content .title").text(messageData.message.title);
+                $("#message-content .time").text('发布:' + moment(messageData.message.release_time).format('YYYY-MM-DD HH:mm:ss'));
+                $("#message-content .content").html(messageData.message.content);
+                $("#message-content").show();
+            }
+        });
+    });
+
+    // 设置全部已读
+    $("#set-all-read").click(function() {
+        if (isLoading) {
+            return false;
+        }
+        $.ajax({
+            url: '/message/read',
+            type: 'post',
+            data: '',
+            dataType: 'json',
+            error: function() {
+                isLoading = false;
+            },
+            beforeSend: function() {
+                isLoading = true;
+            },
+            success: function(response) {
+                isLoading = false;
+                if (response.err === 0) {
+                    $("#message-list tbody .unread").remove();
+                    changeNumByStep(0);
+                } else {
+                    let msg = response.msg !== undefined ? response.msg : '未知错误!';
+                    alert(msg);
+                }
+            }
+        });
+    });
+
+    // 返回按钮
+    $("#message-content .back").click(function() {
+        $("#message-content").hide();
+        $("#message-list").show();
+    });
+
+});
+
+/**
+ * 设置头部信封数量
+ *
+ * @param {Number} step
+ * @return {void}
+ */
+function changeNumByStep(step) {
+    let messageElement = $("#notify-info");
+    let unreadElement = $("#unread-num");
+    if (step === 0) {
+        messageElement.removeClass('new-msg');
+        unreadElement.text(0);
+    } else {
+        let currentNum = unreadElement.text();
+        currentNum = parseInt(currentNum);
+        let num = currentNum + step;
+        if (num === 0) {
+            messageElement.removeClass('new-msg');
+        }
+        unreadElement.text(num);
+    }
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 7 - 0
web/building_saas/js/moment.min.js


+ 104 - 19
web/building_saas/main/html/main.html

@@ -35,27 +35,13 @@
                 <strong id="message"></strong>
             </div>
         </div>
-        <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 justify-content-between">
-            <span class="header-logo px-2">Smartcost</span>
-            <div class="navbar-text" id="fullpath"><a href="/pm">项目管理</a><i class="fa fa-angle-right fa-fw"></i>文件夹<i class="fa fa-angle-right fa-fw"></i>建设项目<i class="fa fa-angle-right fa-fw"></i>单项工程<i class="fa fa-angle-right fa-fw"></i>单位工程</div>
-            <div class="float-lg-right navbar-text pt-0">
-                <div class="dropdown d-inline-block">
-                    <button class="btn btn-link btn-sm dropdown-toggle" type="button" data-toggle="dropdown"><%- userAccount %></button>
-                    <div class="dropdown-menu dropdown-menu-right">
-                        <a class="dropdown-item" href="/user/info" target="_blank">账号资料</a>
-                        <a class="dropdown-item" href="/user/buy" target="_blank">产品购买</a>
-                        <a class="dropdown-item" href="/user/set" target="_blank">偏好设置</a>
-                    </div>
-                </div>
-                <span class="btn btn-link btn-sm new-msg">
-                  <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;2
-                </span>
-                <a class="btn btn-link btn-sm" href="/logout">注销</a>
-            </div>
-        </nav>
+        <%include ../../../common/html/header.html %>
         <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
             <ul class="nav navbar-nav px-1">
                 <li class="nav-item">
+                    <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cube"></i> 项目属性</a>
+                </li>
+                <li class="nav-item">
                     <a class="nav-link" href="#" aria-haspopup="true" aria-expanded="false"><i class="fa fa-sliders"></i> 选项</a>
                 </li>
                 <li class="nav-item dropdown">
@@ -162,7 +148,7 @@
                                       <div class="main-data-bottom ovf-hidden" id="subSpread" style="display: none">
                                       </div>
                                       <div class="main-data-bottom ovf-hidden" style="display: none" id="comments">
-                                          <textarea class="form-control" rows="8"></textarea>
+                                          <textarea class="form-control" rows="8" readonly=""></textarea>
                                       </div>
                                       <div id="tzjnrCon" class="main-data-bottom">
                                           <div class="main-data-bottom ovf-hidden" style="width: 40%; float: left;" id="jobSpread">
@@ -409,6 +395,104 @@
 
         </div>
     </div>
+    <!--弹出项目属性-->
+    <div class="modal fade" id="poj-set" data-backdrop="static">
+        <div class="modal-dialog modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">项目属性</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col-3">
+                            <ul class="nav flex-column nav-pills" role="tablist">
+                                <li class="nav-item"><a class="nav-link active" data-toggle="pill" href="#poj-settings-1" role="tab">基本信息</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-2" role="tab">工程特征</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-3" role="tab">指标信息</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-4" role="tab">取费方式</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-5" role="tab">清单工程精度</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-6" role="tab" id="tab_poj-settings-6">人工单价调整</a></li>
+                            </ul>
+                        </div>
+                        <div class="col-9">
+                            <div class="tab-content">
+                                <!--基本信息-->
+                                <div class="tab-pane fade show active" id="poj-settings-1" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        基本信息
+                                    </div>
+                                </div>
+                                <!--工程特征-->
+                                <div class="tab-pane fade" id="poj-settings-2" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        工程特征
+                                    </div>
+                                </div>
+                                <!--指标信息-->
+                                <div class="tab-pane fade" id="poj-settings-3" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        指标信息
+                                    </div>
+                                </div>
+                                <!--取费方式-->
+                                <div class="tab-pane fade" id="poj-settings-4" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        <fieldset class="form-group">
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" name="optionsRadios" id="optionsRadios1" value="option1" checked="" type="radio">
+                                                    子目含量取费
+                                                </label>
+                                            </div>
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" name="optionsRadios" id="optionsRadios2" value="option2" type="radio">
+                                                    子目单价取费(反算):清单综合合价=清单综合单价*清单工程量
+                                                </label>
+                                            </div>
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" name="optionsRadios" id="optionsRadios3" value="option3" type="radio">
+                                                    子目单价取费(正算):清单综合合价=∑子目综合合价
+                                                </label>
+                                            </div>
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" name="optionsRadios" id="optionsRadios4" value="option4" type="radio">
+                                                    清单单价取费
+                                                </label>
+                                            </div>
+                                        </fieldset>
+                                    </div>
+                                </div>
+                                <!--清单工程精度-->
+                                <div class="tab-pane fade" id="poj-settings-5" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        清单工程精度
+                                    </div>
+                                </div>
+                                <!--人工单价调整-->
+                                <div class="tab-pane fade" id="poj-settings-6" role="tabpanel">
+                                    <div class="row px-3">
+                                        <select class="col-4 form-control form-control-sm"><option>渝建[2016]71号</option><option>渝建[2017]78号</option></select>
+                                    </div>
+                                    <div style="height:8px;"></div>
+                                    <div class="modal-auto-height" id="labourCoeSpread" />
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                    <a href="" class="btn btn-primary">确定</a>
+                </div>
+            </div>
+        </div>
+    </div>
     <!--弹出列设置-->
     <div class="modal fade" id="column" data-backdrop="static">
         <div class="modal-dialog modal-lg" role="document">
@@ -514,6 +598,7 @@
     <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/sub_fee_rate_views.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_labour_coe_view.js"></script>
 
 
    <!-- <script src="/web/building_saas/fee_rates/fee_rate.js"></script>-->

+ 23 - 12
web/building_saas/main/js/controllers/project_controller.js

@@ -36,21 +36,30 @@ ProjectController = {
         }
     },
     addRation: function (project, sheetController, std) {
-        if (!project || !sheetController) { return null; }
+        if (!project || !sheetController) { return; }
 
-        var selected = project.mainTree.selected;
-        var newSource = null, newNode = null;
-        if(selected==null){
-            return;
-        }
-        if (selected.sourceType === project.Bills.getSourceType() && selected.source.children.length === 0) {
-            if (std) {
-                newSource = project.Ration.insertStdRation(selected.source.getID(), null, std);
-                project.ration_glj.addRationGLJ(newSource,std);
+        let selected = project.mainTree.selected, newSource = null, newNode = null;
+        if (selected === null) { return; }
+
+        if (selected.sourceType === project.Bills.getSourceType() && selected.depth() > 0) {
+            if (selected.source.children.length > 0) {
+                alert('当前清单已有清单子项,不能套用定额。');
+            } else if (false) {
+                alert('当前清单已有公式计算,不能套用定额。');
             } else {
-                newSource = project.Ration.insertRation(selected.source.getID());
+                let firstChild = selected.firstChild();
+                if (firstChild && firstChild.sourceType === project.VolumePrice.getSourceType()) {
+                    alert('当前位置已有量价,不能套用定额。');
+                } else {
+                    if (std) {
+                        newSource = project.Ration.insertStdRation(selected.source.getID(), null, std);
+                        project.ration_glj.addRationGLJ(newSource,std);
+                    } else {
+                        newSource = project.Ration.insertRation(selected.source.getID());
+                    }
+                }
+                newNode = project.mainTree.insert(selected.getID(), selected.tree.rootID());
             }
-            newNode = project.mainTree.insert(selected.getID(), selected.tree.rootID());
         } else if (selected.sourceType === project.Ration.getSourceType()) {
             if (std) {
                 newSource = project.Ration.insertStdRation(selected.source[project.masterField.ration], selected.source, std);
@@ -59,6 +68,8 @@ ProjectController = {
                 newSource = project.Ration.insertRation(selected.source[project.masterField.ration], selected.source);
             }
             newNode = project.mainTree.insert(selected.getParentID(), selected.getNextSiblingID());
+        } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
+            alert('当前位置已有量价,不能套用定额。');
         }
         if (newNode) {
             newNode.source = newSource;

+ 18 - 17
web/building_saas/main/js/models/calc_program.js

@@ -511,7 +511,7 @@ let calcTemplates = [
                 feeRateID: 104,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "渝建发[2014]27号"
             },
             {
@@ -523,7 +523,7 @@ let calcTemplates = [
                 feeRateID: 304,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费"
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]"
             },
             {
                 ID: "13",
@@ -534,7 +534,7 @@ let calcTemplates = [
                 feeRateID: 704,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "同定额包干费"
             },
             {
@@ -899,7 +899,7 @@ let calcTemplates = [
                 feeRateID: 102,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "渝建发[2014]27号"
             },
             {
@@ -911,7 +911,7 @@ let calcTemplates = [
                 feeRateID: 302,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费"
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]"
             },
             {
                 ID: "13",
@@ -922,7 +922,7 @@ let calcTemplates = [
                 feeRateID: 702,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "同定额包干费"
             },
             {
@@ -1482,7 +1482,7 @@ let calcTemplates = [
                 feeRate: 0.3,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "渝建发[2014]27号"
             },
             {
@@ -1494,7 +1494,7 @@ let calcTemplates = [
                 feeRateID: 303,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费"
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]"
             },
             {
                 ID: "13",
@@ -1505,7 +1505,7 @@ let calcTemplates = [
                 feeRateID: 703,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "同定额包干费"
             },
             {
@@ -1676,7 +1676,7 @@ let calcTemplates = [
                 feeRateID: 106,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "渝建发[2014]27号"
             },
             {
@@ -1688,7 +1688,7 @@ let calcTemplates = [
                 feeRateID: 306,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费"
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]"
             },
             {
                 ID: "13",
@@ -1699,7 +1699,7 @@ let calcTemplates = [
                 feeRateID: 706,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "同定额包干费"
             },
             {
@@ -2452,7 +2452,7 @@ let calcTemplates = [
                 feeRateID: 105,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "渝建发[2014]27号"
             },
             {
@@ -2464,7 +2464,7 @@ let calcTemplates = [
                 feeRateID: 305,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费"
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]"
             },
             {
                 ID: "13",
@@ -2475,7 +2475,7 @@ let calcTemplates = [
                 feeRateID: 705,
                 expression: "@('3') + @('5') + @('7')",
                 compiledExpr: "",
-                statement: "定额基价人工费",
+                statement: "[定额基价人工费]+[定额基价材料费]+[定额基价机械费]",
                 memo: "同定额包干费"
             },
             {
@@ -2726,7 +2726,7 @@ let calcTemplates = [
     }
 ];
 
-let calcFeeRates = [
+/*let calcFeeRates = [
     {
         "ID" : 1,
         "ParentID" : null,
@@ -3462,7 +3462,7 @@ let calcFeeRates = [
         "rate" : 0.00,
         "memo" : null
     }
-];
+];*/
 
 let calcLabourCoes = [
     {
@@ -3600,6 +3600,7 @@ class CalcProgram {
     };
 
     compileAllTemps(){
+        let calcFeeRates = this.project.FeeRate.datas.rates;
         this.calc.compilePublics(calcFeeRates, calcLabourCoes, feeType, rationCalcBase);
         for (let calcTemplate of calcTemplates){
             this.calc.compileTemplate(calcTemplate);

+ 8 - 3
web/building_saas/main/js/models/fee_rate.js

@@ -143,6 +143,7 @@ var FeeRate = {
             var node = project.mainTree.selected;
             if(node){
                 if (node.sourceType==='ration' && calcProgramObj.sheet) {
+                    project.calcProgram.compileAllTemps();
                     calcProgramObj.showData(node);
                 }
             }
@@ -200,21 +201,25 @@ var FeeRate = {
         FeeRate.prototype.getChangeInfo = function (callback) {
            // var projectID = projectInfoObj.projectInfo.ID;
             var feeRate = this.getActivateFeeRate();
+            var  rootProjectID= feeRate.rootProjectID;
+            if(!rootProjectID){
+                rootProjectID=projectInfoObj.projectInfo.property.rootProjectID;
+            }
             var data={
                 "user_id":userID,
-                "rootProjectID":feeRate.rootProjectID,
+                "rootProjectID":rootProjectID,
             };
             CommonAjax.post('/feeRates/getChangeInfo', data, function (data) {
                 callback(data);
             });
         };
 
-        FeeRate.prototype.changeFeeRateFileFromCurrent = function (newFeeRateFileID,callback){
+        FeeRate.prototype.changeFeeRateFileFromCurrent = function (newFeeRateFile,callback){
             var me=this;
             var projectID = projectInfoObj.projectInfo.ID;
             var data={
                 "projectID": projectID,
-                "newFeeRateFileID":newFeeRateFileID
+                "newFeeRateFile":newFeeRateFile
             };
             CommonAjax.post('/feeRates/changeFeeRateFileFromCurrent', data, function (data) {
                 if (data) {

+ 2 - 1
web/building_saas/main/js/models/project.js

@@ -78,7 +78,8 @@ var PROJECT = {
             this.quantity_detail = quantity_detail.createNew(this);
             this.FeeRate = FeeRate.createNew(this);
             this.VolumePrice = VolumePrice.createNew(this);
-            this.projectGLJ = new projectGLJ(this);
+            this.projectGLJ = new ProjectGLJ();
+            this.projectGLJ.loadData();
             this.Decimal = {
                 common: {
                     quantity: 3,

+ 114 - 15
web/building_saas/main/js/models/project_glj.js

@@ -5,35 +5,134 @@
  * @date 2017/9/14
  * @version
  */
-function projectGLJ(project) {
-    this.project = project;
+function ProjectGLJ() {
     this.datas = null;
+    this.isLoading = false;
 }
 
 /**
- * 获取数据类型
+ * 加载数据
  *
- * @return {String}
+ * @param {function} callback
+ * @return {boolean}
  */
-projectGLJ.prototype.getSourceType = function() {
-    let sourceType = ModuleNames.projectGLJ;
-    return sourceType;
+ProjectGLJ.prototype.loadData = function (callback = null) {
+    let self = this;
+    if (self.isLoading) {
+        return false;
+    }
+    // 加载工料机数据
+    $.ajax({
+        url: '/glj/getData',
+        type: 'post',
+        dataType: 'json',
+        data: {project_id: scUrlUtil.GetQueryString('project')},
+        error: function() {
+            // alert('数据传输错误');
+        },
+        beforeSend: function() {
+            self.isLoading = true;
+        },
+        success: function(response) {
+            self.isLoading = false;
+            if (response.err === 1) {
+                let msg = response.msg !== undefined && response.msg !== '' ? response.msg : '读取工料机数据失败!';
+                alert(msg);
+                return false;
+            }
+            self.datas = response.data;
+            // 回调函数
+            if (callback !== null) {
+                callback(response.data);
+            }
+            // 存入缓存
+            projectObj.project.projectGLJ = self;
+        }
+    });
 };
 
 /**
- * project中调用
+ * 获取对应工料机数据
  *
- * @return {void}
+ * @param {String} code
+ * @return {Object}
  */
-projectGLJ.prototype.createNew =  function() {
-    this.project.registerModule(ModuleNames.projectGLJ, this);
+ProjectGLJ.prototype.getDataByCode = function (code) {
+    let result = {};
+    if (this.datas === null) {
+        return result;
+    }
+
+    let gljList = this.datas.gljList;
+    if (gljList === undefined) {
+        return result;
+    }
+
+    for(let tmp of gljList) {
+        if (tmp.code === code) {
+            result = tmp;
+            break;
+        }
+    }
+
+    return result;
 };
 
 /**
- * 加载数据
+ * 修改工料机数据
  *
- * @return {void}
+ * @param {Number} id
+ * @param {Object} data
+ * @return {boolean}
  */
-projectGLJ.prototype.loadData = function (datas) {
-    this.datas = datas;
+ProjectGLJ.prototype.updateData = function(id, data) {
+    let result = false;
+    if (this.datas === null) {
+        return result;
+    }
+
+    let gljList = this.datas.gljList;
+    if (gljList === undefined) {
+        return result;
+    }
+
+    // 查找对应的index
+    let index = -1;
+    for(let tmp in gljList) {
+        if (gljList[tmp].id === id) {
+            index = tmp;
+            break;
+        }
+    }
+
+    if (index < 0) {
+        return result;
+    }
+
+    // 修改数据
+    for(let tmpIndex in data) {
+        if(tmpIndex.indexOf('_price') >= 0) {
+            // 修改unit_price中的对象
+            this.datas.gljList[index]['unit_price'][tmpIndex] = data[tmpIndex];
+        } else {
+            this.datas.gljList[index][tmpIndex] = data[tmpIndex];
+        }
+    }
+
 };
+
+/**
+ * 加载缓存数据到spread
+ *
+ * @return {void}
+ */
+ProjectGLJ.prototype.loadCacheData = function() {
+    // 加载工料机数据
+    let data = this.datas === null ? null : this.datas;
+    if (data === null) {
+        return;
+    }
+    jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
+    projectGLJSheet.setData(jsonData);
+    projectGLJSpread.specialColumn(jsonData);
+};

+ 7 - 1
web/building_saas/main/js/models/ration.js

@@ -148,12 +148,18 @@ var Ration = {
         ration.prototype.insertStdRation = function (billsID, preRation, std) {
             var br = this.getBillsSortRation(billsID), updateData = this.getInsertRationData(billsID, preRation), newRation = null;
             updateData.forEach(function (data) {
+
                 if (data.updateType === 'ut_create') {
                     data.updateData.code = std.code;
                     data.updateData.name = std.name;
-                    data.updateData.caption=std.caption;
+                    data.updateData.caption = std.caption;
                     data.updateData.unit = std.unit;
                     data.updateData.libID = std.rationRepId;
+                    data.updateData.content = std.jobContent;
+                    if (std.chapter) {
+                        data.updateData.comments = std.chapter.explanation;
+                        data.updateData.ruleText = std.chapter.ruleText;
+                    }
                     data.updateData.rationAssList =  projectObj.project.ration_ass.CreateNewAss(std);
                     newRation = data.updateData;
                 }

+ 17 - 16
web/building_saas/main/js/models/ration_glj.js

@@ -94,14 +94,19 @@ var ration_glj = {
             }
         };
         ration_glj.prototype.refreshAfterSave=function(data){
+            let neRecodes=[];
+            if(data){
+                neRecodes=data.newRecords;
+                gljOprObj.sheetData=data.showDatas;
+            }
             if(projectObj.project.ration_glj.datas&&Array.isArray(projectObj.project.ration_glj.datas)){
-                projectObj.project.ration_glj.datas = projectObj.project.ration_glj.datas.concat(data);
+                if(data){
+                    projectObj.project.ration_glj.datas = projectObj.project.ration_glj.datas.concat(neRecodes);
+                }
             }else {
-                projectObj.project.ration_glj.datas = data;
+                projectObj.project.ration_glj.datas = neRecodes;
             }
-            sheetCommonObj.showData(gljOprObj.sheet,gljOprObj.setting,data);
-            gljOprObj.sheetData=data;
-            // SheetDataHelper.loadSheetData(setting, rationLibObj.sectionRationsSpread.getActiveSheet(), datas);
+            gljOprObj.showRationGLJSheetData();
         };
         ration_glj.prototype.refreshAfterUpdate=function(data){
             var me = this;
@@ -112,23 +117,19 @@ var ration_glj = {
             }else {
                 me.refreshEachItme(data.doc,data.query);
             }
-            var selected = projectObj.project.mainTree.selected
-            if(selected==null){
-                return;
-            }
-            if(selected.sourceType==ModuleNames.ration){
-                var showList = _.filter(projectObj.project.ration_glj.datas,{'rationID':selected.data.ID});
-                gljOprObj.sheetData=showList;
-                sheetCommonObj.showData(gljOprObj.sheet,gljOprObj.setting,showList);
-            }
+            gljOprObj.showRationGLJSheetData();
         };
         ration_glj.prototype.refreshEachItme = function (doc,query) {
             var glj_list = projectObj.project.ration_glj.datas;
             var glj_index= _.findIndex(glj_list,(glj)=>{
-                return glj.ID==query.ID&&glj.projectID==query.projectID;
+                return glj.ID==query.ID;
+            })
+            var sheet_index= _.findIndex(gljOprObj.sheetData,(sd)=>{
+                return sd.ID==query.ID;
             })
             _.forEach(doc, function(n, key) {
                 glj_list[glj_index][key] = n;
+                gljOprObj.sheetData[sheet_index][key]=n;
             });
             return glj_list[glj_index].rationID;
         };
@@ -136,7 +137,7 @@ var ration_glj = {
             var glj_list = projectObj.project.ration_glj.datas;
             _.remove(glj_list,data.query);
             _.remove(gljOprObj.sheetData,data.query);
-            sheetCommonObj.showData(gljOprObj.sheet,gljOprObj.setting,gljOprObj.sheetData);
+            gljOprObj.showRationGLJSheetData();
         };
         // CSL,2017.05.09
         ration_glj.prototype.modifyQuantity = function (data, newQuantity) {

+ 41 - 3
web/building_saas/main/js/views/fee_rate_view.js

@@ -94,10 +94,11 @@ var feeRateObject={
                var selected = feeRateObject.mainViews.getSelections()[0];
                 params.sourceIndex = selected.sourceRow;
             }
+            params.success();
             if(!$('#cascadeSet').prop('checked')||params.hasOwnProperty('viewIndex')){
                 projectObj.project.FeeRate.updateFeeRateByEdit(params,feeRateObject.activateFeeRate);
             }
-            params.success();
+
         }
     },
     createSpreadView:function () {
@@ -261,7 +262,9 @@ var feeRateObject={
             if(data){
                 $('#saveAsConfirm').attr("disabled","disabled");
                 $('#nameError').text("已存在同名费率文件。").show();
+                $('#valid_name').val('');
             }else {
+                $('#valid_name').val(newVal);
                 $('#saveAsConfirm').removeAttr("disabled");
                 $('#nameError').hide();
             }
@@ -270,8 +273,31 @@ var feeRateObject={
         projectObj.project.FeeRate.checkFeeRateName(newVal,callback);
     },
     feeRateFileSaveAs:function (newName) {
+        if(!newName||newName==""){
+            $('#saveAsConfirm').attr("disabled","disabled");
+            $('#nameError').text("请输入文件名称。").show();
+            return;
+        }
+        var valideName = $('#valid_name').val();
+        if(valideName==''||valideName!==newName){
+            var callback=function (data) {
+                if(data){
+                    $('#saveAsConfirm').attr("disabled","disabled");
+                    $('#nameError').text("已存在同名费率文件。").show();
+                    $('#valid_name').val('');
+                }else {
+                    feeRateObject.submitSaveAs(newName);
+                }
+            }
+            projectObj.project.FeeRate.checkFeeRateName(newName,callback)
+        }else {
+            feeRateObject.submitSaveAs(newName);
+        }
+    },
+    submitSaveAs:function (newName) {
         this.activateFeeRate =  projectObj.project.FeeRate.feeRateFileSaveAs(newName);
         feeRateObject.loadPageContent();
+        $('#copy-lv').modal('hide');
     },
     getChangeInfo:function () {
         var me = this;
@@ -308,16 +334,25 @@ var feeRateObject={
         if($("#currentOptions").val()==this.activateFeeRate.ID){
             return;
         }
+        var name =$("#currentOptions").find("option:selected").text();
+        var newFeeRateFile = {
+            id:newVal,
+            name:name
+        }
         var callback=function () {
             feeRateObject.createSpreadView();
             feeRateObject.loadPageContent();
             projectObj.project.FeeRate.synchronizeFeeRate();
         }
-        projectObj.project.FeeRate.changeFeeRateFileFromCurrent(newVal,callback);
+        projectObj.project.FeeRate.changeFeeRateFileFromCurrent(newFeeRateFile,callback);
     },
     changeFeeRateFileFromOthers:function () {
         var feeRateFileID = $("#otherFeeRateOption").val();
         var name =$("#otherFeeRateOption").find("option:selected").text();
+        if(null===feeRateFileID){
+            alert("请选择一个费率文件!");
+            return;
+        }
         var currentOption = _.find(this.changeInfo.currentProject.currentOptions,{name:name})
         if(currentOption){
             $("#rename-lv").modal({show:true});
@@ -372,6 +407,7 @@ $('#setNewFeeRate').bind('click', function () {
     })
     $("#usageProjectList").html(listString);
     $("#set-lv-feeRateName").text(feeRateFile.name);
+    $("#set-use-feeRateName").text(feeRateFile.name);
 
     feeRateObject.getFeeRateStandards(function (data) {
         $('#standardSelect').empty();
@@ -393,6 +429,8 @@ $('#changeConfirm').bind('click', function (){
 $('#saveAs').bind('click', function (){
     var feeRateFile = projectObj.project.FeeRate.getActivateFeeRate();
     $('#copyFeeRateName').val(feeRateFile.name+'副本');
+    $('#valid_name').val(feeRateFile.name+'副本');
+    $('#nameError').hide();
 });
 
 $('#saveAsConfirm').bind('click',function () {
@@ -453,7 +491,7 @@ $('#changFeeRateFile').bind('click',function (){
 $('#otherProject').change(function(){
     var newVal = $(this).val();
     var projects = feeRateObject.changeInfo.others;
-    var selected = _.find(projects,{projectID:parseInt(newVal)});
+    var selected = _.find(projects,{ID:parseInt(newVal)});
     $('#otherFeeRateOption').empty();
     _.forEach(selected.optionList,function (f) {
         var option =  $("<option>").val(f.ID).text(f.name);

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

@@ -454,15 +454,46 @@ var gljOprObj = {
      //   $('#dropdown').hide();
     },
     showRationGLJData:function (node) {
-        let gljList = [];
-        let ration_glj = projectObj.project.ration_glj;
-        let ration = node.data;
-        if(ration_glj.datas&&ration_glj.datas.length>0){
-            gljList = _.filter(ration_glj.datas,{'projectID':ration.projectID,'rationID':ration.ID})
+        var gljList = [];
+        var ration_glj = projectObj.project.ration_glj;
+        node=node?node:projectObj.project.mainTree.selected;
+        if(node.sourceType==ModuleNames.ration){
+            let ration = node.data;
+            gljList=this.filterGljByRation(ration,ration_glj.datas);
+            this.showInSheet(gljList);
+        }
+    },
+    showRationGLJSheetData:function () {
+        sheetCommonObj.showData(this.sheet,this.setting,this.sheetData);
+    },
+    filterGljByRation:function (ration,datas) {
+        var gljList=[];
+        if(datas&&datas.length>0){
+            gljList = _.filter(datas,{'rationID':ration.ID})
         }
+        return gljList;
+    },
+    showInSheet:function(gljList){
+        gljList=this.combineWithProjectGlj(gljList);
         sheetCommonObj.showData(this.sheet,this.setting,gljList);
         this.sheetData=gljList;
     },
+    combineWithProjectGlj:function (ration_gljs) {
+        var projectGljs = projectObj.project.projectGLJ.datas;
+        if(ration_gljs&&ration_gljs.length>0&&projectGljs&&projectGljs.length>0){
+            ration_gljs.forEach(function (a) {
+                var glj = _.find(projectGljs,{id:a.projectGLJID});
+                if(glj){
+                    a.basePrice=glj.unit_price.base_price;
+                    a.marketPrice=glj.unit_price.market_price;
+                    a.adjustPrice=glj.adjust_price;
+                    a.isEstimate=glj.is_evaluate;
+                }
+            })
+        }
+        return ration_gljs;
+    }
+    ,
     showRationCoeData:function (node) {
         var coeList = [];
         var ration_coe= projectObj.project.ration_coe;
@@ -596,6 +627,9 @@ var gljOprObj = {
             }
         }
         return true;
+    },
+    refreshView:function () {
+        this.showRationGLJData();
     }
 }
 

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

@@ -40,7 +40,10 @@ let MainTreeCol = {
         },
         forCalcBase: function (node) {
             // to do according to billsParentType
-            return MainTreeCol.readOnly.billsParent && MainTreeCol.readOnly.non_bills;
+            return MainTreeCol.readOnly.billsParent(node) && MainTreeCol.readOnly.non_bills(node);
+        },
+        forUnitFee: function (node) {
+            return MainTreeCol.readOnly.ration(node) && MainTreeCol.readOnly.billsParent(node);
         }
     },
     cellType: {

+ 87 - 0
web/building_saas/main/js/views/project_property_labour_coe_view.js

@@ -0,0 +1,87 @@
+/**
+ * Created by CSL on 2017-09-22.
+ * 项目属性之人工系数。
+ */
+let labourCoeView = {
+    datas: [],
+    spread: null,
+    sheet: null,
+
+    buildSheet: function (){
+        let me = this;
+        me.datas = calcLabourCoes;
+
+        if (me.spread) {
+            me.spread.destroy();
+            me.spread = null;
+        };
+        me.spread = new GC.Spread.Sheets.Workbook($("#labourCoeSpread")[0], { sheetCount: 1 });
+        me.spread.options.tabStripVisible = false;
+        me.spread.options.showVerticalScrollbar = false;
+        me.spread.options.showHorizontalScrollbar = false;
+        me.spread.options.rowHeight = 50;
+        let sheetArea = GC.Spread.Sheets.SheetArea;
+        let sheet = me.spread.getSheet(0);
+        me.sheet = sheet;
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        sheet.setColumnCount(1, sheetArea.viewport);
+        sheet.setColumnWidth(0, 180, sheetArea.viewport);
+        sheet.setRowCount(1, sheetArea.colHeader);
+        sheet.setRowCount(20, sheetArea.viewport);
+        sheet.setRowHeight(0, 30, sheetArea.colHeader);
+
+        sheet.resumeEvent();
+        sheet.resumePaint();
+    },
+
+    loadData(){          // 树结构转换二维表显示,行列转换
+        let me = this;
+        let libArr = [];
+        for (let v of me.datas) {if (!v.ParentID) libArr.push(v);};
+
+        // 行名称
+        let row = 0;
+        for (let v of me.datas) {
+            if (v.ParentID == libArr[0].ID) {
+                me.sheet.setText(row, 0, v.name, GC.Spread.Sheets.SheetArea.viewport);
+                row++;
+            };
+        };
+
+        me.sheet.setColumnCount(libArr.length + 1, GC.Spread.Sheets.SheetArea.viewport);    // 还多一列行名称
+        me.sheet.setRowCount(row, GC.Spread.Sheets.SheetArea.viewport);
+        me.sheet.setText(0, 0, "定额工种", GC.Spread.Sheets.SheetArea.colHeader);
+        me.sheet.options.isProtected = true;
+        me.sheet.getRange(-1, 1, -1, libArr.length + 1, GC.Spread.Sheets.SheetArea.viewport).locked(false); 
+
+        // 列名称
+        for (let c = 0; c <= libArr.length - 1; c++) {
+            me.sheet.setText(0, c + 1, libArr[c].name, GC.Spread.Sheets.SheetArea.colHeader);
+            me.sheet.setColumnWidth(c + 1, 100, GC.Spread.Sheets.SheetArea.colHeader);
+            // 值明细
+            let libID = libArr[c].ID;
+            for (let r = 0; r < row; r++) {
+                let rowName = me.sheet.getText(r, 0);
+                for (let v of me.datas) {
+                    if ((v.ParentID == libID) && (v.name == rowName)) {
+                        me.sheet.setValue(r, c + 1, v.coe);
+                        break;
+                    };
+                };
+            };
+        };
+    },
+
+    showData(){
+        let me = this;
+        me.buildSheet();
+        me.loadData();
+    }
+};
+
+$(document).ready(function(){
+    $("#tab_poj-settings-6").on('shown.bs.tab', function (e) {
+        labourCoeView.showData();
+    });
+});

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

@@ -8,9 +8,9 @@ var projectObj = {
     mainController: null,
     gljSpreed:null,
     beforeMainTreeSelectedChange: function (node) {
-        if (node) {
+        /*if (node) {
             subViewObj.saveComments(node);
-        }
+        }*/
     },
     checkCommonField: function (editingText, colSetting) {
         let value;

+ 1 - 0
web/building_saas/main/js/views/std_bills_lib.js

@@ -108,6 +108,7 @@ var billsLibObj = {
         var showJobsAndFeatures = function (node) {
             $('#stdBillsJobTab').show();
             $('#stdBillsRemarkTab').hide();
+            billsLibObj.refreshBillsRelaSpread();
             billsLibObj.checkBillsRelaSpread();
             showJobs(getBillsJobs(node));
             showFeatures(getBillsFeatures(node));

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

@@ -129,11 +129,13 @@ let subViewObj = {
         $($(this).attr('href')).show();
     },
     loadComments: function (node) {
-        let select = node;
-        if (node && (node.sourceType === projectObj.project.Bills.getSourceType() || node.sourceType === projectObj.project.Ration.getSourceType())) {
-            $('#comments>textarea').val(node.data.comments).removeAttr('readOnly');
-        } else {
-            $('#rationComments>textarea').val('').attr('readOnly', true);
+        if (node) {
+            if (node.sourceType === projectObj.project.Bills.getSourceType()) {
+                $('#comments>textarea').val(node.data.comments)
+            } else if (node.sourceType === projectObj.project.Ration.getSourceType()) {
+                console.log(node.data);
+                $('#comments>textarea').val(node.data.content);
+            }
         }
     },
     saveComments: function (node) {

+ 43 - 59
web/building_saas/pm/html/project-management.html

@@ -37,66 +37,50 @@
             <strong>注意!</strong> 这是一条消息通知 <a href="#">链接</a>
         </div>
     </div>
-    <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 justify-content-between">
-        <span class="header-logo px-2">Smartcost</span>
-        <div class="navbar-text pt-0">
-            <div class="dropdown d-inline-block">
-                <button class="btn btn-link btn-sm dropdown-toggle" type="button" data-toggle="dropdown"><%- userAccount %></button>
-                <div class="dropdown-menu dropdown-menu-right">
-                    <a class="dropdown-item" href="/user/info" target="_blank">账号资料</a>
-                    <a class="dropdown-item" href="user-buy.html" target="_blank">产品购买</a>
-                    <a class="dropdown-item" href="/user/preferences" target="_blank">偏好设置</a>
+    <%include ../../../common/html/header.html %>
+    <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
+        <ul class="nav navbar-nav px-1">
+            <li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle" href="http://example.com" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">文件</a>
+                <div class="dropdown-menu" aria-labelledby="supportedContentDropdown">
+                    <a class="dropdown-item" href="#">Action</a>
+                    <a class="dropdown-item" href="#">Another action</a>
+                    <a class="dropdown-item" href="#">Something else here</a>
                 </div>
-            </div>
-            <span class="btn btn-link btn-sm new-msg">
-                    <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;2
-                </span>
-                <a href="/logout" class="btn btn-link btn-sm">注销</a>
-            </div>
-        </nav>
-        <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
-            <ul class="nav navbar-nav px-1">
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="http://example.com" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">文件</a>
-                    <div class="dropdown-menu" aria-labelledby="supportedContentDropdown">
-                        <a class="dropdown-item" href="#">Action</a>
-                        <a class="dropdown-item" href="#">Another action</a>
-                        <a class="dropdown-item" href="#">Something else here</a>
-                    </div>
-                </li>
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑</a>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item" href="#">Action</a>
-                        <a class="dropdown-item" href="#">Another action</a>
-                        <a class="dropdown-item" href="#">Something else here</a>
-                    </div>
-                </li>
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">工具</a>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item" href="#">定额库编辑器</a>
-                        <a class="dropdown-item" href="/complementaryGlj">工料机库编辑器</a>
-                    </div>
-                </li>
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o"></i> 帮助</a>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item" href="#">帮助</a>
-                        <a class="dropdown-item" href="#">升级说明</a>
-                        <a class="dropdown-item" href="#">重庆市2008定额说明</a>
-                        <a class="dropdown-item" href="#">纵横官网</a>
-                        <a class="dropdown-item" href="#">动画教程</a>
-                        <a class="dropdown-item" href="#">联系客服</a>
-                        <a class="dropdown-item" href="#">关于</a>
-                    </div>
-                </li>
-            </ul>
-            <form class="form-inline">
-                <input class="form-control form-control-sm mr-1" type="text" placeholder="告诉我你想做什么">
-            </form>
-        </nav>
-    </div>
+            </li>
+            <li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑</a>
+                <div class="dropdown-menu">
+                    <a class="dropdown-item" href="#">Action</a>
+                    <a class="dropdown-item" href="#">Another action</a>
+                    <a class="dropdown-item" href="#">Something else here</a>
+                </div>
+            </li>
+            <li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">工具</a>
+                <div class="dropdown-menu">
+                    <a class="dropdown-item" href="#">定额库编辑器</a>
+                    <a class="dropdown-item" href="/complementaryGlj">工料机库编辑器</a>
+                </div>
+            </li>
+            <li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o"></i> 帮助</a>
+                <div class="dropdown-menu">
+                    <a class="dropdown-item" href="#">帮助</a>
+                    <a class="dropdown-item" href="#">升级说明</a>
+                    <a class="dropdown-item" href="#">重庆市2008定额说明</a>
+                    <a class="dropdown-item" href="#">纵横官网</a>
+                    <a class="dropdown-item" href="#">动画教程</a>
+                    <a class="dropdown-item" href="#">联系客服</a>
+                    <a class="dropdown-item" href="#">关于</a>
+                </div>
+            </li>
+        </ul>
+        <form class="form-inline">
+            <input class="form-control form-control-sm mr-1" type="text" placeholder="告诉我你想做什么">
+        </form>
+    </nav>
+</div>
     <div class="main">
         <div class="poj-manage container-fluid">
             <div class="row">

+ 1 - 1
web/building_saas/pm/js/pm_main.js

@@ -1220,7 +1220,7 @@ function setDataToSideBar() {
                 }
 
                 if (tmp.feeFile !== undefined && feeFileList.indexOf(tmp.feeFile.name) < 0) {
-                    feeFileHtml += '<tr><td>'+ unitPriceFileCounter +'</td><td>'+ tmp.feeFile.name +'</td></tr>';
+                    feeFileHtml += '<tr><td>'+ feeFileCounter +'</td><td>'+ tmp.feeFile.name +'</td></tr>';
                     feeFileCounter++;
                     feeFileList.push(tmp.feeFile.name);
                 }

+ 51 - 7
web/common/html/header.html

@@ -1,18 +1,62 @@
 <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 justify-content-between">
     <span class="header-logo px-2">Smartcost</span>
-    <div class="navbar-text"><a href="project-management.html">项目管理</a></div>
+    <div class="navbar-text" id="fullpath">
+        <% if (action !== 'index' || controller !== 'pm') {%>
+        <a href="/pm">项目管理</a>
+        <% } %>
+    </div>
     <div class="float-lg-right navbar-text pt-0">
         <div class="dropdown d-inline-block">
             <button class="btn btn-link btn-sm dropdown-toggle" type="button" data-toggle="dropdown"><%= sessionUser.email %></button>
             <div class="dropdown-menu dropdown-menu-right">
-                <a class="dropdown-item" href="user-info.html" target="_blank">账号资料</a>
+                <a class="dropdown-item" href="/user/info" target="_blank">账号资料</a>
                 <a class="dropdown-item" href="user-buy.html" target="_blank">产品购买</a>
-                <a class="dropdown-item" href="user-set.html" target="_blank">偏好设置</a>
+                <a class="dropdown-item" href="/user/preferences" target="_blank">偏好设置</a>
             </div>
         </div>
-        <span class="btn btn-link btn-sm new-msg">
-                  <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;2
-                </span>
+        <a class="btn btn-link btn-sm <% if (unreadCount > 0) { %>new-msg<% } %>" id="notify-info" data-toggle="modal" data-target="#msg" href="javacript:void(0);">
+            <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;<span id="unread-num"><%= unreadCount %></span>
+        </a>
         <a class="btn btn-link btn-sm" href="/logout">注销</a>
     </div>
-</nav>
+</nav>
+<!--弹出消息-->
+<div class="modal fade" id="msg" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title"><i class="fa fa-envelope-o"></i> 消息通知</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body modal-auto-height">
+                <div id="message-loading" style="text-align: center; padding-top: 100px;"></div>
+                <!--消息列表 默认显示10条-->
+                <table class="table table-bordered" id="message-list" style="display: none;">
+                    <thead>
+                        <tr>
+                            <th>标题<a href="javascript:void(0);" id="set-all-read" class="float-right btn btn-sm">设置全部已读</a></th>
+                            <th width="120">发布时间</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                    </tbody>
+                </table>
+                <div id="message-content" style="display: none;">
+                    <!--点击标题查看内容-->
+                    <a href="javascript:void(0);" class="back">返回</a>
+                    <h4 class="text-center title"></h4>
+                    <p class="text-center time"></p>
+                    <div class="mx-3 mb-5 content"></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="/lib/jquery/jquery.min.js"></script>
+<script type="text/javascript" src="/web/building_saas/js/moment.min.js"></script>
+<script type="text/javascript" src="/web/building_saas/js/message.js"></script>

+ 1 - 1
web/users/html/user-safe.html

@@ -103,7 +103,7 @@
                             <td><%= log.message.os %></td>
                             <td><%= log.message.browser %></td>
                             <td><%= moment(log.create_time).format('YYYY-MM-DD HH:mm:ss') %></td>
-                            <td>(<%= log.message.ip %>)</td>
+                            <td><%= log.message.ip_info %>(<%= log.message.ip %>)</td>
                         </tr>
                         <% }) %>
                         </tbody>