Преглед на файлове

新的分享给页面功能

vian преди 5 години
родител
ревизия
f7435fe392

+ 238 - 218
modules/pm/controllers/pm_controller.js

@@ -35,15 +35,16 @@ const rationLibModel = mongoose.model('std_ration_lib_map');
 const multiparty = require("multiparty");
 let logger = require("../../../logs/log_helper").logger;
 let rp = require('request-promise');
+const commonUtil = require('../../../public/common_util');
 //统一回调函数
-let callback = function(req, res, err, message, data){
-    res.json({error: err, message: message, data: data});
+let callback = function (req, res, err, message, data) {
+    res.json({ error: err, message: message, data: data });
 };
 
 
 module.exports = {
     checkRight: function (req, res) {
-        if(typeof req.body.data === 'object'){
+        if (typeof req.body.data === 'object') {
             req.body.data = JSON.stringify(req.body.data);
         }
         let data = JSON.parse(req.body.data);
@@ -61,7 +62,7 @@ module.exports = {
              */
             let shareInfo = null;
             //判断是否是打开分享的项目,分享项目shareInfo不为null
-            if(userId !== result.userID){
+            if (userId !== result.userID) {
                 shareInfo = await pm_facade.getShareInfo(userId, result.ID);
             }
             if ((userId === result.userID || shareInfo) && result._doc.projType === projType.tender) {
@@ -74,8 +75,8 @@ module.exports = {
             callback(false);
         });
     },
-    getProjects: async function(req, res){
-        await ProjectsData.getUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, function(err, message, projects){
+    getProjects: async function (req, res) {
+        await ProjectsData.getUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, function (err, message, projects) {
             console.log(err);
             if (projects) {
                 callback(req, res, err, message, projects);
@@ -86,7 +87,7 @@ module.exports = {
     },
     updateProjects: async function (req, res) {
         let data = JSON.parse(req.body.data);
-        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name,req.session.sessionCompilation.overWriteUrl, data.updateData, function (err, message, data) {
+        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name, req.session.sessionCompilation.overWriteUrl, data.updateData, function (err, message, data) {
             if (err === 0) {
                 callback(req, res, err, message, data);
             } else {
@@ -95,7 +96,7 @@ module.exports = {
         });
     },
     // CSL, 2017-12-14 该方法用于项目属性:提交保存混合型数据,这些数据来自不同的表,包括projects.property、ration、bills、labour_coes.
-    updateMixDatas: async function(req, res){
+    updateMixDatas: async function (req, res) {
         let datas = JSON.parse(req.body.data).mixDataArr;
         let functions = [];
 
@@ -105,7 +106,7 @@ module.exports = {
             }
         };
 
-        function updateLC(){
+        function updateLC() {
             return function (cb) {
                 datas.labourCoes.updateData.projectID = datas.projectID;
                 labourCoe.save(datas.labourCoes.updateData, cb);
@@ -113,12 +114,12 @@ module.exports = {
         };
 
         // 项目属性
-        if (Object.keys(datas.properties).length > 0){
+        if (Object.keys(datas.properties).length > 0) {
             //基本信息特殊处理,更新建设项目
-            if(datas.properties['property.basicInformation']){
+            if (datas.properties['property.basicInformation']) {
                 let constructionProject = await pm_facade.getConstructionProject(datas.projectID);
-                if(constructionProject){
-                    functions.push(updateFunc(projectModel, {ID: constructionProject.ID}, {'property.basicInformation': datas.properties['property.basicInformation']}));
+                if (constructionProject) {
+                    functions.push(updateFunc(projectModel, { ID: constructionProject.ID }, { 'property.basicInformation': datas.properties['property.basicInformation'] }));
                 }
                 delete datas.properties['property.basicInformation'];
             }
@@ -126,79 +127,79 @@ module.exports = {
             if (datas.properties['property.compilationIllustrationProject']) {
                 let constructionProject = await pm_facade.getConstructionProject(datas.projectID);
                 if (constructionProject) {
-                    functions.push(updateFunc(projectModel, {ID: constructionProject.ID}, {'property.compilationIllustration': datas.properties['property.compilationIllustrationProject']}));
+                    functions.push(updateFunc(projectModel, { ID: constructionProject.ID }, { 'property.compilationIllustration': datas.properties['property.compilationIllustrationProject'] }));
                 }
                 delete datas.properties['property.compilationIllustrationProject'];
             }
-            functions.push(updateFunc(projectModel, {ID: datas.projectID}, datas.properties));
+            functions.push(updateFunc(projectModel, { ID: datas.projectID }, datas.properties));
         };
 
         //选项
-        if(datas.options && datas.options.updateData){
-            functions.push(updateFunc(optionModel, {user_id: req.session.sessionUser.id, compilation_id: req.session.sessionCompilation._id}, {'options.GENERALOPTS': datas.options.updateData}));
+        if (datas.options && datas.options.updateData) {
+            functions.push(updateFunc(optionModel, { user_id: req.session.sessionUser.id, compilation_id: req.session.sessionCompilation._id }, { 'options.GENERALOPTS': datas.options.updateData }));
         }
 
         // 人工系数
-        if (datas.labourCoes&&datas.labourCoes.updateData){
+        if (datas.labourCoes && datas.labourCoes.updateData) {
             functions.push(updateLC());
         };
 
         // 清单:每文档doc只存储一条清单,每条清单都必须定位一次文档,无法合并处理
-        if (datas.bills && datas.bills.length > 0){
-            for (let bill of datas.bills){
-                functions.push(updateFunc(billsModel, {projectID: datas.projectID, ID: bill.ID, deleteInfo: null}, bill));
+        if (datas.bills && datas.bills.length > 0) {
+            for (let bill of datas.bills) {
+                functions.push(updateFunc(billsModel, { projectID: datas.projectID, ID: bill.ID, deleteInfo: null }, bill));
             };
         };
 
         // 定额:每文档doc只存储一条定额,每条定额都必须定位一次文档,无法合并处理
-        if (datas.rations&& datas.rations.length > 0){
-            for (let ration of datas.rations){
-                functions.push(updateFunc(rationsModel, {projectID: datas.projectID, ID: ration.ID, deleteInfo: null}, ration));
+        if (datas.rations && datas.rations.length > 0) {
+            for (let ration of datas.rations) {
+                functions.push(updateFunc(rationsModel, { projectID: datas.projectID, ID: ration.ID, deleteInfo: null }, ration));
             };
         };
 
-        asyncTool.parallel(functions, function(err, result){
+        asyncTool.parallel(functions, function (err, result) {
             {
                 if (!err) {
-                    res.json({error: 0, message: err, data: result});
+                    res.json({ error: 0, message: err, data: result });
                 } else {
-                    res.json({error: 1, message: err, data: null});
+                    res.json({ error: 1, message: err, data: null });
                 }
             }
         });
     },
-    updateFiles: async function(req, res){
+    updateFiles: async function (req, res) {
         let data = JSON.parse(req.body.data);
         let updateDatas = data.updateDatas;
         await ProjectsData.udpateUserFiles(req.session.sessionUser.id, updateDatas, function (err, message, data) {
             callback(req, res, err, message, data);
         });
     },
-    defaultSettings: async function(req, res){
-        try{
+    defaultSettings: async function (req, res) {
+        try {
             let data = JSON.parse(req.body.data);
             let projectID = data.projectID;
             let defaultSettingSc = await ProjectsData.defaultSettings(req.session.sessionUser.id, req.session.sessionCompilation._id, projectID);
-            if(!defaultSettingSc){
+            if (!defaultSettingSc) {
                 throw '恢复失败';
             }
-            res.json({error: 0, message: '恢复成功', data: null});
+            res.json({ error: 0, message: '恢复成功', data: null });
         }
-        catch(error){
+        catch (error) {
             console.log(error);
-            res.json({error: 1, message: error, data: null});
-        }
-    },
- /*   copyProjects: function (req, res) {
-        let data = JSON.parse(req.body.data);
-        ProjectsData.copyUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, data.updateData, function (err, message, data) {
-            if (err === 0) {
-                callback(req, res, err, message, data);
-            } else {
-                callback(req, res, err, message, null);
-            }
-        });
-    },*/
+            res.json({ error: 1, message: error, data: null });
+        }
+    },
+    /*   copyProjects: function (req, res) {
+           let data = JSON.parse(req.body.data);
+           ProjectsData.copyUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, data.updateData, function (err, message, data) {
+               if (err === 0) {
+                   callback(req, res, err, message, data);
+               } else {
+                   callback(req, res, err, message, null);
+               }
+           });
+       },*/
     rename: function (req, res) {
         let data = JSON.parse(req.body.data);
         ProjectsData.rename(req.session.sessionUser.id, req.session.sessionCompilation._id, data, function (err, message) {
@@ -206,8 +207,8 @@ module.exports = {
         });
     },
     //project getData接口
-    getData: function(projectID, callback) {
-        projectModel.findOne({$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], ID: projectID}, '-_id').then(async function (project) {
+    getData: function (projectID, callback) {
+        projectModel.findOne({ $or: [{ deleteInfo: null }, { 'deleteInfo.deleted': false }], ID: projectID }, '-_id').then(async function (project) {
             if (!project) {
                 callback('', consts.projectConst.PROJECT_INFO, {});
             }
@@ -217,7 +218,7 @@ module.exports = {
             //查找定额库的定额库编码
             if (Array.isArray(engineeringInfo.ration_lib)) {
                 let rationLibIDs = engineeringInfo.ration_lib.map(data => data.id);
-                let rationLibs = await rationLibModel.find({ID: {$in: rationLibIDs}}, 'ID libCode');
+                let rationLibs = await rationLibModel.find({ ID: { $in: rationLibIDs } }, 'ID libCode');
                 for (let rationLib of rationLibs) {
                     let lib = engineeringInfo.ration_lib.find(data => data.id == rationLib.ID);
                     lib.libCode = rationLib.libCode;
@@ -225,10 +226,10 @@ module.exports = {
             }
             let projInfo = project._doc;
             if (engineeringInfo !== null) {
-                if(engineeringInfo.billsGuidance_lib){
-                    for(let billsGuidanceLib of engineeringInfo.billsGuidance_lib){
-                        let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ID: billsGuidanceLib.id});
-                        if(stdBillsGuidanceLib){
+                if (engineeringInfo.billsGuidance_lib) {
+                    for (let billsGuidanceLib of engineeringInfo.billsGuidance_lib) {
+                        let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ ID: billsGuidanceLib.id });
+                        if (stdBillsGuidanceLib) {
                             billsGuidanceLib.type = stdBillsGuidanceLib.type ? stdBillsGuidanceLib.type : 1;
                         }
                     }
@@ -251,10 +252,10 @@ module.exports = {
             callback(err, consts.projectConst.PROJECT_INFO, {});
         });
     },
-    getProject: function(req, res){
+    getProject: function (req, res) {
         let data = JSON.parse(req.body.data);
         let projectID = data.proj_id;
-        ProjectsData.getUserProject(req.session.sessionUser.id, data.proj_id, async function(err, message, data){
+        ProjectsData.getUserProject(req.session.sessionUser.id, data.proj_id, async function (err, message, data) {
             if (err === 0) {
                 let engineeringLibModel = new EngineeringLibModel();
                 let engineeringInfo = data !== null && data.property.engineering_id !== undefined ?
@@ -262,10 +263,10 @@ module.exports = {
                 let strData = JSON.stringify(data);
                 let projInfo = JSON.parse(strData);
                 if (engineeringInfo !== null) {
-                    if(engineeringInfo.billsGuidance_lib){
-                        for(let billsGuidanceLib of engineeringInfo.billsGuidance_lib){
-                            let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ID: billsGuidanceLib.id});
-                            if(stdBillsGuidanceLib){
+                    if (engineeringInfo.billsGuidance_lib) {
+                        for (let billsGuidanceLib of engineeringInfo.billsGuidance_lib) {
+                            let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ ID: billsGuidanceLib.id });
+                            if (stdBillsGuidanceLib) {
                                 billsGuidanceLib.type = stdBillsGuidanceLib.type ? stdBillsGuidanceLib.type : 1;
                             }
                         }
@@ -302,12 +303,12 @@ module.exports = {
         });
     },
     // 项目管理首页
-    index: async function(request, response) {
+    index: async function (request, response) {
         // TODO 上线后删除,处理旧的分享数据
         await pm_facade.prepareShareList();
         // 获取编办信息
         let sessionCompilation = request.session.sessionCompilation;
-        if (sessionCompilation === undefined ||sessionCompilation ===null) {
+        if (sessionCompilation === undefined || sessionCompilation === null) {
             return response.redirect('/logout');
         }
         let compilationModel = new CompilationModel();
@@ -330,13 +331,13 @@ module.exports = {
             sessionCompilation.ration_valuation : [];
         rationValuation = await engineeringLibModel.getLib(rationValuation);
         let absoluteUrl = compilationData.overWriteUrl ? request.app.locals.rootDir + compilationData.overWriteUrl : request.app.locals.rootDir;
-        let overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilationData.overWriteUrl : null;
+        let overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile() ? compilationData.overWriteUrl : null;
         //欢迎页显示控制
-        let [isShow,context] = await pm_facade.getWelcomeInfo(sessionCompilation._id,request.session.sessionUser,request.session.compilationVersion.includes('免费'));
+        let [isShow, context] = await pm_facade.getWelcomeInfo(sessionCompilation._id, request.session.sessionUser, request.session.compilationVersion.includes('免费'));
         let renderData = {
             isFirst: isFirst,
-            isShow:isShow,
-            context:context,
+            isShow: isShow,
+            context: context,
             userAccount: request.session.userAccount,
             userID: request.session.sessionUser.id,
             compilationData: JSON.stringify(sessionCompilation),
@@ -346,13 +347,13 @@ module.exports = {
             engineeringList: JSON.stringify(engineering.List),
             compilationName: sessionCompilation.name,
             versionName: request.session.compilationVersion,
-            LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
+            LicenseKey: config.getLicenseKey(process.env.NODE_ENV)
         };
 
         response.render('building_saas/pm/html/project-management.html', renderData);
     },
     //第一次进入该费用定额时准备的初始数据
-    prepareInitialData: async function(request, response) {
+    prepareInitialData: async function (request, response) {
         try {
             const data = {
                 userID: request.session.sessionUser.id,
@@ -361,19 +362,19 @@ module.exports = {
             };
             await redirectToImportServer(data, 'prepareInitialData', request);
             callback(request, response, 0, 'success', null);
-        } catch(err) {
+        } catch (err) {
             console.log(err);
             callback(request, response, 1, err.toString(), null);
         }
     },
     // 获取单价文件列表
-    getUnitFileList: async function(request, response) {
+    getUnitFileList: async function (request, response) {
         let data = request.body.data;
         try {
             data = JSON.parse(data);
             let projectId = data.parentID !== undefined ? data.parentID : 0;
             if (isNaN(projectId) && projectId <= 0) {
-                throw {msg: 'id数据有误!', err: 1};
+                throw { msg: 'id数据有误!', err: 1 };
             }
             /*// 获取对应建设项目下所有的单位工程id
             let idList = await ProjectsData.getTenderByProjectId(projectId);
@@ -385,7 +386,7 @@ module.exports = {
             let unitPriceFileData = await unitPriceFileModel.getDataByRootProject(projectId);
 
             if (unitPriceFileData === null) {
-                throw {msg: '不存在对应单价文件', err: 0};
+                throw { msg: '不存在对应单价文件', err: 0 };
             }
 
             // 整理数据
@@ -405,17 +406,17 @@ module.exports = {
             callback(request, response, error.err, error.msg, responseData);
         }
     },
-    getFeeRateFileList:async function(request, response) {
+    getFeeRateFileList: async function (request, response) {
         let data = request.body.data;
         try {
             data = JSON.parse(data);
             let projectId = data.parentID !== undefined ? data.parentID : 0;
             if (isNaN(projectId) && projectId <= 0) {
-                throw {msg: 'id数据有误!', err: 1};
+                throw { msg: 'id数据有误!', err: 1 };
             }
             // 获取对应建设项目下所有的单位工程id
             let feeRateFileList = await fee_rate_facade.getFeeRatesByProject(projectId);
-            callback(request, response, 0, '',feeRateFileList );
+            callback(request, response, 0, '', feeRateFileList);
         } catch (error) {
             console.log(error);
             let responseData = error.err === 1 ? null : [];
@@ -423,95 +424,95 @@ module.exports = {
         }
     },
 
-    getGCDatas: async function(request, response) {
+    getGCDatas: async function (request, response) {
         let userID = request.session.sessionUser.id;
         let compilatoinId = request.session.sessionCompilation._id;
         let rst = [];
         let _projs = Object.create(null), _engs = Object.create(null), prefix = 'ID_';
-        try{
+        try {
             let gc_unitPriceFiles = await ProjectsData.getGCFiles(fileType.unitPriceFile, userID);
             let gc_feeRateFiles = await ProjectsData.getGCFiles(fileType.feeRateFile, userID);
             let gc_tenderFiles = await ProjectsData.getGCFiles(projType.tender, userID);
-            for(let i = 0, len = gc_unitPriceFiles.length; i < len; i++){
+            for (let i = 0, len = gc_unitPriceFiles.length; i < len; i++) {
                 let gc_uf = gc_unitPriceFiles[i];
                 let theProj = _projs[prefix + gc_uf.root_project_id] || null;
-                if(!theProj){
+                if (!theProj) {
                     let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_uf.root_project_id]);
-                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                    if (tempProj.length > 0 && tempProj[0].projType !== projType.folder) {
                         theProj = _projs[prefix + gc_uf.root_project_id] = tempProj[0]._doc;
                         buildProj(theProj);
                     }
                 }
-                if(theProj){
+                if (theProj) {
                     theProj.unitPriceFiles.push(gc_uf);
                 }
             }
-            for(let i = 0, len = gc_feeRateFiles.length; i < len; i++){
+            for (let i = 0, len = gc_feeRateFiles.length; i < len; i++) {
                 let gc_ff = gc_feeRateFiles[i];
                 let theProj = _projs[prefix + gc_ff.rootProjectID] || null;
-                if(!theProj){
+                if (!theProj) {
                     let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_ff.rootProjectID]);
-                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                    if (tempProj.length > 0 && tempProj[0].projType !== projType.folder) {
                         theProj = _projs[prefix + gc_ff.rootProjectID] = tempProj[0]._doc;
                         buildProj(theProj);
                     }
                 }
-                if(theProj) {
+                if (theProj) {
                     theProj.feeRateFiles.push(gc_ff);
                 }
             }
-            if(gc_tenderFiles.length > 0){
-                for(let i = 0, len = gc_tenderFiles.length; i < len; i++){
+            if (gc_tenderFiles.length > 0) {
+                for (let i = 0, len = gc_tenderFiles.length; i < len; i++) {
                     let gc_t = gc_tenderFiles[i];
                     let theEng = _engs[prefix + gc_t.ParentID] || null;
-                    if(!theEng){
+                    if (!theEng) {
                         let tempEngs = await ProjectsData.getProjectsByIds(userID, compilatoinId, [gc_t.ParentID]);
-                        if(tempEngs.length > 0 && tempEngs[0].projType === projType.engineering){
+                        if (tempEngs.length > 0 && tempEngs[0].projType === projType.engineering) {
                             theEng = _engs[prefix + gc_t.ParentID] = tempEngs[0]._doc;
                             theEng.children = [];
                         }
                     }
-                    if(theEng) {
+                    if (theEng) {
                         theEng.children.push(gc_t);
                         let theProj = _projs[prefix + theEng.ParentID] || null;
-                        if(!theProj){
+                        if (!theProj) {
                             let tempProj = await ProjectsData.getProjectsByIds(userID, compilatoinId, [theEng.ParentID]);
-                            if(tempProj.length > 0 && tempProj[0].projType === projType.project){
+                            if (tempProj.length > 0 && tempProj[0].projType === projType.project) {
                                 theProj = _projs[prefix + theEng.ParentID] = tempProj[0]._doc;
                                 buildProj(theProj);
                             }
                         }
-                        if(theProj) {
+                        if (theProj) {
                             let isExist = false;
-                            for(let j = 0, jLen = theProj.children.length; j < jLen; j++){
-                                if(theProj.children[j].ID === theEng.ID){
+                            for (let j = 0, jLen = theProj.children.length; j < jLen; j++) {
+                                if (theProj.children[j].ID === theEng.ID) {
                                     isExist = true;
                                     break;
                                 }
                             }
-                            if(!isExist){
+                            if (!isExist) {
                                 theProj.children.push(theEng);
                             }
                         }
                     }
                 }
             }
-            for(let i in _projs){
+            for (let i in _projs) {
                 rst.push(_projs[i]);
             }
-            function buildProj(proj){
+            function buildProj(proj) {
                 proj.children = [];
                 proj.unitPriceFiles = [];
                 proj.feeRateFiles = [];
             }
             callback(request, response, 0, 'success', rst);
         }
-        catch (error){
+        catch (error) {
             callback(request, response, true, error, null);
         }
     },
 
-    recGC: function(request, response){
+    recGC: function (request, response) {
         let userID = request.session.sessionUser.id,
             compilationId = request.session.sessionCompilation._id;
         let data = JSON.parse(request.body.data);
@@ -519,101 +520,108 @@ module.exports = {
         ProjectsData.recGC(userID, compilationId, nodes, function (err, msg, data) {
             callback(request, response, err, msg, data);
         });
-   },
+    },
 
-    delGC: async function(request, response){
+    delGC: async function (request, response) {
         let data = JSON.parse(request.body.data);
         let delDatas = data.delDatas;
         let bulkProjs = [], bulkUFs = [], bulkFFs = [];
-        try{
-            for(let data of delDatas){
-                if(data.updateType === 'Project'){
-                    bulkProjs.push({updateOne: {filter: {ID: data.ID}, update: {'deleteInfo.completeDeleted': true}}});
+        try {
+            for (let data of delDatas) {
+                if (data.updateType === 'Project') {
+                    bulkProjs.push({ updateOne: { filter: { ID: data.ID }, update: { 'deleteInfo.completeDeleted': true } } });
                 }
-                else if(data.updateType === fileType.unitPriceFile){
-                    bulkUFs.push({updateOne: {filter: {id: data.ID}, update: {'deleteInfo.completeDeleted': true}}});
+                else if (data.updateType === fileType.unitPriceFile) {
+                    bulkUFs.push({ updateOne: { filter: { id: data.ID }, update: { 'deleteInfo.completeDeleted': true } } });
                 }
-                else{
-                    bulkFFs.push({updateOne: {filter: {ID: data.ID}, update: {'deleteInfo.completeDeleted': true}}});
+                else {
+                    bulkFFs.push({ updateOne: { filter: { ID: data.ID }, update: { 'deleteInfo.completeDeleted': true } } });
                 }
             }
-            if(bulkProjs.length > 0){
+            if (bulkProjs.length > 0) {
                 await projectModel.bulkWrite(bulkProjs);
             }
-            if(bulkUFs.length > 0){
+            if (bulkUFs.length > 0) {
                 await unitPriceFileModel.bulkWrite(bulkUFs);
             }
-            if(bulkFFs.length > 0){
+            if (bulkFFs.length > 0) {
                 await feeRateFileModel.bulkWrite(bulkFFs);
             }
             callback(request, response, 0, 'success', null);
-        } catch(err){
+        } catch (err) {
             callback(request, response, 1, err, null);
         }
     },
-    moveProject:async function(req,res){
-        let result={
-            error:0
+    moveProject: async function (req, res) {
+        let result = {
+            error: 0
         };
         try {
             let data = req.body.data;
-            let rdata= await pm_facade.moveProject(data);
-            result.data= rdata;
-        }catch (err){
+            let rdata = await pm_facade.moveProject(data);
+            result.data = rdata;
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    copyProjects:async function (req, res) {
-        let result={
-            error:0
+    copyProjects: async function (req, res) {
+        let result = {
+            error: 0
         };
         try {
-            let data = {dataString:req.body.data,userID:req.session.sessionUser.id,compilationID:req.session.sessionCompilation._id};
-            result = await redirectToImportServer(data,"copyProject",req);
-        }catch (err){
+            let data = { dataString: req.body.data, userID: req.session.sessionUser.id, compilationID: req.session.sessionCompilation._id };
+            result = await redirectToImportServer(data, "copyProject", req);
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    projectShareInfo: async function(req, res){
-        try{
+    projectShareInfo: async function (req, res) {
+        try {
             const data = JSON.parse(req.body.data);
-            const shareList = await pm_facade.getShareList({projectID: data.projectID});
+            const shareList = await pm_facade.getShareList({ projectID: data.projectID });
             const shareInfoMap = await pm_facade.getShareInfoMap(null, shareList);
             const shareInfo = shareInfoMap[data.projectID] || [];
             //let shareInfo = await projectModel.findOne({ID: data.projectID, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}, '-_id shareInfo');
             callback(req, res, 0, 'success', shareInfo);
         }
-        catch (err){
+        catch (err) {
             callback(req, res, 1, err, null);
         }
     },
-    getRecentShareInfo: async function (req, res) {
+    getInitialShareData: async function (req, res) {
         try {
-            const { count } = JSON.parse(req.body.data);
+            const { count, projectID } = JSON.parse(req.body.data);
             const userID = req.session.sessionUser.id;
+            // 最近分享
             const recentUsers = await pm_facade.getRecentShareList(userID, count);
+            // 联系人
             const contacts = await userModelObj.getContacts(userID);
-            callback(req, res, 0, 'success', { recentUsers, contacts });
+            // 分享过的人
+            const sharedUsers = await pm_facade.getProjectShareList(projectID);
+            callback(req, res, 0, 'success', { recentUsers, contacts, sharedUsers });
         } catch (err) {
             console.log(err);
             callback(req, res, 1, err.message, null);
         }
     },
-    share: async function(req, res){
-        try{
+    share: async function (req, res) {
+        try {
             const data = JSON.parse(req.body.data);
-            const { type, shareData, projectID } = data;
+            const { type, shareData, projectID, count } = data;
             const userID = req.session.sessionUser.id;
             const shareDate = moment(Date.now()).format('YYYY-MM-DD HH:mm:ss');
             shareData.forEach(item => item.shareDate = shareDate);
             const task = [];
+            // 是否只是单纯的更新分享选项,如果不是,需要重新获取最近分享和联系人
+            let isSimpleUpdate = true;
             if (type === 'create') {
+                isSimpleUpdate = false;
                 // 生成分享记录
                 const docs = shareData.map(item => ({
                     ID: uuidV1(),
@@ -629,58 +637,70 @@ module.exports = {
                 // 分享即互相添加为联系人
                 task.push(userModelObj.addContact(docs[0].owner, docs[0].receiver));
             } else if (type === 'update') {
-                // 取消分享(项目管理界面可一次进行更新和取消)
+                // 取消分享(以前项目管理界面可一次进行更新和取消)
                 const cancelReceivers = shareData
                     .filter(item => item.isCancel)
                     .map(item => item.userID);
                 if (cancelReceivers.length) {
-                    task.push(pm_facade.deleteShareList({projectID, receiver: {$in: cancelReceivers}}));
+                    isSimpleUpdate = false;
+                    task.push(pm_facade.deleteShareList({ projectID, receiver: { $in: cancelReceivers } }));
                 }
                 // 更新分享选项
                 const updateData = shareData
                     .filter(item => !item.isCancel)
-                    .map(item => (
-                        {
+                    .map(item => {
+                        const update = { updateDate: shareDate };
+                        if (commonUtil.isDef(item.allowCopy)) {
+                            update.allowCopy = item.allowCopy;
+                        }
+                        if (commonUtil.isDef(item.allowCooperate)) {
+                            update.allowCooperate = item.allowCooperate;
+                        }
+                        return {
                             query: {
                                 projectID,
                                 receiver: item.userID
                             },
-                            update: {
-                                allowCopy: item.allowCopy,
-                                allowCooperate: item.allowCooperate,
-                                updateDate: shareDate
-                            }
-                        }
-                    ));
+                            update
+                        };
+                    });
                 if (updateData.length) {
                     task.push(pm_facade.updateShareList(updateData))
                 }
             } else { // 取消分享
                 const cancelReceivers = shareData.map(item => item.userID);
-                task.push(pm_facade.deleteShareList({projectID, receiver: {$in: cancelReceivers}}));
+                task.push(pm_facade.deleteShareList({ projectID, receiver: { $in: cancelReceivers } }));
             }
             await Promise.all(task);
-            const rstData = shareData.filter(item => !item.isCancel);
-            callback(req, res, 0, 'success', rstData);
+            const rstTask = [
+                pm_facade.getRecentShareList(userID, count),
+                userModelObj.getContacts(userID)
+            ]
+            let rst = null;
+            if (!isSimpleUpdate) {
+                const [recentUsers, contacts] = await Promise.all(rstTask);
+                rst = { recentUsers, contacts };
+            }
+            callback(req, res, 0, 'success', rst);
         }
-        catch (err){
+        catch (err) {
             callback(req, res, 1, err, null);
         }
     },
-    receiveProjects: async function(req, res) {
+    receiveProjects: async function (req, res) {
         try {
-            let rst = {grouped: [], ungrouped: [], summaryInfo: null};
+            let rst = { grouped: [], ungrouped: [], summaryInfo: null };
             let userID = req.session.sessionUser.id;
             const shareList = await pm_facade.getShareList({ receiver: userID });
             const receiveProjectIDs = shareList.map(item => item.projectID);
             const compilation = req.session.sessionCompilation._id;
-            const notDeleted = [{deleteInfo: null}, {'deleteInfo.deleted': false}];
-            const receiveProjects = await projectModel.find({ID: {$in: receiveProjectIDs}, compilation, $or: notDeleted}, '-_id').lean();
+            const notDeleted = [{ deleteInfo: null }, { 'deleteInfo.deleted': false }];
+            const receiveProjects = await projectModel.find({ ID: { $in: receiveProjectIDs }, compilation, $or: notDeleted }, '-_id').lean();
             //设置原项目用户信息
             const shareInfoMap = await pm_facade.getShareInfoMap(null, shareList);
-            if(receiveProjects.length > 0){
+            if (receiveProjects.length > 0) {
                 let orgUserIDs = [];
-                for(let proj of receiveProjects){
+                for (let proj of receiveProjects) {
                     orgUserIDs.push(proj.userID);
                     if (proj.projType === projType.tender) {
                         //设置工程专业
@@ -692,14 +712,14 @@ module.exports = {
                 }
                 orgUserIDs = Array.from(new Set(orgUserIDs));
                 let userObjIDs = [];
-                for(let uID of orgUserIDs){
+                for (let uID of orgUserIDs) {
                     userObjIDs.push(mongoose.Types.ObjectId(uID));
                 }
-                let orgUsersInfo = await userModel.find({_id: {$in : userObjIDs}});
+                let orgUsersInfo = await userModel.find({ _id: { $in: userObjIDs } });
                 //建设项目
                 let consProjIDs = [],
                     ungroupedTenders = [];
-                for(let proj of receiveProjects){
+                for (let proj of receiveProjects) {
                     // 设置分享信息
                     proj.shareInfo = shareInfoMap[proj.ID] || [];
                     if (proj.projType === projType.project) {
@@ -735,9 +755,9 @@ module.exports = {
                     }
                     //设置项目类型为来自别人分享
                     proj.shareType = 'receive';
-                    for(let userData of orgUsersInfo){
-                        if(proj.userID == userData._id.toString()){
-                            let userInfo = {name: userData.real_name, mobile: userData.mobile, company: userData.company, email: userData.email};
+                    for (let userData of orgUsersInfo) {
+                        if (proj.userID == userData._id.toString()) {
+                            let userInfo = { name: userData.real_name, mobile: userData.mobile, company: userData.company, email: userData.email };
                             proj.userInfo = userInfo;
                         }
                     }
@@ -745,17 +765,17 @@ module.exports = {
                 consProjIDs = Array.from(new Set(consProjIDs));
                 let summaryInfo = await pm_facade.getSummaryInfo(consProjIDs);
                 let tendersFeeInfo = await pm_facade.getTendersFeeInfo(ungroupedTenders);
-                rst.summaryInfo = {grouped: summaryInfo, ungrouped: tendersFeeInfo};
+                rst.summaryInfo = { grouped: summaryInfo, ungrouped: tendersFeeInfo };
             }
             callback(req, res, 0, 'success', rst);
         }
-        catch (err){
+        catch (err) {
             console.log(err);
             callback(req, res, 1, err, null);
         }
     },
     getProjectsByQuery: async function (req, res) {
-        try{
+        try {
             let data = JSON.parse(req.body.data);
             let compilation = req.session.sessionCompilation._id;
             let query = data.query;
@@ -764,107 +784,107 @@ module.exports = {
             let projects = await projectModel.find(query, options);
             callback(req, res, 0, 'success', projects);
         }
-        catch (err){
+        catch (err) {
             callback(req, res, 1, err, null);
         }
     },
-    getSummaryInfo: async function(req, res){
-        try{
+    getSummaryInfo: async function (req, res) {
+        try {
             let data = JSON.parse(req.body.data);
             let summaryInfo = await pm_facade.getSummaryInfo(data.projectIDs);
             callback(req, res, 0, 'success', summaryInfo);
         }
-        catch (err){
+        catch (err) {
             callback(req, res, 1, err, null);
         }
     },
-    changeFile:async function(req,res){
-        try{
+    changeFile: async function (req, res) {
+        try {
             let data = JSON.parse(req.body.data);
             console.log(data);
-            await pm_facade.changeFile(data.projects,data.user_id,data.fileID,data.name,data.from,data.type);
+            await pm_facade.changeFile(data.projects, data.user_id, data.fileID, data.name, data.from, data.type);
             callback(req, res, 0, 'success', []);
         }
-        catch (err){
+        catch (err) {
             console.log(err);
             callback(req, res, 1, err, null);
         }
     },
-    exportProject:async function(req,res){
-        let result={
-            error:0
+    exportProject: async function (req, res) {
+        let result = {
+            error: 0
         };
         try {
-            let data = {dataString:req.body.data,userID:req.session.sessionUser.id};
-            result =  await redirectToImportServer(data,"exportProject",req);
-        }catch (err){
+            let data = { dataString: req.body.data, userID: req.session.sessionUser.id };
+            result = await redirectToImportServer(data, "exportProject", req);
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
 
     },
-    importProject:async function(req,res){
+    importProject: async function (req, res) {
         let data = JSON.parse(req.body.data);
-        let result={
-            error:0
+        let result = {
+            error: 0
         };
         try {
             data.session = req.session;
-            result = await redirectToImportServer(data,"importProject",req);
-        }catch (err){
+            result = await redirectToImportServer(data, "importProject", req);
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    importChongqingProject:async function(req,res){
+    importChongqingProject: async function (req, res) {
         let data = JSON.parse(req.body.data);
-        let result={
-            error:0
+        let result = {
+            error: 0
         };
         try {
             data.session = req.session;
-            result = await redirectToImportServer(data,"importChongqingProject",req);
-        }catch (err){
+            result = await redirectToImportServer(data, "importChongqingProject", req);
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    importProcessChecking:async function (req,res) {
-        let result={
-            error:0
+    importProcessChecking: async function (req, res) {
+        let result = {
+            error: 0
         };
-        try{
+        try {
             let data = JSON.parse(req.body.data);
             data.userID = req.session.sessionUser.id;
             data.compilationID = req.session.sessionCompilation._id;
             result.data = await pm_facade.importProcessChecking(data);
-        } catch (err){
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    getUploadToken:function (req,res) {
-        let result={
-            error:0
+    getUploadToken: function (req, res) {
+        let result = {
+            error: 0
         };
         try {
-            result =pm_facade.uploadToken();
-        }catch (err){
+            result = pm_facade.uploadToken();
+        } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
     },
-    getBasicInfo: async function(req, res) {
+    getBasicInfo: async function (req, res) {
         try {
             let data = JSON.parse(req.body.data);
             let infoLib = await pm_facade.getBasicInfo(req.session.sessionCompilation._id, data.fileKind);
@@ -874,7 +894,7 @@ module.exports = {
             callback(req, res, 1, err, []);
         }
     },
-    getProjectFeature: async function(req, res) {
+    getProjectFeature: async function (req, res) {
         try {
             let data = JSON.parse(req.body.data);
             let featureLib = await pm_facade.getProjectFeature(data.valuationID, data.engineeringName, data.feeName);
@@ -893,7 +913,7 @@ module.exports = {
             callback(req, res, 1, err, []);
         }
     },
-    getProjectByGranularity: async function(req, res) {
+    getProjectByGranularity: async function (req, res) {
         try {
             let data = JSON.parse(req.body.data);
             const userID = req.session.sessionUser.id;
@@ -904,7 +924,7 @@ module.exports = {
             callback(req, res, 1, err, null);
         }
     },
-    getProjectPlaceholder: async function(req, res) {
+    getProjectPlaceholder: async function (req, res) {
         let data = JSON.parse(req.body.data);
         try {
             let countRst = await pm_facade.getProjectPlaceholder(data);
@@ -948,15 +968,15 @@ module.exports = {
     } */
     importInterface: async function (req, res) {
         const data = JSON.parse(req.body.data);
-        let result={
-            error:0
+        let result = {
+            error: 0
         };
         try {
             data.session = req.session;
-            result = await redirectToImportServer(data,"importInterface",req);
+            result = await redirectToImportServer(data, "importInterface", req);
         } catch (err) {
             console.log(err);
-            result.error=1;
+            result.error = 1;
             result.message = err.message;
         }
         res.json(result);
@@ -964,15 +984,15 @@ module.exports = {
     redirectToImportServer: redirectToImportServer
 };
 
-async function redirectToImportServer(data,action,req) {
-    let importURL = config.getImportURL(process.env.NODE_ENV,req.headers.origin);
+async function redirectToImportServer(data, action, req) {
+    let importURL = config.getImportURL(process.env.NODE_ENV, req.headers.origin);
     let options = {
         method: 'POST',
         uri: `http://${importURL}/import/${action}`,
         body: data,
-        timeout:220000,
+        timeout: 220000,
         json: true
     };
-    console.log("post import data to:"+options.uri);
+    console.log("post import data to:" + options.uri);
     return await rp.post(options);
 }

+ 22 - 0
modules/pm/facade/pm_facade.js

@@ -16,6 +16,7 @@ module.exports={
     deleteShareList,
     getShareInfoMap,
     getRecentShareList,
+    getProjectShareList,
     moveProject:moveProject,
     accessToCopyProject,
     copyProject:copyProject,
@@ -266,6 +267,27 @@ async function getRecentShareList(userID, count) {
     return users;
 }
 
+// 获取某项目的分享记录
+async function getProjectShareList(projectID) {
+    const sharedList = await shareListModel.find({ projectID }).lean();
+    const userIDs = [];
+    const userMap = {};
+    sharedList.forEach(item => {
+        userIDs.push(item.receiver);
+        userMap[item.receiver] = item;
+    });
+    const userObjectIDs = userIDs.map(userID => mongoose.Types.ObjectId(userID));
+    const users = await userModel.find({_id: {$in: userObjectIDs}}, 'real_name mobile company').lean();
+    users.forEach(user => {
+        const matched = userMap[user._id];
+        if (matched) {
+            user.allowCopy = matched.allowCopy;
+            user.allowCooperate = matched.allowCooperate;
+        }
+    });
+    return users;
+}
+
 //拷贝例题项目
 //@param {String}userID {Array}projIDs拷贝的例题项目ID(建设项目、文件夹)@return {Boolean}
 async function copyExample(userID, compilation, projIDs){

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

@@ -59,7 +59,7 @@ module.exports = function (app) {
     pmRouter.post('/delGC', pmController.delGC);
     //share
     pmRouter.post('/getProjectShareInfo', pmController.projectShareInfo);
-    pmRouter.post('/getRecentShareInfo', pmController.getRecentShareInfo);
+    pmRouter.post('/getInitialShareData', pmController.getInitialShareData);
     pmRouter.post('/share', pmController.share);
     pmRouter.post('/receiveProjects', pmController.receiveProjects);
     pmRouter.post('/changeFile', pmController.changeFile);

+ 4 - 81
web/building_saas/pm/html/project-management.html

@@ -683,7 +683,7 @@
         </div>
     </div>
 </div>
-
+<%include ../../../common/components/share/index.html %>
 <!--弹出删除 单价/费率 文件-->
 <div class="modal fade" id="del-wj" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -705,86 +705,6 @@
         </div>
     </div>
 </div>
-<!--弹出分享-->
-<div class="modal fade" id="share" data-backdrop="static">
-    <div class="modal-dialog modal-lg" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h5 class="modal-title">分享给...</h5>
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                    <span aria-hidden="true">&times;</span>
-                </button>
-            </div>
-            <div class="modal-body">
-                <!--添加分享-->
-                <div class="form-group">
-                    <div class="input-group input-group-sm">
-                        <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享" autofocus="autofocus">
-                        <div class="input-group-append">
-                            <a class="btn btn-outline-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuBook">
-                              最新分享
-                            </a>
-                            <div id="shareSubMenu" class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuBook" style="width: 250px;">
-                              <div class="">
-                                  <ul class="nav nav-tabs nav-fill mx-1">
-                                      <li class="nav-item">
-                                          <a class="nav-link active" id="zuijin-tab" data-toggle="tab" href="#zuijin" role="tab" aria-controls="zuijin" aria-selected="true">最近分享</a>
-                                        </li>
-                                        <li class="nav-item">
-                                          <a class="nav-link" id="lianxi-tab" data-toggle="tab" href="#lianxi" role="tab" aria-controls="lianxi" aria-selected="false">联系人</a>
-                                        </li>
-                                  </ul>
-                                  <div class="tab-content" id="myTabContent">
-                                    <div class="tab-pane fade show active" id="zuijin" role="tabpanel" aria-labelledby="zuijin-tab">
-                                      <!--最近分享5个人-->
-                                      <ul class="book-list" id="recentList">
-                                      </ul>
-                                    </div>
-                                    <div class="tab-pane fade" id="lianxi" role="tabpanel" aria-labelledby="lianxi-tab">
-                                        <ul class="book-list" id="contactsList">
-                                      </ul>
-                                    </div>
-                                  </div>
-                              </div>
-                            </div>
-                      </div>
-                    </div>
-                </div>
-                <table class="table table-sm" id="shareFindDiv">
-                    <tbody style="display: block">
-                    <tr><th style="width: 112px;">姓名</th><th style="width: 165px;">公司</th><th style="width: 136px;">手机</th><th style="width: 160px;">邮箱</th><th style="width: 90px;">允许拷贝</th><th style="width: 90px;">允许编辑</th><th style="width: 90px;">添加分享</th></tr>
-                    <tr>
-                        <td id="user_name"></td>
-                        <td id="user_company"></td>
-                        <td id="user_mobile"></td>
-                        <td id="user_email"></td>
-                        <td><input id="allowCopy" type="checkbox"></td>
-                        <td><input id="allowCooperate" type="checkbox"></td>
-                        <td>
-                            <a id="share-confirm" href="javascript:void(0)" class="btn btn-sm btn-primary">添加分享</a>
-                        </td>
-                    </tr>
-                    </tbody>
-                </table>
-                <p id="share-info" class="text-danger">不存在手机号 15812777651 的用户</p>
-                <!--已分享数据-->
-                <legend>已分享</legend>
-                <table class="table table-sm">
-                    <tbody id="shareToInfo">
-                    <tr><th>姓名</th><th>公司</th><th>手机</th><th>邮箱</th><th width="90">允许拷贝</th><th width="90">取消分享</th></tr>
-                    <tr><td>张三</td><td>XX公司</td><td>12345678900</td><td></td><td><input type="checkbox"></td><td><input type="checkbox"></td></tr>
-                    <tr><td>王五</td><td>XX公司</td><td>12345678900</td><td></td><td><input type="checkbox"></td><td><input type="checkbox"></td></tr>
-                    </tbody>
-                </table>
-            </div>
-            <div class="modal-footer">
-                <!--分享人可以取消-->
-                <button id="shareToConfirm" type="button" class="btn btn-primary" data-dismiss="modal">确定</button>
-                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
-            </div>
-        </div>
-    </div>
-</div>
 <!--进度条-->
 <div class="modal fade" id="progress" data-backdrop="static">
     <div class="modal-dialog" role="document">
@@ -1011,6 +931,8 @@
 <script src="/public/web/tree_sheet/tree_sheet_helper.js"></script>
 <script src="/public/web/sheet/sheet_data_helper.js"></script>
 <script src="/public/web/sheet/sheet_common.js"></script>
+<script src="/lib/pinyinjs/pinyin_dict_firstletter.js"></script>
+<script src="/lib/pinyinjs/pinyinUtil.js"></script>
 <script type="text/javascript" src="/public/web/common_ajax.js"></script>
 <script src="/lib/JSExpressionEval_src/Date.js"></script>
 <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
@@ -1020,6 +942,7 @@
 <script src="/web/building_saas/main/js/models/importStdInterfaceBase.js"></script>
 <!-- <script src="/web/building_saas/main/js/models/importStandardInterface.js"></script> -->
 <script src="/web/building_saas/pm/js/pm_ajax.js"></script>
+<script src="/web/common/components/share/index.js" ></script>
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>
 <script src="/web/building_saas/pm/js/pm_import.js"></script>
 <script src="/web/building_saas/pm/js/pm_gc.js"></script>

+ 6 - 361
web/building_saas/pm/js/pm_newMain.js

@@ -52,17 +52,7 @@ let regions = [];
 function isDef(v) {
     return typeof v !== 'undefined' && v !== null;
 }
-let keyupTime = 0,
-    delayTime = 500;
-function delayKeyup(callback) {
-    let nowTime = Date.now();
-    keyupTime = nowTime;
-    setTimeout(function () {
-        if (nowTime - keyupTime == 0) {
-            callback();
-        }
-    }, delayTime);
-}
+
 const addPath = {p_e_t: 'p_e_t', e_t: 'e_t', t: 't'};
 //操作状态
 const STATE = {
@@ -368,18 +358,12 @@ const projTreeObj = {
             disabled: function () {
                 let selectedItem = projTreeObj.tree.selected;
                 return !selectedItem;
-                //return !(selectedItem && selectedItem.data.projType === projectType.tender);
             },
             callback: function (key, opt) {
                 let selected = projTreeObj.tree.selected;
-                $('#sharePhone').val('');
-                $('#share-info').hide();
-                $('#shareFindDiv').hide();
-                setShareToModal(selected);
-                $('#share').modal('show');
-                setTimeout(function () {
-                    $('#sharePhone').focus();
-                }, 200);
+                if (selected && selected.data.ID) {
+                    SHARE_TO.initModal(selected.data.ID);
+                }
             }
         },
         importInterface: {
@@ -703,7 +687,6 @@ const projTreeObj = {
             return;
         }
         me.tree.selected = node;
-        shareSeleted = node;
         me.preSelection = newSel;
     },
     onSelectionChanging: function (sender, args) {
@@ -1727,88 +1710,9 @@ function setupRequiredWarn(compilation) {
     }
 }
 
-// 显示最近分享信息
-function showRecentShareInfo() {
-    function getBookListHtml(users) {
-        return users.reduce((acc, user) => {
-            const mobile = user.mobile || '';
-            const realName = user.real_name || '';
-            const company = user.company || '';
-            // 手机最后一位
-            const lastMobileNumer = mobile.substring(mobile.length - 1);
-            // 显示名称为真实名称后两位
-            const nickName = realName.substring(realName.length - 2);
-            return acc +
-            `<li>
-                <span class="avatar bg-${lastMobileNumer}">${nickName}</span>
-                <div class="book-body">
-                    <h5 class="mt-0" title="${company}">${realName}</h5>
-                    <span class="mobile-info">${mobile}</span>
-                </div>
-            </li>`;
-        }, '');
-        
-    }
-    $.bootstrapLoading.start();
-    CommonAjax.post('/pm/api/getRecentShareInfo', {user_id: userID, count: 5}, (data) => {
-        const recentHtml = getBookListHtml(data.recentUsers);
-        // 联系人按拼英首字母降序排序
-        data.contacts.sort((a, b) => {
-            const realNameA = a.real_name || '';
-            const realNameB = b.real_name || '';
-            return realNameA.localeCompare(realNameB, 'zh-Hans-CN', {sensitivity: 'accent'})
-        });
-        const contactsHtml = getBookListHtml(data.contacts);
-        $('#recentList').html(recentHtml);
-        $('#contactsList').html(contactsHtml);
-        $('#myTabContent ul li').click(function () {
-            const mobile = $(this).find('.mobile-info')[0].textContent;
-            $('#sharePhone').val(mobile);
-            shareTender();
-            const $subMenu = $('#shareSubMenu');
-            const subMenu = $subMenu[0];
-            $(subMenu.parentElement).removeClass('show');
-            $subMenu.removeClass('show');
-        });
-        $.bootstrapLoading.end();
-    }, () => {
-        $.bootstrapLoading.end();
-    });
-}
-
 $(document).ready(function() {
-    // 最近分享、联系人列表相关
-    $('body').click(function (e) {
-        const body = $(this)[0];
-        const $subMenu = $('#shareSubMenu');
-        const subMenu = $subMenu[0];
-        const menuBook = $('#dropdownMenuBook')[0]
-        if (!$subMenu.is(':visible')) {
-            return;
-        }
-        let target = e.target;
-        while (target !== body) {
-            if ([subMenu, menuBook].includes(target)) {
-                return;
-            }
-            target = target.parentElement;
-        }
-        $(subMenu.parentElement).removeClass('show');
-        $subMenu.removeClass('show');
-    });
-    // 最近分享、联系人列表相关
-    $('#dropdownMenuBook').click(function () {
-        const $subMenu = $('#shareSubMenu');
-        const visible = $subMenu.is(':visible');
-        if (visible) {
-            $(this).parent().removeClass('show');
-            $subMenu.removeClass('show');
-        } else {
-            $(this).parent().addClass('show');
-            $subMenu.addClass('show');
-            showRecentShareInfo();
-        }
-    });
+    // 分享给的一些事件监听
+    SHARE_TO.handleEventListener();
 
     // 单位工程超限提示后,弹出客服列表
     $('#hintBox_form').on('hide.bs.modal', function () {
@@ -4703,127 +4607,6 @@ function refreshTenderFile(tenders, type, newName){
        }
    });
 }
-//添加分享 pm share共用
-const shareType = {create: 'create', cancel: 'cancel'};
-let shareSeleted = null;
-let shareUserID = null;
-let canShare = false;
-$('#sharePhone').bind('keypress', function (e) {
-    if(e.keyCode === 13){
-        $(this).blur();
-    }
-});
-function shareTender(){
-    canShare = false;
-    let phone = $('#sharePhone').val();
-    const hintInfo = $('#share-info');
-    const userInfo = $('#shareFindDiv');
-    const addUser = $('#addShareUser');
-    const copyInput = $('#allowCopy');
-    const coopInput = $('#allowCooperate');
-    const shareConfirm = $('#share-confirm');
-    shareConfirm.addClass('disabled');
-    //可分享才可添加用户
-    addUser.addClass('disabled');
-    if (hintInfo.is(':visible')) {
-        hintInfo.hide();
-    }
-    if(!phone || phone.trim() === ''){
-        setDangerInfo(hintInfo, '请输入手机号码。', true);
-        if(userInfo.is(':visible')){
-            userInfo.hide();
-        }
-        return;
-    }
-    //根据手机号获取用户
-    CommonAjax.post('/user/getUserByMobile', {mobile: phone}, function (userData) {
-        if(!userData){
-            setDangerInfo(hintInfo, '账号不存在。', true);
-            if(userInfo.is(':visible')){
-                userInfo.hide();
-            }
-            return;
-        }
-        else{
-            if(userData._id === userID){
-                setDangerInfo(hintInfo, '不可分享给自己。', true);
-                return;
-            }
-            shareUserID = userData._id;
-            setDangerInfo(hintInfo, '', false);
-            $('#user_name').text(userData.real_name ? userData.real_name : '');
-            $('#user_company').text(userData.company ? userData.company : '');
-            $('#user_mobile').text(userData.mobile ? userData.mobile : '');
-            $('#user_email').text(userData.email ? userData.email : '');
-            //默认可拷贝
-            copyInput.prop('checked', true);
-            //默认不可协作
-            coopInput.prop('checked', false);
-            userInfo.show();
-            //判断项目是否已经分享
-            CommonAjax.post('/pm/api/getProjectShareInfo', {user_id: userID, projectID: shareSeleted.data.ID}, function (shareInfo) {
-                for(let shareData of shareInfo){
-                    if(shareData.userID === userData._id){
-                        setDangerInfo(hintInfo, '已与该用户分享。', true);
-                        return;
-                    }
-                }
-                addUser.removeClass('disabled');
-                shareConfirm.removeClass('disabled');
-                canShare = true;
-            });
-        }
-    });
-}
-$('#sharePhone').on('keyup',function () {
-    delayKeyup(function () {
-        shareTender();
-    });
-});
-
-//确认分享
-$('#share-confirm').click(function(){
-    const hintInfo = $('#share-info');
-    $.bootstrapLoading.start();
-    const perHeight = 30;
-    let allowCopy = $('#allowCopy').prop('checked'),
-        allowCoop = $('#allowCooperate').prop('checked');
-        userName = $('#user_name').text() || '',
-        userCompany = $('#user_company').text() || '',
-        userMobile = $('#user_mobile').text() || '',
-        userEmail = $('#user_email').text() || '';
-    let shareData = [{userID: shareUserID, allowCopy: allowCopy, allowCooperate: allowCoop}];
-    //分享
-    CommonAjax.post('/pm/api/share', {user_id: userID, type: shareType.create,  projectID: shareSeleted.data.ID, shareData: shareData}, function (rstData) {
-        //更新已分享table
-        let $tr = $(`<tr>
-                        <td style="width: 112px">${userName}</td>
-                        <td style="width: 165px">${userCompany}</td>
-                        <td style="width: 136px">${userMobile}</td>
-                        <td style="width: 136px">${userEmail}</td>
-                        <td style="width: 90px;"><input value="allowCopy" ${allowCopy ? 'checked' : ''} type="checkbox"></td>
-                        <td style="width: 90px;"><input value="allowCooperate" ${allowCoop ? 'checked' : ''} type="checkbox"></td>
-                        <td style="width: 90px;"><input value="cancelShare" type="checkbox"></td>
-                    </tr>`);
-        let tbodyTotalHeight = $('#shareToInfo').height() + perHeight > 200 ? 200 : $('#shareToInfo').height() + perHeight;
-        $('#shareToInfo').height(tbodyTotalHeight);
-        $('#shareToInfo').append($tr);
-        //更新缓存
-        shareSeleted.data.shareInfo = shareSeleted.data.shareInfo.concat(shareData);
-        let sheet = projTreeObj.workBook.getSheet(0);
-        projTreeObj.renderSheetFuc(sheet, function () {
-            sheet.invalidateLayout();
-            sheet.repaint();
-        });
-        bindCancelShareCheck(shareSeleted);
-        $.bootstrapLoading.end();
-        $('#shareFindDiv').hide();
-        $('#share-confirm').addClass('disabled');
-    }, function () {
-        $.bootstrapLoading.end();
-        $('#share-confirm').removeClass('disabled');
-    });
-});
 
 //批量替换文件,切换建设项目
 $('#otherProject').change(function(){
@@ -4844,17 +4627,6 @@ $('#otherProject').change(function(){
     }
 });
 
-//分享给...界面确认
-$('#shareToConfirm').click(function () {
-    let selected = projTreeObj.tree.selected;
-    if (!selected) {
-        return;
-    }
-    updateShareInfo(selected);
-});
-
-
-
 $("#confirm-import").click(function() {
     //$.bootstrapLoading.start();
     if (STATE.importing) {
@@ -4915,133 +4687,6 @@ $('#import').on('show.bs.modal', function(){
 });
 
 
-//设置分享给界面数据
-//@param {Object}selected @return {void}
-function setShareToModal(selected){
-    $.bootstrapLoading.start();
-    const perHeight = 30; //每条分享给数据的高度
-    $('#shareToInfo').empty();
-    if(!selected){
-        return;
-    }
-    let userIDs = [];
-    for(let user of selected.data.shareInfo){
-        userIDs.push(user.userID);
-    }
-    CommonAjax.post('/user/getUsers', {userIDs: userIDs}, function (rstData) {
-        for(let userInfo of rstData){
-            for(let user of selected.data.shareInfo){
-                if(user.userID === userInfo._id){
-                    user.exist = true;
-                    user.name = userInfo.real_name;
-                    user.company = userInfo.company;
-                    user.mobile = userInfo.mobile;
-                    user.email = userInfo.email;
-                }
-            }
-        }
-        let infoArr = [];
-        //居中style="width: 90px;text-align: center"
-        let theadHtml = `<tr>
-                            <th style="width: 112px;">姓名</th>
-                            <th style="width: 165px;">公司</th>
-                            <th style="width: 136px;">手机</th>
-                            <th style="width: 136px;">邮箱</th>
-                            <th style="width: 90px;">允许拷贝</th>
-                            <th style="width: 90px;">允许编辑</th>
-                            <th style="width: 90px;">取消分享</th>
-                        </tr>`;
-        infoArr.push(theadHtml);
-        for(let user of selected.data.shareInfo){
-            if (!user.exist) {
-                continue;
-            }
-            let infoHtml = `<tr>
-                                <td style="width: 112px;">${user.name}</td>
-                                <td style="width: 165px;">${user.company}</td>
-                                <td style="width: 136px;">${user.mobile}</td>
-                                <td style="width: 160px;">${user.email}</td>
-                                <td style="width: 90px;"><input value="allowCopy" ${user.allowCopy ? 'checked' : ''} type="checkbox"></td>
-                                <td style="width: 90px;"><input value="allowCooperate" ${user.allowCooperate ? 'checked' : ''} type="checkbox"></td>
-                                <td style="width: 90px;"><input value="cancelShare" type="checkbox"></td>
-                            </tr>`;
-            infoArr.push(infoHtml);
-        }
-        let tbodyTotalHeight = infoArr.length * perHeight + 5 > 200 ? 200 : infoArr.length * perHeight + 5;
-        $('#shareToInfo').height(tbodyTotalHeight);
-        let infoHtml = infoArr.join('');
-        $('#shareToInfo').html(infoHtml);
-        bindCancelShareCheck(selected);
-        $.bootstrapLoading.end();
-    });
-}
-//取消分享的勾选操作
-//@param {Object}selected(选中项目节点) @return {void}
-function bindCancelShareCheck(selected) {
-    $('#shareToInfo input[value="cancelShare"]').unbind('click');
-    $('#shareToInfo input[value="cancelShare"]').click(function () {
-        let index = $(this).parent().parent().index() - 1,
-            $coo = $(this).parent().prev().children(),
-            $copy = $(this).parent().prev().prev().children();
-        let isCancel = $(this).prop('checked');
-        if (isCancel) {//取消分享,取消拷贝、协作勾选
-            $coo.prop('checked', false);
-            $copy.prop('checked', false);
-        } else {//取消取消分享,恢复原本的拷贝、协作勾选状态
-            let shareData = selected.data.shareInfo[index];
-            if (shareData) {
-                $coo.prop('checked', shareData.allowCooperate);
-                $copy.prop('checked', shareData.allowCopy);
-            }
-        }
-    });
-}
-//更新项目分享信息
-//@param {Object}selected
-function updateShareInfo(selected){
-    if(!selected){
-        return;
-    }
-    let usersTr = $('#shareToInfo').find('tr');
-    if (usersTr && usersTr.length > 0) {
-        usersTr = usersTr.slice(1);
-    }
-    let newShareInfo = [],
-        orgShareInfo = [];
-    const postData = [];
-    //数据不同才提交
-    for (let shareData of selected.data.shareInfo) {
-        orgShareInfo.push({userID: shareData.userID, allowCopy: shareData.allowCopy, allowCooperate: shareData.allowCooperate})
-    }
-    for(let i = 0; i < usersTr.length; i++){
-        let userTr = usersTr[i];
-        let allowCopy = $(userTr).find('input:eq(0)').prop('checked');
-        let allowCoop = $(userTr).find('input:eq(1)').prop('checked');
-        let cancelShare = $(userTr).find('input:eq(2)').prop('checked');
-        selected.data.shareInfo[i].allowCopy = allowCopy;
-        selected.data.shareInfo[i].allowCooperate = allowCoop;
-        let shareItem;
-        if(cancelShare){
-            shareItem = {userID: selected.data.shareInfo[i].userID, isCancel: true};
-        } else {
-            shareItem = {userID: selected.data.shareInfo[i].userID, allowCopy: allowCopy, allowCooperate: allowCoop};
-            newShareInfo.push(shareItem);
-        }
-        postData.push(shareItem);
-    }
-    if (_.isEqual(newShareInfo, orgShareInfo)) {
-        return;
-    }
-    CommonAjax.post('/pm/api/share', {user_id: userID, type: 'update', projectID: selected.data.ID, shareData: postData}, function (shareData) {
-        selected.data.shareInfo = shareData;
-        let sheet = projTreeObj.workBook.getSheet(0);
-        projTreeObj.renderSheetFuc(sheet, function () {
-            sheet.invalidateLayout();
-            sheet.repaint();
-        });
-    });
-}
-
 //刷新建设项目汇总金额信息
 function refreshProjSummary(project, summaryInfo) {
     let refreshNodes = [];

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

@@ -10,6 +10,7 @@
 
 const pmShare = (function () {
     const spreadDom = $('#shareSpread');
+    let shareSeleted = null;
     let spreadObj = {workBook: null, sheet: null};
     let preSelection = null;
     //项目分享类型,由别人分享给自己的,和自己分享给别人的
@@ -499,75 +500,6 @@ const pmShare = (function () {
         };
         return new InteractionCell();
     }
-    //设置分享给界面数据
-    //@param {Object}selected @return {void}
-    function setShareToModal(selected){
-        $('#shareToInfo').empty();
-        if(!selected){
-            return;
-        }
-        let userIDs = [];
-        for(let user of selected.data.shareInfo){
-            userIDs.push(user.userID);
-        }
-        CommonAjax.post('/user/getUsers', {userIDs: userIDs}, function (rstData) {
-            for(let userInfo of rstData){
-                for(let user of selected.data.shareInfo){
-                    if(user.userID === userInfo._id){
-                        user.name = userInfo.real_name;
-                        user.company = userInfo.company;
-                        user.mobile = userInfo.mobile;
-                        user.email = userInfo.email;
-                    }
-                }
-            }
-            let infoArr = [];
-            for(let user of selected.data.shareInfo){
-                let infoHtml = `<tr>
-                                          <td style="width: 106px;">${user.name}</td>
-                                          <td style="width: 146px;">${user.company}</td>
-                                          <td style="width: 146px;">${user.mobile}</td>
-                                          <td style="width: 156px;">${user.email}</td>
-                                          <td style="width: 70px;text-align: center"><input value="allowCopy" ${user.allowCopy ? 'checked' : ''} type="checkbox"></td>
-                                          <td style="width: 70px;text-align: center"><input value="cancelShare" type="checkbox"></td>
-                               </tr>`;
-                infoArr.push(infoHtml);
-            }
-            let infoHtml = infoArr.join('');
-            $('#shareToInfo').html(infoHtml);
-        });
-    }
-    //更新项目分享信息
-    //@param {Object}selected
-    function updateShareInfo(selected){
-        if(!selected){
-            return;
-        }
-        let usersTr = $('#shareToInfo').find('tr');
-        let newShareInfo = [];
-        for(let i = 0; i < usersTr.length; i++){
-            let userTr = usersTr[i];
-            let allowCopy = $(userTr).find('input:first').prop('checked');
-            let cancelShare = $(userTr).find('input:last').prop('checked');
-            selected.data.shareInfo[i].allowCopy = allowCopy;
-            if(!cancelShare){
-                newShareInfo.push(selected.data.shareInfo[i]);
-            }
-        }
-        CommonAjax.post('/pm/api/updateProjects', {user_id: userID, updateData: [{updateType: 'update', updateData: {ID: selected.data.ID, shareInfo: newShareInfo}}]}, function () {
-            selected.data.shareInfo = newShareInfo;
-            if(newShareInfo.length === 0){
-                renderSheetFunc(spreadObj.sheet, function () {
-                    let rIdx = selected.serialNo();
-                    tree.removeNode(selected);
-                    spreadObj.sheet.deleteRows(rIdx, 1);
-                    spreadObj.sheet.setRowCount(tree.items);
-                    initSelection({row: spreadObj.sheet.getActiveRowIndex(), rowCount: 1},null);
-
-                });
-            }
-        });
-    }
     const foreColor = '#007bff';
     const cancelForeColor = 'red';
     //显示树结构数据

+ 9 - 206
web/common/components/share/index.html

@@ -11,88 +11,14 @@
                 <div class="row justify-content-center">
                     <div class="form-group col-6 ">
                         <div class="input-group input-group-sm">
-                            <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享" autofocus="autofocus">
+                            <input id="share-phone" type="text" class="form-control" placeholder="输入 手机号 添加分享" autofocus="autofocus">
                             <div class="btn-group btn-group-sm ml-1">
-                                <a class="btn btn-primary dropdown-toggle" href="#" role="button" id="dropdownMenuBook2">
+                                <a class="btn btn-primary dropdown-toggle" href="#" role="button" id="contacts-dropdown">
                                     联系人
                                 </a>
-                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuBook2" style="width: 250px;">
+                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="contacts-dropdown" style="width: 250px;" id="contacts-menue">
                                     <div class="">
-                                        <ul class="book-list">
-                                            <li>C</li>
-                                            <li>
-                                                <span class="avatar bg-0">桐杉</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">陈桐杉</h5>
-                                                    15800000000
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-1">迎华</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">陈迎华</h5>
-                                                    15800000001
-                                                </div>
-                                            </li>
-                                            <li>H</li>
-                                            <li>
-                                                <span class="avatar bg-2">木贤</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">黄木贤</h5>
-                                                    15800000002
-                                                </div>
-                                            </li>
-                                            <li>L</li>
-                                            <li>
-                                                <span class="avatar bg-3">李旭</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">李旭</h5>
-                                                    15800000003
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-4">伽淼</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">李伽淼</h5>
-                                                    15800000004
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-5">川汉</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">李川汉</h5>
-                                                    15800000005
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-6">石宝</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">李石宝</h5>
-                                                    15800000006
-                                                </div>
-                                            </li>
-                                            <li>Z</li>
-                                            <li>
-                                                <span class="avatar bg-7">白尔</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0">张白尔</h5>
-                                                    15800000007
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-8">润隆</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">张润隆</h5>
-                                                    15800000008
-                                                </div>
-                                            </li>
-                                            <li>
-                                                <span class="avatar bg-9">佳玫</span>
-                                                <div class="book-body">
-                                                    <h5 class="mt-0" title="某某有限公司">张佳玫</h5>
-                                                    15800000009
-                                                </div>
-                                            </li>
+                                        <ul class="book-list" id="contacts">
                                         </ul>
                                     </div>
                                 </div>
@@ -104,141 +30,18 @@
                     <div class="col-3">
                         <legend>最近分享</legend>
                         <!--最近分享-->
-                        <ul class="book-list">
-                            <li>
-                                <span class="avatar bg-8">润隆</span>
-                                <div class="book-body">
-                                    <h5 class="mt-0" title="某某有限公司">张润隆</h5>
-                                    15800000008
-                                </div>
-                            </li>
-                            <li>
-                                <span class="avatar bg-3">李旭</span>
-                                <div class="book-body">
-                                    <h5 class="mt-0" title="某某有限公司">李旭</h5>
-                                    15800000003
-                                </div>
-                            </li>
-                            <li>
-                                <span class="avatar bg-9">佳玫</span>
-                                <div class="book-body">
-                                    <h5 class="mt-0" title="某某有限公司">张佳玫</h5>
-                                    15800000009
-                                </div>
-                            </li>
-                            <li>
-                                <span class="avatar bg-4">伽淼</span>
-                                <div class="book-body">
-                                    <h5 class="mt-0" title="某某有限公司">李伽淼</h5>
-                                    15800000004
-                                </div>
-                            </li>
-                            <li>
-                                <span class="avatar bg-7">白尔</span>
-                                <div class="book-body">
-                                    <h5 class="mt-0" title="某某有限公司">张白尔</h5>
-                                    15800000007
-                                </div>
-                            </li>
+                        <ul class="book-list" id="recent-share">
                         </ul>
                     </div>
                     <div class="col-9">
-                    
+                <!-- 搜索提示 -->
+                <p class="text-danger" id="share-hint"></p>    
                 <!--搜索结果-->
-                <ul class="book-list my-3" style="height:auto">
-                    <li class="card mb-1">
-                        <div class="card-body p-1 row m-0">
-                            <span class="avatar bg-8">伽淼</span>
-                            <!--姓名手机-->
-                            <div class="book-body col-auto pl-0">
-                                <h5 class="mt-0">李伽淼</h5>
-                                15800000004
-                            </div>
-                            <!--公司-->
-                            <div class="col-5">珠海纵横创新软件有限公司</div>
-                            <!--操作-->
-                            <div class="col ml-auto p-0">
-                                <div class="d-flex justify-content-end">
-                                    <div>
-                                        <div class="custom-control custom-checkbox">
-                                            <input type="checkbox" class="custom-control-input" id="customCheck1" checked="">
-                                            <label class="custom-control-label" for="customCheck1">允许拷贝</label>
-                                        </div>
-                                        <div class="custom-control custom-checkbox">
-                                            <input type="checkbox" class="custom-control-input" id="customCheck2" checked="">
-                                            <label class="custom-control-label" for="customCheck2">允许编辑</label>
-                                        </div>
-                                    </div>
-                                    <div class="ml-3 d-flex align-items-center"><button class="btn btn-sm btn-primary">分享给Ta</button></div>
-                                </div>
-                            </div>
-                            <!--提示信息-->
-                            <!-- <div class="col-12">
-                                <p class="text-danger mb-0">允许拷贝,用户可以对您分享的项目进行拷贝复制。</p>
-                                <p class="text-danger mb-0">允许编辑,用户可以对您分享的项目进行任意编辑修改,请谨慎开启。</p>
-                            </div> -->
-                        </div>
-                    </li>
+                <ul class="book-list my-3" style="height:auto" id="share-search-result">
                 </ul>
                 <!--已分享列表-->
                 <legend>已分享</legend>
-                <ul class="book-list" style="max-height: 250px">
-                    <li class="card mb-1">
-                        <div class="card-body p-1 row m-0">
-                            <span class="avatar bg-8">润隆</span>
-                            <!--姓名手机-->
-                            <div class="book-body col-auto pl-0">
-                                <h5 class="mt-0">张润隆</h5>
-                                15800000008
-                            </div>
-                            <!--公司-->
-                            <div class="col-5">珠海纵横创新软件有限公司</div>
-                            <!--操作-->
-                            <div class="col ml-auto p-0">
-                            <div class="d-flex justify-content-end">
-                                <div>
-                                    <div class="custom-control custom-checkbox">
-                                        <input type="checkbox" class="custom-control-input" id="customCheck3" checked="">
-                                        <label class="custom-control-label" for="customCheck3">允许拷贝</label>
-                                    </div>
-                                    <div class="custom-control custom-checkbox">
-                                        <input type="checkbox" class="custom-control-input" id="customCheck4">
-                                        <label class="custom-control-label" for="customCheck4">允许编辑</label>
-                                    </div>
-                                </div>
-                                <div class="ml-3 d-flex align-items-center"><button class="btn btn-sm btn-outline-danger">取消分享</button></div>
-                                </div>
-                            </div>
-                        </div>
-                    </li>
-                    <li class="card mb-1">
-                        <div class="card-body p-1 row m-0">
-                            <span class="avatar bg-7">白尔</span>
-                            <!--姓名手机-->
-                            <div class="book-body col-auto pl-0">
-                                <h5 class="mt-0">张白尔</h5>
-                                15800000007
-                            </div>
-                            <!--公司-->
-                            <div class="col-5">珠海纵横创新软件有限公司</div>
-                            <!--操作-->
-                            <div class="col ml-auto p-0">
-                            <div class="d-flex justify-content-end">
-                                <div>
-                                    <div class="custom-control custom-checkbox">
-                                      <input type="checkbox" class="custom-control-input" id="customCheck5">
-                                      <label class="custom-control-label" for="customCheck5">允许拷贝</label>
-                                    </div>
-                                    <div class="custom-control custom-checkbox">
-                                      <input type="checkbox" class="custom-control-input" id="customCheck6">
-                                      <label class="custom-control-label" for="customCheck6">允许编辑</label>
-                                    </div>
-                                </div>
-                                <div class="ml-3 d-flex align-items-center"><button class="btn btn-sm btn-outline-danger">取消分享</button></div>
-                                </div>
-                            </div>
-                        </div>
-                    </li>
+                <ul class="book-list" style="max-height: 250px" id="shared-list">
                     </ul>
                     </div>
                 </div>

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

@@ -0,0 +1,413 @@
+const SHARE_TO = (() => {
+
+    const ShareType = {
+        CREATE: 'create',
+        UPDATE: 'update',
+        CANCEL: 'cancel',
+    };
+
+    // 当前分享的项目ID
+    let curProjectID;
+
+    // 当前项目的已分享列表
+    let curSharedUsers = [];
+
+    // 清除缓存
+    function clearCache() {
+        curProjectID = null;
+        curSharedUsers = [];
+    }
+
+    // 最近联系人显示数量
+    const rencentCount = 5;
+
+    // 获取初始数据:1.最近分享人 2.联系人 3.已分享人
+    async function getInitalData(projectID) {
+        return await ajaxPost('/pm/api/getInitialShareData', { user_id: userID, count: rencentCount, projectID }, false);
+
+    }
+
+    // 获取头像视图html
+    function getAvatarHTML(mobile, realName) {
+        // 手机最后一位
+        const lastMobileNumer = mobile.substr(-1);
+        // 显示名称为真实名称后两位
+        const nickName = realName.substr(-2);
+        return `<span class="avatar bg-${lastMobileNumer}">${nickName}</span>`;
+    }
+
+    /**
+     * 获取用户列表视图html:最近分享和联系人用
+     * @param {Array} users - 排序过的用户数据
+     * @param {Boolean} showAlphabet - 是否显示字母表分类
+     * @return {HTMLString}
+     *   */
+    function getUserHTML(users, showAlphabet) {
+        let curLetter = '';
+        return users.reduce((html, user) => {
+            const mobile = user.mobile || '';
+            const realName = user.real_name || '';
+            const company = user.company || '';
+            if (showAlphabet) {
+                // 名字首个字符对应拼音
+                const letter = pinyinUtil.getFirstLetter(realName).substr(0, 1);
+                if (letter !== curLetter) {
+                    curLetter = letter;
+                    html += `<li class="letter">${letter}</li>`;
+                }
+            }
+            const avatarHtml = getAvatarHTML(mobile, realName);
+            return html +
+                `<li>
+                    ${avatarHtml}
+                    <div class="book-body">
+                        <h5 class="mt-0" title="${company}">${realName}</h5>
+                        <span>${mobile}</span>
+                    </div>
+                </li>`;
+
+        }, '');
+    }
+
+    // 初始化最近分享视图
+    function initRecentView(recentUsers) {
+        const recentShareHTML = getUserHTML(recentUsers, false);
+        $('#recent-share').html(recentShareHTML);
+        // 点击最近分享列表自动添加该用户添加到搜索框中
+        $('#recent-share li').click(function () {
+            const mobile = $(this).find('div span')[0].textContent;
+            $('#share-phone').val(mobile);
+            handleSearch()
+        })
+    }
+    // 初始化联系人视图
+    function initContactsView(contacts) {
+        // 联系人按拼英首字母降序排序
+        contacts.sort((a, b) => {
+            const realNameA = a.real_name || '';
+            const realNameB = b.real_name || '';
+            return realNameA.localeCompare(realNameB, 'zh-Hans-CN', { sensitivity: 'accent' })
+        });
+        const contactsHTML = getUserHTML(contacts, true);
+        $('#contacts').html(contactsHTML);
+        // 点击联系人自动添加该联系人添加到搜索框中
+        $('#contacts li:not(.letter)').click(function () {
+            const mobile = $(this).find('div span')[0].textContent;
+            $('#share-phone').val(mobile);
+            $('#contacts-menue').removeClass('show');
+            handleSearch()
+        });
+    }
+
+    // 初始化已分享视图
+    function initSharedView(sharedUsers) {
+        const html = sharedUsers.reduce((html, user, index) => {
+            const mobile = user.mobile || '';
+            const realName = user.real_name || '';
+            const company = user.company || '';
+            const avatarHTML = getAvatarHTML(mobile, realName);
+            const copyLabelFor = `allowCopy${index}`;
+            const editLabelFor = `allowEdit${index}`;
+            return html +
+                `<li class="card mb-1">
+                    <div class="card-body p-1 row m-0">
+                        ${avatarHTML}
+                        <div class="book-body col-auto pl-0">
+                            <h5 class="mt-0">${realName}</h5>
+                            ${mobile}
+                        </div>
+                        <div class="col-5">${company}</div>
+                        <div class="col ml-auto p-0">
+                            <div class="d-flex justify-content-end">
+                                <div>
+                                    <div class="custom-control custom-checkbox">
+                                        <input type="checkbox" class="custom-control-input allow-copy" id="${copyLabelFor}" data-user="${user._id}" ${user.allowCopy ? 'checked' : ''}>
+                                        <label class="custom-control-label" for="${copyLabelFor}">允许拷贝</label>
+                                    </div>
+                                    <div class="custom-control custom-checkbox">
+                                        <input type="checkbox" class="custom-control-input allow-edit" id="${editLabelFor}" data-user="${user._id}" ${user.allowCooperate ? 'checked' : ''}>
+                                        <label class="custom-control-label" for="${editLabelFor}">允许编辑</label>
+                                    </div>
+                                </div>
+                                <div class="ml-3 d-flex align-items-center">
+                                    <button class="btn btn-sm btn-outline-danger cancel-share" data-user="${user._id}">取消分享</button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </li>`;
+        }, '');
+        $('#shared-list').html(html);
+        // 编辑允许拷贝
+        $('#shared-list .allow-copy').click(function () {
+            handleCheckBoxClick.call(this);
+            handleShareAction.call(this, ShareType.UPDATE);
+        });
+        // 编辑允许编辑
+        $('#shared-list .allow-edit').click(function () {
+            handleCheckBoxClick.call(this);
+            handleShareAction.call(this, ShareType.UPDATE);
+        });
+        // 取消分享
+        $('#shared-list .cancel-share').click(function () {
+            handleShareAction.call(this, ShareType.CANCEL);
+        });
+    }
+
+    // 初始化搜索结果视图
+    function initSearchResultView(user) {
+        if (!user) {
+            $('#share-search-result').html('')
+        } else {
+            const mobile = user.mobile || '';
+            const realName = user.real_name || '';
+            const company = user.company || '';
+            const avatarHTML = getAvatarHTML(mobile, realName);
+            const html =
+                `<li class="card mb-1">
+                        <div class="card-body p-1 row m-0">
+                            ${avatarHTML}
+                            <div class="book-body col-auto pl-0">
+                                <h5 class="mt-0">${realName}</h5>
+                                ${mobile}
+                            </div>
+                            <div class="col-5">${company}</div>
+                            <div class="col ml-auto p-0">
+                                    <div class="d-flex justify-content-end">
+                                        <div>
+                                            <div class="custom-control custom-checkbox">
+                                                <input type="checkbox" class="custom-control-input" id="allow-copy" checked="">
+                                                <label class="custom-control-label" for="allow-copy">允许拷贝</label>
+                                            </div>
+                                            <div class="custom-control custom-checkbox">
+                                                <input type="checkbox" class="custom-control-input" id="allow-edit" checked="">
+                                                <label class="custom-control-label" for="allow-edit">允许编辑</label>
+                                            </div>
+                                        </div>
+                                        <div class="ml-3 d-flex align-items-center"><button class="btn btn-sm btn-primary" id="share-to" data-user="${user._id}">分享给Ta</button></div>
+                                    </div>
+                                </div>
+                        </div>
+                    </li>`;
+            $('#share-search-result').html(html);
+
+            // 允许拷贝
+            $('#allow-copy').click(function () {
+                handleCheckBoxClick.call(this);
+            });
+            // 允许编辑
+            $('#allow-edit').click(function () {
+                handleCheckBoxClick.call(this);
+            });
+            // 分享给事件
+            $('#share-to').click(function () {
+                handleShareAction.call(this, ShareType.CREATE, user);
+            });
+        }
+    }
+
+    // 复选框框的状态随点击更改,不处理的话input的checked并不会自动处理
+    function handleCheckBoxClick() {
+        const curChecked = !$(this).attr('checked');
+        if (curChecked) {
+            $(this).attr('checked', 'checked');
+        } else {
+            $(this).removeAttr('checked');
+        }
+    }
+
+    // 添加分享、编辑分享、取消分享的动作
+    async function handleShareAction(shareType, user) {
+        try {
+            $.bootstrapLoading.start();
+            const receiver = $(this).data('user');
+            let shareData;
+            let type = ShareType.UPDATE;
+            if (shareType === ShareType.CREATE) {
+                const allowCopy = !!$('#allow-copy').attr('checked');
+                const allowCooperate = !!$('#allow-edit').attr('checked');
+                shareData = [{ userID: receiver, allowCopy, allowCooperate }];
+                type = ShareType.CREATE; // 上传的服务器的type,删除跟更新是一样的
+            } else if (shareType === ShareType.UPDATE) {
+                const allowCopy = !!$(`[data-user=${receiver}].allow-copy`).attr('checked');
+                const allowCooperate = !!$(`[data-user=${receiver}].allow-edit`).attr('checked');
+                shareData = [{ userID: receiver, allowCopy, allowCooperate }];
+            } else {
+                shareData = [{ userID: receiver, isCancel: true }];
+            }
+            const postData = {
+                user_id: userID,
+                type,
+                projectID: curProjectID,
+                count: rencentCount,
+                shareData
+            };
+            const rst = await ajaxPost('/pm/api/share', postData);
+            // 请求成功后刷新视图
+            if (shareType === ShareType.CREATE || shareType === ShareType.CANCEL) {
+                if (shareType === ShareType.CREATE) {
+                    user.allowCopy = shareData[0].allowCopy;
+                    user.allowCooperate = shareData[0].allowCooperate;
+                    curSharedUsers.unshift(user);
+                    $('#share-phone').val('');
+                    initSearchResultView();
+                } else {
+                    curSharedUsers = curSharedUsers.filter(user => user._id !== receiver);
+                }
+                if (Array.isArray(rst.recentUsers)) {
+                    initRecentView(rst.recentUsers);
+                }
+                if (Array.isArray(rst.contacts)) {
+                    initContactsView(rst.contacts)
+                }
+                initSharedView(curSharedUsers);
+                refreshTreeView();
+            }
+        } catch (err) {
+            console.log(err);
+            alert(`${String(err)} 请重试。`);
+            initSharedView(curSharedUsers);
+        } finally {
+            $.bootstrapLoading.end();
+        }
+    }
+
+    // 刷新项目管理树视图
+    // 如果是在项目管理页面,需要刷新树(分享图标可能需要清除)
+    function refreshTreeView() {
+        if (typeof projTreeObj !== 'undefined' && projTreeObj.tree.selected) {
+            projTreeObj.tree.selected.data.shareInfo = curSharedUsers;
+            const sheet = projTreeObj.workBook.getSheet(0);
+            projTreeObj.renderSheetFuc(sheet, function () {
+                sheet.invalidateLayout();
+                sheet.repaint();
+            });
+        }
+    }
+
+    // 初始化分享给的页面
+    async function initModal(projectID) {
+        try {
+            curProjectID = projectID;
+            $.bootstrapLoading.start();
+            // 恢复
+            $('#share-phone').val('');
+            initSearchResultView();
+            $('#share-hint').text('');
+            const { sharedUsers, recentUsers, contacts } = await getInitalData(projectID);
+            curSharedUsers = sharedUsers;
+            initSharedView(sharedUsers);
+            initRecentView(recentUsers);
+            initContactsView(contacts);
+            $.bootstrapLoading.end();
+            setTimeout(() => $('#sharePhone').focus(), 200);
+            $('#share').modal('show');
+        } catch (err) {
+            console.log(err);
+            alert(err);
+            $.bootstrapLoading.end();
+        }
+    }
+
+    // 退出分享给页面
+    function exitModal() {
+        clearCache();
+    }
+
+    // 分享给
+    async function handleSearch() {
+        let phone = $('#share-phone').val();
+        phone = phone && phone.trim() || '';
+        //$('#share-hint').text('');
+        initSearchResultView();
+        if (!phone) {
+            $('#share-hint').text('请输入手机号码。');
+            return;
+        }
+        // 根据手机号获取用户
+        const user = await ajaxPost('/user/getUserByMobile', { mobile: phone });
+        if (!user) {
+            $('#share-hint').text('账号不存在。');
+            return;
+        }
+        if (user._id === userID) {
+            $('#share-hint').text('不可分享给自己。');
+            return;
+        }
+        const matched = curSharedUsers.find(item => item._id === user._id);
+        if (matched) {
+            $('#share-hint').text('已与该用户分享。');
+            return;
+        }
+        $('#share-hint').text('');
+        initSearchResultView(user);
+
+    }
+
+    // 一些事件的监听
+    function handleEventListener() {
+        // 界面消失
+        $('#share').on('hide.bs.modal', function () {
+            exitModal();
+        });
+
+        // 联系人下拉
+        $('#contacts-dropdown').click(function () {
+            const $subMenu = $('#contacts-menue');
+            const visible = $subMenu.is(':visible');
+            if (visible) {
+                $subMenu.removeClass('show');
+            } else {
+                $subMenu.addClass('show');
+            }
+        });
+
+        // 点击body时,联系人菜单的处理
+        $('body').click(function (e) {
+            const body = $(this)[0];
+            const $contactsMenu = $('#contacts-menue');
+            const contactsMenu = $contactsMenu[0];
+            const dropdownButton = $('#contacts-dropdown')[0]
+            if (!$contactsMenu.is(':visible')) {
+                return;
+            }
+            let target = e.target;
+            while (target !== body) {
+                if ([contactsMenu, dropdownButton].includes(target)) {
+                    return;
+                }
+                target = target.parentElement;
+            }
+            $(contactsMenu.parentElement).removeClass('show');
+            $contactsMenu.removeClass('show');
+        });
+
+        // 输入手机号查找要分享给的用户
+        let keyupTime = 0;
+        let delayTime = 500;
+        function delayKeyup(callback) {
+            const nowTime = Date.now();
+            keyupTime = nowTime;
+            setTimeout(function () {
+                if (nowTime - keyupTime == 0) {
+                    callback();
+                }
+            }, delayTime);
+        }
+        $('#share-phone').on('keyup', function () {
+            delayKeyup(function () {
+                handleSearch();
+            });
+        });
+        $('#sharePhone').on('keypress', function (e) {
+            if (e.keyCode === 13) {
+                $(this).blur();
+            }
+        });
+    }
+
+    return {
+        initModal,
+        handleEventListener,
+    }
+})();