浏览代码

标段管理功能

ellisran 1 年之前
父节点
当前提交
a963caa68c

+ 185 - 48
app/controller/setting_controller.js

@@ -7,9 +7,11 @@
  * @date
  * @version
  */
-
+const tenderConst = require('../const/tender');
+const auditConst = require('../const/audit');
 const officeList = require('../const/cld_office').list;
 const settingConst = require('../const/setting.js');
+const scheduleConst = require('../const/schedule');
 const settingMenu = require('../../config/menu').settingMenu;
 const accountGroup = require('../const/account_group').group;
 const permission = require('../const/account_permission').permission;
@@ -141,7 +143,7 @@ module.exports = app => {
                 const accountData = await ctx.service.projectAccount.getListWithBuilder();
 
                 // 获取账号个数
-                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
+                const user_total = await ctx.service.projectAccount.count({project_id: projectId});
 
                 // 分页相关
                 const pageInfo = {
@@ -153,7 +155,7 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: projectId}});
 
                 const renderData = {
                     projectData,
@@ -190,7 +192,7 @@ module.exports = app => {
                     throw '非管理员无权解绑账号';
                 }
                 const accountId = parseInt(ctx.request.body.id);
-                const result = await ctx.service.projectAccount.update({ bind: 0 }, { id: accountId });
+                const result = await ctx.service.projectAccount.update({bind: 0}, {id: accountId});
                 if (!result) {
                     throw '解绑失败';
                 }
@@ -242,7 +244,7 @@ module.exports = app => {
                 const accountData = await ctx.service.projectAccount.getListWithBuilder();
 
                 // 获取账号个数
-                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
+                const user_total = await ctx.service.projectAccount.count({project_id: projectId});
 
                 // 分页相关
                 const pageInfo = {
@@ -254,7 +256,7 @@ module.exports = app => {
                     queryData: JSON.stringify(ctx.urlInfo.query),
                 };
 
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: projectId}});
 
                 const renderData = {
                     projectData,
@@ -293,14 +295,19 @@ module.exports = app => {
                     throw '没有访问权限';
                 }
 
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: projectId } });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: projectId}});
                 for (const u of unitList) {
-                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectId, company: u.name } });
+                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                        where: {
+                            project_id: projectId,
+                            company: u.name
+                        }
+                    });
                     u.account_num = accountList.length;
                 }
 
                 // 获取账号个数
-                const user_total = await ctx.service.projectAccount.count({ project_id: projectId });
+                const user_total = await ctx.service.projectAccount.count({project_id: projectId});
 
                 const renderData = {
                     projectData,
@@ -376,12 +383,22 @@ module.exports = app => {
                             throw '保存单位失败';
                         }
                         if (post_data.name) {
-                            const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: post_data.name } });
-                            responseData.data = { account_num: accountList.length };
+                            const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                                where: {
+                                    project_id: projectData.id,
+                                    company: post_data.name
+                                }
+                            });
+                            responseData.data = {account_num: accountList.length};
                         }
                         break;
                     case 'delete':
-                        const accountList = await ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: projectData.id, company: data.name } });
+                        const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                            where: {
+                                project_id: projectData.id,
+                                company: data.name
+                            }
+                        });
                         if (accountList.length > 0) {
                             throw '该单位下还存在账号,无法删除';
                         }
@@ -393,7 +410,7 @@ module.exports = app => {
                             // 不删除地址,只删除数据库数据,防止已签章的报表丢失
                             // await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + info.sign_path);
                         }
-                        await ctx.service.constructionUnit.update({ sign_path: null }, { id: info.id });
+                        await ctx.service.constructionUnit.update({sign_path: null}, {id: info.id});
                         break;
                     default:
                         break;
@@ -401,7 +418,7 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
@@ -416,9 +433,9 @@ module.exports = app => {
                 const filepath = `app/public/upload/sign/unit/qianzhang_${create_time + fileInfo.ext}`;
                 await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
                 await sendToWormhole(stream);
-                const result = await ctx.service.constructionUnit.update({ sign_path: filepath }, { id: stream.fields.id });
+                const result = await ctx.service.constructionUnit.update({sign_path: filepath}, {id: stream.fields.id});
                 if (result) {
-                    responseData.data = { sign_path: filepath };
+                    responseData.data = {sign_path: filepath};
                 } else {
                     throw '添加数据库失败';
                 }
@@ -450,7 +467,7 @@ module.exports = app => {
                     throw '没有访问权限';
                 }
                 const data = JSON.parse(ctx.request.body.data);
-                const result = await ctx.service.projectAccount.update(data, { id: data.id });
+                const result = await ctx.service.projectAccount.update(data, {id: data.id});
                 if (!result) {
                     throw '提交数据失败';
                 }
@@ -655,7 +672,7 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
@@ -668,20 +685,20 @@ module.exports = app => {
         async updateCategory(ctx) {
             try {
                 const projectId = ctx.session.sessionProject.id;
-                const responseData = { err: 0, msg: '', data: null };
+                const responseData = {err: 0, msg: '', data: null};
 
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.id) {
                     throw '提交数据错误';
                 }
                 if (data.name) {
-                    const count = await ctx.service.category.count({ pid: projectId, name: data.name });
+                    const count = await ctx.service.category.count({pid: projectId, name: data.name});
                     if (count >= 1) {
                         throw '存在同名类别';
                     }
                 }
 
-                const result = await ctx.service.category.update(data, { id: data.id });
+                const result = await ctx.service.category.update(data, {id: data.id});
                 if (!result) {
                     throw '提交数据失败';
                 }
@@ -691,13 +708,13 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
         async setCategoryValue(ctx) {
             try {
-                const responseData = { err: 0, msg: '', data: {} };
+                const responseData = {err: 0, msg: '', data: {}};
 
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.id) {
@@ -711,7 +728,7 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err instanceof String ? err : '提交数据失败', data: null };
+                ctx.body = {err: 1, msg: err instanceof String ? err : '提交数据失败', data: null};
             }
         }
 
@@ -738,7 +755,7 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
@@ -761,14 +778,18 @@ module.exports = app => {
                 ctx.body = responseData;
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
+
         async selfCategoryLevel(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
 
-                await ctx.service.projectAccount.defaultUpdate({id: ctx.session.sessionUser.accountId, self_category_level: data.self_category_level || ''});
+                await ctx.service.projectAccount.defaultUpdate({
+                    id: ctx.session.sessionUser.accountId,
+                    self_category_level: data.self_category_level || ''
+                });
                 ctx.body = {
                     err: 0, msg: '', data: null,
                 };
@@ -802,7 +823,7 @@ module.exports = app => {
                 ctx.redirect('/setting/info');
             } catch (err) {
                 this.log(err);
-                ctx.body = { err: 1, msg: err.toString(), data: null };
+                ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
 
@@ -826,7 +847,7 @@ module.exports = app => {
                 responseData.data = await ctx.service.projectAccount.isAccountExist(account, projectData.id);
                 ctx.body = responseData;
             } catch (error) {
-                ctx.body = { err: 1, msg: error.toString(), data: null };
+                ctx.body = {err: 1, msg: error.toString(), data: null};
             }
         }
 
@@ -849,7 +870,7 @@ module.exports = app => {
                 }
                 const showList = await ctx.service.settingShow.getList(projectData.page_path);
                 const sjsRela = await ctx.service.project.getSjsRela(projectId);
-                const renderData = { projectData, showList, sjsRela, settingConst };
+                const renderData = {projectData, showList, sjsRela, settingConst};
                 await this.layout('setting/show.ejs', renderData);
             } catch (error) {
                 this.log(error);
@@ -866,14 +887,14 @@ module.exports = app => {
             try {
                 // 获取项目id
                 const projectId = ctx.session.sessionProject.id;
-                const { data } = ctx.request.body;
-                const { id } = JSON.parse(data);
+                const {data} = ctx.request.body;
+                const {id} = JSON.parse(data);
                 const result = await ctx.service.settingShow.setDefaultLabel(id, projectId);
-                const responseData = { err: 0, msg: '', data: result };
+                const responseData = {err: 0, msg: '', data: result};
                 ctx.body = responseData;
             } catch (error) {
                 this.log(error);
-                ctx.body = { err: 1, msg: error.toString(), data: null };
+                ctx.body = {err: 1, msg: error.toString(), data: null};
             }
         }
 
@@ -882,7 +903,7 @@ module.exports = app => {
                 const projectId = ctx.session.sessionProject.id;
                 const data = JSON.parse(ctx.request.body.data);
                 const result = await ctx.service.project.updateSjsRela(projectId, data.sub, data.field, data.key, data.value);
-                ctx.body = { err: 0, msg: '', data: result };
+                ctx.body = {err: 0, msg: '', data: result};
             } catch (err) {
                 this.log(err);
                 this.ajaxErrorBody(err, '保存数据失败');
@@ -986,14 +1007,14 @@ module.exports = app => {
                     if (!result3) throw '保存数据失败';
                 }
 
-                ctx.body = { err: 0, msg: '', data: null };
+                ctx.body = {err: 0, msg: '', data: null};
             } catch (error) {
                 ctx.helper.log(error);
                 this.ajaxErrorBody(error, '保存数据失败');
             }
         }
 
-        async s2b (ctx) {
+        async s2b(ctx) {
             try {
                 const projectId = ctx.session.sessionProject.id;
                 await this._checkMenu(projectId);
@@ -1001,10 +1022,10 @@ module.exports = app => {
                 if (projectData === null) throw '没有对应的项目数据';
                 if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
 
-                const tenders = await ctx.service.tender.getAllDataByCondition({ where: { project_id: projectId } });
+                const tenders = await ctx.service.tender.getAllDataByCondition({where: {project_id: projectId}});
                 for (const t of tenders) {
                     t.measure_type_str = t.measure_type === measureType.tz.value ? measureType.tz.title : measureType.gcl.title;
-                    const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: t.id } });
+                    const stages = await ctx.service.stage.getAllDataByCondition({where: {tid: t.id}});
                     t.stage_count_str = `第${stages.length || 0}期`;
                     const user = await ctx.service.projectAccount.getAccountInfoById(t.user_id);
                     t.user_str = user.name + '-' + user.company;
@@ -1036,7 +1057,7 @@ module.exports = app => {
                 if (data.dagl_limit !== undefined) updateData.s2b_dagl_limit = data.dagl_limit;
                 if (data.dagl_check !== undefined) updateData.s2b_dagl_check = data.dagl_check;
                 await ctx.service.tender.saveApiRela(data.tid, updateData);
-                ctx.body = { err: 0, msg: '', data: null };
+                ctx.body = {err: 0, msg: '', data: null};
             } catch (error) {
                 ctx.helper.log(error);
                 this.ajaxErrorBody(error, '保存数据失败');
@@ -1053,7 +1074,7 @@ module.exports = app => {
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.type || data.status === undefined) throw '提交数据错误';
 
-                const responseData = { err: 0, msg: '', data: null };
+                const responseData = {err: 0, msg: '', data: null};
                 switch (data.type) {
                     case 'gxby':
                         responseData.data = await this.ctx.service.s2bProj.updateGxbyStatus(projectId, data.status, data.limit, data.ratio);
@@ -1061,7 +1082,8 @@ module.exports = app => {
                     case 'dagl':
                         responseData.data = await this.ctx.service.s2bProj.updateDaglStatus(projectId, data.status, data.limit, data.ratio);
                         break;
-                    default: throw '提交数据错误';
+                    default:
+                        throw '提交数据错误';
                 }
                 ctx.body = responseData;
             } catch (error) {
@@ -1082,17 +1104,17 @@ module.exports = app => {
                 const dataCollectAudits = await ctx.service.datacollectAudit.getList(projectId);
                 // 获取所有项目参与者
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({
-                    where: { project_id: ctx.session.sessionProject.id, enable: 1 },
+                    where: {project_id: ctx.session.sessionProject.id, enable: 1},
                     columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
                 });
                 // const accountGroupList = accountGroup.map((item, idx) => {
                 //     const groupList = accountList.filter(item => item.account_group === idx);
                 //     return { groupName: item, groupList };
                 // });
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
                 const accountGroupList = unitList.map(item => {
                     const groupList = accountList.filter(item1 => item1.company === item.name);
-                    return { groupName: item.name, companyId: item.id, groupList };
+                    return {groupName: item.name, companyId: item.id, groupList};
                 });
                 const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
                 const tenders = await ctx.service.tender.getList('', null, 1);
@@ -1127,13 +1149,19 @@ module.exports = app => {
                 }
                 switch (data.type) {
                     case 'show':
-                        responseData.data = await ctx.service.project.update({ data_collect: data.data_collect, data_collect_pages: data.data_collect_pages.join(',') }, { id: projectId });
+                        responseData.data = await ctx.service.project.update({
+                            data_collect: data.data_collect,
+                            data_collect_pages: data.data_collect_pages.join(',')
+                        }, {id: projectId});
                         ctx.session.sessionProject.dataCollect = data.data_collect;
                         ctx.session.sessionProject.showDataCollect = data.data_collect ? 1 : 0;
                         break;
                     case 'add-audit':
                         // 判断该用户是否已加入到表中,已加入则提示无需添加
-                        const auditInfo = await ctx.service.datacollectAudit.getDataByCondition({ pid: projectId, uid: data.id });
+                        const auditInfo = await ctx.service.datacollectAudit.getDataByCondition({
+                            pid: projectId,
+                            uid: data.id
+                        });
                         if (auditInfo) {
                             throw '该用户已存在权限中,无需重复添加';
                         }
@@ -1188,7 +1216,116 @@ module.exports = app => {
                             addDatacollect: ctx.session.sessionProject.page_show.addDataCollect,
                         };
                         break;
-                    default: throw '参数有误';
+                    default:
+                        throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        async manage(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) throw '没有对应的项目数据';
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+
+                const tenderList = await ctx.service.tender.getList('', null, 1);
+                const removeTenders = await ctx.service.shenpiAudit.getRemoveTenders(tenderList);
+                if (removeTenders.length > 0) {
+                    ctx.helper._.remove(tenderList, function(n) {
+                        return removeTenders.indexOf(n.id) !== -1;
+                    });
+                }
+                for (const t of tenderList) {
+                    t.visitor = (await this.ctx.service.tenderTourist.getTourists(t.id)).map(x => { return x.user_name; });
+                    await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
+                }
+                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+
+                // 获取所有项目参与者
+                const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                    where: {project_id: ctx.session.sessionProject.id, enable: 1},
+                    columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
+                });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
+                const accountGroupList = unitList.map(item => {
+                    const groupList = accountList.filter(item1 => item1.company === item.name);
+                    return {groupName: item.name, groupList};
+                });
+                const renderData = {
+                    projectData,
+                    tenderList,
+                    auditConst,
+                    auditType: auditConst.auditType,
+                    accountList,
+                    accountGroup: accountGroupList,
+                    categoryData,
+                    settingConst,
+                    measureType: tenderConst.measureType,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.manage),
+                    uid: this.ctx.session.sessionUser.accountId,
+                    pid: this.ctx.session.sessionProject.id,
+                    scPermission: scheduleConst.permission,
+                };
+                renderData.selfCategoryLevel = await this.ctx.service.projectAccount.getSelfCategoryLevel(this.ctx.session.sessionUser.accountId);
+                await this.layout('setting/manage.ejs', renderData, 'setting/manage_modal.ejs');
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        async manageTenderSave(ctx) {
+            try {
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有设置权限';
+                const projectId = ctx.session.sessionProject.id;
+                const responseData = {
+                    err: 0, msg: '', data: {},
+                };
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type) {
+                    throw '提交数据错误';
+                }
+                if (!data.tid) {
+                    throw '参数有误';
+                }
+                const tender = await ctx.service.tender.getDataById(data.tid);
+                if (!tender) {
+                    throw '标段不存在';
+                }
+                const tenderInfo = await ctx.service.tenderInfo.getTenderInfo(tender.id);
+                switch (data.type) {
+                    case 'msg':
+                        const lastRevise = await ctx.service.ledgerRevise.getLastestRevise(tender.id);
+                        const revising = (lastRevise && lastRevise.status !== auditConst.revise.status.checked) || false;
+                        // 获取审批流,游客账号,及投资进度账号信息
+                        responseData.data.tender = tender;
+                        responseData.data.revising = revising;
+                        responseData.data.shenpi = await ctx.service.shenpiAudit.getShenpi(tender.id, tenderInfo);
+                        const tourists = await ctx.service.tenderTourist.getTourists(tender.id);
+                        for (const t of tourists) {
+                            t.permission = await ctx.service.tenderTourist.getTouristPermission(t);
+                        }
+                        responseData.data.tourists = tourists;
+                        responseData.data.scheduleAuditList = ctx.session.sessionProject.page_show.xxjd ? await ctx.service.scheduleAudit.getAllDataByCondition({ where: { tid: tender.id } }) : [];
+                        break;
+                    case 'copy2otu':
+                        if (data.userType === 'tourist') {
+                            await ctx.service.tenderTourist.setOtherTender(data.tidList, data.auditList);
+                        } else if (data.userType === 'schedule') {
+                            await ctx.service.scheduleAudit.setOtherTender(data.tidList, data.auditList);
+                        } else {
+                            throw '参数有误';
+                        }
+                        break;
+                    default:
+                        throw '参数有误';
                 }
                 ctx.body = responseData;
             } catch (err) {

+ 1 - 22
app/controller/tender_controller.js

@@ -914,28 +914,7 @@ module.exports = app => {
                 }
             }
             const tenders = await ctx.service.tender.getList('', null, 1);
-            const removeTenders = [];
-            for (const tender of tenders) {
-                const shenpiInfo = await ctx.service.tenderInfo.getTenderShenpiInfo(tender.id);
-                if (!shenpiInfo) {
-                    removeTenders.push(tender.id);
-                } else {
-                    tender.shenpiInfo = shenpiInfo;
-                    // 获取所有的固定审批流或固定终审
-                    const shenpiauditList = {};
-                    for (const shenpi in tender.shenpiInfo) {
-                        if (tender.shenpiInfo[shenpi] === shenpiConst.sp_status.gdspl) {
-                            const shenpiList = await ctx.service.shenpiAudit.getAllDataByCondition({ where: { tid: tender.id, sp_type: shenpiConst.sp_type[shenpi], sp_status: tender.shenpiInfo[shenpi] } });
-                            const shenpiIdList = ctx.helper._.map(shenpiList, 'audit_id');
-                            shenpiauditList[shenpi] = shenpiIdList.length ? shenpiIdList : null;
-                        } else if (tender.shenpiInfo[shenpi] === shenpiConst.sp_status.gdzs) {
-                            const shenpiInfo = await ctx.service.shenpiAudit.getDataByCondition({ tid: tender.id, sp_type: shenpiConst.sp_type[shenpi], sp_status: tender.shenpiInfo[shenpi] });
-                            shenpiauditList[shenpi] = shenpiInfo && shenpiInfo.audit_id ? [shenpiInfo.audit_id] : null;
-                        }
-                    }
-                    tender.shenpiauditList = shenpiauditList;
-                }
-            }
+            const removeTenders = await ctx.service.shenpiAudit.getRemoveTenders(tenders);
             if (removeTenders.length > 0) {
                 ctx.helper._.remove(tenders, function(n) {
                     return removeTenders.indexOf(n.id) !== -1;

+ 2 - 2
app/public/js/material_checklist.js

@@ -1180,8 +1180,8 @@ $(document).ready(() => {
                     if(t.children.length === 0) continue;
                     const findObject = {
                         b_code: t.b_code ? t.b_code.toString() : t.b_code,
-                        name: t.name.toString() ? t.name.toString() : t.name,
-                        unit: t.unit.toString() ? t.unit.toString() : t.unit,
+                        name: t.name ? t.name.toString() : t.name,
+                        unit: t.unit ? t.unit.toString() : t.unit,
                     };
                     if (!ignoreUnitPrice) findObject.unit_price = t.unit_price ? parseFloat(t.unit_price) : null;
                     const order = _.findIndex(gclGatherData, findObject);

+ 1 - 1
app/public/js/setting_datacollect_tender.js

@@ -87,7 +87,7 @@ function initTenderTree () {
             tenderTree.push(t);
         }
     }
-    sortTenderTree();
+    sortTenderTree(tenderTree);
 }
 function recursiveGetTenderNodeHtml (node, arr, pid) {
     // console.log(node, tender)

+ 728 - 0
app/public/js/setting_manage.js

@@ -0,0 +1,728 @@
+
+// 游客及投资管理用户添加到其他标段
+const tenderTree4User = [];
+let parentId4User = 0;
+// 初始化TenderTree数据
+function initTenderTree4User () {
+    const levelCategory = category.filter(function (c) {
+        return c.level && c.level > 0;
+    });
+    function findCategoryNode4User(cid, value, array) {
+        for (const a of array) {
+            if (a.cid === cid && a.vid === value) {
+                return a;
+            }
+        }
+    }
+    function getCategoryNode4User(category, value, parent, i = null) {
+        const array = parent ?  parent.children : tenderTree4User;
+        let cate = findCategoryNode4User(category.id, value, array);
+        if (!cate) {
+            const cateValue = findNode2('id', value, category.value);
+            if (!cateValue) return null;
+            cate = {
+                cid: category.id,
+                vid: value,
+                name: cateValue.value,
+                children: [],
+                level: i ? i : category.level,
+                sort_id: ++parentId2,
+                sort: cateValue.sort,
+            };
+            array.push(cate);
+        }
+        return cate;
+    }
+    function loadTenderCategory4User (tender) {
+        let tenderCategory = null;
+        for (const [index,lc] of levelCategory.entries()) {
+            const tenderCate = findNode2('cid', lc.id, tender.category);
+            if (tenderCate) {
+                tenderCategory = getCategoryNode4User(lc, tenderCate.value, tenderCategory);
+            } else {
+                if (index === 0 && tender.category) {
+                    for (const [i,c] of tender.category.entries()) {
+                        const cate = findNode2('id', c.cid, category);
+                        if (cate) tenderCategory = getCategoryNode4User(cate, c.value, tenderCategory, i+1);
+                    }
+                }
+                return tenderCategory;
+            }
+        }
+        return tenderCategory;
+    }
+    tenderTree4User.splice(0, tenderTree4User.length);
+    for (const t of tenders) {
+        t.valid = true;
+        delete t.level;
+        if (t.category && levelCategory.length > 0) {
+            const parent = loadTenderCategory4User(t);
+            if (parent) {
+                t.level = parent.level + 1;
+                parent.children.push(t);
+            } else {
+                tenderTree4User.push(t);
+            }
+        } else {
+            tenderTree4User.push(t);
+        }
+    }
+    sortTenderTree(tenderTree4User);
+}
+function recursiveGetTenderNodeHtml4User (node, arr, pid) {
+    const html = [];
+    html.push('<tr pid="' + pid + '">');
+    // 名称
+    html.push('<td class="in-' + node.level + '">');
+    if (node.cid) {
+        html.push('<i class="fa fa-folder-o"></i> ', node.name);
+    } else {
+        html.push('<span class="text-muted mr-2">');
+        html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+        html.push('</span>');
+        //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
+        html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
+    }
+    html.push('</td>');
+    html.push('<td>');
+    if (!node.cid) {
+        html.push('<input data-tid="'+ node.id +'" type="checkbox"'+ (cur_tenderid === node.id ? ' checked disabled' : '') +'>');
+    }
+    html.push('</td>');
+    html.push('</tr>');
+    if (node.children) {
+        for (const c of node.children) {
+            html.push(recursiveGetTenderNodeHtml4User(c, node.children, node.sort_id));
+        }
+    }
+    return html.join('');
+}
+// 根据TenderTree数据获取Html代码
+function getTenderTreeHtml4User () {
+    if (tenderTree4User.length > 0) {
+        const html = [];
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead>', '<tr>');
+        html.push('<th>名称</th>');
+        html.push('<th width="40">选择</th>');
+        html.push('</tr>', '</thead>');
+        parentId4User = 0;
+        for (const t of tenderTree4User) {
+            html.push(recursiveGetTenderNodeHtml4User(t, tenderTree4User, ''));
+        }
+        html.push('</table>');
+        return html.join('');
+    } else {
+        return EmptyTenderHtml.join('');
+    }
+}
+
+
+$(document).ready(() => {
+    autoFlashHeight();
+    function getObjHeight(select) {
+        return select.length > 0 ? select.height() : 0;
+    }
+    const cHeader = getObjHeight($(".c-header"));
+    $('.tab-content').height($(window).height()-cHeader-90+53-46);
+
+    $('body').on('click', '.c-body .tender-info', function () {
+        $('.c-body .tender-info').removeClass('table-warning');
+        $(this).addClass('table-warning');
+        const tid = parseInt($(this).attr('data-id'));
+        if (!tid) {
+            toastr.warning('不存在标段无法设置');
+            return;
+        }
+        // 请求获取右侧列表信息
+        postData('/setting/manage/tender/save', { type: 'msg', tid: parseInt($(this).attr('data-id'))}, function (result) {
+            sp_lc = result.shenpi.sp_lc;
+            sp_type = result.shenpi.sp_type;
+            sp_status = result.shenpi.sp_status;
+            sp_status_list = result.shenpi.sp_status_list;
+            cur_tenderid = result.tender.id;
+            cur_uid = result.tender.user_id;
+            setShenpiHtml(result.shenpi, result.tender, result.revising);
+            setTouristHtml(result.tourists);
+            setScheduleHtml(result.scheduleAuditList);
+            resetAddUserHtml();
+        });
+    });
+
+    setTimeout(function () {
+        $('.c-body .tender-info').eq(0).click();// 需要延时加载
+    }, 500);
+
+
+    $('body').on('click', '.nav .nav-link', function () {
+        if ($(this).attr('href') === '#splc') {
+            $('#user-set').hide();
+        } else {
+            $('#user-set').show();
+            if ($(this).attr('href') === '#guest') {
+                $('#add_user_dropdownMenuButton').attr('data-type', 'tourist');
+            } else if ($(this).attr('href') === '#tzpro') {
+                $('#add_user_dropdownMenuButton').attr('data-type', 'schedule');
+            }
+        }
+    });
+
+    $('body').on('click', '.c-body a', function (e) {
+        e.stopPropagation();
+    });
+
+    // 权限设置
+    $('body').on('click', '#tourist-users .set-tourist-permission', function () {
+        const id = parseInt($(this).data('id'));
+        const permission = {
+            file: ($(this).attr('name') === 'file' ? $(this).is(':checked') : $('#' + id + '_file').is(':checked')) ? 1 : 0,
+            tag: ($(this).attr('name') === 'tag' ? $(this).is(':checked') : $('#' + id + '_tag').is(':checked')) ? 1 : 0,
+        }
+        const prop = {
+            id,
+            type: 'permission',
+            permission,
+        }
+        console.log(prop);
+        postData('/tender/' + cur_tenderid + '/tourist/audit/save', prop, function (data) {
+        });
+    });
+
+    // 权限更改
+    $('body').on('click', '#schedule-users input[type="checkbox"]', function () {
+        let permission = scPermission.no;
+        const value = parseInt($(this).data('zhi'));
+        if ($(this).is(':checked')) {
+            if (value === scPermission.edit) {
+                permission = scPermission.edit;
+                $(this).parents('td').siblings().find('input').prop('checked', true);
+            } else if (value === scPermission.show) {
+                permission = scPermission.show;
+            }
+        } else {
+            if (value === scPermission.edit) {
+                permission = scPermission.show;
+            } else if (value === scPermission.show) {
+                permission = scPermission.no;
+                $(this).parents('td').siblings().find('input').prop('checked', false);
+            }
+        }
+        const id = parseInt($(this).data('id'));
+        const prop = {
+            id,
+            permission,
+            type: 'edit',
+        };
+        const _self = $(this);
+        postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+        });
+    });
+
+    // 移除游客用户
+    $('body').on('click', '#tourist-users .remove-tourist-user', function () {
+        $('#remove_user_type').val('tourist');
+        $('#remove_user_id').val($(this).data('id'));
+    });
+
+    // 移除投资进度用户
+    $('body').on('click', '#schedule-users .remove-schedule-user', function () {
+        $('#remove_user_type').val('schedule');
+        $('#remove_user_id').val($(this).data('id'));
+    });
+
+    // 移除用户确定
+    $('#remove_user_btn').click(function () {
+        const type = $('#remove_user_type').val();
+        if (type !== 'tourist' && type !== 'schedule') {
+            toastr.error('参数有误');
+            return;
+        }
+        const id = parseInt($('#remove_user_id').val());
+        const prop = {
+            id: id,
+            type: 'del',
+        };
+        postData('/tender/' + cur_tenderid + '/' + type + '/audit/save', prop, function (data) {
+            $('#'+ type + '-users').find('tr[data-id="'+ id +'"]').remove();
+            $('#remove-user').modal('hide');
+        });
+    });
+
+    // 投资进度
+    let timerAddUser = null;
+    let oldSearchValAddUser = null;
+    $('body').on('input propertychange', '#add_user_dropdownMenu2 .gr-search', function (e) {
+        oldSearchValAddUser = e.target.value;
+        timerAddUser && clearTimeout(timerAddUser);
+        timerAddUser = setTimeout(() => {
+            const newVal = $(this).val();
+            if (newVal && newVal === oldSearchValAddUser) {
+                let html = '';
+                accountList.filter(item => item && item.id !== cur_uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                });
+                $('#add_user_dropdownMenu2 .book-list').empty();
+                $('#add_user_dropdownMenu2 .book-list').append(html);
+            } else {
+                if (!$('#add_user_dropdownMenu2 .acc-btn').length) {
+                    resetAddUserHtml();
+                }
+            }
+        }, 400);
+    });
+
+    function resetAddUserHtml() {
+        let html = '';
+        accountGroup.forEach((group, idx) => {
+            if (!group) return;
+            html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+            group.groupList.forEach(item => {
+                if (item.id !== cur_uid) {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                }
+            });
+            html += '</div>';
+        });
+        $('#add_user_dropdownMenu2 .book-list').empty();
+        $('#add_user_dropdownMenu2 .book-list').append(html);
+    }
+
+    // 添加审批流程按钮逻辑
+    $('body').on('click', '#add_user_dropdownMenu2 .book-list dt', function () {
+        const idx = $(this).find('.acc-btn').attr('data-groupid');
+        const type = $(this).find('.acc-btn').attr('data-type');
+        if (type === 'hide') {
+            $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o');
+                $(this).find('.acc-btn').attr('data-type', 'show');
+
+            })
+        } else {
+            $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square');
+                $(this).find('.acc-btn').attr('data-type', 'hide');
+            })
+        }
+        return false;
+    });
+
+    // 选中用户
+    $('body').on('click', '#add_user_dropdownMenu2 dl dd', function () {
+        const id = parseInt($(this).data('id'));
+        if (id) {
+            const user = _.find(accountList, function (item) {
+                return item.id === id;
+            });
+            const type = $('#add_user_dropdownMenuButton').attr('data-type');
+            if (type === 'tourist') {
+                const saIdList = [];
+                for (let i = 0; i < $('#tourist-users tr').length; i++) {
+                    saIdList.push(parseInt($('#tourist-users tr').eq(i).data('uid')));
+                }
+                if (_.includes(saIdList, id)) {
+                    toastr.error('该用户已存在列表中,无需重复添加');
+                    return;
+                }
+
+                const prop = {
+                    user_id: id,
+                    type: 'add',
+                };
+                postData('/tender/' + cur_tenderid + '/tourist/audit/save', prop, function (data) {
+                    const html = `<tr data-uid="${user.id}" data-id="${data.id}">
+                                    <td>${user.name}</td>
+                                    <td>${user.role}</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="${data.id}_file" data-id="${data.id}" name="file" class="custom-control-input set-tourist-permission">
+                                            <label class="custom-control-label" for="${data.id}_file"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="${data.id}_tag" data-id="${data.id}" name="tag" class="custom-control-input set-tourist-permission">
+                                            <label class="custom-control-label" for="${data.id}_tag"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-id="${data.id}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-tourist-user">移除</a>
+                                    </td>
+                                </tr>`;
+                    $('#tourist-users').append(html);
+                });
+            } else if (type === 'schedule') {
+                const user = _.find(accountList, function (item) {
+                    return item.id === id;
+                });
+                const saIdList = [];
+                for (let i = 0; i < $('#schedule-users tr').length; i++) {
+                    saIdList.push(parseInt($('#schedule-users tr').eq(i).data('uid')));
+                }
+                if (_.includes(saIdList, id)) {
+                    toastr.error('该用户已存在列表中,无需重复添加');
+                    return;
+                }
+
+                const prop = {
+                    audit_id: id,
+                    type: 'add',
+                };
+                postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+                    const html = `<tr data-uid="${user.id}" data-id="${data.id}">
+                                    <td>${user.name}</td>
+                                    <td>${user.role}</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" data-zhi="${scPermission.show}" data-id="${data.id}" id="${data.id}_customRadio41" name="customCheckbox" class="custom-control-input" checked>
+                                            <label class="custom-control-label" for="${data.id}_customRadio41"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" data-zhi="${scPermission.edit}" data-id="${data.id}" id="${data.id}_customRadio42" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="${data.id}_customRadio42"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-id="${data.id}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-schedule-user">移除</a>
+                                    </td>
+                                </tr>`;
+                    $('#schedule-users').append(html);
+                });
+            }
+        }
+    });
+
+    initTenderTree4User();
+    $('#set-other-tender-user-a').click(function () {
+        if(!cur_tenderid) {
+            toastr.warning('未选中标段无法设置');
+            return;
+        }
+        const userType = $('#add_user_dropdownMenuButton').attr('data-type');
+        const saIdList = [];
+        for (let i = 0; i < $('#'+ userType +'-users tr').length; i++) {
+            saIdList.push(parseInt($('#'+ userType +'-users tr').eq(i).data('uid')));
+        }
+        if (saIdList.length > 0) {
+            $('#bdcopy').modal('show');
+        } else {
+            toastr.warning('未存在'+ (userType === 'tourist' ? '游客' : '投资进度') +'用户账号,无法应用至其他标段');
+            return;
+        }
+    });
+    $('#bdcopy').on('show.bs.modal', function () {
+        const html = getTenderTreeHtml4User();
+        $('#tender-list-4user').html(html);
+        $('#search-tender').val('');
+        $('#search-tender-result').text('0/0');
+        $('#up-tender-search').attr('disabled', true);
+        $('#down-tender-search').attr('disabled', true);
+        setTimeout(function () { $('#tender-list-4user').scrollTop(0); },500);
+    });
+
+
+    let timer4SearchTender = null;
+    let oldSearchVal4SearchTender = null;
+    $('body').on('input propertychange', '#bdcopy input[name="tender-name"]', function(e) {
+        oldSearchVal4SearchTender = e.target.value;
+        timer4SearchTender && clearTimeout(timer4SearchTender);
+        timer4SearchTender = setTimeout(() => {
+            const newVal = $(this).val();
+
+            const resultLength = $('#tender-list-4user').find('.result').length;
+            if (resultLength > 0) {
+                let content = $('#tender-list-4user').html();
+                const replaceStr = $('#tender-list-4user').find('.result').eq(0).html();
+                const regExp2 = new RegExp('<span class="result" style="background: yellow;">' + replaceStr + '</span>', 'g');
+                content = content.replace(regExp2, replaceStr);
+                const regExp3 = new RegExp('<span class="result" style="background: orange;">' + replaceStr + '</span>', 'g');
+                content = content.replace(regExp3, replaceStr);
+                $('#tender-list-4user').html(content);
+            }
+            $('#search-tender-result').text('0/0');
+            $('#up-tender-search').attr('disabled', true);
+            $('#down-tender-search').attr('disabled', true);
+            if (newVal && newVal === oldSearchVal4SearchTender) {
+                const regExp = new RegExp(newVal, 'g');
+                for (let i = 0; i < $('#tender-list-4user tr').length; i++) {
+                    if (_.includes($('#tender-list-4user tr').eq(i).children('td').eq(0).children('a').html(), newVal)) {
+                        $('#tender-list-4user tr').eq(i).children('td').eq(0).children('a').html($('#tender-list-4user tr').eq(i).children('td').eq(0).children('a').html().replace(regExp, '<span class="result" style="background: yellow;">' + newVal + '</span>'))
+                    }
+                }
+                const resultLength2 = $('#tender-list-4user').find('.result').length;
+                if (resultLength2 > 0) {
+                    $('#tender-list-4user').find('.result').eq(0).css('background', 'orange');
+                    $('#search-tender-result').text('1/' + resultLength2);
+                    $('#up-tender-search').attr('disabled', false);
+                    $('#down-tender-search').attr('disabled', false);
+                }
+            }
+            if($('#tender-list-4user').find('.result').length > 0) {
+                const X = $('#tender-list-4user').find('.result').eq(0).offset().top;
+                $('#tender-list-4user').scrollTop(X - $('#tender-list-4user').offset().top + $('#tender-list-4user').scrollTop() - 30);
+            }
+        }, 400);
+    });
+
+    $('#up-tender-search').on('click', function () {
+        const cur = parseInt($('#search-tender-result').text().split('/')[0]);
+        const total = parseInt($('#search-tender-result').text().split('/')[1]);
+        const now = cur - 1 !== 0 ? cur - 1: total;
+        $('#tender-list-4user').find('.result').eq(cur-1).css('background', 'yellow');
+        $('#tender-list-4user').find('.result').eq(now-1).css('background', 'orange');
+        // $('#tender-list tr').eq(searchUser[cur-1]).children('td').eq(2).html($('#tender-list tr').eq(searchUser[cur-1]).children('td').eq(2).html().replace('<span class="result" style="background:orange;">', '<span class="result" style="background:yellow;">'))
+        // $('#tender-list tr').eq(searchUser[now-1]).children('td').eq(2).html($('#tender-list tr').eq(searchUser[now-1]).children('td').eq(2).html().replace('<span class="result" style="background:yellow;">', '<span class="result" style="background:orange;">'))
+        $('#search-tender-result').text(now + '/' + total);
+        const X = $('#tender-list-4user').find('.result').eq(now-1).offset().top;
+        $('#tender-list-4user').scrollTop(X - $('#tender-list-4user').offset().top + $('#tender-list-4user').scrollTop() - 30);
+    });
+
+    $('#down-tender-search').on('click', function () {
+        const cur = parseInt($('#search-tender-result').text().split('/')[0]);
+        const total = parseInt($('#search-tender-result').text().split('/')[1]);
+        const now = cur + 1 > total ? 1: cur + 1;
+        $('#tender-list-4user').find('.result').eq(cur-1).css('background', 'yellow');
+        $('#tender-list-4user').find('.result').eq(now-1).css('background', 'orange');
+        // $('#tender-list tr').eq(searchUser[cur-1]).children('td').eq(2).html($('#tender-list tr').eq(searchUser[cur-1]).children('td').eq(2).html().replace('<span class="result" style="background:orange;">', '<span class="result" style="background:yellow;">'))
+        // $('#tender-list tr').eq(searchUser[now-1]).children('td').eq(2).html($('#tender-list tr').eq(searchUser[now-1]).children('td').eq(2).html().replace('<span class="result" style="background:yellow;">', '<span class="result" style="background:orange;">'))
+        $('#search-tender-result').text(now + '/' + total);
+        const X = $('#tender-list-4user').find('.result').eq(now-1).offset().top;
+        $('#tender-list-4user').scrollTop(X - $('#tender-list-4user').offset().top + $('#tender-list-4user').scrollTop() -30);
+    });
+
+    $('#save-other-tender-user').click(function () {
+        $(this).attr('disabled', true);
+        const num = $('#tender-list-4user input:checked').length;
+        if (num < 2) {
+            toastr.warning('请选择需要应用同步的标段');
+            $(this).attr('disabled', false);
+            return;
+        }
+        const userType = $('#add_user_dropdownMenuButton').attr('data-type');
+        const data = {
+            type: 'copy2otu',
+            tid: cur_tenderid,
+            userType,
+        };
+        const saIdList = [];
+        for (let i = 0; i < $('#'+ userType +'-users tr').length; i++) {
+            const userData = {
+                uid: parseInt($('#'+ userType +'-users tr').eq(i).data('uid')),
+            }
+            if (userType === 'tourist') {
+                userData.permission = {
+                    file: $('#tourist-users tr').eq(i).find('input[type="checkbox"]').eq(0).is(':checked') ? 1 : 0,
+                    tag: $('#tourist-users tr').eq(i).find('input[type="checkbox"]').eq(1).is(':checked') ? 1 : 0,
+                };
+            } else if (userType === 'schedule') {
+                let permission = scPermission.no;
+                const _this = $('#schedule-users tr').eq(i).find('input[type="checkbox"]').eq(0);
+                const _other = $('#schedule-users tr').eq(i).find('input[type="checkbox"]').eq(1);
+                if (_this.is(':checked') && _other.is(':checked')) {
+                    permission = scPermission.edit;
+                } else if (_this.is(':checked') && !_other.is(':checked')) {
+                    permission = scPermission.show;
+                }
+                userData.permission = permission;
+            }
+            saIdList.push(userData);
+        }
+        data.auditList = saIdList;
+        // 获取已选中的标段
+        const tenderList = [];
+        for (let i = 0; i < num; i++) {
+            const tid = parseInt($('#tender-list-4user input:checked').eq(i).data('tid'));
+            if (tid !== cur_tenderid) {
+                tenderList.push(tid);
+            }
+        }
+        data.tidList = tenderList.join(',');
+        console.log(data);
+        // 请求获取右侧列表信息
+        const _self = $(this);
+        postData('/setting/manage/tender/save', data, function (result) {
+            toastr.success('应用至其他标段成功');
+            _self.attr('disabled', false);
+            $('#bdcopy').modal('hide');
+        }, function () {
+            _self.attr('disabled', false);
+        });
+    });
+})
+
+const tenderListSpec = (function(){
+    function getTenderTreeHeaderHtml() {
+        const html = [];
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead style="position: fixed;left:176px;top: 34px;">', '<tr>');
+        html.push('<th class="text-center" style="width: 80%">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 20%">', '创建人', '</th>');
+        html.push('</tr>', '</thead>');
+        return html.join('');
+    }
+    function getTenderNodeHtml(node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '"', (node.cid ? '' : 'class="tender-info" data-id="'+ node.id +'"'), '>');
+        // 名称
+        html.push('<td style="width: 80%" class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
+        } else {
+            html.push('<div class="d-flex justify-content-between align-items-center">');
+            html.push('<div>');
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
+            html.push('<a href="javascript: void(0)" id="' + node.id + '" name="name">', node.name, '</a>');
+            html.push('</div>');
+            html.push('<div class="btn-group-table" tid="' + node.id + '">');
+            html.push('<a href="javascript:void(0);" name="edit" class="mr-1"><i class="fa fa-pencil fa-fw"></i></a>');
+            const hasStage = node.progress ? node.stage_count > 0 : !!node.lastStage;
+            if (!hasStage) {
+                html.push('<a href="javascript:void(0);" name="del" class="mr-1"><i class="fa fa-trash-o fa-fw text-danger"></i></a>');
+            } else {
+                html.push('<a href="javascript:void(0);" data-toggle="tooltip" title="请先删除所有期" class="mr-1"><i class="fa fa-trash-o fa-fw text-secondary"></i></a>');
+            }
+            html.push('</div></div>');
+        }
+        html.push('</td>');
+        // 创建人
+        html.push('<td style="width: 20%" class="text-center">', node.user_name ? node.user_name : '', '</td>');
+        html.push('</tr>');
+        return html.join('');
+    }
+    return { getTenderNodeHtml, getTenderTreeHeaderHtml }
+})();
+
+function setShenpiHtml(shenpi, tender, revising) {
+    let html = '';
+    if (shenpi.sp_lc.length > 0) {
+        for (const sp of shenpi.sp_lc) {
+            html += `<div class="card mb-3">
+                                <div class="card-body ${sp.code}_div">
+                                    <a class="pull-right set-otherTender" data-name="${sp.name}" data-code="${sp.code}" href="#batch" data-toggle="modal" data-target="#batch">设置其他标段</a>
+                                    <a class="pull-right set-otherShenpi mr-3" data-name="${sp.name}" data-code="${sp.code}" href="javascript: void(0);">设置其他流程</a>`;
+            if (sp.code === 'stage' && !revising && tender.ledger_status === auditConst.ledger.status.checked) {
+                html += `<a class="pull-right mr-3" id="stage_cooperation" ${sp.status !== shenpi.sp_status.gdspl ? 'style="display: none"' : '' } data-name="${sp.name}" data-code="${sp.code}" href="#cooperation" data-toggle="modal" data-target="#cooperation">多人协同 <i class="fa fa-lock"></i></a>`;
+            }
+            html += `
+                                    <h5 class="card-title">${sp.name}</h5>
+                                    <div class="form-group">
+                                        <div class="form-group form-check">`;
+            for (const st in shenpi.sp_status_list) {
+                if (shenpi.sp_status_list[st]) {
+                    html += `<div class="custom-control custom-checkbox custom-control-inline">
+                                                    <input type="radio" class="custom-control-input" data-code="${sp.code}" value="${shenpi.sp_status_list[st].status}" name="${sp.code}" id="${sp.code}_${shenpi.sp_status_list[st].status}" ${sp.status === shenpi.sp_status_list[st].status ? 'checked' : ''}>
+                                                    <label class="custom-control-label" for="${sp.code}_${shenpi.sp_status_list[st].status}">${shenpi.sp_status_list[st].name}</label>
+                                                </div>`;
+                }
+            }
+            html += `
+                                        </div>
+                                    </div>
+                                    <div class="alert alert-warning">${shenpi.sp_status_list[sp.status].name}:${shenpi.sp_status_list[sp.status].msg}</div>
+                                    <div class="lc-show">`;
+            if (sp.status === sp_status.sqspr) {
+                html += ``;
+            } else if (sp.status === sp_status.gdspl) {
+                let addhtml = '<ul class="list-unstyled">\n';
+                const flow = sp_lc.find(x => { return x.code === sp.code; });
+                flow.auditGroupList = sp.auditGroupList;
+                if (sp.auditGroupList && sp.auditGroupList.length !== 0) {
+                    for(const [i, auditGroup] of sp.auditGroupList.entries()) {
+                        addhtml += auditUtils.getAuditGroupHtml(sp.code, auditGroup, i + 1);
+                    }
+                    addhtml += '<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>';
+                } else {
+                    addhtml += auditUtils.getAuditGroupHtml(sp.code, [], 1);
+                }
+                addhtml += '</ul>\n';
+                html += addhtml;
+            } else if (sp.status === sp_status.gdzs) {
+                let addhtml = '<ul class="list-unstyled">\n' +
+                    '                                        <li class="d-flex justify-content-start mb-3">\n' +
+                    '                                            <span class="col-auto">授权审批人</span>\n' +
+                    '                                            <span class="col-7">\n' +
+                    '                                                <span class="d-inline-block"></span>\n' +
+                    '                                            </span>\n' +
+                    '                                        </li>\n';
+                addhtml += sp.audit ? makeAudit(sp.audit) : makeSelectAudit(sp.code);
+                addhtml += '</ul>\n';
+                html += addhtml;
+            }
+            html += `                </div>
+                                </div>
+                            </div>`;
+        }
+    }
+    $('#splc').html(html);
+}
+
+function setTouristHtml(tourists) {
+    let html = '';
+    if (tourists.length > 0) {
+        for (const t of tourists) {
+            html += `<tr data-uid="${t.user_id}" data-id="${t.id}">
+                                    <td>${t.user_name}</td>
+                                    <td>${t.user_role}</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="${t.id}_file" data-id="${t.id}" name="file" class="custom-control-input set-tourist-permission" ${t.permission.file ? 'checked' : ''}>
+                                            <label class="custom-control-label" for="${t.id}_file"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="${t.id}_tag" data-id="${t.id}" name="tag" class="custom-control-input set-tourist-permission" ${t.permission.tag ? 'checked' : ''}>
+                                            <label class="custom-control-label" for="${t.id}_tag"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-id="${t.id}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-tourist-user">移除</a>
+                                    </td>
+                                </tr>`;
+        }
+    }
+    $('#tourist-users').html(html);
+}
+
+function setScheduleHtml(scheduleAuditList) {
+    let html = '';
+    if (scheduleAuditList.length > 0) {
+        for (const sa of scheduleAuditList) {
+            const audit = _.find(accountList, { id: sa.audit_id });
+            html += `<tr data-uid="${sa.audit_id}" data-id="${sa.id}">
+                                    <td>${audit ? audit.name : ''}</td>
+                                    <td>${audit ? audit.role : ''}</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" data-zhi="${scPermission.show}" data-id="${sa.id}" id="${sa.id}_customRadio41" name="customCheckbox" class="custom-control-input" ${sa.permission !== scPermission.no ? 'checked' : ''}>
+                                            <label class="custom-control-label" for="${sa.id}_customRadio41"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" data-zhi="${scPermission.edit}" data-id="${sa.id}" id="${sa.id}_customRadio42" name="customCheckbox" class="custom-control-input" ${sa.permission === scPermission.edit ? 'checked' : ''}>
+                                            <label class="custom-control-label" for="${sa.id}_customRadio42"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-id="${sa.id}" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger remove-schedule-user">移除</a>
+                                    </td>
+                                </tr>`;
+        }
+    }
+    $('#schedule-users').html(html);
+}

+ 102 - 100
app/public/js/shenpi.js

@@ -7,11 +7,14 @@
  * @date 2020/10/09
  * @version
  */
-const tenderTree = [];
-let parentId = 0;
+// 冲突解决,要把这里丢tenderTree改成tenderTree2
+const tenderTree2 = [];
+let parentId2 = 0;
 let selects;
+let auditUtils;
+const needYB = ['advance', 'ledger', 'revise', 'change', 'audit-ass'];
 // 查询方法
-function findNode (key, value, arr) {
+function findNode2 (key, value, arr) {
     for (const a of arr) {
         if (a[key] && a[key] === value) {
             return a;
@@ -19,22 +22,22 @@ function findNode (key, value, arr) {
     }
 }
 // 初始化TenderTree数据
-function initTenderTree () {
+function initTenderTree2 () {
     const levelCategory = category.filter(function (c) {
         return c.level && c.level > 0;
     });
-    function findCategoryNode(cid, value, array) {
+    function findCategoryNode2(cid, value, array) {
         for (const a of array) {
             if (a.cid === cid && a.vid === value) {
                 return a;
             }
         }
     }
-    function getCategoryNode(category, value, parent, i = null) {
-        const array = parent ?  parent.children : tenderTree;
-        let cate = findCategoryNode(category.id, value, array);
+    function getCategoryNode2(category, value, parent, i = null) {
+        const array = parent ?  parent.children : tenderTree2;
+        let cate = findCategoryNode2(category.id, value, array);
         if (!cate) {
-            const cateValue = findNode('id', value, category.value);
+            const cateValue = findNode2('id', value, category.value);
             if (!cateValue) return null;
             cate = {
                 cid: category.id,
@@ -42,24 +45,24 @@ function initTenderTree () {
                 name: cateValue.value,
                 children: [],
                 level: i ? i : category.level,
-                sort_id: ++parentId,
+                sort_id: ++parentId2,
                 sort: cateValue.sort,
             };
             array.push(cate);
         }
         return cate;
     }
-    function loadTenderCategory (tender) {
+    function loadTenderCategory2 (tender) {
         let tenderCategory = null;
         for (const [index,lc] of levelCategory.entries()) {
-            const tenderCate = findNode('cid', lc.id, tender.category);
+            const tenderCate = findNode2('cid', lc.id, tender.category);
             if (tenderCate) {
-                tenderCategory = getCategoryNode(lc, tenderCate.value, tenderCategory);
+                tenderCategory = getCategoryNode2(lc, tenderCate.value, tenderCategory);
             } else {
                 if (index === 0 && tender.category) {
                     for (const [i,c] of tender.category.entries()) {
-                        const cate = findNode('id', c.cid, category);
-                        if (cate) tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
+                        const cate = findNode2('id', c.cid, category);
+                        if (cate) tenderCategory = getCategoryNode2(cate, c.value, tenderCategory, i+1);
                     }
                 }
                 return tenderCategory;
@@ -67,7 +70,7 @@ function initTenderTree () {
         }
         return tenderCategory;
     }
-    function calculateTender(tender) {
+    function calculateTender2(tender) {
         if (tender.lastStage) {
             tender.gather_tp = ZhCalc.add(tender.lastStage.contract_tp, tender.lastStage.qc_tp);
             tender.end_contract_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.contract_tp);
@@ -78,26 +81,26 @@ function initTenderTree () {
             tender.end_yf_tp = ZhCalc.add(tender.lastStage.pre_yf_tp, tender.yf_tp);
         }
     }
-    tenderTree.splice(0, tenderTree.length);
+    tenderTree2.splice(0, tenderTree2.length);
     for (const t of tenders) {
-        calculateTender(t);
+        calculateTender2(t);
         t.valid = true;
         delete t.level;
         if (t.category && levelCategory.length > 0) {
-            const parent = loadTenderCategory(t);
+            const parent = loadTenderCategory2(t);
             if (parent) {
                 t.level = parent.level + 1;
                 parent.children.push(t);
             } else {
-                tenderTree.push(t);
+                tenderTree2.push(t);
             }
         } else {
-            tenderTree.push(t);
+            tenderTree2.push(t);
         }
     }
-    sortTenderTree();
+    sortTenderTree(tenderTree2);
 }
-function recursiveGetTenderNodeHtml (node, arr, pid, this_code, this_status, aidList = []) {
+function recursiveGetTenderNodeHtml2 (node, arr, pid, this_code, this_status, aidList = []) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
@@ -156,14 +159,14 @@ function recursiveGetTenderNodeHtml (node, arr, pid, this_code, this_status, aid
     html.push('</tr>');
     if (node.children) {
         for (const c of node.children) {
-            html.push(recursiveGetTenderNodeHtml(c, node.children, node.sort_id, this_code, this_status, aidList));
+            html.push(recursiveGetTenderNodeHtml2(c, node.children, node.sort_id, this_code, this_status, aidList));
         }
     }
     return html.join('');
 }
 // 根据TenderTree数据获取Html代码
-function getTenderTreeHtml (this_code, this_status, aidList = []) {
-    if (tenderTree.length > 0) {
+function getTenderTreeHtml2 (this_code, this_status, aidList = []) {
+    if (tenderTree2.length > 0) {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
         html.push('<thead>', '<tr>');
@@ -172,9 +175,9 @@ function getTenderTreeHtml (this_code, this_status, aidList = []) {
         html.push('<th>详细流程</th>');
         html.push('<th width="40">选择</th>');
         html.push('</tr>', '</thead>');
-        parentId = 0;
-        for (const t of tenderTree) {
-            html.push(recursiveGetTenderNodeHtml(t, tenderTree, '', this_code, this_status, aidList));
+        parentId2 = 0;
+        for (const t of tenderTree2) {
+            html.push(recursiveGetTenderNodeHtml2(t, tenderTree2, '', this_code, this_status, aidList));
         }
         html.push('</table>');
         return html.join('');
@@ -222,7 +225,7 @@ function getShenpiHtml (this_code) {
     return html.join('');
 }
 $(document).ready(function () {
-    const auditUtils = {
+    auditUtils = {
         getAuditHtml: function(audit) {
             return '<span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
                 '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
@@ -332,8 +335,7 @@ $(document).ready(function () {
 
     let timer = null;
     let oldSearchVal = null;
-    const needYB = ['advance', 'ledger', 'revise', 'change', 'audit-ass'];
-    $('body').on('input propertychange', '.gr-search', function(e) {
+    $('body').on('input propertychange', 'div[id$="_dropdownMenu"] .gr-search', function(e) {
         oldSearchVal = e.target.value;
         timer && clearTimeout(timer);
         timer = setTimeout(() => {
@@ -376,7 +378,7 @@ $(document).ready(function () {
     });
 
     // 添加审批流程按钮逻辑
-    $('body').on('click', '.book-list dt', function () {
+    $('body').on('click', 'div[id$="_dropdownMenu"] .book-list dt', function () {
         const idx = $(this).find('.acc-btn').attr('data-groupid');
         const type = $(this).find('.acc-btn').attr('data-type');
         if (type === 'hide') {
@@ -395,7 +397,7 @@ $(document).ready(function () {
     });
 
     // 更改审批流程状态
-    $('.form-check input').on('change', function () {
+    $('body').on('change', '.form-check input[type="radio"]', function () {
         // 获取所有审批的checked值并更新
         const this_status = parseInt($(this).val());
         const this_code = $(this).data('code');
@@ -449,7 +451,7 @@ $(document).ready(function () {
     });
 
     // 选中审批人
-    $('body').on('click', 'dl dd', function () {
+    $('body').on('click', 'div[id$="_dropdownMenu"] dl dd', function () {
         const id = parseInt($(this).data('id'));
         if (!id) return;
 
@@ -566,70 +568,9 @@ $(document).ready(function () {
         $(this).parents('li').remove();
     });
 
-    // 审批流程-审批人html 生成
-    function makeAudit(audit, i = '终') {
-        return '<li class="d-flex justify-content-start mb-3">\n' +
-            '                                            <span class="col-auto">'+ i +'审</span>\n' +
-            '                                            <span class="col-7 spr-span">\n' +
-            '                                            <span class="d-inline-block"></span>\n' +
-            '                                            <span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
-            '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
-            '                                                            <div class="dropdown-menu">\n' +
-            '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
-            '                                                                <div class="dropdown-divider"></div>\n' +
-            '                                                                <div class="px-2 py-1 text-center">\n' +
-            '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + audit.audit_id + '">移除</button>\n' +
-            '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
-            '                                                                </div>\n' +
-            '                                                            </div>\n' +
-            '                                                        </span> ' +
-            // '<a href="javascript:void(0);" class="remove-audit btn-sm text-danger px-1" title="移除" data-id="'+ audit.audit_id +'"><i class="fa fa-remove"></i></a></span> </span>\n' +
-            '                                            </span></span></span>\n' +
-            '                                        </li>';
-    }
-
-    // 审批流程-选择审批人html 生成
-    function makeSelectAudit(code, i = '终') {
-        let divhtml = '';
-        accountGroup.forEach((group, idx) => {
-            let didivhtml = '';
-            if(group) {
-                group.groupList.forEach(item => {
-                    didivhtml += (item.id !== cur_uid || (item.id === cur_uid && needYB.indexOf(code) !== -1)) ? '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
-                        '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
-                        '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
-                        '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
-                        '                                                                    </dd>\n' : '';
-                });
-                divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
-                    '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
-                    '                                                                </div>\n';
-            }
-        });
-        let html = '<li class="d-flex justify-content-start mb-3">\n' +
-            '                                            <span class="col-auto">' + i + '审</span>\n' +
-            '                                            <span class="col-7 spr-span">\n' +
-            '                                            <span class="d-inline-block">\n' +
-            '                                                <div class="dropdown text-right">\n' +
-            '                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="' + code + '_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
-            '                                                        选择审批人\n' +
-            '                                                    </button>\n' +
-            '                                                    <div class="dropdown-menu dropdown-menu-right" id="' + code + '_dropdownMenu" aria-labelledby="' + code + '_dropdownMenuButton" style="width:220px">\n' +
-            '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
-            '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="' + code + '"></div>\n' +
-            '                                                        <dl class="list-unstyled book-list">\n' + divhtml +
-            '                                                        </dl>\n' +
-            '                                                    </div>\n' +
-            '                                                </div>\n' +
-            '                                            </span>\n' +
-            '                                        </span>\n' +
-            '                                        </li>';
-        return html;
-    }
-
-    initTenderTree();
+    initTenderTree2();
 
-    $('.set-otherTender').on('click', function () {
+    $('body').on('click', '.set-otherTender', function () {
         const this_code = $(this).data('code');
         const this_status = parseInt($(this).siblings('.lc-show').siblings('.form-group').find('input:checked').val());
         const lis = $(this).siblings('.lc-show').find('li');
@@ -645,7 +586,7 @@ $(document).ready(function () {
                 aidList.push(parseInt(remove.getAttribute('data-id')));
             }
         });
-        const html = getTenderTreeHtml(this_code, this_status, aidList);
+        const html = getTenderTreeHtml2(this_code, this_status, aidList);
         $('#shenpi-name').text($(this).data('name'));
         $('#shenpi_code').val(this_code);
         $('#shenpi_status').val(this_status);
@@ -690,7 +631,7 @@ $(document).ready(function () {
         })
     });
 
-    $('.set-otherShenpi').on('click', function () {
+    $('body').on('click', '.set-otherShenpi', function () {
         let canSetOther = true;
         const this_code = $(this).data('code');
         if (this_code === 'stage') {
@@ -1061,3 +1002,64 @@ $(document).ready(function () {
         $('#tender-list').scrollTop(X - $('#tender-list').offset().top + $('#tender-list').scrollTop() -30);
     });
 });
+
+// 审批流程-审批人html 生成
+function makeAudit(audit, i = '终') {
+    return '<li class="d-flex justify-content-start mb-3">\n' +
+        '                                            <span class="col-auto">'+ i +'审</span>\n' +
+        '                                            <span class="col-7 spr-span">\n' +
+        '                                            <span class="d-inline-block"></span>\n' +
+        '                                            <span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
+        '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
+        '                                                            <div class="dropdown-menu">\n' +
+        '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
+        '                                                                <div class="dropdown-divider"></div>\n' +
+        '                                                                <div class="px-2 py-1 text-center">\n' +
+        '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + audit.audit_id + '">移除</button>\n' +
+        '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
+        '                                                                </div>\n' +
+        '                                                            </div>\n' +
+        '                                                        </span> ' +
+        // '<a href="javascript:void(0);" class="remove-audit btn-sm text-danger px-1" title="移除" data-id="'+ audit.audit_id +'"><i class="fa fa-remove"></i></a></span> </span>\n' +
+        '                                            </span></span></span>\n' +
+        '                                        </li>';
+}
+
+// 审批流程-选择审批人html 生成
+function makeSelectAudit(code, i = '终') {
+    let divhtml = '';
+    accountGroup.forEach((group, idx) => {
+        let didivhtml = '';
+        if(group) {
+            group.groupList.forEach(item => {
+                didivhtml += (item.id !== cur_uid || (item.id === cur_uid && needYB.indexOf(code) !== -1)) ? '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
+                    '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
+                    '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
+                    '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
+                    '                                                                    </dd>\n' : '';
+            });
+            divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
+                '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
+                '                                                                </div>\n';
+        }
+    });
+    let html = '<li class="d-flex justify-content-start mb-3">\n' +
+        '                                            <span class="col-auto">' + i + '审</span>\n' +
+        '                                            <span class="col-7 spr-span">\n' +
+        '                                            <span class="d-inline-block">\n' +
+        '                                                <div class="dropdown text-right">\n' +
+        '                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="' + code + '_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+        '                                                        选择审批人\n' +
+        '                                                    </button>\n' +
+        '                                                    <div class="dropdown-menu dropdown-menu-right" id="' + code + '_dropdownMenu" aria-labelledby="' + code + '_dropdownMenuButton" style="width:220px">\n' +
+        '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
+        '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-code="' + code + '"></div>\n' +
+        '                                                        <dl class="list-unstyled book-list">\n' + divhtml +
+        '                                                        </dl>\n' +
+        '                                                    </div>\n' +
+        '                                                </div>\n' +
+        '                                            </span>\n' +
+        '                                        </span>\n' +
+        '                                        </li>';
+    return html;
+}

+ 1 - 1
app/public/js/tender_copy_setting.js

@@ -99,7 +99,7 @@ function initTenderTree () {
             tenderTree.push(t);
         }
     }
-    sortTenderTree();
+    sortTenderTree(tenderTree);
 }
 function recursiveGetTenderNodeHtml (node, arr, pid) {
     // console.log(node, tender)

+ 1 - 1
app/public/js/tender_list_base.js

@@ -271,7 +271,7 @@ function initTenderTree () {
             tenderTree.push(t);
         }
     }
-    sortTenderTree();
+    sortTenderTree(tenderTree);
     if (tenderListSpec.calculateParent) {
         for (const t of tenderTree) {
             tenderListSpec.calculateParent(t);

+ 10 - 9
app/public/js/tender_showhide.js

@@ -8,6 +8,7 @@
  * @version
  */
 let hideList = [];
+const tTree = typeof tenderTree !== 'undefined' ? tenderTree : typeof tenderTree2 !== 'undefined' ? tenderTree2 : [];
 let returnItem;
 const findTenderTreeNode = function(sortId, tree) {
     tree.forEach((item) => {
@@ -53,7 +54,7 @@ function recursiveExpand(nodes, parent, checkFun) {
 }
 function expandByCustom(checkFun) {
     hideList = [];
-    recursiveExpand(tenderTree, null, checkFun);
+    recursiveExpand(tTree, null, checkFun);
 }
 
 function expandByLevel(level) {
@@ -64,7 +65,7 @@ function expandByLevel(level) {
 }
 
 // 根据标段类别设置排序
-function sortTenderTree(teTree = tenderTree) {
+function sortTenderTree(teTree = tTree) {
     for (const tender of teTree) {
         if (tender.sort) {
             // 当前层排序
@@ -96,7 +97,7 @@ function localHideList(wap = false) {
             hideList = JSON.parse(userTenderHideList);
             for (const h of hideList) {
                 const cid = h.sort_id;
-                const node = findTenderTreeNode(parseInt(cid), tenderTree);
+                const node = findTenderTreeNode(parseInt(cid), tTree);
                 $('.c-body tr td span[cid="' + cid + '"]').children('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
                 $('.c-body tr td span[cid="' + cid + '"]').attr('title', '展开');
                 doTrStatus(returnItem, 'hide');
@@ -172,7 +173,7 @@ $(document).ready(() => {
             $(this).children('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
             $(this).attr('title', '展开');
             const cid = $(this).attr('cid');
-            const node = findTenderTreeNode(parseInt(cid), tenderTree);
+            const node = findTenderTreeNode(parseInt(cid), tTree);
             doTrStatus(returnItem, 'hide');
             hideList.push({sort_id: cid});
             setLocalCache(uphlname, JSON.stringify(hideList));
@@ -180,7 +181,7 @@ $(document).ready(() => {
             $(this).children('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
             $(this).attr('title', '收起');
             const cid = $(this).attr('cid');
-            const node = findTenderTreeNode(parseInt(cid), tenderTree);
+            const node = findTenderTreeNode(parseInt(cid), tTree);
             doTrStatus(returnItem, 'show');
             const index = hideList.findIndex(function(item) {
                 return parseInt(item.sort_id) === parseInt(cid);
@@ -198,10 +199,10 @@ $(document).ready(() => {
         if (item === 'open') {
             setLocalCache(uphlname, JSON.stringify(hideList));
         }
-        for (const tree of tenderTree) {
+        for (const tree of tTree) {
             if (tree && tree.sort_id !== undefined) {
                 const cid = tree.sort_id;
-                const node = findTenderTreeNode(parseInt(cid), tenderTree);
+                const node = findTenderTreeNode(parseInt(cid), tTree);
                 if (item === 'open') {
                     $('.c-body tr td span[cid="' + cid + '"]').children('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
                     $('.c-body tr td span[cid="' + cid + '"]').attr('title', '收起');
@@ -221,12 +222,12 @@ $(document).ready(() => {
         levels: [
             {
                 type: 'sort', count: 5, visible_count: function () {
-                    return tenderTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                    return tTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
                 }
             },
             {
                 type: 'last', title: '最底层', visible: function () {
-                    const count = tenderTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                    const count = tTree.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
                     return count > 0;
                 }
             },

+ 1 - 1
app/public/js/wap/list.js

@@ -135,7 +135,7 @@ function initTenderTree () {
             tenderTree.push(t);
         }
     }
-    sortTenderTree();
+    sortTenderTree(tenderTree);
     for (const t of tenderTree) {
         calculateParent(t);
     }

+ 3 - 1
app/router.js

@@ -130,7 +130,9 @@ module.exports = app => {
     // 决策大屏
     app.get('/setting/datacollect', sessionAuth, 'settingController.dataCollect');
     app.post('/setting/datacollect/save', sessionAuth, 'settingController.dataCollectSave');
-
+    // 标段管理
+    app.get('/setting/manage', sessionAuth, 'settingController.manage');
+    app.post('/setting/manage/tender/save', sessionAuth, 'settingController.manageTenderSave');
 
     // 项目相关
     app.get('/project/info', sessionAuth, 'projectController.info');

+ 35 - 0
app/service/schedule_audit.js

@@ -49,6 +49,41 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async setOtherTender(tidList, userList) {
+            // 根据标段找出创建人去除,已存在的先删除再插入
+            const transaction = await this.db.beginTransaction();
+            try {
+                const tenderList = await this.ctx.service.tender.getAllDataByCondition({
+                    columns: ['id', 'user_id'],
+                    where: { id: tidList.split(',') },
+                });
+                const oldTouristList = await this.getAllDataByCondition({ where: { tid: tidList.split(',') } });
+                const insertData = [];
+                const deleteIdData = [];
+                for (const user of userList) {
+                    for (const t of tenderList) {
+                        const delId = this._.find(oldTouristList, { tid: t.id, user_id: user.uid });
+                        if (delId) deleteIdData.push(delId.id);
+                        if (user.uid !== t.user_id) {
+                            insertData.push({
+                                tid: t.id,
+                                audit_id: user.uid,
+                                permission: user.permission,
+                                in_time: new Date(),
+                            });
+                        }
+                    }
+                }
+                if (deleteIdData.length > 0) await transaction.delete(this.tableName, { id: deleteIdData });
+                if (insertData.length > 0) await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
     return ScheduleAudit;
 };

+ 38 - 0
app/service/shenpi_audit.js

@@ -24,6 +24,18 @@ module.exports = app => {
             this.tableName = 'shenpi_audit';
         }
 
+        async getShenpi(tid, info) {
+            for (const sp of shenpiConst.sp_lc) {
+                sp.status = info.shenpi ? info.shenpi[sp.code] : shenpiConst.sp_status.sqspr;
+                if (sp.status === shenpiConst.sp_status.gdspl) {
+                    sp.auditGroupList = await this.getAuditGroupList(tid, sp.type, sp.status);
+                } else if (sp.status === shenpiConst.sp_status.gdzs) {
+                    sp.audit = await this.getAudit(tid, sp.type, sp.status);
+                }
+            }
+            return shenpiConst;
+        }
+
         async getAudit(tid, type, status) {
             const sql = 'SELECT sp.audit_id, sp.audit_type, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
                 ' WHERE sp.tid = ? AND sp.sp_type = ? AND sp.sp_status = ?';
@@ -353,6 +365,32 @@ module.exports = app => {
             }
             await transaction.insert(this.tableName, insertDatas);
         }
+
+        async getRemoveTenders(tenders) {
+            const removeTenders = [];
+            for (const tender of tenders) {
+                const shenpiInfo = await this.ctx.service.tenderInfo.getTenderShenpiInfo(tender.id);
+                if (!shenpiInfo) {
+                    removeTenders.push(tender.id);
+                } else {
+                    tender.shenpiInfo = shenpiInfo;
+                    // 获取所有的固定审批流或固定终审
+                    const shenpiauditList = {};
+                    for (const shenpi in tender.shenpiInfo) {
+                        if (tender.shenpiInfo[shenpi] === shenpiConst.sp_status.gdspl) {
+                            const shenpiList = await this.getAllDataByCondition({ where: { tid: tender.id, sp_type: shenpiConst.sp_type[shenpi], sp_status: tender.shenpiInfo[shenpi] } });
+                            const shenpiIdList = this._.map(shenpiList, 'audit_id');
+                            shenpiauditList[shenpi] = shenpiIdList.length ? shenpiIdList : null;
+                        } else if (tender.shenpiInfo[shenpi] === shenpiConst.sp_status.gdzs) {
+                            const shenpiInfo = await this.getDataByCondition({ tid: tender.id, sp_type: shenpiConst.sp_type[shenpi], sp_status: tender.shenpiInfo[shenpi] });
+                            shenpiauditList[shenpi] = shenpiInfo && shenpiInfo.audit_id ? [shenpiInfo.audit_id] : null;
+                        }
+                    }
+                    tender.shenpiauditList = shenpiauditList;
+                }
+            }
+            return removeTenders;
+        }
     }
 
     return ShenpiAudit;

+ 35 - 0
app/service/tender_tourist.js

@@ -79,6 +79,41 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async setOtherTender(tidList, userList) {
+            // 根据标段找出创建人去除,已存在的先删除再插入
+            const transaction = await this.db.beginTransaction();
+            try {
+                const tenderList = await this.ctx.service.tender.getAllDataByCondition({
+                    columns: ['id', 'user_id'],
+                    where: { id: tidList.split(',') },
+                });
+                const oldTouristList = await this.getAllDataByCondition({ where: { tid: tidList.split(',') } });
+                const insertData = [];
+                const deleteIdData = [];
+                for (const user of userList) {
+                    for (const t of tenderList) {
+                        const delId = this._.find(oldTouristList, { tid: t.id, user_id: user.uid });
+                        if (delId) deleteIdData.push(delId.id);
+                        if (user.uid !== t.user_id) {
+                            insertData.push({
+                                tid: t.id,
+                                user_id: user.uid,
+                                permission: JSON.stringify(user.permission),
+                                in_time: new Date(),
+                            });
+                        }
+                    }
+                }
+                if (deleteIdData.length > 0) await transaction.delete(this.tableName, { id: deleteIdData });
+                if (insertData.length > 0) await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
 
     return TenderTourist;

+ 290 - 0
app/view/setting/manage.ejs

@@ -0,0 +1,290 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>标段管理
+                <!--  <a href="#add-company" data-toggle="modal" data-target="#add-company" class="btn btn-primary btn-sm pull-right">添加单位</a>
+                 <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a> -->
+            </h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0 row" style="background-color: #fff;overflow: hidden">
+            <div class="col-4">
+                <div class="c-body sjs-height-0" style="overflow: auto"></div>
+            </div>
+            <div class="col-8">
+                <nav class="nav nav-tabs m-3" role="tablist">
+                    <a class="nav-item nav-link active" data-toggle="tab" href="#splc" role="tab">审批流程</a>
+                    <a class="nav-item nav-link" data-toggle="tab" href="#guest" role="tab">游客账号</a>
+                    <% if (ctx.session.sessionProject.page_show.xxjd) { %>
+                    <a class="nav-item nav-link" data-toggle="tab" href="#tzpro" role="tab">投资进度</a>
+                    <% } %>
+<!--                    <a class="nav-item nav-link" data-toggle="tab" href="#subadmin" role="tab">标段管理员</a>-->
+                    <div class="ml-auto" id="user-set" style="display: none">
+                        <div class="row">
+                            <div class="d-inline-block dropdown">
+                                <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" data-type="" id="add_user_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                    添加用户
+                                </button>
+                                <div class="dropdown-menu" id="add_user_dropdownMenu2" aria-labelledby="add_user_dropdownMenuButton" style="width:220px">
+                                    <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"
+                                                                 placeholder="姓名/手机 检索" autocomplete="off"></div>
+                                    <dl class="list-unstyled book-list">
+                                        <% accountGroup.forEach((group, idx) => { %>
+                                            <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>" data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                            <div class="dd-content" data-toggleid="<%- idx %>">
+                                                <% group.groupList.forEach(item => { %>
+                                                    <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>" >
+                                                        <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                                    class="ml-auto"><%- item.mobile %></span></p>
+                                                        <span class="text-muted"><%- item.role %></span>
+                                                    </dd>
+                                                <% });%>
+                                            </div>
+                                        <% }) %>
+                                    </dl>
+                                </div>
+                            </div>
+                            <div class="d-inline-block ml-3">
+                                <a href="javascript:void(0)" class="btn btn-primary btn-sm pull-right" id="set-other-tender-user-a">应用账号至其他标段</a>
+                            </div>
+                        </div>
+                    </div>
+                </nav>
+                <div class="tab-content m-3" style="overflow: auto">
+                    <!--审批流程 -->
+                    <div id="splc" class="tab-pane active">
+                    </div>
+                    <!--游客账号 -->
+                    <div id="guest" class="tab-pane">
+                        <div class="col-8" style="max-width: 500px">
+                            <table class="table table-hover table-bordered table-sm">
+                                <thead>
+                                <tr>
+                                    <th class="text-center" width="100px">用户</th>
+                                    <th class="text-center" width="120px">职位/角色</th>
+                                    <th class="text-center" width="60px">附件</th>
+                                    <th class="text-center" width="60px">书签</th>
+                                    <th class="text-center" >操作</th>
+                                </tr>
+                                </thead>
+                                <tbody id="tourist-users">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <% if (ctx.session.sessionProject.page_show.xxjd) { %>
+                    <!--投资进度 -->
+                    <div id="tzpro" class="tab-pane">
+                        <div class="col-8" style="max-width: 500px">
+                            <table class="table table-hover table-bordered table-sm">
+                                <thead>
+                                <tr>
+                                    <th class="text-center" width="100px">用户</th>
+                                    <th class="text-center" width="120px">职位/角色</th>
+                                    <th class="text-center" width="60px">查看</th>
+                                    <th class="text-center" width="60px">修改</th>
+                                    <th class="text-center" >操作</th>
+                                </tr>
+                                </thead>
+                                <tbody id="schedule-users">
+                                <tr>
+                                    <td>陈特</td>
+                                    <td>业主</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio301" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio301"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio302" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio302"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>仁温书</td>
+                                    <td>项目经理</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio311" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio311"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio312" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio312"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>玉安然</td>
+                                    <td>集团经理</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio321" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio321"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio322" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio322"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <% } %>
+                    <!--标段管理员 -->
+                    <div id="subadmin" class="tab-pane">
+                        <div class="col-6">
+                            <div class="mt-3">
+                                <label class="form-text alert alert-danger">设置标段管理员,开放部分管理员权限给标段管理员。</label>
+                            </div>
+                            <table class="table table-hover table-bordered table-sm">
+                                <thead>
+                                <tr class="text-center">
+                                    <th width="10%">用户</th>
+                                    <th >职位/角色</th>
+                                    <th width="10%">审批流程</th>
+                                    <th width="10%">游客账号</th>
+                                    <th width="10%">投资进度</th>
+                                    <th width="10%">施工日志</th>
+                                    <th width="10%">操作</th>
+                                </tr>
+                                </thead>
+                                <tbody>
+                                <tr class="text-center">
+                                    <td>陈特</td>
+                                    <td>业主</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio401" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio401"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio402" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio402"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio403" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio403"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio404" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio404"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                <tr class="text-center">
+                                    <td>仁温书</td>
+                                    <td>项目经理</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio411" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio411"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio412" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio412"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio413" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio413"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio414" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio414"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                <tr class="text-center">
+                                    <td>玉安然</td>
+                                    <td>集团经理</td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio421" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio421"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio422" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio422"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio423" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio423"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <div class="custom-control custom-checkbox mb-2">
+                                            <input type="checkbox" id="customRadio424" name="customCheckbox" class="custom-control-input">
+                                            <label class="custom-control-label" for="customRadio424"></label>
+                                        </div>
+                                    </td>
+                                    <td class="text-center">
+                                        <a href="#remove-user1" data-toggle="modal" data-target="#remove-user" class="btn btn-sm btn-outline-danger">移除</a>
+                                    </td>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+    const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+    const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
+    const categoryType = JSON.parse('<%- JSON.stringify(settingConst.cType) %>');
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const selfCategoryLevel = '<%- (selfCategoryLevel || '') %>';
+    const measureType = JSON.parse('<%- JSON.stringify(measureType) %>');
+    const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const scPermission = JSON.parse(unescape('<%- escape(JSON.stringify(scPermission)) %>'));
+    const uid = '<%- uid %>';
+    const pid = '<%- pid %>';
+    const uphlname = 'user_' + uid + '_pro_' + pid + '_tender_manage_list';
+    let sp_lc, sp_type, sp_status, sp_status_list, cur_tenderid, cur_uid;
+</script>

+ 244 - 0
app/view/setting/manage_modal.ejs

@@ -0,0 +1,244 @@
+<!--设置其他标段-->
+<div class="modal fade" id="batch" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">设置其他标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="alert alert-warning">将「<span id="shenpi-name"></span>」设置至其他标段</div>
+                <div class="input-group input-group-sm mb-2">
+                    <input class="form-control" placeholder="输入审批人名称搜索" type="text" name="audit-name" id="search-audit">
+                    <div class="input-group-append">
+                        <span class="input-group-text" id="search-result">0/0</span>
+                        <button class="btn btn-outline-secondary" id="up-search" disabled><i class="fa fa-arrow-up" aria-hidden="true"></i></button>
+                        <button class="btn btn-outline-secondary" id="down-search" disabled><i class="fa fa-arrow-down" aria-hidden="true"></i></button>
+                    </div>
+                </div>
+                <div class="modal-height-300" id="tender-list">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="shenpi_auditors" value="">
+                <input type="hidden" id="shenpi_code" value="">
+                <input type="hidden" id="shenpi_status" value="">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" id="save-other-tender">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--设置其他流程-->
+<div class="modal fade" id="batch2" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">设置其他流程</h5>
+            </div>
+            <div class="modal-body">
+                <div class="alert alert-warning">将「<span id="shenpi-name2"></span>」设置至其他流程</div>
+                <div class="modal-height-300" id="shenpi-list">
+                    <table class="table table-hover table-bordered">
+                        <thead>
+                        <th>名称</th>
+                        <th width="100">审批流程</th>
+                        <th width="40">选择</th>
+                        </thead>
+                        <tr>
+                            <td>预付款审批</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td></td>
+                        </tr>
+                        <tr>
+                            <td>台账审批</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td><input type="checkbox"></td>
+                        </tr>
+                        <tr>
+                            <td>台账修订</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td><input type="checkbox"></td>
+                        </tr>
+                        <tr>
+                            <td>计量期审批</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td><input type="checkbox"></td>
+                        </tr>
+                        <tr>
+                            <td>工程变更审批</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td><input type="checkbox"></td>
+                        </tr>
+                        <tr>
+                            <td>材料调差审批</td>
+                            <td>固定流程<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" data-original-title="王五-张三-李四"></i></td>
+                            <td><input type="checkbox"></td>
+                        </tr>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="shenpi_auditors2" value="">
+                <input type="hidden" id="shenpi_code2" value="">
+                <input type="hidden" id="shenpi_status2" value="">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" id="save-other-shenpi">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--多人协同-->
+<div class="modal fade" id="cooperation" data-backdrop="static">
+    <div class="modal-dialog modal-xl" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">多人协同</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-6">
+                        <div class="modal-height-500" style="overflow: auto;">
+                            <table class="table table-hover table-bordered">
+                                <thead class="text-center">
+                                <tr><th colspan="4" id="stage_audit"></th></tr>
+                                <tr><th>姓名</th><th>单位</th><th>协同处理</th></tr>
+                                </thead>
+                                <tbody id="coo_table">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <div class="col-6">
+                        <div class="row">
+                            <div class="col-6">
+                                <div class="mb-2">
+                                    <select class="form-control form-control-sm" id="stage_audits">
+                                    </select>
+                                </div>
+                            </div>
+                            <div class="col-6">
+                                <div class="text-right">
+                                    <div class="d-inline-block">
+                                        <button class="btn btn-outline-danger btn-sm" id="del-audit-ass">
+                                            移除协同人
+                                        </button>
+                                    </div>
+                                    <div class="dropdown d-inline-block" data-code="audit-ass">
+                                        <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="audit-ass_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                            添加协同人
+                                        </button>
+                                        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="audit-ass_dropdownMenuButton" style="width:220px" id="audit-ass_dropdownMenu">
+                                            <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search" placeholder="姓名/手机 检索" autocomplete="off" data-code="audit-ass"></div>
+                                            <dl class="list-unstyled book-list">
+                                                <% accountGroup.forEach((group, idx) => { %>
+                                                <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>" data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                                <div class="dd-content" data-toggleid="<%- idx %>">
+                                                    <% group.groupList.forEach(item => { %>
+                                                    <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>">
+                                                        <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                                    class="ml-auto"><%- item.mobile %></span></p>
+                                                        <span class="text-muted"><%- item.role %></span>
+                                                    </dd>
+                                                    <% });%>
+                                                </div>
+                                                <% }) %>
+                                            </dl>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="modal-height-500" style="overflow: auto;" id="ledger-spread">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--弹出编辑标段-->
+<div class="modal fade" id="edit-bd" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">编辑标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>标段名称<b class="text-danger">*</b></label>
+                    <input class="form-control form-control-sm"  placeholder="输入标段名称" type="text" name="name">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="edit-bd-ok">确定修改</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--删除标段-->
+<div class="modal fade" id="del-bd" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">确认删除标段</h5>
+            </div>
+            <div class="modal-body">
+                <h5>删除后,数据无法恢复,请谨慎操作。</h5>
+                <h5>确定删除「<strong style="word-break: break-word;" id="del-tender-name"></strong>」?</h5>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-danger btn-sm" id="del-bd-ok">确定删除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--移除账号-->
+<div class="modal fade" id="remove-user" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">移除账号</h5>
+            </div>
+            <div class="modal-body">
+                <span>移除账号后,该账号则取消相关权限。确认移除账号?</span>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="remove_user_type">
+                <input type="hidden" id="remove_user_id">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-sm btn-primary" id="remove_user_btn">确定移除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--应用账号至其他标段-->
+<div class="modal fade" id="bdcopy" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">应用账号至其他标段</h5>
+            </div>
+            <div class="modal-body">
+                <div class="input-group input-group-sm mb-2">
+                    <input class="form-control" placeholder="输入标段名称搜索" type="text" name="tender-name" id="search-tender">
+                    <div class="input-group-append">
+                        <span class="input-group-text" id="search-tender-result">0/0</span>
+                        <button class="btn btn-outline-secondary" id="up-tender-search" disabled><i class="fa fa-arrow-up" aria-hidden="true"></i></button>
+                        <button class="btn btn-outline-secondary" id="down-tender-search" disabled><i class="fa fa-arrow-down" aria-hidden="true"></i></button>
+                    </div>
+                </div>
+                <div class="modal-height-300" id="tender-list-4user">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-sm btn-primary" id="save-other-tender-user">确认</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 6 - 0
config/menu.js

@@ -348,6 +348,12 @@ const settingMenu = {
         url: '/setting/datacollect',
         caption: '决策大屏',
     },
+    manage: {
+        name: '标段管理',
+        display: true,
+        url: '/setting/manage',
+        caption: '标段管理',
+    },
 };
 
 const profileMenu = {

+ 21 - 0
config/web.js

@@ -1290,6 +1290,27 @@ const JsFiles = {
                 mergeFile: 'construction_info',
             },
         },
+        setting: {
+            manage: {
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js', '/public/js/decimal.min.js', '/public/js/moment/moment.min.js'],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/PinYinOrder.bundle.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/shares/show_level.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/shenpi.js',
+                    '/public/js/setting_manage.js',
+                    '/public/js/tender_list_base.js',
+                ],
+                mergeFile: 'tender_list',
+            },
+        },
     },
 };