瀏覽代碼

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

TonyKang 5 年之前
父節點
當前提交
eef902ab07

+ 32 - 0
config/gulpConfig.js

@@ -296,6 +296,38 @@ module.exports = {
         'public/web/common_ajax.js',
         'web/building_saas/complementary_ration_lib/js/global.js',
         'web/building_saas/complementary_ration_lib/js/installation.js'
+    ],
+    unitPrice_css:[],
+    unitPrice_jspaths:[
+      "lib/popper/popper.min.js",
+      "lib/bootstrap/bootstrap.min.js",
+      "lib/bootstrap/bootstrap-submenu.js",
+      "web/building_saas/glj/js/socket.io.slim.js",
+      "public/web/socket/connection.js",
+      "web/building_saas/js/moment.min.js",
+      "web/building_saas/js/message.js",
+      "public/web/scMathUtil.js",
+      "public/web/gljUtil.js",
+      "public/web/PerfectLoad.js",
+      "lib/lodash/lodash.js",
+      "public/web/commonAlert.js",
+      "public/web/headerOpr.js",
+      "public/web/common_ajax.js",
+      "public/common_util.js",
+      "public/common_constants.js",
+      "public/web/treeDataHelper.js",
+      "public/web/ztree_common.js",
+      "web/building_saas/main/js/models/main_consts.js",
+      "lib/jquery-editable-select/jquery.editable-select.min.js",
+      "public/web/tree_sheet/tree_sheet_helper.js",
+      "public/web/sheet/sheet_data_helper.js",
+      "public/web/sheet/sheet_common.js",
+      "public/web/slideResize.js",
+      "web/building_saas/main/js/views/glj_view_contextMenu.js",
+      "web/building_saas/main/js/views/glj_view.js",
+      "web/building_saas/main/js/views/options_view.js",
+      'web/building_saas/main/js/views/project_glj_view.js',
+      "web/building_saas/unit_price_file/index.js",
     ]
 }
 

+ 21 - 10
gulpfile.js

@@ -38,7 +38,7 @@ let commonOptions={
 
 let headerOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     concatName:'header.all.min',
     srcHtml:'web/src/html/common/header.html',
     htmlDest:'web/common/html',
@@ -48,7 +48,7 @@ let headerOptions={
 
 let loginOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:login_jspaths,
     concatName:'login.all.min',
     srcHtml:'web/src/html/login/login.html',
@@ -61,7 +61,7 @@ let loginOptions={
 
 let pmOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:pm_jspaths,
     csspaths:pm_csspaths,
     concatName:'pm.all.min',
@@ -75,7 +75,7 @@ let pmOptions={
 
 let mainOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:main_jspaths,
     csspaths:main_csspaths,
     concatName:'main.all.min',
@@ -89,7 +89,7 @@ let mainOptions={
 
 let compleGljOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleGlj_jspaths,
     csspaths: compleGlj_csspaths,
     concatName: 'compleGlj.all.min',
@@ -105,7 +105,7 @@ let compleGljOptions = {
 
 let compleRation_rationOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_ration_jspaths,
     csspaths: compleRation_ration_csspaths,
     concatName: 'compleRation_ration.all.min',
@@ -122,7 +122,7 @@ let compleRation_rationOptions = {
 
 let compleRation_gljOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_glj_jspaths,
     csspaths: compleRation_glj_csspaths,
     concatName: 'compleRation_glj.all.min',
@@ -139,7 +139,7 @@ let compleRation_gljOptions = {
 
 let compleRation_coeOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_coe_jspaths,
     csspaths: compleRation_coe_csspaths,
     concatName: 'compleRation_coe.all.min',
@@ -156,7 +156,7 @@ let compleRation_coeOptions = {
 
 let compleRation_instOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_inst_jspaths,
     csspaths: compleRation_inst_csspaths,
     concatName: 'compleRation_inst.all.min',
@@ -364,6 +364,17 @@ gulp.task('compleRation_inst', ['compleRation_inst_inject'], function () {
     return htmlmin(compleRation_instOptions);
 });
 
+gulp.task('unitPrice_minify', function (){
+  return minify(unitPriceOptions);
+});
+
+gulp.task('unitPrice_inject',['unitPrice_minify'],function (){
+  return inject(unitPriceOptions);
+})
+
+gulp.task('unit_price',['unitPrice_inject'], function (){
+  return htmlmin(unitPriceOptions);
+});
 
 
-gulp.task('build',['header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst']);
+gulp.task('build',['header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst','unit_price']);

+ 30 - 0
modules/all_models/stdGlj_lib.js

@@ -0,0 +1,30 @@
+/**
+ * Created by Zhong on 2018/3/23.
+ */
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+let gjlMapRationLibsSchema = new Schema(
+    {
+        ID: Number,
+        dispName: String
+    },
+    {_id: false},
+    {versionKey: false}
+);
+let gljMapSchema = new Schema({
+        deleted: Boolean,
+        ID: Number,
+        dispName: String,
+        appType: String,
+        creator: String,
+        createDate: String,
+        recentOpr: [oprSchema],
+        rationLibs: [gjlMapRationLibsSchema],
+        compilationId: String,
+        compilationName: String
+    },
+    {versionKey: false}
+);
+
+mongoose.model('std_glj_lib_map', gljMapSchema, 'std_glj_lib_map');

+ 4 - 1
modules/all_models/unit_price_file.js

@@ -11,7 +11,10 @@ let Schema = mongoose.Schema;
 let collectionName = 'unit_price_file';
 let modelSchema = {
     // 自增id
-    id: Number,
+    id: {
+      type: Number,
+      index: true
+    },
     // 标段id
     project_id: {
         type: Number,

+ 4 - 0
modules/glj/controllers/glj_controller.js

@@ -230,6 +230,10 @@ class GLJController extends BaseController {
      * @return {void}
      */
     async deleteMixRatio(request, response) {
+        if(request.body.data){
+          let data = request.body.data;
+          request.body = JSON.parse(data);
+        }
         let id = request.body.id;
         id = parseInt(id);
         let responseData = {

+ 17 - 34
modules/glj/models/glj_list_model.js

@@ -11,13 +11,12 @@ import CounterModel from "./counter_model";
 import UnitPriceModel from "./unit_price_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";
 import STDGLJLibGLJListModel from "../../common/std/std_glj_lib_glj_list_model";
 import MixRatioModel from "./mix_ratio_model";
 import GljModel from "../../complementary_glj_lib/models/gljModel";
 const ProjectModel = require('../../pm/models/project_model').project;
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
-import decimal_facade from "../../main/facade/decimal_facade";
+import unitPriceFacade from "../../unit_price_file/facade/unit_price_facade";
 let gljCollectionName = 'glj_list';
 let GLJSchemas = mongoose.model(gljCollectionName);
 let _ = require("lodash");
@@ -90,7 +89,7 @@ class GLJListModel extends BaseModel {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
             let fields = {_id: 0};
-            gljData = await this.db.find(condition, fields);
+            gljData = await this.model.find(condition, fields).lean();
             // 没有数据则直接返回空
             if (gljData.length <= 0) {
                 throw '无数据';
@@ -99,35 +98,19 @@ class GLJListModel extends BaseModel {
             // 获取标段设置的单价文件数据
             let unitPriceModel = new UnitPriceModel();
             unitPriceList = await unitPriceModel.getDataByFileId(unitPriceFileId);
-            // 整理获取工料机ID list
-            let gljIdList = [];
-            for(let tmp of gljData) {
-                gljIdList.push(tmp.id);
-                let c_key = this.getIndex(tmp,['code','name','specs','unit','type']);
-                keyMap[tmp.id] = c_key; //工料机ID和连接key的对照表;
-            }
-            // 从定额工料机库中获取消耗量
-            condition = {
-                projectID: projectId
-               // projectGLJIDList: gljIdList 这里是取所有项目工料机,所以也是取所有定额工料机,不需要根据项目工料机ID再过滤
-            };
-            //为了提高性成计算消耗量功能改成了在前端计算
-            // 整理获取有组成物的项目工料机的数据
             let connect_keys = [];
             for(let tmp of gljData) {
-                // 有组成物的类型才查找
-                let key = keyMap[tmp.id];
+                let c_key = this.getIndex(tmp,['code','name','specs','unit','type']);
                 if(this.ownCompositionTypes.indexOf(tmp.type)!=-1){
-                    connect_keys.push(key);
-                }
-                /*if (tmp.type === GLJTypeConst.CONCRETE || tmp.type === GLJTypeConst.MORTAR ||
-                    tmp.type === GLJTypeConst.MIX_RATIO || tmp.type === GLJTypeConst.GENERAL_MACHINE|| tmp.type === GLJTypeConst.MAIN_MATERIAL){
-                    connect_keys.push(key);
-                }*/
+                  connect_keys.push(c_key);
+               }
+               keyMap[c_key] = tmp; //项目工料机连接key和工料机的对照表;
             }
+        
             // 查找组成物的消耗量
             let totalComposition = {};
             let mixRatioData = {};
+            let missMaxRatio = [];//由于共享单价文件,单价文件编辑器造成的,存在组成物和对应的单价,但是项目工料机不存的情况
             if (connect_keys.length > 0) {
                 let mixRatioModel = new MixRatioModel();
                 condition = {connect_key: {"$in": connect_keys}, unit_price_file_id: unitPriceFileId};
@@ -135,15 +118,6 @@ class GLJListModel extends BaseModel {
                 for (let tmp of mixRatioList) {
                    let t_index = tmp.connect_key;
                    let m_index = this.getIndex(tmp,['code','name','specs','unit','type']);
-                   /*
-                    let consumption=parseFloat(tmp.consumption);
-                    let r_quantity = quantityList[t_index]?quantityList[t_index]:0;
-                   if(quantityList[m_index]!==undefined){
-                       quantityList[m_index]=quantityList[m_index]+r_quantity*consumption;
-                   }else {
-                       quantityList[m_index] = r_quantity*consumption;
-                   }
-                    quantityList[m_index] = scMathUtil.roundTo(quantityList[m_index],-quantity_decimal);*/
                     if (mixRatioData[t_index] !== undefined) {
                         mixRatioData[t_index].push(tmp);
                     } else {
@@ -159,8 +133,17 @@ class GLJListModel extends BaseModel {
                     } else {
                         mixRatioConnectData[m_index] = [tmp.connect_key];
                     }
+                    //检查该组成物对应的项目工料机是否存在  
+                    if(!keyMap[m_index]) missMaxRatio.push(tmp);//这里可能会有重复,但是后面的添加操作中会去重 
                 }
             }
+
+            if(missMaxRatio.length > 0){
+              let newList =  await unitPriceFacade.getNewProjectGLJFromMissMixratio(projectId,missMaxRatio,keyMap,[]);
+              await unitPriceFacade.setIDfromCounter("glj_list",newList);
+              await this.model.insertMany(newList);
+              gljData=gljData.concat(newList);         
+            }
             // 组合单价数据
             gljData = this.combineData(gljData, unitPriceList, {}, mixRatioData, totalComposition);
             // 排序

+ 12 - 8
modules/glj/models/unit_price_model.js

@@ -294,24 +294,28 @@ class UnitPriceModel extends BaseModel {
         let tasks = [];
         let parentTask = [];
         let newValueMap = {};
+        let needCheckDatas= [];
         for(let d of data){//第一次循环生成更新提交的记录,并生成一个新值的映射表,为更新父节点使用
             let condition = {id:d.unit_price.id,unit_price_file_id:d.unit_price.unit_price_file_id};
-            let doc = {};
-            doc[d.field]=d.newval;
-            newValueMap[d.unit_price.id] = doc;
+            let doc = d.ext?d.ext:{};
+            if(d.field){//共用接口后有可能只更新其它属性,不更新价格
+              doc[d.field]=d.newval;
+              newValueMap[d.unit_price.id] = doc;
+              needCheckDatas.push(d);
+            }
             tasks.push(this.generateUpdateTask(condition,doc));
         }
-        for(let d of data){//第二次更新父节点
-           let rList = await this.checkAndUpdateParent(d.unit_price,d.field,d.project_id,newValueMap,true);
-           parentTask = parentTask.concat(rList);
-        }
+        for(let d of needCheckDatas){//第二次更新父节点
+          let rList = await this.checkAndUpdateParent(d.unit_price,d.field,d.project_id,newValueMap,true);
+          parentTask = parentTask.concat(rList);
+       }
         tasks = tasks.concat(parentTask);
         tasks.length>0?this.model.bulkWrite(tasks):'';
         return parentTask;
     }
 
     async updateParentUnitPrice(mixRatio,fieid,project_id,newValueMap,batchUpdate){//batchUpdate 批量更新标记,如果true,只生成task
-        let  decimalObject =await decimal_facade.getProjectDecimal(project_id);
+        let  decimalObject =project_id?await decimal_facade.getProjectDecimal(project_id):null
         let quantity_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
         let price_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.unitPrice)?decimalObject.glj.unitPrice:2;
         //查找该工料机所有组成物

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

@@ -258,7 +258,8 @@ module.exports = {
                 projInfo.shareState = await pm_facade.getShareState(projectID, userID);
             }
             // 获取项目所属用户
-            projInfo.owner = await userModel.findOne({_id: mongoose.Types.ObjectId(project.userID)}, 'real_name');
+            projInfo.owner = await userModel.findOne({_id: mongoose.Types.ObjectId(project.userID)}, 'real_name mobile').lean();
+            projInfo.opener = await userModel.findOne({_id: mongoose.Types.ObjectId(userID)}, 'real_name mobile').lean();
             callback('', consts.projectConst.PROJECT_INFO, project);
         }, function (err) {
             callback(err, consts.projectConst.PROJECT_INFO, {});

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

@@ -677,7 +677,7 @@ ProjectsDAO.prototype.getTenderByUnitPriceFileId = async function (unitPriceFile
 
     //let condition = {projType: 'Tender', "property.unitPriceFile.id": unitPriceFileId};
     let condition = {"property.unitPriceFile.id": unitPriceFileId,deleteInfo:null};
-    result = await Projects.find(condition,['name','property']);
+    result = await Projects.find(condition,['ID','name','property']);
 
     return result;
 };

+ 4 - 3
modules/ration_glj/controllers/ration_glj_controller.js

@@ -63,11 +63,12 @@ async function getGLJDataPaging(req, res) {
     };
     let data = JSON.parse(req.body.data);
     try {
-        let gljLibId = await ration_glj_facade.getGLJLibByEngineerID(data.engineerID);
+        const compilationId = req.session.sessionCompilation._id;
+        const gljLib = await ration_glj_facade.getGLJLib({ compilationId });
         let info = {
-            gljLibId,
+            gljLibId: gljLib.ID,
             userID: req.session.sessionUser.id,
-            compilationId: req.session.sessionCompilation._id
+            compilationId
         };
         result.data = await ration_glj_facade.getGLJDataPaging(info, data.condition);
         if (req.session.sessionCompilation.priceProperties) {

+ 6 - 0
modules/ration_glj/facade/ration_glj_facade.js

@@ -7,6 +7,7 @@ module.exports = {
     deleteByRation: deleteByRation,
     getQuantityByProjectGLJ: getQuantityByProjectGLJ,
     getLibInfo: getLibInfo,
+    getGLJLib,
     getGLJData: getGLJData,
     getGLJDataPaging: getGLJDataPaging,
     getGLJDataByCodes:getGLJDataByCodes,
@@ -53,6 +54,7 @@ import gljType from "../../common/const/glj_type_const.js";
 const complementaryGljModel = mongoose.model('complementary_glj_lib');
 const stdGljModel = mongoose.model('std_glj_lib_gljList');
 const gljClassModel = mongoose.model('std_glj_lib_gljClass');
+const stdGLJLibModel = mongoose.model('std_glj_lib_map');
 const projectDao = require('../../pm/models/project_model').project;
 const compleClassModel = mongoose.model('complementary_glj_section');
 
@@ -535,6 +537,10 @@ async function getLibInfo(req) {
     return data;
 }
 
+async function getGLJLib(query) {
+    return stdGLJLibModel.findOne(query);
+}
+
 async function getGLJLibByEngineerID  (engineerID) {
     let engineeringLibModel = new EngineeringLibModel() ;
     let engineeringInfo = await engineeringLibModel.findDataByCondition({'_id': engineerID});

+ 2 - 0
public/common_constants.js

@@ -139,6 +139,7 @@
         BILLS: 'bills',
         RATION: 'ration',
     };
+    const DEFAULT_REGION = '全省';
     return {
         fixedFlag,
         billType,
@@ -148,5 +149,6 @@
         supplyType,
         supplyText,
         SourceType,
+        DEFAULT_REGION,
     };
 });

+ 10 - 1
public/web/gljUtil.js

@@ -595,13 +595,22 @@ let gljUtil = {
     },
     setProperty:function(Obj,updateData) {
         for(let ukey in updateData){
-            if(_.isObject(updateData[ukey]) && _.isObject(Obj[ukey])){
+            if(_.isObject(updateData[ukey]) && _.isObject(Obj[ukey])&&!_.isArray(updateData[ukey])){
                 setProperty(Obj[ukey],updateData[ukey]);
             }else {
                 Obj[ukey] = updateData[ukey];
             }
         }
     },
+    sortProjectGLJ:function(jsonData) {
+      if (jsonData.length > 0) {
+          jsonData = _.sortByAll(jsonData, [function (item) {
+            let unit_price = item.unit_price?item.unit_price:item;
+            return _.indexOf(gljTypeSeq,unit_price.type);
+          }, 'code']);
+      }
+      return jsonData
+  },
     fixedFlag : {
         // 分部分项工程
         SUB_ENGINERRING: 1,

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

@@ -371,7 +371,7 @@ var sheetCommonObj = {
             }
             if(setting.owner==='gljTree'){
                 if(setting.header[col].cellType === "checkBox"){
-                    val==1?val:0;
+                    val = val==1?val:0;
                     this.setCheckBoxCell(row,col,sheet,val);
                 }
                 if(setting.header[col].dataCode === 'gljType' && data[row].gljType){

+ 47 - 33
public/web/socket/connection.js

@@ -1,29 +1,40 @@
 /**
  * Created by zhangweicheng on 2017/8/7.
  */
-socketObject={
-  roomInfo : null,
-  messages:[],
-  connect:function (from) {
+socketObject = {
+  roomInfo: null,
+  messages: [],
+  connect: function (from, payload) {
       // 连接socket服务器
       var hostName = window.location.hostname;
       let me = this;
-      let port = window.location.protocol ==='http:'?3300:3301;
-      socket = io(window.location.protocol+'//'+hostName+':'+port);
+      let port = window.location.protocol === 'http:' ? 3300 : 3301;
+      socket = io(window.location.protocol + '//' + hostName + ':' + port);
       socket.on('connect', function () {
-          if(from == 'pm'){
-              me.roomInfo={
-                  userID:userID
-              };
-          }else {
-              me.roomInfo={
-                  feeRate:me.getFeeRateRoomID(),
-                  unitFile:me.getUnitFileRoomID()
+          if (from == 'pm') {
+              me.roomInfo = {
+                  userID: userID
               };
+        }else if(from == 'unitPrice'){
+          me.roomInfo = {
+            unitFile:unitPriceFileID
+          }
+        } else {
+            me.roomInfo={
+                feeRate:me.getFeeRateRoomID(),
+                unitFile:me.getUnitFileRoomID()
+            };
+              if (payload && payload.projectReadOnly === false) {
+                  me.roomInfo.projectID = `projectID${projectObj.project.ID()}`;
+              }
           }
-          socket.emit('join', me.roomInfo);
-          if(me.messages.length > 0){//发送缓存消息;
-              for(let m of me.messages){
+          const emitData = {
+              roomData: me.roomInfo,
+              payload
+          };
+          socket.emit('join', emitData);
+          if (me.messages.length > 0) {//发送缓存消息;
+              for (let m of me.messages) {
                   socket.emit(m.message, m.data);
               }
           }
@@ -32,7 +43,7 @@ socketObject={
 
       //=========================================================
       //造价书页面接收消息部分
-      socket.on('feeRateChange', function(data) {
+      socket.on('feeRateChange', function (data) {
           //data = JSON.parse(data);
 
           $("#message").html('费率文件已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
@@ -49,34 +60,37 @@ socketObject={
           $("#notify").show();
       });
       socket.on('changeFileNotify', function (data) {//收到文件改变的消息
-          if(data.projectID==projectObj.project.ID()){//如果是同个项目,则给出提示,否则忽略
-                let preString ="";
-                if(data.name=='feeRate'){
-                    preString = "费率文件";
-                }
-                if(data.name=='unitFile'){
-                    preString = "单价文件";
-                }
-              $("#message").html(preString+'已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
+          if (data.projectID == projectObj.project.ID()) {//如果是同个项目,则给出提示,否则忽略
+              let preString = "";
+              if (data.name == 'feeRate') {
+                  preString = "费率文件";
+              }
+              if (data.name == 'unitFile') {
+                  preString = "单价文件";
+              }
+              $("#message").html(preString + '已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
               $("#notify").show();
           }
       });
+      socket.on('handleAvatarList', function ({ editingUsers }) {
+          projectInfoObj.handleAvatarList(editingUsers);
+      });
 
 
       //=============================================================================================
       //项目管理页面接收消息部分
 
       socket.on('refreshProjectIcon', function (data) {//收到刷新图标消息
-           if(data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshProjectIcon(data.projectID);
+          if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshProjectIcon(data.projectID);
       });
       socket.on('fileDataChange', function (data) {//收到单价文件、费率文件内容修改、文件切换、另存(暂时能共用,以后有需要可分离)推送消息
-          if(data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
+          if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
       });
   },
-  getFeeRateRoomID:function (){
-      return  projectObj.project.FeeRate.getActivateFeeRateFileID();
+  getFeeRateRoomID: function () {
+      return projectObj.project.FeeRate.getActivateFeeRateFileID();
   },
-  getUnitFileRoomID:function () {
-      return projectObj.project.projectGLJ.datas.constData.roomId?projectObj.project.projectGLJ.datas.constData.roomId:roomId;
+  getUnitFileRoomID: function () {
+      return projectObj.project.projectGLJ.datas.constData.roomId ? projectObj.project.projectGLJ.datas.constData.roomId : roomId;
   }
 }

+ 60 - 5
socket.js

@@ -9,16 +9,58 @@ import socket from "socket.io";
 
 const socketIO = socket(3300);
 
+const userCache = {};
+
+function getEditingUsers(projectID) {
+    const users = userCache[projectID];
+    if (!users) {
+        return [];
+    }
+    const rst = [];
+    const existMap = {};
+    for (const user of users) {
+        if (existMap[user._id]) {
+            continue;
+        }
+        existMap[user._id] = true;
+        rst.push(user);
+    }
+    return rst;
+}
+
 // socket.io相关操作
 socketIO.on('connection', function(socket) {
     let roomInfo = {};
+    let curUser;
+    let curProjectID;
     console.log("new connection");
     // 加入房间
     socket.on('join', function(data) {
-        console.log(data);
-        for(let key in data){
-            roomInfo[key] = data[key];
-            socket.join(data[key]);
+        const { roomData, payload } = data;
+        if (payload && payload.user) {
+            curUser = payload.user;
+        }
+        if (roomData) {
+            if (roomData.projectID) {
+                curProjectID = roomData.projectID;
+                if (!userCache[roomData.projectID]) {
+                    userCache[roomData.projectID] = [];
+                }
+                // 由于用户可以重复打开项目,因此不做唯一性处理
+                userCache[roomData.projectID].push(curUser);
+            }
+            for(let key in roomData){
+                roomInfo[key] = roomData[key];
+                socket.join(roomData[key], () => {
+                    if (key === 'projectID') {
+                        const editingUsers = getEditingUsers(curProjectID);
+                        if (editingUsers.length > 1) { // 只有一个用户数据,说明只有用户自己打开项目,不需要推送数据,减少触发。
+                            socket.broadcast.to(roomInfo.projectID).emit('handleAvatarList', {editingUsers});
+                            socket.emit('handleAvatarList', {editingUsers});
+                        }
+                    }
+                });
+            }
         }
     });
     // 数据更改通知
@@ -60,6 +102,19 @@ socketIO.on('connection', function(socket) {
 
 
     socket.on('disconnect', function () {
+        // 由于用户可以重复打开项目,因此不做唯一性处理,只删除一个数据,不删除所有的同用户数据
+        if (curProjectID && userCache[curProjectID]) {
+            const index = userCache[curProjectID].findIndex(user => user._id === curUser._id);
+            if (index >= 0) {
+                userCache[curProjectID].splice(index, 1);
+                if (!userCache[curProjectID].length) {
+                    delete userCache[curProjectID];
+                }
+                const editingUsers = getEditingUsers(curProjectID);
+                socket.broadcast.to(roomInfo.projectID).emit('handleAvatarList', {editingUsers});
+                socket.emit('handleAvatarList', {editingUsers});
+            }
+        }
         console.log("client disconnect =========="+JSON.stringify(roomInfo));
     });
-});
+});

+ 256 - 247
web/building_saas/css/custom.css

@@ -1,146 +1,146 @@
 
 .text-green{
-    color: #172a30
+  color: #172a30
 }
 label.title{
-    display: inline-block;
-    width: 100px;
+  display: inline-block;
+  width: 100px;
 }
 .modal-feeRate {max-width: 550px}
 
 div.resize{
-    height: 10px;
-    background: #efefef;
-    width: 100%;
-    cursor: s-resize;
+  height: 10px;
+  background: #efefef;
+  width: 100%;
+  cursor: s-resize;
 }
 div.resize-y{
-    height: 5px;
-    background: #efefef;
-    width: 100%;
-    cursor: s-resize;
+  height: 5px;
+  background: #efefef;
+  width: 100%;
+  cursor: s-resize;
 }
 div.resize-x{
-    width: 4px;
-    height: 100%;
-    background: #efefef;
-    cursor: w-resize;
-    float: left;
+  width: 4px;
+  height: 100%;
+  background: #efefef;
+  cursor: w-resize;
+  float: left;
 }
 /*.zlfb-check{
-    margin-left: 20px;
+  margin-left: 20px;
 }*/
 legend.legend{
-    display:block;
-    width:auto;
-    font-size:0.9rem;
-    top:-15px;
-    background: white;
+  display:block;
+  width:auto;
+  font-size:0.9rem;
+  top:-15px;
+  background: white;
 }
 
 .toolsbar_feeRate {
-    border-bottom: 1px solid #ccc
+  border-bottom: 1px solid #ccc
 }
 
 .li_sub {
-    margin-left: 14px;
+  margin-left: 14px;
 }
 
 .filterType{
-    width: 122px;
+  width: 122px;
 }
 
 .filterType ul{
-    width: 110px;
+  width: 110px;
 }
 .a_color{
-    color: #007bff
+  color: #007bff
 }
 
 .filterType a{
-    padding: 1px;
-    padding-top: 7px;
-    padding-bottom: 7px;
+  padding: 1px;
+  padding-top: 7px;
+  padding-bottom: 7px;
 }
 
 #gljPriceTenderCoe::-webkit-outer-spin-button,
 #gljPriceTenderCoe::-webkit-inner-spin-button {
-         -webkit-appearance: none;
-     }
+       -webkit-appearance: none;
+   }
 #gljPriceTenderCoe {
-    -moz-appearance: textfield;
+  -moz-appearance: textfield;
 }
 
 .modal-quantity-edit-height {
-    height: 200px;
-    overflow-y: auto;
+  height: 200px;
+  overflow-y: auto;
 }
 
 .message-box {
-    position:absolute;
-    background:#000;
-    padding:8px 10px;
-    line-height: 18px;
-    border-radius:4px;
-    text-align:left;
-    font:0.9rem Calibri;
-    box-shadow:2px 2px 6px #ccc;
-    color:#fff;
+  position:absolute;
+  background:#000;
+  padding:8px 10px;
+  line-height: 18px;
+  border-radius:4px;
+  text-align:left;
+  font:0.9rem Calibri;
+  box-shadow:2px 2px 6px #ccc;
+  color:#fff;
 }
 .triangle-border {
-    position:absolute;
-    left:10px;
-    overflow:hidden;
-    width:0;
-    height:0;
-    border-width:6px;
-    border-style:solid dashed dashed dashed;
+  position:absolute;
+  left:10px;
+  overflow:hidden;
+  width:0;
+  height:0;
+  border-width:6px;
+  border-style:solid dashed dashed dashed;
 }
 .triangle-border_dropdown {
-    position:absolute;
-    left:0px;
-    overflow:hidden;
-    width:0;
-    height:0;
-    border-width:4px;
-    z-index: 10;
-    border-style:solid dashed dashed dashed;
+  position:absolute;
+  left:0px;
+  overflow:hidden;
+  width:0;
+  height:0;
+  border-width:4px;
+  z-index: 10;
+  border-style:solid dashed dashed dashed;
 }
 .tb-border_dropdown {
-    top:7px;
-    border-color:#000 transparent transparent transparent;
+  top:7px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-background_dropdown {
-    bottom:-11px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-11px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-border {
-    bottom:-12px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-12px;
+  border-color:#000 transparent transparent transparent;
 }
 .tb-background {
-    bottom:-11px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-11px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-border_up {
-    top:-12px;
-    border-color:transparent transparent #000 transparent;
+  top:-12px;
+  border-color:transparent transparent #000 transparent;
 }
 .tb-background_up {
-    top:-11px;
-    border-color:transparent transparent #000 transparent;
+  top:-11px;
+  border-color:transparent transparent #000 transparent;
 }
 
 
 .elf-options:hover{
-    background-color: #CCCCCC;
+  background-color: #CCCCCC;
 }
 
 .inline-div{
-    display:inline;
+  display:inline;
 }
 
 /*快捷切换单位工程*/
@@ -150,326 +150,335 @@ legend.legend{
 
 
 /*.menu a:hover {
-    color: #fff;
-    background: #40DE5A;
+  color: #fff;
+  background: #40DE5A;
 }*/
 
 
 .menu ul ul {
-    visibility: hidden;
-    position: absolute;
-    top: -1px;
-    left: 100px;
+  visibility: hidden;
+  position: absolute;
+  top: -1px;
+  left: 100px;
 }
 
 .menu ul li:hover ul, .menu ul a:hover ul {
-    visibility: visible;
+  visibility: visible;
 }
 
 .menu ul :hover ul ul {
-    visibility: hidden;
+  visibility: hidden;
 }
 
 .menu ul :hover ul :hover ul {
-    visibility: visible;
+  visibility: visible;
 }
 
 .ui-datepicker-next, .ui-datepicker-next:hover{
-    background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
-    background-repeat: no-repeat;
-    background-position: -28px -12px;
+  background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
+  background-repeat: no-repeat;
+  background-position: -28px -12px;
 }
 .ui-datepicker-prev, .ui-datepicker-prev:hover{
-    background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
-    background-repeat: no-repeat;
-    background-position: -90px -12px;
+  background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
+  background-repeat: no-repeat;
+  background-position: -90px -12px;
 }
 
 .feerateInput {
-    padding: 0;
+  padding: 0;
 }
 
 #toolToastWrap{
-    display:-webkit-box;
-    display:-ms-flexbox;
-    display:flex;
-    -webkit-box-pack:center;
-    -ms-flex-pack:center;
-    justify-content:center;
-    pointer-events:none;
+  display:-webkit-box;
+  display:-ms-flexbox;
+  display:flex;
+  -webkit-box-pack:center;
+  -ms-flex-pack:center;
+  justify-content:center;
+  pointer-events:none;
 }
 
 #toolToast{
-    padding:11px 20px;
-    line-height:18px;
-    font-size:14px;
-    position:relative;
-    word-wrap:break-word;
-    color:#fff;
-    text-align:center;
-    background:rgba(0,0,0,.75);
-    background-size:cover;
-    -webkit-user-select:none;
-    -moz-user-select:none;
-    -ms-user-select:none;
-    user-select:none;
-    display:-webkit-box;
-    display:-ms-flexbox;
-    display:flex;
-    -webkit-box-align:center;
-    -ms-flex-align:center;
-    align-items:center;
-    max-width:686px;
-    box-sizing:border-box;
-    box-shadow:0 0 0 0 rgba(0,0,0,.15),0 2px 5px 0 rgba(0,0,0,.25);
-    pointer-events:auto;
-    border-radius:2px
+  padding:11px 20px;
+  line-height:18px;
+  font-size:14px;
+  position:relative;
+  word-wrap:break-word;
+  color:#fff;
+  text-align:center;
+  background:rgba(0,0,0,.75);
+  background-size:cover;
+  -webkit-user-select:none;
+  -moz-user-select:none;
+  -ms-user-select:none;
+  user-select:none;
+  display:-webkit-box;
+  display:-ms-flexbox;
+  display:flex;
+  -webkit-box-align:center;
+  -ms-flex-align:center;
+  align-items:center;
+  max-width:686px;
+  box-sizing:border-box;
+  box-shadow:0 0 0 0 rgba(0,0,0,.15),0 2px 5px 0 rgba(0,0,0,.25);
+  pointer-events:auto;
+  border-radius:2px
 }
 
 #toolToastBtn{
-    color:#0188fb;
-    margin:0 2px;
-    white-space:nowrap;
-    cursor:pointer;
-    font-weight:700
+  color:#0188fb;
+  margin:0 2px;
+  white-space:nowrap;
+  cursor:pointer;
+  font-weight:700
 }
 
 #toolToastBtn:hover{
-    color:#4060c9
+  color:#4060c9
 }
 
 #toolToastBtn:active{
-    color:#354ea1
+  color:#354ea1
 }
 
 .select_input{
-    border:none;
+  border:none;
 }
 
 /*.es-list>li:hover{
-    background: lightgrey;
+  background: lightgrey;
 }*/
 
 .es-list-selected{
-    background: lightgrey;
+  background: lightgrey;
 }
 
 .es-list>li{
-    font-size:13px;
+  font-size:13px;
 }
 .es-list{
-    white-space:nowrap;
+  white-space:nowrap;
 }
 
 /*.dropdown-toggle::after{
-    vertical-align:.5em
+  vertical-align:.5em
 }*/
 #esInput{
-    font-size:13px;
-    color: black;
+  font-size:13px;
+  color: black;
 }
 .ration_glj_spread{
-    width: 83%;
-    float: left;
+  width: 83%;
+  float: left;
 }
 
 .item_spread{
-    width: 29.8%;
-    float: left;
-    background: #F1F1F1;
-    word-wrap:break-word
+  width: 29.8%;
+  float: left;
+  background: #F1F1F1;
+  word-wrap:break-word
 }
 
 input.text-right{
-    text-align: right;
+  text-align: right;
 }
 
 .cus-width{
-    width: 100px;
-    margin-left: 10px;
+  width: 100px;
+  margin-left: 10px;
 }
 .more{
-    padding-left:.25rem!important
+  padding-left:.25rem!important
 }
 .bottom-tznrTools {
-    height: 30px;
-    line-height: 30px;
-    background:#efefef;
-    bottom:30px;
-    left:0px;
-    z-index: 999
+  height: 30px;
+  line-height: 30px;
+  background:#efefef;
+  bottom:30px;
+  left:0px;
+  z-index: 999
 }
 
 .zmhs-link{
-    padding:0.4em 0.4em !important;
+  padding:0.4em 0.4em !important;
 }
 
 /*修改tooltip默认最大宽度 */
 .tooltip-inner{
-    max-width: 400px !important;
+  max-width: 400px !important;
 }
 .applySuccess{
-    display: none;
-    color: #43CD80;
-    margin-left: 8px
+  display: none;
+  color: #43CD80;
+  margin-left: 8px
 }
 .font_blue{
-    color: #3FB2E5;
+  color: #3FB2E5;
 }
 
 .nomargin{
-    margin: 0px;
+  margin: 0px;
 }
 .border_bottom{
-    border-bottom:  1px solid #ccc
+  border-bottom:  1px solid #ccc
 }
 .export-check{
-    overflow: auto;
-    height: 400px;
+  overflow: auto;
+  height: 400px;
 }
 .shake-input {
-    animation: shake-input 0.2s 4;
+  animation: shake-input 0.2s 4;
 }
 @keyframes shake-input {
-    0% {
-        transform: translateX(0);
-    }
-    30% {
-        transform: translateX(-2%);
-    }
-    100% {
-        transform: translateX(4%)
-    }
+  0% {
+      transform: translateX(0);
+  }
+  30% {
+      transform: translateX(-2%);
+  }
+  100% {
+      transform: translateX(4%)
+  }
 }
 /*占位底色*/
 .occupied {
-    background: #f1f1f1;
+  background: #f1f1f1;
 }
 /*书签批注*/
 .annotate-color-1::before{
-    color: #E2F2C5 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color: #E2F2C5 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-2::before{
-    color: #F9E2CF !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color: #F9E2CF !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-3::before{
-    color:#F2EFD9 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#F2EFD9 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-4::before{
-    color:#F5D1DA !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#F5D1DA !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-5::before{
-    color:#E3E3E3 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#E3E3E3 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-6::before{
-    color:#B6F3F2 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#B6F3F2 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-7::before{
-    color:#ECE0F5 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#ECE0F5 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .z-index-3000 {
-    z-index: 3000;
+  z-index: 3000;
 }
 .progress-bar {
-    position: relative;
-    width: 100%;
+  position: relative;
+  width: 100%;
 }
 .progress-move {
-    position: absolute;
-    left: 0;
-    top: 0;
-    z-index: 999;
-    width: 200%;
-    height: 100%;
-    border-radius: .25rem;
-    animation: progressMove 20s linear infinite;
+  position: absolute;
+  left: 0;
+  top: 0;
+  z-index: 999;
+  width: 200%;
+  height: 100%;
+  border-radius: .25rem;
+  animation: progressMove 20s linear infinite;
 }
 @keyframes progressMove {
-    from {
-        transform: translateX(0);
-    }
-    to {
-        transform: translateX(-280px);
-    }
+  from {
+      transform: translateX(0);
+  }
+  to {
+      transform: translateX(-280px);
+  }
 }
 .progress-cover {
-    position: absolute;
-    left: 0;
-    top: 0;
-    z-index: 1000;
-    width: 100%;
-    height: 100%;
-    background-color: #e9ecef;
+  position: absolute;
+  left: 0;
+  top: 0;
+  z-index: 1000;
+  width: 100%;
+  height: 100%;
+  background-color: #e9ecef;
 }
 .progress-cover-move {
-    animation: progressCoverMove 40s linear;
-    animation-fill-mode: forwards;
+  animation: progressCoverMove 40s linear;
+  animation-fill-mode: forwards;
 }
 @keyframes progressCoverMove {
-    0% {
-        transform: translateX(0);
-    }
-    20% {
-        transform: translateX(330px);
-    }
-    40% {
-        transform: translateX(380px);
-    }
-    100% {
-        transform: translateX(420px);
-    }
+  0% {
+      transform: translateX(0);
+  }
+  20% {
+      transform: translateX(330px);
+  }
+  40% {
+      transform: translateX(380px);
+  }
+  100% {
+      transform: translateX(420px);
+  }
 }
 .text-ellipsis {
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
 }
 .border-radius {
-    border-radius: .2rem !important;
+  border-radius: .2rem !important;
 }
 .pm-i {
-    width: 18px;
+  width: 18px;
 }
 .calcbase-btn {
-    width: 24px;
-    padding-left: 0;
-    padding-right: 0;
+  width: 24px;
+  padding-left: 0;
+  padding-right: 0;
 }
 .over-height {
-    max-width: 550px !important;
+  max-width: 550px !important;
 }
 .hide-area {
-    display: none;
+  display: none;
 }
 .middle-modal-width {
-    max-width: 650px;
+  max-width: 650px;
 }
 .middle-modal-height {
-    height: 500px;
+  height: 500px;
 }
 
 @media screen and (max-width: 1366px), (max-height: 768px) {
-    .middle-modal-width {
-        max-width: 500px;
-    }
-    .middle-modal-height {
-        height: 350px;
-    }
+  .middle-modal-width {
+      max-width: 500px;
+  }
+  .middle-modal-height {
+      height: 350px;
+  }
 }
 .textarea-inherit {
-    width: 100%;
-    overflow: auto;
-    word-break: break-all;
+  width: 100%;
+  overflow: auto;
+  word-break: break-all;
 }
 /* 初始样式,防止projspread初始化完后背景从白突然变灰 */
 .poj-list {
-    height: 1000px; 
-    background: #f7f7f9;
+  height: 1000px; 
+  background: #f7f7f9;
+}
+.default-cursor {
+  cursor: default !important;
+}
+
+.unit_price_header{
+padding-top:6px;
+margin-left: 50px;
+margin-right: 100px !important;
 }

+ 33 - 12
web/building_saas/css/main.css

@@ -57,8 +57,11 @@ height: calc(1.5em + .5rem + 2px);
 font-size: .875rem;
 }
 .btn-xs{
-  padding:0rem .5rem;
-  font-size:.875rem;
+padding:0rem .5rem;
+font-size:.875rem;
+}
+.table-sc thead tr{
+background-color: #f1f1f1
 }
 /*自定义css*/
 .login-body,.login-html{
@@ -766,34 +769,52 @@ background:#999;
 color:#fff;
 border-radius: 30px
 }
-.book-list li .avatar.bg-1{
+.avatar-list {
+padding:0;
+margin:0 0 0 5px;
+list-style: none;
+}
+.avatar-list li{
+  float: left;
+  margin-right: 5px;
+}
+.avatar-list li .avatar{
+height: 24px;
+line-height: 24px;
+width:24px;
+border-radius: 20px;
+color:#fff;
+font-size: 10px;
+text-align: center;
+}
+.book-list li .avatar.bg-1,.avatar-list li .avatar.bg-1{
 background:rgb(16,109,156)
 }
-.book-list li .avatar.bg-2{
+.book-list li .avatar.bg-2,.avatar-list li .avatar.bg-2{
 background:rgb(90,146,173)
 }
-.book-list li .avatar.bg-3{
+.book-list li .avatar.bg-3,.avatar-list li .avatar.bg-3{
 background:rgb(150,164,139)
 }
-.book-list li .avatar.bg-4{
+.book-list li .avatar.bg-4,.avatar-list li .avatar.bg-4{
 background:rgb(216,202,175)
 }
-.book-list li .avatar.bg-5{
+.book-list li .avatar.bg-5,.avatar-list li .avatar.bg-5{
 background:rgb(201,192,211)
 }
-.book-list li .avatar.bg-6{
+.book-list li .avatar.bg-6,.avatar-list li .avatar.bg-6{
 background:rgb(61,89,171)
 }
-.book-list li .avatar.bg-7{
+.book-list li .avatar.bg-7,.avatar-list li .avatar.bg-7{
 background:rgb(243,203,211)
 }
-.book-list li .avatar.bg-8{
+.book-list li .avatar.bg-8,.avatar-list li .avatar.bg-8{
 background:rgb(181,196,177)
 }
-.book-list li .avatar.bg-9{
+.book-list li .avatar.bg-9,.avatar-list li .avatar.bg-9{
 background:rgb(150,84,84)
 }
-.book-list li .avatar.bg-0{
+.book-list li .avatar.bg-0,.avatar-list li .avatar.bg-0{
 background:rgb(191,191,191)
 }
 .book-list li .book-body{

+ 11 - 1
web/building_saas/css/style.css

@@ -1,4 +1,6 @@
-/*
+
+*/
+/*
 01 -  Global styles
 */
 html,
@@ -101,6 +103,14 @@ background-color: #f5f5f5;
 color: #000000;
 font-size: 18px;
 }
+.btn-danger{
+  background: #ff6501;
+  border-color: #ff6501;
+}
+.btn-danger:hover{
+  background-color: #d95e0e;
+  border-color: #d95e0e;
+}
 /*02 Header*/
 div#header-sticky-wrapper {
 position: absolute;

+ 3 - 1
web/building_saas/glj/html/project_glj.html

@@ -7,7 +7,9 @@
     <div class="form-inline py-1">
         <label class="mx-2">使用单价文件:<span id="current-name"></span>(<label class="a_color" id="pop-used-list" data-original-title="" title="">与<span id="used-project-count">0</span>个单位工程同步</label>)
             <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#change-unitFile"><i class="fa fa-exchange"></i> 选择其他</a>
-            <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#unitFile-save-as"><i class="fa fa-files-o"></i> 另存单独用</a></label>
+            <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#unitFile-save-as"><i class="fa fa-files-o"></i> 另存单独用</a>
+            <a class="btn btn-sm ml-1" id="editUnitFile" href="/unitPrice/index/123" target="_"> 编辑单价文件</a>
+        </label>
         <select class="form-control form-control-sm" style="width: auto; font-size: .875rem ;color: #007bff" id="adjustType">
             <option value="priceInfo" selected>造价信息差额调整法</option>
             <option value="priceCoe">价格指数调整法</option>

+ 4 - 4
web/building_saas/main/js/models/ration_glj.js

@@ -517,9 +517,8 @@ let ration_glj = {
         ration_glj.prototype.getGLJDataPaging = function (condition, cb) {
             gljOprObj.loadingPagination = true;
             const property = projectObj.project.projectInfo.property;
-            const engineerID = property.engineering_id;
             const actionType = $('#actionType').val();
-            CommonAjax.post('/rationGlj/getGLJDataPaging', {engineerID, condition}, function (data) {
+            CommonAjax.post('/rationGlj/getGLJDataPaging', { condition }, function (data) {
                 gljOprObj.curPageTotal = data.total;
                 data.complementaryGLJs.forEach(glj => {
                     glj.isComplementary = true;
@@ -529,7 +528,7 @@ let ration_glj = {
                     : 'complementaryGLJs';
                 const newData = data[gljType];
                 // 添加组成物,类型为主材时,需要排除自身
-                if (actionType === 'addMix' && projectGljObject.selectedProjectGLJ.type === gljType.MAIN_MATERIAL) {
+                if ((actionType === 'addMix' || actionType === 'unitPriceAddMix') && projectGljObject.selectedProjectGLJ.type === gljType.MAIN_MATERIAL) {
                     const pIndex = gljOprObj.getIndex(projectGljObject.selectedProjectGLJ, gljKeyArray);
                     const delIndex = newData.findIndex(item => gljOprObj.getIndex(item, gljLibKeyArray) === pIndex);
                     if (!~delIndex) {
@@ -559,7 +558,8 @@ let ration_glj = {
                 // 设置人材机类型名称
                 gljOprObj.setTypeName(gljOprObj.distTypeTree.comboDatas, newData);
                 if (data.priceProperties && data.priceProperties.length > 0) {
-                    let tmp = _.find(data.priceProperties, {region: property.region, taxModel: parseInt(property.taxType)});
+                    // region: region目前跟单位工程绑定,这里暂时固定写死commonConstants.DEFAULT_REGION,以后应改成跟建设项目绑定的region,
+                    let tmp = _.find(data.priceProperties, {region: commonConstants.DEFAULT_REGION, taxModel: parseInt(property.taxType)});
                     if (tmp) {
                         let dataCode = tmp.price.dataCode;
                         let allData = data.stdGLJ.concat(data.complementaryGLJs);

+ 36 - 29
web/building_saas/main/js/views/glj_view.js

@@ -341,7 +341,8 @@ var gljOprObj = {
     },
     loadPageData: function (sheet, reset, index) {
         let condition = this.getPagingCondition(false, reset, false, index);
-        projectObj.project.ration_glj.getGLJDataPaging(condition, function (data) {
+        let getPagingFun =  typeof unitPriceObj != 'undefined' ? unitPriceObj.getGLJDataPaging:projectObj.project.ration_glj.getGLJDataPaging;
+        getPagingFun(condition, function (data) {
             sheetCommonObj.appendData(sheet, condition.index, 0, gljOprObj.gljLibSheetSetting, data);
             gljOprObj.initSelection({row: gljOprObj.gljLibSheet.getActiveRowIndex()});
         });
@@ -358,6 +359,7 @@ var gljOprObj = {
         style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
         style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
         let colorOpts = optionsOprObj.getOption(optionsOprObj.optionsTypes.COLOROPTS);
+        if(!colorOpts) colorOpts = {SELECTED:{backColor:"#FFFACD"}};
         let selectedColor = colorOpts.SELECTED.backColor,
             recColor =  rcolor?rcolor:'White';
         style.backColor = selected ? selectedColor : recColor;
@@ -390,35 +392,38 @@ var gljOprObj = {
         }
     },
     onCheckBoxClick: function (sender, args) {
+      if(typeof projectObj != 'undefined'){  
         let selected = projectObj.project.mainTree.selected;
         if(selected.sourceType == ModuleNames.ration_glj){//选中的是工料机时不可编辑
             return ;
         }
-        let checkboxValue = args.sheet.getCell(args.row, args.col).value();
-        if(args.sheetName == 'rationInstallSheet' && checkboxValue){
-            return;
-        }
         if(gljOprObj.isInstallationNode(selected)==true || OVER_HEIGHT.isOverHeight(selected)){
-            return;
-        }
-        let newval = checkboxValue? 0:1;
-        args.sheet.getCell(args.row, args.col).value(newval);
-        if (args.sheetName == 'ration_glj') {
-            gljOprObj.updateIsEstimate(args,newval);
-        }  else if (args.sheetName == 'quantity_detail') {
-            projectObj.project.quantity_detail.isSummationUpdate(args, gljOprObj.detailData, newval);
-        } else if (args.sheetName == 'glj_lib') {
-            if (gljOprObj.gljLibSheetSetting.header[args.col].readOnly) {
-                args.sheet.getCell(args.row, args.col).value(checkboxValue);
-                return;
-            }
-            if(gljOprObj.gljLibSheetSetting.header[args.col].dataCode === 'select'){
-                gljOprObj.setGLJSelection(args, newval);
-            }
-        }else if(args.sheetName == 'rationInstallSheet'){
-            args.newValue = newval;
-            installationFeeObj.onRationInstallValueChange(sender,args);
+          return;
         }
+      }
+
+      let checkboxValue = args.sheet.getCell(args.row, args.col).value();
+      if(args.sheetName == 'rationInstallSheet' && checkboxValue){
+          return;
+      }
+      let newval = checkboxValue? 0:1;
+      args.sheet.getCell(args.row, args.col).value(newval);
+      if (args.sheetName == 'ration_glj') {
+          gljOprObj.updateIsEstimate(args,newval);
+      }  else if (args.sheetName == 'quantity_detail') {
+          projectObj.project.quantity_detail.isSummationUpdate(args, gljOprObj.detailData, newval);
+      } else if (args.sheetName == 'glj_lib') {
+          if (gljOprObj.gljLibSheetSetting.header[args.col].readOnly) {
+              args.sheet.getCell(args.row, args.col).value(checkboxValue);
+              return;
+          }
+          if(gljOprObj.gljLibSheetSetting.header[args.col].dataCode === 'select'){
+              gljOprObj.setGLJSelection(args, newval);
+          }
+      }else if(args.sheetName == 'rationInstallSheet'){
+          args.newValue = newval;
+          installationFeeObj.onRationInstallValueChange(sender,args);
+      }
     },
     onCellDoubleClick:function (sender, args) {
         // 含组成物的材料市场价改为只读,改为双击无反应不提示
@@ -1183,7 +1188,7 @@ var gljOprObj = {
                 unit: selected.unit,
                 gljType: selected.type
             };
-        } else if (actionType === 'addMix') {
+        } else if (actionType === 'addMix'|| actionType === 'unitPriceAddMix') {
             condition.queryExtend = projectGljObject.getQueryExtForMixRatio();
         }
         if (init) {
@@ -1222,7 +1227,7 @@ var gljOprObj = {
         }
         if ($('#actionType').val() == 'replace' || $('#actionType').val() == 'm_replace') {
             me.filterLibGLJByType();
-        }else if($('#actionType').val() == 'addMix'){
+        }else if($('#actionType').val() == 'addMix' || $('#actionType').val() == 'unitPriceAddMix'){
             projectGljObject.filterLibGLJForMixRatio();
         }
         //文本筛选
@@ -1245,7 +1250,7 @@ var gljOprObj = {
         }
     },
     setGLJSelection: function (args, newVal) {
-        if ($('#actionType').val() == 'add' || $('#actionType').val() == 'insert'|| $('#actionType').val() == 'addMix') {
+        if ($('#actionType').val() == 'add' || $('#actionType').val() == 'insert'|| $('#actionType').val() == 'addMix'|| $('#actionType').val() == 'unitPriceAddMix') {
             this.addGLJsSelection(args, newVal);
         } else {
             this.replaceGLJSelection(args, newVal);
@@ -1754,7 +1759,7 @@ $(function () {
             selected,
             connect_key;
         const actionType = $('#actionType').val();
-        const addActions = ['add', 'insert', 'addMix'];
+        const addActions = ['add', 'insert', 'addMix','unitPriceAddMix'];
         const replaceActions = ['m_replace', 'replace'];
         if (addActions.includes(actionType)) {//插入,添加,添加组成物(项目人材机页面)
             gljOprObj.GLJSelection = [];
@@ -1778,7 +1783,7 @@ $(function () {
             gljOprObj.gljLibSheet.setActiveCell(index, 0);
             gljOprObj.initSelection({row: index});
             gljOprObj.gljLibSpresd.focus(true);
-        } else if (actionType === 'add' || actionType === 'addMix') {
+        } else if (actionType === 'add' || actionType === 'addMix'|| actionType === 'unitPriceAddMix') {
             gljOprObj.locateZTree(null);
             sheetCommonObj.appendData(gljOprObj.gljLibSheet, 0, 0, gljOprObj.gljLibSheetSetting, gljOprObj.AllRecode);
             gljOprObj.gljLibSheet.showRow(0, GC.Spread.Sheets.VerticalPosition.top);
@@ -1824,6 +1829,8 @@ $(function () {
             gljOprObj.doMReplaceGLJ();
         }else if($('#actionType').val() == 'addMix'){
             projectGljObject.addMixRatio();
+        }else if($('#actionType').val() == 'unitPriceAddMix'){
+          unitPriceObj.addMixRatio();
         }
         $("#glj_tree_div").modal('hide');
     })

+ 3 - 2
web/building_saas/main/js/views/glj_view_contextMenu.js

@@ -435,7 +435,7 @@ var gljContextMenu = {
     }
 }
 
-function getGLJData(actionType) {
+function getGLJData(actionType,getLibFunc) {
     $('#actionType').val(actionType);
     // 清除选中人材机缓存数据
     gljOprObj.GLJSelection = [];
@@ -450,7 +450,8 @@ function getGLJData(actionType) {
     const reset = true;
     const condition = gljOprObj.getPagingCondition(init, reset, location, 0);
     console.time('getGLJData');
-    projectObj.project.ration_glj.getGLJDataPaging(condition, function (result) {
+    if(!getLibFunc)getLibFunc = projectObj.project.ration_glj.getGLJDataPaging;
+    getLibFunc(condition, function (result) {
         gljOprObj.initClassTree('std', gljOprObj.treeData.std);
         $('#modalCon').width($(window).width()*0.5);
         $("input[name='glj']").get(0).checked=true;

+ 6 - 6
web/building_saas/main/js/views/options_view.js

@@ -10,7 +10,7 @@ let optionsOprObj = {
         let me = this;
         me.options = projectOptins;
         let gOpts = me.options[me.optionsTypes.GENERALOPTS];
-        if(isDef(gOpts)){
+        if(gljUtil.isDef(gOpts)){
             for(let attr in gOpts){
                 me[attr].prop('checked', gOpts[attr]);
             }
@@ -24,19 +24,19 @@ let optionsOprObj = {
     },
     //更新optionsOprObj对象options数据
     updateOptions: function (options, updateObj) {
-        if(isDef(options[updateObj.type])){
+        if(gljUtil.isDef(options[updateObj.type])){
             options[updateObj.type][updateObj.opt] = updateObj.value;
         }
     },
     getOptsByType: function (options, type) {
-        return isDef(options[type]) ? options[type] : null;
+        return gljUtil.isDef(options[type]) ? options[type] : null;
     },
     getOption: function (type, optionName) {
-        if(!isDef(optionName)){
-            return isDef(this.options[type]) ? this.options[type] : null;
+        if(!gljUtil.isDef(optionName)){
+            return this.options&&gljUtil.isDef(this.options[type]) ? this.options[type] : null;
         }
         else {
-            return isDef(this.options[type][optionName])
+            return this.options&&gljUtil.isDef(this.options[type][optionName])
                 ? this.options[type][optionName]
                     : optionName === this.optionsTypes.GENERALOPTS
                     ? true

+ 90 - 47
web/building_saas/main/js/views/project_glj_view.js

@@ -135,6 +135,7 @@ let projectGljObject={
         $("#current-name").text(me.usedUnitPriceInfo.name);
         let usedCount = me.usedTenderList.length <= 0 ? 1 : me.usedTenderList.length;
         $("#used-project-count").text(usedCount);
+        $("#editUnitFile").attr("href",`/unitPrice/index/${me.usedUnitPriceInfo.id}`)
     },
     getUsedTenderInfo:function() {
         return "人材机单价的变化,将自动影响以下单位工程造价:<br>"+projectGljObject.usedTenderList.join("<br>");
@@ -177,7 +178,7 @@ let projectGljObject={
             });
         }
     },
-    addMixRatio:function () {
+    addMixRatio:async function () {
         let me = this, projectGLJ = projectObj.project.projectGLJ;
         let tdatas = me.mixRatioData;
         if(me.subList.length > 0) tdatas = me.subList;
@@ -186,15 +187,85 @@ let projectGljObject={
             let t_index = gljOprObj.GLJSelection.indexOf(m_key);
             t_index != -1?gljOprObj.GLJSelection.splice(t_index,1):'';
         }
-        projectGLJ.addMixRatio(gljOprObj.GLJSelection,function (mixRatios) {
-            me.showMixRatioData();//这里添加的组成物的消耗量默认都是0,所以对父工料机的价格不会有影响,不用触发计算
-            projectGLJ.loadData(function () {
-                me.showProjectGljData();
-                gljOprObj.showRationGLJSheetData();
-                me.onUnitFileChange(me.selectedProjectGLJ);
-            });
+        await me.addMixRatioFromLib(gljOprObj.GLJSelection,()=>{
+          me.showMixRatioData();//这里添加的组成物的消耗量默认都是0,所以对父工料机的价格不会有影响,不用触发计算
+          projectGLJ.loadData(function () {
+              me.showProjectGljData();
+              gljOprObj.showRationGLJSheetData();
+              me.onUnitFileChange(me.selectedProjectGLJ);
+          });
         });
     },
+    addMixRatioFromLib:async function(selections,callback){
+      let gljList = [],allGLJ = gljOprObj.AllRecode;
+      let url = "/glj/add-ratio";
+      let result = null;
+      if(selections.length == 0) {
+          return;
+      }
+      try {
+        for(let glj of allGLJ){
+        let i_key = gljUtil.getIndex(glj,gljLibKeyArray);
+        if(_.includes(selections,i_key)){
+            let pglj = {
+                glj_id: glj.ID,
+                name: glj.name,
+                code: glj.code,
+                original_code: glj.code,
+                unit: glj.unit,
+                specs: glj.specs,
+                base_price: glj.basePrice,
+                market_price: glj.basePrice,
+                shortName: glj.shortName,
+                type: glj.gljType,
+                model:glj.model,
+                adjCoe: glj.adjCoe,
+                from:'std',
+                repositoryId:glj.repositoryId,
+                materialType:glj.materialType,
+                materialCoe:glj.materialCoe,
+                grossWeightCoe:glj.grossWeightCoe,
+                purchaseStorageRate:glj.purchaseStorageRate,
+                offSiteTransportLossRate:glj.offSiteTransportLossRate,
+                handlingLossRate:glj.handlingLossRate
+            };
+            if(typeof projectObj !== 'undefined')  pglj.project_id = projectObj.project.ID();
+            if (glj.hasOwnProperty("compilationId")) {
+                pglj.from = "cpt";
+                if (glj.code.indexOf('-') != -1) {//这条工料机是用户通过修改名称、规格、型号等保存到补充工料机库的
+                    pglj.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
+                }
+            }
+            gljList.push(pglj);
+          }
+        }
+        gljList = _.sortByAll(gljList, ['type', 'code']);
+        if(gljList.length == 0) return;
+        let parentInfo ={};
+        if(typeof unitPriceObj !== 'undefined'){
+          url = "/unitPrice/addMixRatio";
+          pdata = unitPriceObj.getSelectedUnitPrice();
+          parentInfo = {
+            unit_price_file_id:pdata.unit_price_file_id,
+            connect_key:gljUtil.getIndex(pdata)
+          };
+        }else{
+          parentInfo = {
+            unit_price_file_id:projectObj.project.property.unitPriceFile.id,
+            connect_key:gljOprObj.getIndex(projectGljObject.selectedProjectGLJ,gljKeyArray)
+          };
+        }
+
+        $.bootstrapLoading.start();
+        result= await ajaxPost(url,{gljList:gljList,parentInfo:parentInfo})
+        if(callback) callback(result);
+      } catch (error) {
+        alert(error);
+        console.log(error);
+      }
+      $.bootstrapLoading.end();
+      return result;
+    },
     showMixRatioData:function () {
         let me = this,gljId = null,gljType = null;
         if(!me.projectGljSpread) return;
@@ -313,9 +384,11 @@ let projectGljObject={
           let style = gljOprObj.getSelStyle(true,{});
           me.projectGljSheet.setStyle(newSel.row, -1, style);
           let orow = oldSel.row==''||oldSel.row==-1?0:oldSel.row;
-          let tstyle = gljOprObj.getSelStyle(false,{},me.projectGljSheetData[orow].bgColour);
-          me.projectGljSheet.setStyle(orow, -1, tstyle);
-          me.projectGljRowChang();
+          if(me.projectGljSheetData[orow]){
+            let tstyle = gljOprObj.getSelStyle(false,{},me.projectGljSheetData[orow].bgColour);
+            me.projectGljSheet.setStyle(orow, -1, tstyle);
+            me.projectGljRowChang();
+          }
         }else{
           me.projectGljSheet.repaint();
         }
@@ -376,14 +449,15 @@ let projectGljObject={
         let me = projectGljObject;
         let canChange = true;
         let changeInfo=[];
-        if (info.action == GC.Spread.Sheets.RangeChangedAction.clear) {
+       /*  if (info.action == GC.Spread.Sheets.RangeChangedAction.clear) {
             info.newValue = 0;
             me.onMixRatioValueChange(sender,info);
             info.sheet.getCell(info.row, info.col).text(0);
             return ;
-        }
+        } */
         for(let c of info.changedCells){
             let value = info.sheet.getCell(c.row, c.col).text();
+            if(_.isEmpty(value)) value = 0;
             if (!me.checkData(c.col,me.mixRatioSetting,value)) {
                 alert('输入的数据类型不对,请重新输入!');
                 canChange = false;
@@ -392,12 +466,8 @@ let projectGljObject={
                 changeInfo.push({row:c.row,col:c.col,value:value});
             }
         }
-        if(canChange == false){//数据类型不对
-            me.showMixRatioData();
-        }
-        if(changeInfo.length > 0){
-            me.batchUpdateConsumption(changeInfo);
-        }
+        if(canChange == false) me.showMixRatioData(); //数据类型不对
+        if(changeInfo.length > 0) me.batchUpdateConsumption(changeInfo);
     },
     batchUpdateProjectGLJ:function(changeInfo,sheetName){
         let projectGLJ = projectObj.project.projectGLJ;
@@ -822,18 +892,7 @@ let projectGljObject={
             return false;
         }
         value = scMathUtil.roundToString(value,getDecimal("glj.quantity"));
-        let [parentMarketPrice, parentBasePrice] = me.getCompositionSumPrice('modify', row, value);
-        let updateData ={id: recode.mix_ratio_id, field: 'mix_ratio.' + dataCode, value: value, market_price: parentMarketPrice, base_price: parentBasePrice};
-        let prow = parentSheet.getActiveRowIndex();//取父机械或组成物的下标
-        let prowData = parentSheet.name() == 'projectGljSheet'?me.projectGljSheetData[prow]:me.materialTree.items[prow].data;
-        composition.updateConsumption(updateData,recode,prowData.id,function (sid) {
-            /* if(parentSheet.name() == 'projectGljSheet'){ 之前是单行刷新,父工料机与组成物对应的工料机分开刷,发现这样比整个刷新慢所以先整个刷新,当以后数据量大的时候再测试
-             me.refreshProjectGljRowByID(sid);
-             }*/
-            projectObj.project.projectGLJ.calcQuantity();
-            me.refreshParentData(prow,prowData.id,sid);
-            me.onUnitFileChange(recode);
-        });
+        me.batchUpdateConsumption([{row:row,col:col,value}]);
     },
     refreshParentData:function (row,pid,sid) {
         let me = this;
@@ -1035,22 +1094,6 @@ let projectGljObject={
     },
     checkData : function(col,setting, value) {
         return sheetCommonObj.checkData(col,setting, value);
-     /*   let result = true;
-        let validator = setting.header[col].validator !== undefined ? setting.header[col].validator : null;
-        if (validator === null) {
-            return result;
-        }
-        switch (validator) {
-            case 'number':
-                let regular = /^\d+(\.\d+)?$/;
-                result = regular.test(value);
-                break;
-            case 'boolean':
-                let booleanValue = [true, false];
-                result = booleanValue.indexOf(value) >= 0;
-                break;
-        }
-        return result;*/
     },
     getProjectGLJSelected:function () {
         let me = projectGljObject;

+ 42 - 26
web/building_saas/main/js/views/project_info.js

@@ -3,20 +3,42 @@
  */
 
 var projectInfoObj = {
-    getSubShareInfo: function (proj) {
-        const { allowCopy, allowCooperate } = proj.shareState;
-        let str = '(';
-        if (allowCopy) {
-            str += '可拷贝 ';
+    // 头部同时编辑的用户头像
+    handleAvatarList: function (users) {
+        const opener = projectObj.project.projectInfo.opener;
+        if (opener) {
+            users = users.filter(user => user._id !== opener._id);
         }
-        if (allowCooperate) {
-            if (allowCopy) {
-                str += ' ';
-            }
-            str += '可编辑';
+        const avatarListHtml = users.reduce((acc, user) => {
+            const avatarSpan = SHARE_TO.getAvatarHTML(user.mobile, user.real_name);
+            const li = 
+                `<li data-toggle="tooltip" data-placement="bottom" title="${user.real_name}" data-original-title="${user.real_name}">
+                    ${avatarSpan}
+                </li>`
+            return acc += li;
+        }, '');
+        $('#avatar-list').html(avatarListHtml);
+        $('#avatar-list [data-toggle="tooltip"]').tooltip(); 
+    },
+    getReceiveInfo: function (projectReadOnly, projectCooperate, owner) {
+        if (!projectReadOnly && !projectCooperate) {
+            return '';
+        }
+        const action = projectCooperate ? '可编辑' : '只能查看';
+        const ownerName = owner && owner.real_name || '';
+        return `
+            <span class="pl-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="来自 ${ownerName} 的分享">
+                <a href="javascript:;" class="btn btn-xs btn-primary default-cursor"><i class="fa fa-share-alt"></i> ${action}</a>
+            </span>`;
+    },
+    getShareButton: function (projectReadOnly, projectCooperate, shareTip) {
+        if (projectReadOnly || projectCooperate) {
+            return '';
         }
-        str += ')';
-        return str === '()' ? '' : str;
+        return `
+            <span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${shareTip}">
+                <a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a>
+            </span>`;
     },
     getFullPathHtml: function (proj) {
         let fullPath = [], i, pm = '<span class="text-truncate"><a href="/pm">项目管理</a></span>', angleRight = '<span class="text-truncate"><i class="fa fa-angle-right fa-fw"></i></span>';
@@ -25,26 +47,22 @@ var projectInfoObj = {
             let engName = pathArr[pathArr.length - 2] || '',
                 projectName = pathArr[pathArr.length - 3] || '',
                 folderName = pathArr[pathArr.length - 4] || '';
-            const subShareInfo = projectInfoObj.getSubShareInfo(proj);
-            const receiveTip = `<span class="alert alert-success py-0 px-2 m-0" id="share-info"><i class="fa fa-share-alt"></i>来自 ${proj.owner && proj.owner.real_name || ''} 的分享${subShareInfo}</span>`;
+            const receiveInfo = this.getReceiveInfo(projectReadOnly, projectCooperate, proj.owner);
+            const shareButton = this.getShareButton(projectReadOnly, projectCooperate, proj.shareTip);
             let newHtml = `   <span data-toggle="tooltip" data-placement="bottom" data-original-title="${folderName}"><i class="fa fa-folder-open-o"></i>...</span>
                 <span class="text-muted px-1">\</span>
                 <span data-toggle="tooltip" data-placement="bottom" data-original-title="${projectName}"><i class="fa fa-cubes"></i>...</span>
                 <span class="text-muted px-1">\</span>
                 <span data-toggle="tooltip" data-placement="bottom" data-original-title="${engName}"><i class="fa fa-cube"></i>...</span>
                 <span class="text-muted px-1">\</span>
-                 <span><i class="fa fa-sticky-note-o"></i></span>
+                <span><i class="fa fa-sticky-note-o"></i></span>
                 <span class="text-truncate"  data-toggle="tooltip" data-placement="bottom" data-original-title="${proj.name}">&nbsp;${proj.name}</span>
-                ${projectReadOnly || projectCooperate ? receiveTip : `<span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${proj.shareTip}"><a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a></span>`}
+                ${receiveInfo}${shareButton}
+                <span>
+                    <ul class="avatar-list mb-0" id="avatar-list">
+                    </ul>
+                </span>
                 `;
-            /* ${projectReadOnly ?
-                    '<span data-toggle="tooltip" data-placement="bottom" data-original-title="当前的工程状态为“只读”,如果要进行编辑,请在项目管理-分享界面,使用“拷贝工程”功能。">(只读)</span>'
-                    : ''}
-                ${projectCooperate ?
-                    '<span data-toggle="tooltip" data-placement="bottom" data-original-title="当前的工程状态为“协作”,可直接编辑分享人的原始数据。">(协作)</span>'
-                    : ''}
-                
-                ${projectReadOnly || projectCooperate ? '' : `<span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${proj.shareTip}"><a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a></span>`} */
             fullPath.push(newHtml);
             fullPath.push(`<input id="rootProjectName" value="${projectName}" type="hidden">`);
 
@@ -62,8 +80,6 @@ var projectInfoObj = {
             billsQuanDecimal.datas = data.property.billsQuantityDecimal || [billsDecimalView.angleDecimal];
             basicInfoView.orgDatas = data.property.basicInformation ? basicInfoView.toViewDatas(data.property.basicInformation) : [];
             projFeatureView.orgDatas = data.property.projectFeature ? projFeatureView.toViewDatas(data.property.projectFeature) : [];
-            console.log(`me.orgDatas`);
-            console.log(projFeatureView.orgDatas);
             $('#fullpath').html(this.getFullPathHtml(data));
             // 分享给
             $('#init-share').click(() => SHARE_TO.initModal(projectObj.project.ID()));

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

@@ -1011,7 +1011,8 @@ var projectObj = {
                 //if(!projectReadOnly){
                     that.loadMainSpreadContextMenu();
                 //}
-                socketObject.connect();//连接socket服务器
+                
+                socketObject.connect('main', { projectReadOnly: !!projectReadOnly, user: { ...projectObj.project.projectInfo.opener } });//连接socket服务器
                 let endTime = +new Date();
                 console.log(`其它时间——${endTime - endShowTime}`);
                 console.log("加载完成-----"+endTime);

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

@@ -248,7 +248,7 @@ const importView = (() => {
         let curEngineering = tbcObj.engineeringList.find(data => query.engineeringName === data.lib.name &&
             query.feeStandard === data.lib.feeName);
         return {
-            region: '全省',   //地区
+            region: commonConstants.DEFAULT_REGION,   //地区
             valuationType: tbcObj.valuationType,    //计价方式
             valuation: tbcObj.valuation.id, //计价规则
             valuationName: tbcObj.valuation.name,

+ 3 - 2
web/building_saas/pm/js/pm_newMain.js

@@ -3821,7 +3821,7 @@ function AddTender() {
             };
         let selectedItem = projTreeObj.tree.selected;
         //地区
-        let region = $('#regionDiv').find('select').val() || '全省';
+        let region = $('#regionDiv').find('select').val() || commonConstants.DEFAULT_REGION;
         let tenderInfo = {
             gljAdjustType: 'priceInfo', //默认为造价信息差额调整法
             valuation: valuation,
@@ -4529,7 +4529,8 @@ function set_file_table(target, poj_tenders, fileList, type){
         let fileId = type === fileType.unitPriceFile ? fileList[i].id : fileList[i].ID;
         let usedObj = getUsedObj(poj_tenders, fileId, type);
         let usedHtml = usedObj.usedCount > 0 ?  '<td class="text-center"><a href="javascript:void(0);">' + usedObj.usedCount + '</a></td>' : '<td class="text-center">' + usedObj.usedCount + '</td>';
-        let hoverHtml = '<p style="display: none; height: 14px;"><a class="btn btn-sm" href="javascript:void(0);" data-toggle="modal" data-target="#del-wj">删除</a><a class="btn btn-sm" href="javascript:void(0);">重命名</a></p></div>';
+        let unitPriceEditHtml = type === fileType.unitPriceFile? `<a class="btn btn-sm" href="/unitPrice/index/${fileId}" target="_">编辑</a>`:"";
+        let hoverHtml = `<p style="display: none; height: 14px;"><a class="btn btn-sm" href="javascript:void(0);" data-toggle="modal" data-target="#del-wj">删除</a><a class="btn btn-sm" href="javascript:void(0);">重命名</a>${unitPriceEditHtml}</p></div>`;
         let renHtml = '<div class="input-group" style="display: none;">'
             + '<input class="form-control form-control-sm" value="">'
 

+ 1 - 0
web/common/components/share/index.js

@@ -433,5 +433,6 @@ const SHARE_TO = (() => {
     return {
         initModal,
         handleEventListener,
+        getAvatarHTML,
     }
 })();

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

@@ -8,7 +8,7 @@
       <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
       <link rel="stylesheet" href="/web/building_saas/css/all.css">
       <link rel="stylesheet" href="/web/building_saas/css/style.css">
-      <title>大司空云计价 - 正版软件永久免费</title>
+      <title>大司空云计价 - 远程办公,用免费正版市政计价软件</title>
       <link rel="shortcut icon" href="/web/building_saas/css/favicon.ico" />
    </head>
    <body>
@@ -20,10 +20,10 @@
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarLexar" aria-controls="navbarLexar" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
                </button>
-               <div class="collapse navbar-collapse" id="navbarLexar">      
+               <div class="collapse navbar-collapse" id="navbarLexar">
                <ul class="navbar-nav ml-auto">
                      <li class="nav-item active">
-                        <a class="btn btn-primary my-3" href="/login">登录软件</a>
+                        <a class="btn btn-danger my-3" href="/login">登录软件</a>
                      </li>
                   </ul>
                </div>
@@ -35,10 +35,10 @@
          <div class="container text-center">
             <h1 class="text-white mb-4 f-50 ">大司空市政云计价,正版软件永久免费</h1>
             <p class="lead text-white mb-5">跨平台,打开浏览器即可使用,全新在线计价体验。
-            <!-- <div class="btn_hero mb-5">
-               <a href="#" class="btn btn-primary btn-round mr-2">GET THIS APP</a>
-               <a href="#" class="btn btn-outline-primary btn-round ml-2">TRY IT FOR FREE</a>
-            </div> -->
+            <div class="btn_hero">
+               <a href="/login" class="btn btn-danger mr-2">登录软件</a>
+               <a href="https://smartcost.com.cn/contact2" target="_blank" class="btn btn-primary ml-2">联系客服</a>
+            </div>
             <div class="home-desk mt-5">
                <div class="container pt-5">
                   <img src="/web/users/images/home-desk.png" alt="" class="img-fluid mx-auto  d-block">
@@ -351,7 +351,7 @@
                      <div class="footer_bottom text-center">
                         Copyright@珠海纵横创新软件有限公司 all rights reserved <a href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a>
                      </div>
-                     <div style="position:fixed;right:-100px"><script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1278513319'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "v1.cnzz.com/stat.php%3Fid%3D1278513319%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));</script></div>
+                     <div><script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1278513319'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "v1.cnzz.com/stat.php%3Fid%3D1278513319%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));</script></div>
                   </div>
                </div>
             </div>