Browse Source

分享实时状态相关(推送)

vian 5 years ago
parent
commit
bf6cc94852

+ 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, {});

+ 79 - 69
public/web/socket/connection.js

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

+ 62 - 5
socket.js

@@ -9,16 +9,60 @@ 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);
+    }
+    console.log(`rst`);
+    console.log(rst);
+    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 +104,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));
     });
-});
+});

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

@@ -472,4 +472,7 @@ input.text-right{
 .poj-list {
     height: 1000px; 
     background: #f7f7f9;
+}
+.default-cursor {
+    cursor: default !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{

+ 42 - 24
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">`);
 

+ 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 - 0
web/common/components/share/index.js

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