Explorar o código

项目管理页面,当树结构在其他页面被修改后,应提示刷新

vian %!s(int64=5) %!d(string=hai) anos
pai
achega
9a0f1e96b5

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

@@ -269,6 +269,7 @@ class UserModel extends BaseModel {
      * 一个账号不允许多处同时在线,每次登陆都会更新session和数据库的token,每个请求都会比对session和数据库的token
      * @param {String} id - 用户ID
      * @param {String} token - 登陆生成的token
+     * @return {Boolean}
      */
     async checkToken(id, token) {
         const user = await this.findDataById(id, '-_id token');

+ 7 - 0
public/web/id_tree.js

@@ -864,6 +864,13 @@ var idTree = {
                         console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
                         return false;
                     }
+                     // nextSibling跟parent children的下一节点对应不上
+                    if (nodeIdx !== -1 && 
+                        (nodeIdx === sameDepthNodes.length - 1 && nextIdx !== -1) || 
+                        (nodeIdx !== sameDepthNodes.length - 1 && nodeIdx + 1 !== nextIdx)) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} nextSibling与树显示的下一节点对应不上`);
+                        return false;
+                    }
                     if (node.children.length) {
                         let v = isValid(node.children);
                         if (!v) {

+ 9 - 0
public/web/socket/connection.js

@@ -91,6 +91,15 @@ socketObject = {
         socket.on('fileDataChange', function (data) {//收到单价文件、费率文件内容修改、文件切换、另存(暂时能共用,以后有需要可分离)推送消息
             if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
         });
+        // 项目管理树数据发生变化,提示刷新
+        socket.on('pmTreeChange', function ({ expandState, selection }) {
+            const isActive = $('#tab_pm_all').hasClass('active');
+            if (isActive) {
+                $("#message").html(`树结构发生变化,请<a href="javascript:void(0);" id="load-data">点击刷新列表</a>`);
+                $('#load-data').on('click', () => projTreeObj.handleNotifyClick(expandState, selection));
+                $("#notify").show();
+            }
+        });
     },
     getFeeRateRoomID: function () {
         return projectObj.project.FeeRate.getActivateFeeRateFileID();

+ 5 - 0
socket.js

@@ -123,6 +123,11 @@ socketIO.on('connection', function(socket) {
         }
     });
 
+    // 项目管理树结构变化
+    socket.on('pmTreeChange', function ({ userID, compilationID, expandState, selection }) {
+        socket.broadcast.to(`${userID}@${compilationID}`).emit('pmTreeChange', { expandState, selection });
+    });
+
     socket.on('disconnect', function () {
         // 由于用户可以重复打开项目,因此不做唯一性处理,只删除一个数据,不删除所有的同用户数据
         if (curProjectID && userCache[curProjectID]) {

+ 6 - 0
web/building_saas/pm/js/pm_gc.js

@@ -974,8 +974,10 @@ function e_recFiles(btn){
             let findData = type === fileType.unitPriceFile ? {id: recObjs[i].id} : {ID: recObjs[i].id};
             updateDatas.push(getUpdateObj(type, findData, {deleteInfo: null, name: delPostFix(recObjs[i].name) + decDate}));
         }
+        let isRecoverProj = false;
         //恢复建设项目
         if(updateDatas.length > 0 && deleted(selected)){
+            isRecoverProj = true;
             updateDatas.push(getUpdateObj(projectType.project, {ID: selected.data.ID}, {deleteInfo: null, name: delPostFix(selected.data.name) + decDate}));
         }
         updateDatas = deWeightName(updateDatas);
@@ -994,6 +996,9 @@ function e_recFiles(btn){
                     gcTreeObj.refreshNodeData(selected);
                 }
             }
+            if (isRecoverProj) {
+                projTreeObj.emitTreeChange();
+            }
         }
     });
 }
@@ -1055,6 +1060,7 @@ function e_recProj(btn){
             }
             v_removeNode(selected);
             v_refreshNode(selected, true);
+            projTreeObj.emitTreeChange();
         }
     });
 }

+ 64 - 18
web/building_saas/pm/js/pm_newMain.js

@@ -560,6 +560,7 @@ const projTreeObj = {
             }
             projTreeObj.moveTo(selected, null, parent, next, null, action);
             $.bootstrapLoading.end();
+            projTreeObj.emitTreeChange();
         });
     },
     //升级后选中节点的后兄弟节点不成为其子节点,因为有层级类型限制(相当于选中节点移动到父项后成为其后兄弟)
@@ -1693,9 +1694,41 @@ const projTreeObj = {
         let result =await ajaxGet("/pm/api/getUploadToken");
         $("#confirm-import").show();
         projTreeObj.uptoken=result.uptoken;
+    },
+    // 树数据发生变化,触发推送
+    emitTreeChange: function () {
+        const compilationID = compilationData._id;
+        // 获取当前树节点展开状态和焦点行
+        const isActive = $('#tab_pm_all').hasClass('active');
+        const expandState = isActive ? this.tree.getExpState(this.tree.items) : null;
+        const selection = isActive && this.tree.selected ? { row: this.tree.selected.serialNo(), rowCount: 1 } : null;
+        socket.emit('pmTreeChange', { userID, compilationID, expandState, selection });
+    },
+    initTree: function (refresh = false, callback, expandCallback) {
+        if(gcTreeObj.workBook){
+            gcTreeObj.workBook.destroy();
+            gcTreeObj.workBook = null;
+        }
+        gcTreeObj.tree = null;
+        init(refresh, callback, expandCallback);
+    },
+    handleNotifyClick: function(expandState, selection) {
+        $('#notify').hide();
+        const callback = () => {
+            const sheet = this.workBook.getSheet(0);
+            if (selection && this.tree.items[selection.row]) {
+                this.initSelection(selection, { row : 0, rowCount: 1 }, sheet);
+                const col = sheet.getActiveColumnIndex();
+                sheet.setSelection(selection.row, col, 1, 1);
+            }
+        };
+        const expandCallback = () => {
+            if (expandState) {
+                this.tree.setExpandedByState(this.tree.items, expandState);
+            }
+        }
+        this.initTree(true, callback, expandCallback);
     }
-
-
 };
 // 新建项目必填项提示框设置“ 比如:注:为响应重庆地区指标采集标准数据要求,以上工程信息及特征必填项为必填项,请正确填写。”
 function setupRequiredWarn(compilation) {
@@ -1736,15 +1769,8 @@ $(document).ready(function() {
 
     });
 
-    init();
-    $('#tab_pm_all').on('show.bs.tab', function () {
-        if(gcTreeObj.workBook){
-            gcTreeObj.workBook.destroy();
-            gcTreeObj.workBook = null;
-        }
-        gcTreeObj.tree = null;
-        init();
-    });
+    projTreeObj.initTree();
+    $('#tab_pm_all').on('show.bs.tab', () => projTreeObj.initTree(true));
 
     //单价、费率文件删除确认
     $('#fileDelConfirm').click(function () {
@@ -2522,6 +2548,7 @@ $(document).ready(function() {
                 setTimeout(function () {
                     STATE.deleting = false;
                 }, 500);
+                projTreeObj.emitTreeChange();
             }, function () {
                 $.bootstrapLoading.end();
                 setTimeout(function () {
@@ -2573,6 +2600,7 @@ $(document).ready(function() {
             select.data.name = newName;
             let sheet = projTreeObj.workBook.getActiveSheet();
             projTreeObj.setCellValue({ row: sheet.getActiveRowIndex(), col: 0 }, select, sheet, projTreeObj.setting);
+            projTreeObj.emitTreeChange();
         });
     });
 
@@ -2705,7 +2733,6 @@ function changeFeeRate(engLib) {
 
 //根据文件类型筛选新的基本信息数据
 function getNeedfulBasicInfo(info, fileKind) {
-    debugger;
     /* let strMap = {
         1: 'tender',    //投标
         2: 'bid',       //招标
@@ -2885,7 +2912,7 @@ function initNodesVisibility(nodes, visible) {
     });
 }
 
-function initProjects(callback) {
+function initProjects(callback, expandCallback) {
     GetAllProjectData(function (datas) {
         //设置工程专业
         for (let data of datas) {
@@ -2904,9 +2931,12 @@ function initProjects(callback) {
             sheet.name('projectSheet');
             sheetCommonObj.spreadDefaultStyle(projTreeObj.workBook);
             projTreeObj.sumEngineeringCost();
-            initNodesVisibility(projTreeObj.tree.items, false);
+            if (expandCallback) {
+                expandCallback();
+            } else {
+                initNodesVisibility(projTreeObj.tree.items, false);
+            }
             projTreeObj.showTreeData(projTreeObj.tree.items, projTreeObj.setting, sheet);
-            const rows = projTreeObj.tree.items.map((item, index) => index);
             //初始选择
             const initSel = sheet.getSelections()[0] ? sheet.getSelections()[0] : { row: 0, rowCount: 1 };
             projTreeObj.initSelection(initSel, null, sheet);
@@ -2924,7 +2954,7 @@ function initProjects(callback) {
 /**
  * 初始化数据
  */
-async function init(refresh = false) {
+async function init(refresh = false, callback, expandCallback) {
     console.log('init');
     //init spread and pmTree
     try {
@@ -2935,7 +2965,12 @@ async function init(refresh = false) {
             $("#progress_modal_body").text('首次加载例题,请稍候……');
             await ajaxPost('/pm/api/prepareInitialData', {user_id: userID});
             await importProcessChecking(null, null, () => {
-                initProjects(() => $.bootstrapLoading.progressEnd());
+                initProjects(() => {
+                    $.bootstrapLoading.progressEnd();
+                    if (callback) {
+                        callback();
+                    }
+                }, expandCallback);
             }, true);
         } else {
             await importProcessChecking(null, ({ content }) => {
@@ -2943,7 +2978,12 @@ async function init(refresh = false) {
                 $("#progress_modal_body").text(content);
             }, () => {
                 $.bootstrapLoading.start();
-                initProjects(() => $.bootstrapLoading.end());
+                initProjects(() => {
+                    $.bootstrapLoading.end();
+                    if (callback) {
+                        callback();
+                    }
+                }, expandCallback);
             }, true);
         }
         engineering = engineeringList !== null && engineeringList !== undefined ? JSON.parse(engineeringList) : [];
@@ -3011,6 +3051,7 @@ function AddProject() {
         setTimeout(function () {
             STATE.addingProject = false;
         }, 500);
+        projTreeObj.emitTreeChange();
     };
     let errCB = function () {
         $.bootstrapLoading.end();
@@ -3684,6 +3725,7 @@ function AddEngineering() {
         setTimeout(function () {
             STATE.addingEng = false;
         }, 500);
+        projTreeObj.emitTreeChange();
     };
     let errCB = function () {
         $.bootstrapLoading.end();
@@ -3814,6 +3856,7 @@ function AddTender() {
                 setTimeout(function () {
                     STATE.addingTender = false;
                 }, 500);
+                projTreeObj.emitTreeChange();
             },
             errCB = function () {
                 $.bootstrapLoading.end();
@@ -3922,6 +3965,7 @@ function AddFolder() {
         setTimeout(function () {
             STATE.addingFolder = false;
         }, 500);
+        projTreeObj.emitTreeChange();
     };
     let errCB = function () {
         $.bootstrapLoading.end();
@@ -4756,6 +4800,7 @@ function handleProjectAfterChecking(projectData) {
     const rootData = projectData.find(item => item.projType === projectType.project);
     const sorted = commonUtil.getSortedTreeData(rootData.ParentID, projectData);
     importView.doAfterImport(sorted);
+    projTreeObj.emitTreeChange();
 }
 // 导入检查完成时,对新增单位工程的处理
 function handleTenderAfterChecking(projectData, orgTender) {
@@ -4768,6 +4813,7 @@ function handleTenderAfterChecking(projectData, orgTender) {
     const newNode = projTreeObj.insert(tenderData, parent, next);
     const refreshNodes = projTreeObj.calEngineeringCost(newNode);
     projTreeObj.refreshNodeData(refreshNodes);
+    projTreeObj.emitTreeChange();
 }
 
 async function importProcessChecking(key, processingFunc = null, completeFunc = null, immediately = false) {

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

@@ -937,7 +937,7 @@ const pmShare = (function () {
             $.bootstrapLoading.progressStart('拷贝项目', true);
             $("#progress_modal_body").text('正在拷贝项目,请稍候……');
             await ajaxPost('/pm/api/copyProjects', {projectMap: copyMap, user_id: userID, tenderCount: 1});
-            importProcessChecking();
+            importProcessChecking(null, null, projTreeObj.emitTreeChange);
         } catch (err) {
             alert(err);
         }

+ 31 - 0
web/building_saas/pm/js/pm_tree.js

@@ -433,6 +433,13 @@ const pmTree = {
                             console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
                             return false;
                         }
+                        // nextSibling跟parent children的下一节点对应不上
+                        if (nodeIdx !== -1 && 
+                            (nodeIdx === parent.children.length - 1 && nextIdx !== -1) || 
+                            (nodeIdx !== parent.children.length - 1 && nodeIdx + 1 !== nextIdx)) {
+                            console.log(`${node.serialNo() + 1}:${node.data.name} nextSibling与树显示的下一节点对应不上`);
+                            return false;
+                        }
                         if (node.nextSibling && node.parent !== node.nextSibling.parent) {
                             console.log(`${node.serialNo() + 1}:${node.data.name} 与兄弟节点 ${node.nextSibling.serialNo() + 1}:${node.nextSibling.data.name} 父节点不同`);
                             return false;
@@ -448,6 +455,30 @@ const pmTree = {
                 }
             };
 
+            Tree.prototype.getExpState = function (nodes) {
+                let sessionExpanded = [];
+                function getStat(items){
+                    for(let item of items){
+                        sessionExpanded.push(item.expanded ? 1 : 0);
+                    }
+                }
+                getStat(nodes);
+                let expState = sessionExpanded.join('');
+                return expState;
+            };
+    
+            //节点根据展开收起列表'010101'展开收起
+            Tree.prototype.setExpandedByState = function (nodes, expState) {
+                let expStateArr = expState.split('');
+                for(let i = 0; i < nodes.length; i++){
+                    let expanded = expStateArr[i] == 1 ? true : false;
+                    if(nodes[i].expanded === expanded){
+                        continue;
+                    }
+                    nodes[i].setExpanded(expanded);
+                }
+            };
+
             Tree.prototype.setNodesExpanded = function (nodes, sheet) {
                 TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
                     nodes.forEach(node => {