浏览代码

进度权限设置

laiguoran 4 年之前
父节点
当前提交
10b4e503ae

+ 6 - 0
app/const/schedule.js

@@ -4,7 +4,13 @@ const plan_mode = {
     tp: 1,
     tp: 1,
     gcl: 2,
     gcl: 2,
 };
 };
+const permission = {
+    no: 0,
+    show: 1,
+    edit: 2,
+}
 
 
 module.exports = {
 module.exports = {
     plan_mode,
     plan_mode,
+    permission,
 };
 };

+ 42 - 0
app/controller/schedule_controller.js

@@ -37,6 +37,9 @@ module.exports = app => {
             if (await this._getLastReviseStatus(ctx)) {
             if (await this._getLastReviseStatus(ctx)) {
                 throw '台账修订中,请勿修改提交进度数据';
                 throw '台账修订中,请勿修改提交进度数据';
             }
             }
+            if (ctx.tender.schedule_permission !== scheduleConst.permission.edit) {
+                throw '权限不足,无法修改进度数据';
+            }
         }
         }
 
 
         async index(ctx) {
         async index(ctx) {
@@ -67,6 +70,7 @@ module.exports = app => {
                     planMonth: await this._getLastPlanMonth(ctx),
                     planMonth: await this._getLastPlanMonth(ctx),
                     scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                     scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                     preUrl: '/tender/' + ctx.tender.id,
                     preUrl: '/tender/' + ctx.tender.id,
+                    scPermission: scheduleConst.permission,
                     revising: await this._getLastReviseStatus(ctx),
                     revising: await this._getLastReviseStatus(ctx),
                 };
                 };
                 await this.layout('schedule/index.ejs', renderData, 'schedule/modal.ejs');
                 await this.layout('schedule/index.ejs', renderData, 'schedule/modal.ejs');
@@ -102,6 +106,7 @@ module.exports = app => {
                 scheduleLedgerList,
                 scheduleLedgerList,
                 hadDataLidList,
                 hadDataLidList,
                 scheduleStage,
                 scheduleStage,
+                scPermission: scheduleConst.permission,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.ledger),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.ledger),
                 revising: await this._getLastReviseStatus(ctx),
                 revising: await this._getLastReviseStatus(ctx),
             };
             };
@@ -120,6 +125,7 @@ module.exports = app => {
                 planMonth: await this._getLastPlanMonth(ctx),
                 planMonth: await this._getLastPlanMonth(ctx),
                 measureType,
                 measureType,
                 mode: scheduleConst.plan_mode,
                 mode: scheduleConst.plan_mode,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.plan),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.plan),
                 revising: await this._getLastReviseStatus(ctx),
                 revising: await this._getLastReviseStatus(ctx),
@@ -146,6 +152,7 @@ module.exports = app => {
                 endSlmList,
                 endSlmList,
                 yearSlmList,
                 yearSlmList,
                 curYearStageData,
                 curYearStageData,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageTp),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageTp),
@@ -169,6 +176,7 @@ module.exports = app => {
                 // scheduleStage,
                 // scheduleStage,
                 curScheduleMonth,
                 curScheduleMonth,
                 gclScheduleMonth,
                 gclScheduleMonth,
+                scPermission: scheduleConst.permission,
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 scheduleLedgerList: await this._getSelectedLedgerList(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 revising: await this._getLastReviseStatus(ctx),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageGcl),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.schedule.stageGcl),
@@ -557,6 +565,40 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
             }
         }
         }
+
+        async saveAudit(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data) {
+                    throw '提交数据错误';
+                }
+                // 判断修改权限
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '你没有权限修改形象进度';
+                }
+                let info = '';
+                switch (data.type) {
+                    case 'add':
+                        const result = await ctx.service.scheduleAudit.addAudit(data);
+                        if (!result) {
+                            throw '添加审批人失败';
+                        }
+                        info = result;
+                        break;
+                    case 'del':
+                        await ctx.service.scheduleAudit.removeAudit(data);
+                        break;
+                    case 'edit':
+                        await ctx.service.scheduleAudit.editAudit(data);
+                        break;
+                    default:break;
+                }
+                ctx.body = { err: 0, msg: '', data: info };
+            } catch (err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '保存审批流程设置失败');
+            }
+        }
     }
     }
 
 
     return ScheduleController;
     return ScheduleController;

+ 17 - 0
app/controller/tender_controller.js

@@ -20,6 +20,7 @@ const measureType = require('../const/tender').measureType;
 const billsPosConvert = require('../lib/bills_pos_convert');
 const billsPosConvert = require('../lib/bills_pos_convert');
 const path = require('path');
 const path = require('path');
 const sendToWormhole = require('stream-wormhole');
 const sendToWormhole = require('stream-wormhole');
+const scheduleConst = require('../const/schedule');
 
 
 module.exports = app => {
 module.exports = app => {
 
 
@@ -452,6 +453,22 @@ module.exports = app => {
                     audit: auditConst,
                     audit: auditConst,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.tenderInfo),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.tender.tenderInfo),
                 };
                 };
+                if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) {
+                    // 形象进度内容
+                    // 获取所有项目参与者
+                    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 accountGroupList = accountGroup.map((item, idx) => {
+                        const groupList = accountList.filter(item => item.account_group === idx);
+                        return { groupName: item, groupList };
+                    });
+                    renderData.scheduleAuditList = await ctx.service.scheduleAudit.getAllDataByCondition({ where: { tid: tender.id } });
+                    renderData.accountList = accountList;
+                    renderData.accountGroup = accountGroupList;
+                    renderData.scPermission = scheduleConst.permission;
+                }
                 await this.layout('tender/detail.ejs', renderData, 'tender/detail_modal.ejs');
                 await this.layout('tender/detail.ejs', renderData, 'tender/detail_modal.ejs');
             } catch (error) {
             } catch (error) {
                 this.log(error);
                 this.log(error);

+ 43 - 0
app/middleware/schedule_check.js

@@ -0,0 +1,43 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author EllisRan
+ * @date
+ * @version
+ */
+const scPermission = require('../const/schedule').permission;
+module.exports = options => {
+    /**
+     * 形象进度校验 中间件
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* scheduleCheck(next) {
+        try {
+            if (this.tender.schedule_permission === scPermission.no) {
+                throw '您无权查看该内容';
+            }
+            yield next;
+        } catch (err) {
+            // 输出错误到日志
+            if (err.stack) {
+                this.logger.error(err);
+            } else {
+                this.getLogger('fail').info(JSON.stringify({
+                    error: err,
+                    project: this.session.sessionProject,
+                    user: this.session.sessionUser,
+                    body: this.session.body,
+                }));
+            }
+            if (this.helper.isWap(this.request)) {
+                this.redirect('/wap/list');
+            } else {
+                err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/list');
+            }
+        }
+    };
+};

+ 12 - 0
app/middleware/tender_check.js

@@ -10,6 +10,7 @@
 
 
 const auditConst = require('../const/audit').ledger;
 const auditConst = require('../const/audit').ledger;
 const messageType = require('../const/message_type');
 const messageType = require('../const/message_type');
+const scPermission = require('../const/schedule').permission;
 
 
 module.exports = options => {
 module.exports = options => {
     /**
     /**
@@ -74,6 +75,17 @@ module.exports = options => {
             tender.ledgerUsers = tender.ledger_status === auditConst.status.uncheck ? [tender.data.user_id] : [tender.data.user_id, ...auditorsId];
             tender.ledgerUsers = tender.ledger_status === auditConst.status.uncheck ? [tender.data.user_id] : [tender.data.user_id, ...auditorsId];
             this.tender = tender;
             this.tender = tender;
             this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
             this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
+            // 形象进度权限获取
+            let schedule_permission = scPermission.no;
+            if (this.session.sessionUser.accountId === tender.data.user_id) {
+                schedule_permission = scPermission.edit;
+            } else {
+                const scheduleUser = yield this.service.scheduleAudit.getDataByCondition({ tid: tender.id, audit_id: this.session.sessionUser.accountId });
+                if (scheduleUser) {
+                    schedule_permission = scheduleUser.permission;
+                }
+            }
+            tender.schedule_permission = schedule_permission;
             yield next;
             yield next;
         } catch (err) {
         } catch (err) {
             // 输出错误到日志
             // 输出错误到日志

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

@@ -713,4 +713,4 @@ $(document).ready(function() {
             $('#edit-6').parent().show();
             $('#edit-6').parent().show();
         });
         });
     });
     });
-});
+});

+ 13 - 13
app/public/js/xlsx-populate/xlsx-populate.js

@@ -7356,7 +7356,7 @@ var Workbook = function () {
 
 
         /**
         /**
          * Add a new sheet to the workbook.
          * Add a new sheet to the workbook.
-         * 
+         *
          * **WARN:** this function has limits:  if you clone a sheet with some images or other things link outside the Sheet object, these things in the cloned sheet will be locked when you open in MS Excel app.
          * **WARN:** this function has limits:  if you clone a sheet with some images or other things link outside the Sheet object, these things in the cloned sheet will be locked when you open in MS Excel app.
          * @param {Sheet} from - The sheet to be cloned.
          * @param {Sheet} from - The sheet to be cloned.
          * @param {string} name - The name of the new sheet. Must be unique, less than 31 characters, and may not contain the following characters: \ / * [ ] : ?
          * @param {string} name - The name of the new sheet. Must be unique, less than 31 characters, and may not contain the following characters: \ / * [ ] : ?
@@ -19910,7 +19910,7 @@ $export.P = 8;   // proto
 $export.B = 16;  // bind
 $export.B = 16;  // bind
 $export.W = 32;  // wrap
 $export.W = 32;  // wrap
 $export.U = 64;  // safe
 $export.U = 64;  // safe
-$export.R = 128; // real proto method for `library` 
+$export.R = 128; // real proto method for `library`
 module.exports = $export;
 module.exports = $export;
 },{"./_core":86,"./_ctx":87,"./_global":92,"./_hide":93}],91:[function(require,module,exports){
 },{"./_core":86,"./_ctx":87,"./_global":92,"./_hide":93}],91:[function(require,module,exports){
 module.exports = function(exec){
 module.exports = function(exec){
@@ -31243,8 +31243,8 @@ exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinarySt
 
 
     // if inputData is already a promise, this flatten it.
     // if inputData is already a promise, this flatten it.
     var promise = external.Promise.resolve(inputData).then(function(data) {
     var promise = external.Promise.resolve(inputData).then(function(data) {
-        
-        
+
+
         var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);
         var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);
 
 
         if (isBlob && typeof FileReader !== "undefined") {
         if (isBlob && typeof FileReader !== "undefined") {
@@ -33773,7 +33773,7 @@ function race(iterable) {
 
 
     /*------------------------------------------------------------------------*/
     /*------------------------------------------------------------------------*/
 
 
-    /**
+    t/**
      * Creates a `lodash` object which wraps `value` to enable implicit method
      * Creates a `lodash` object which wraps `value` to enable implicit method
      * chain sequences. Methods that operate on and return arrays, collections,
      * chain sequences. Methods that operate on and return arrays, collections,
      * and functions can be chained together. Methods that retrieve a single value
      * and functions can be chained together. Methods that retrieve a single value
@@ -62607,13 +62607,13 @@ Script.prototype.runInContext = function (context) {
     if (!(context instanceof Context)) {
     if (!(context instanceof Context)) {
         throw new TypeError("needs a 'context' argument.");
         throw new TypeError("needs a 'context' argument.");
     }
     }
-    
+
     var iframe = document.createElement('iframe');
     var iframe = document.createElement('iframe');
     if (!iframe.style) iframe.style = {};
     if (!iframe.style) iframe.style = {};
     iframe.style.display = 'none';
     iframe.style.display = 'none';
-    
+
     document.body.appendChild(iframe);
     document.body.appendChild(iframe);
-    
+
     var win = iframe.contentWindow;
     var win = iframe.contentWindow;
     var wEval = win.eval, wExecScript = win.execScript;
     var wEval = win.eval, wExecScript = win.execScript;
 
 
@@ -62622,7 +62622,7 @@ Script.prototype.runInContext = function (context) {
         wExecScript.call(win, 'null');
         wExecScript.call(win, 'null');
         wEval = win.eval;
         wEval = win.eval;
     }
     }
-    
+
     forEach(Object_keys(context), function (key) {
     forEach(Object_keys(context), function (key) {
         win[key] = context[key];
         win[key] = context[key];
     });
     });
@@ -62631,11 +62631,11 @@ Script.prototype.runInContext = function (context) {
             win[key] = context[key];
             win[key] = context[key];
         }
         }
     });
     });
-    
+
     var winKeys = Object_keys(win);
     var winKeys = Object_keys(win);
 
 
     var res = wEval.call(win, this.code);
     var res = wEval.call(win, this.code);
-    
+
     forEach(Object_keys(win), function (key) {
     forEach(Object_keys(win), function (key) {
         // Avoid copying circular objects like `top` and `window` by only
         // Avoid copying circular objects like `top` and `window` by only
         // updating existing context properties or new properties in the `win`
         // updating existing context properties or new properties in the `win`
@@ -62650,9 +62650,9 @@ Script.prototype.runInContext = function (context) {
             defineProp(context, key, win[key]);
             defineProp(context, key, win[key]);
         }
         }
     });
     });
-    
+
     document.body.removeChild(iframe);
     document.body.removeChild(iframe);
-    
+
     return res;
     return res;
 };
 };
 
 

+ 9 - 6
app/router.js

@@ -25,6 +25,8 @@ module.exports = app => {
     const advanceCheck = app.middlewares.advanceCheck();
     const advanceCheck = app.middlewares.advanceCheck();
     // 变更令中间件
     // 变更令中间件
     const changeCheck = app.middlewares.changeCheck();
     const changeCheck = app.middlewares.changeCheck();
+    // 形象进度中间件
+    const scheduleCheck = app.middlewares.scheduleCheck();
     // 登入登出相关
     // 登入登出相关
     app.get('/login', 'loginController.index');
     app.get('/login', 'loginController.index');
     app.get('/login/port', api2otherCheck, 'loginController.port');
     app.get('/login/port', api2otherCheck, 'loginController.port');
@@ -438,19 +440,20 @@ module.exports = app => {
     app.get('/MP_verify_t3MkWAMqplVxPjmr.txt', 'wechatController.oauthTxt');
     app.get('/MP_verify_t3MkWAMqplVxPjmr.txt', 'wechatController.oauthTxt');
 
 
     // 形象进度
     // 形象进度
-    app.get('/tender/:id/schedule', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.index');
-    app.get('/tender/:id/schedule/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.ledger');
+    app.get('/tender/:id/schedule', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.index');
+    app.get('/tender/:id/schedule/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.ledger');
     app.post('/tender/:id/schedule/ledger/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadLedgerData');
     app.post('/tender/:id/schedule/ledger/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadLedgerData');
     app.post('/tender/:id/schedule/ledger/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveLedger');
     app.post('/tender/:id/schedule/ledger/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveLedger');
-    app.get('/tender/:id/schedule/plan', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.plan');
+    app.get('/tender/:id/schedule/plan', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.plan');
     app.post('/tender/:id/schedule/plan/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.savePlan');
     app.post('/tender/:id/schedule/plan/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.savePlan');
-    app.get('/tender/:id/schedule/stage', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageTp');
-    app.get('/tender/:id/schedule/stage/order/:order', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageTp');
+    app.get('/tender/:id/schedule/stage', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageTp');
+    app.get('/tender/:id/schedule/stage/order/:order', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageTp');
     app.post('/tender/:id/schedule/stage/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageTp');
     app.post('/tender/:id/schedule/stage/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageTp');
-    app.get('/tender/:id/schedule/stage/gcl', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageGcl');
+    app.get('/tender/:id/schedule/stage/gcl', sessionAuth, tenderCheck, uncheckTenderCheck, scheduleCheck, 'scheduleController.stageGcl');
     app.post('/tender/:id/schedule/stage/gcl/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageGcl');
     app.post('/tender/:id/schedule/stage/gcl/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageGcl');
     app.post('/tender/:id/schedule/stage/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadTpLedgerData');
     app.post('/tender/:id/schedule/stage/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadTpLedgerData');
     app.post('/tender/:id/schedule/stage/gcl/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadGclLedgerData');
     app.post('/tender/:id/schedule/stage/gcl/:order/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.loadGclLedgerData');
+    app.post('/tender/:id/schedule/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveAudit');
 
 
     // 书签
     // 书签
     app.post('/tender/:id/ledger/tag', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.billsTag');
     app.post('/tender/:id/ledger/tag', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.billsTag');

+ 54 - 0
app/service/schedule_audit.js

@@ -0,0 +1,54 @@
+'use strict';
+const scheduleConst = require('../const/schedule');
+module.exports = app => {
+    class ScheduleAudit extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'schedule_audit';
+        }
+
+        async addAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertData = {
+                    tid: this.ctx.tender.id,
+                    audit_id: data.audit_id,
+                    in_time: new Date(),
+                };
+                const result = await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return await this.getDataById(result.insertId);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async removeAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, { id: data.id });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+        async editAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (!this._.includes(this._.values(scheduleConst.permission), data.permission)) {
+                    throw '修改失败';
+                }
+                await transaction.update(this.tableName, { id: data.id, permission: data.permission });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+    return ScheduleAudit;
+};

+ 1 - 1
app/view/schedule/ledger.ejs

@@ -29,7 +29,7 @@
                     </a>
                     </a>
                 </div>
                 </div>
             </div>
             </div>
-            <% if (tender.user_id === ctx.session.sessionUser.accountId && !revising) { %>
+            <% if (ctx.tender.schedule_permission === scPermission.edit && !revising) { %>
             <div class="ml-auto">
             <div class="ml-auto">
                 <button type="button" id="ledger_submit" class="btn btn-primary btn-sm pull-right">确认提交</button>
                 <button type="button" id="ledger_submit" class="btn btn-primary btn-sm pull-right">确认提交</button>
             </div>
             </div>

+ 1 - 1
app/view/schedule/plan.ejs

@@ -6,7 +6,7 @@
             <h2>
             <h2>
                 <% if (planMonth) { %>计划至 <%- planMonth.split('-')[0] %>年<%- parseInt(planMonth.split('-')[1]) %>月 <% } %>
                 <% if (planMonth) { %>计划至 <%- planMonth.split('-')[0] %>年<%- parseInt(planMonth.split('-')[1]) %>月 <% } %>
             </h2>
             </h2>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
             <div class="ml-auto">
                 <a href="#mode" data-toggle="modal" data-target="#mode" class="btn btn-sm btn-outline-primary">计算方式</a>
                 <a href="#mode" data-toggle="modal" data-target="#mode" class="btn btn-sm btn-outline-primary">计算方式</a>
                 <a href="#edit-plan" data-toggle="modal" data-target="#edit-plan" class="btn btn-sm btn-outline-primary">管理计划</a>
                 <a href="#edit-plan" data-toggle="modal" data-target="#edit-plan" class="btn btn-sm btn-outline-primary">管理计划</a>

+ 1 - 1
app/view/schedule/stage_gcl.ejs

@@ -15,7 +15,7 @@
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
             <div class="ml-auto">
                 <a href="#edit-stage" data-toggle="modal" data-target="#edit-stage" class="btn btn-sm btn-outline-primary">管理计量</a>
                 <a href="#edit-stage" data-toggle="modal" data-target="#edit-stage" class="btn btn-sm btn-outline-primary">管理计量</a>
                 <a href="#add" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建实际计量</a>
                 <a href="#add" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建实际计量</a>

+ 2 - 2
app/view/schedule/stage_tp.ejs

@@ -29,14 +29,14 @@
                         </div>
                         </div>
                     </div>
                     </div>
                 </div>
                 </div>
-                <% if (!revising) { %>
+                <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
                 <div class="d-inline-flex">
                 <div class="d-inline-flex">
                     <a href="#delete" data-toggle="modal" data-target="#delete" class="btn btn-sm btn-outline-danger">删除本期进度</a>
                     <a href="#delete" data-toggle="modal" data-target="#delete" class="btn btn-sm btn-outline-danger">删除本期进度</a>
                 </div>
                 </div>
                 <% } %>
                 <% } %>
                 <% } %>
                 <% } %>
             </div>
             </div>
-            <% if (!revising) { %>
+            <% if (!revising && ctx.tender.schedule_permission === scPermission.edit) { %>
             <div class="ml-auto">
             <div class="ml-auto">
                 <a href="" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建新计量进度</a>
                 <a href="" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建新计量进度</a>
                 <% if (scheduleStage.length > 0) { %>
                 <% if (scheduleStage.length > 0) { %>

+ 3 - 0
app/view/tender/detail.ejs

@@ -101,6 +101,9 @@
                                 <a href="#bd-set-6" data-toggle="modal" data-target="#bd-set-6" class="btn btn-sm btn-outline-primary">章节设置</a>
                                 <a href="#bd-set-6" data-toggle="modal" data-target="#bd-set-6" class="btn btn-sm btn-outline-primary">章节设置</a>
                                 <a href="#bd-set-7" data-toggle="modal" data-target="#bd-set-7" class="btn btn-sm btn-outline-primary">付款账号</a>
                                 <a href="#bd-set-7" data-toggle="modal" data-target="#bd-set-7" class="btn btn-sm btn-outline-primary">付款账号</a>
                                 <i class="mx-2">|</i>
                                 <i class="mx-2">|</i>
+                                <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+                                <a href="#xxjd-set" data-toggle="modal" data-target="#xxjd-set" class="btn btn-sm btn-outline-primary">形象进度</a>
+                                <% } %>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="copyBtn">拷贝设置</a>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="copyBtn">拷贝设置</a>
                                 <% if (ctx.session.sessionUser.is_admin) { %>
                                 <% if (ctx.session.sessionUser.is_admin) { %>
                                 <a href="/tender/<%- tender.id %>/shenpi" class="btn btn-sm btn-outline-primary">审批流程</a>
                                 <a href="/tender/<%- tender.id %>/shenpi" class="btn btn-sm btn-outline-primary">审批流程</a>

+ 224 - 0
app/view/tender/detail_modal.ejs

@@ -1505,3 +1505,227 @@
         });
         });
     }
     }
 </script>
 </script>
+
+<% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+<!--标段设置-形象进度-->
+<div class="modal fade" id="xxjd-set" 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">设置可使用「形象进度」用户</div>
+                <div class="dropdown">
+                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="xxjd_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                        添加用户
+                    </button>
+                    <div class="dropdown-menu" id="xxjd_dropdownMenu" aria-labelledby="xxjd_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 => { %>
+                                        <% if (item.id !== ctx.tender.data.user_id) { %>
+                                            <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="mt-1" style="max-height: 300px;overflow: auto">
+                    <table class="table table-bordered">
+                        <tr><th>用户</th><th>查看</th><th>修改</th><th>移除</th></tr>
+                        <tbody id="schedule-users">
+                        <% for (const sa of scheduleAuditList) { %>
+                        <% const audit = ctx.helper._.find(accountList, { id : sa.audit_id }); %>
+                        <% if (audit) { %>
+                        <tr data-id="<%- audit.id %>"><td><p class="mb-0 d-flex"><b class="col-3 pl-0"><%- audit.name %></b> <span class="text-muted"><%- audit.mobile %></span><p class="text-muted mb-0"><%- audit.role %></p></td><td>
+                                <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" <% if (sa.permission !== scPermission.no) { %>checked<% } %>>
+                                    <label class="custom-control-label" for="<%- sa.id %>_customRadio41"></label>
+                                </div>
+                            </td><td>
+                                <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" <% if (sa.permission === scPermission.edit) { %>checked<% } %>>
+                                    <label class="custom-control-label" for="<%- sa.id %>_customRadio42"></label>
+                                </div>
+                            </td>
+                            <td><a href="javascript:void(0);" class="text-danger remove-schedule-user" data-id="<%- sa.id %>">移除</a></td>
+                        </tr>
+                        <% } %>
+                        <% } %>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    $(function () {
+        const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+        const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+        const cur_uid = parseInt('<%- ctx.tender.data.user_id %>');
+        const cur_tenderid = parseInt('<%- ctx.tender.id %>');
+        const scPermission = JSON.parse(unescape('<%- escape(JSON.stringify(scPermission)) %>'));
+        // 形象进度
+        let timer = null;
+        let oldSearchVal = null;
+        $('body').on('input propertychange', '.gr-search', function(e) {
+            oldSearchVal = e.target.value;
+            timer && clearTimeout(timer);
+            timer = setTimeout(() => {
+                const newVal = $(this).val();
+                let html = '';
+                if (newVal && newVal === oldSearchVal) {
+                    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>`
+                    });
+                    $('#xxjd_dropdownMenu .book-list').empty();
+                    $('#xxjd_dropdownMenu .book-list').append(html);
+                } else {
+                    if (!$('#xxjd_dropdownMenu .acc-btn').length) {
+                        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>';
+                        });
+                        $('#xxjd_dropdownMenu .book-list').empty();
+                        $('#xxjd_dropdownMenu .book-list').append(html);
+                    }
+                }
+            }, 400);
+        });
+
+        // 添加审批流程按钮逻辑
+        $('body').on('click', '.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', 'dl dd', function () {
+            const id = parseInt($(this).data('id'));
+            if (id) {
+                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('id')));
+                }
+                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-id="'+ user.id + '"><td><p class="mb-0 d-flex"><b class="col-3 pl-0">'+ user.name + '</b> <span class="text-muted">'+ user.mobile + '</span><p class="text-muted mb-0">'+ user.role + '</p></td><td>\n' +
+                        '                                <div class="custom-control custom-checkbox mb-2">\n' +
+                        '                                    <input type="checkbox" data-zhi="'+ scPermission.show +'" data-id="'+ data.id + '" id="'+ data.id + '_customRadio41" name="customCheckbox" class="custom-control-input" checked>\n' +
+                        '                                    <label class="custom-control-label" for="'+ data.id + '_customRadio41"></label>\n' +
+                        '                                </div>\n' +
+                        '                            </td><td>\n' +
+                        '                                <div class="custom-control custom-checkbox mb-2">\n' +
+                        '                                    <input type="checkbox" data-zhi="'+ scPermission.edit +'" data-id="'+ data.id + '" id="'+ data.id + '_customRadio42" name="customCheckbox" class="custom-control-input">\n' +
+                        '                                    <label class="custom-control-label" for="'+ data.id + '_customRadio42"></label>\n' +
+                        '                                </div>\n' +
+                        '                            </td>\n' +
+                        '                            <td><a href="javascript:void(0);" class="text-danger remove-schedule-user" data-id="'+ data.id + '">移除</a></td>\n' +
+                        '                        </tr>';
+                    $('#schedule-users').append(html);
+                });
+            }
+        });
+
+        // 移除用户
+        $('body').on('click', '.remove-schedule-user', function () {
+            const id = parseInt($(this).data('id'));
+            if (id) {
+                const prop = {
+                    id,
+                    type: 'del',
+                };
+                const _self = $(this);
+                postData('/tender/' + cur_tenderid + '/schedule/audit/save', prop, function (data) {
+                    _self.parents('tr').remove();
+                });
+            }
+        });
+
+        // 权限更改
+        $('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) {
+            });
+        });
+    })
+</script>
+<% } %>

+ 2 - 0
app/view/tender/tender_sub_menu.ejs

@@ -43,6 +43,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
             </ul>
             </ul>
         </div>
         </div>
+        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.tender.schedule_permission !== 0) { %>
         <div class="nav-box">
         <div class="nav-box">
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <ul class="nav-list list-unstyled sub-list">
             <ul class="nav-list list-unstyled sub-list">
@@ -51,6 +52,7 @@
                 <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/schedule/stage') !== -1) { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
                 <li <% if (ctx.url.indexOf('/tender/' + ctx.tender.id + '/schedule/stage') !== -1) { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
             </ul>
             </ul>
         </div>
         </div>
+        <% } %>
         <div class="nav-box">
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
             <ul class="nav-list list-unstyled">
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>

+ 2 - 0
app/view/tender/tender_sub_mini_menu.ejs

@@ -41,6 +41,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material"><i class="fa fa-line-chart"></i> <span>材料调差</span></a></li>
             </ul>
             </ul>
         </div>
         </div>
+        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.tender.schedule_permission !== 0) { %>
         <div class="nav-box">
         <div class="nav-box">
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <h3><i class="fa fa-bar-chart "></i> 形象进度</h3>
             <ul class="nav-list list-unstyled sub-list">
             <ul class="nav-list list-unstyled sub-list">
@@ -49,6 +50,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage' || ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage/gcl') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage' || ctx.url === '/tender/' + ctx.tender.id + '/schedule/stage/gcl') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/schedule/stage"><span>计量进度</span></a></li>
             </ul>
             </ul>
         </div>
         </div>
+        <% } %>
         <div class="nav-box">
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
             <ul class="nav-list list-unstyled">
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/report') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/report"><i class="fa fa-file-text-o"></i> <span>报表</span></a></li>