Browse Source

项目设置和变更审批流程更改

laiguoran 6 years ago
parent
commit
9232e26581

+ 26 - 0
app/const/account_group.js

@@ -0,0 +1,26 @@
+'use strict';
+
+/**
+ * 项目组和账号权限
+ *
+ * @author ELlisran
+ * @date 2019/3/20
+ * @version
+ */
+const accountGroup = {
+    JSDW: 1,
+    JLDW: 2,
+    SGDW: 3,
+    SJDW: 4,
+};
+
+const group = [];
+group[accountGroup.JSDW] = '建设单位';
+group[accountGroup.JLDW] = '监理单位';
+group[accountGroup.SGDW] = '施工单位';
+group[accountGroup.SJDW] = '设计单位';
+
+module.exports = {
+    type: accountGroup,
+    group,
+};

+ 25 - 0
app/controller/change_controller.js

@@ -15,6 +15,7 @@ const path = require('path');
 const audit = require('../const/audit');
 const codeRuleConst = require('../const/code_rule');
 const changeConst = require('../const/change');
+const accountGroup = require('../const/account_group').group;
 // const tenderMenu = require('../../config/menu').tenderMenu;
 
 module.exports = app => {
@@ -291,6 +292,30 @@ module.exports = app => {
 
                     renderData.dealBillList = dealBillList;
                     renderData.changeUnits = ctx.tender.info.precision;
+                    renderData.accountGroup = accountGroup;
+                    // 获取所有项目参与者
+                    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'],
+                    });
+                    renderData.accountList = accountList;
+                    // 重新上报获取审批流程
+                    if (auditStatus === 2) {
+                        const auditList2 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
+                        // 展示页右侧审批流程列表
+                        const auditList3 = [];
+                        for (let time = 1; time <= change.times; time++) {
+                            const auditTimeList = [];
+                            for (const al of auditList2) {
+                                if (al.times === time) {
+                                    auditTimeList.push(al);
+                                }
+                            }
+                            auditList3.push(auditTimeList);
+                        }
+                        renderData.auditList3 = auditList3;
+                    }
+
                     // 根据清单获取提交数据和计算总金额
                     const changeListData = [];
                     const changeWhiteListData = [];

+ 172 - 0
app/controller/setting_controller.js

@@ -11,6 +11,7 @@
 const officeList = require('../const/cld_office').list;
 const settingConst = require('../const/setting.js');
 const settingMenu = require('../../config/menu').settingMenu;
+const accountGroup = require('../const/account_group').group;
 
 module.exports = app => {
 
@@ -85,9 +86,25 @@ module.exports = app => {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                 }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
+
+                // 获取数据规则
+                // const rule = ctx.service.projectAccount.rule('updateUser');
+                // const frontRule = ctx.helper.validateConvert(rule);
+
+                // 获取项目用户列表
+                const accountData = await ctx.service.projectAccount.getAllDataByCondition({
+                    where: { project_id: projectId },
+                    columns: ['id', 'account', 'name', 'company', 'role', 'mobile', 'telephone', 'enable', 'is_admin', 'account_group'],
+                });
 
                 const renderData = {
                     projectData,
+                    accountData,
+                    accountGroup,
+                    // rule: JSON.stringify(frontRule),
                 };
                 await this.layout('setting/user.ejs', renderData, 'setting/user_modal.ejs');
             } catch (error) {
@@ -97,6 +114,134 @@ module.exports = app => {
         }
 
         /**
+         * 项目设置 -- 账号启用和停用设置(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async userSwitch(ctx) {
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) {
+                    throw '没有对应的项目数据';
+                }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.projectAccount.update(data, { id: data.id });
+                if (!result) {
+                    throw '提交数据失败';
+                }
+            } catch (err) {
+                this.log(err);
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+            ctx.body = responseData;
+        }
+
+        /**
+         * 项目设置 -- 账号添加(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async addUser(ctx) {
+            const projectData = ctx.session.sessionProject;
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+
+                // 获取验证规则
+                const rule = ctx.service.projectAccount.rule('add');
+                ctx.validate(rule);
+
+                const result = await ctx.service.projectAccount.save(ctx.request.body);
+                if (!result) {
+                    throw '保存账号数据失败';
+                }
+
+                this.setMessage('保存账号数据成功', this.messageType.SUCCESS);
+                ctx.redirect('/' + ctx.controllerName + '/user');
+            } catch (error) {
+                console.log(error);
+                this.setMessage(error.toString(), this.messageType.ERROR);
+                ctx.redirect(ctx.request.headers.referer);
+            }
+        }
+
+        /**
+         * 项目设置 -- 账号编辑(Post)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async updateUser(ctx) {
+            const projectData = ctx.session.sessionProject;
+            try {
+                // 验证数据
+                if (projectData.id === undefined) {
+                    throw '不存在对应的项目数据';
+                }
+
+                // 获取验证规则
+                const rule = ctx.service.projectAccount.rule('modify');
+                ctx.validate(rule);
+
+                const result = await ctx.service.projectAccount.save(ctx.request.body);
+                if (!result) {
+                    throw '保存账号数据失败';
+                }
+
+                this.setMessage('保存账号数据成功', this.messageType.SUCCESS);
+                ctx.redirect('/' + ctx.controllerName + '/user');
+            } catch (error) {
+                console.log(error);
+                this.setMessage(error.toString(), this.messageType.ERROR);
+                ctx.redirect(ctx.request.headers.referer);
+            }
+        }
+
+        async resetUserPassword(ctx) {
+            const response = {
+                err: 0,
+                msg: '',
+            };
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (projectData === null) {
+                    throw '没有对应的项目数据';
+                }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
+
+                const accountId = parseInt(ctx.request.body.id);
+                let password = ctx.request.body.reset_password;
+                password = password.toString();
+                if (isNaN(accountId) || accountId <= 0 || password.length < 6) {
+                    throw '参数错误';
+                }
+                const result = await ctx.service.projectAccount.resetPassword(accountId, password);
+                if (!result) {
+                    throw '重置密码失败!';
+                }
+            } catch (error) {
+                response.err = 1;
+                response.msg = error;
+            }
+
+            ctx.body = response;
+        }
+
+        /**
          * 项目设置 -- 自定义标段分类(Get)
          *
          * @param ctx
@@ -110,6 +255,9 @@ module.exports = app => {
                 if (projectData === null) {
                     throw '没有对应的项目数据';
                 }
+                if (ctx.session.sessionUser.is_admin === 0) {
+                    throw '没有访问权限';
+                }
 
                 const categoryData = await ctx.service.category.getAllCategory(projectId);
                 const tenderData = await ctx.service.tender.getList();
@@ -285,6 +433,30 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 检测账户是否存在
+         *
+         * @param {Object} ctx -egg全局变量
+         * @return {void}
+         */
+        async accountExist(ctx) {
+            const projectData = ctx.session.sessionProject;
+            const responseData = {
+                err: 0, msg: '', data: null,
+            };
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const account = data.account;
+                if (projectData.id === undefined) {
+                    throw '不存在对应项目';
+                }
+                responseData.data = await ctx.service.projectAccount.isAccountExist(account, projectData.id);
+                ctx.body = responseData;
+            } catch (error) {
+                ctx.body = { err: 1, msg: error.toString(), data: null };
+            }
+        }
     }
 
     return SettingController;

+ 31 - 0
app/extend/helper.js

@@ -92,6 +92,37 @@ module.exports = {
         return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
     },
 
+    accAdd(arg1, arg2) {
+        let r1;
+        let r2;
+        try {
+            r1 = arg1.toString().split('.')[1].length;
+        } catch (e) {
+            r1 = 0;
+        }
+        try {
+            r2 = arg2.toString().split('.')[1].length;
+        } catch (e) {
+            r2 = 0;
+        }
+        const c = Math.abs(r1 - r2);
+        const m = Math.pow(10, Math.max(r1, r2));
+        if (c > 0) {
+            const cm = Math.pow(10, c);
+            if (r1 > r2) {
+                arg1 = Number(arg1.toString().replace('.', ''));
+                arg2 = Number(arg2.toString().replace('.', '')) * cm;
+            } else {
+                arg1 = Number(arg1.toString().replace('.', '')) * cm;
+                arg2 = Number(arg2.toString().replace('.', ''));
+            }
+        } else {
+            arg1 = Number(arg1.toString().replace('.', ''));
+            arg2 = Number(arg2.toString().replace('.', ''));
+        }
+        return (arg1 + arg2) / m;
+    },
+
     // 四舍五入或末尾加零,实现类似php的 sprintf("%.".decimal."f", val);
     roundNum(val, decimals) {
         if (val !== '') {

+ 2 - 2
app/public/css/toast.css

@@ -7,7 +7,7 @@
     margin: 0 auto;
     opacity: 0.8;
     box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
-    z-index: 999;
+    z-index: 9999 !important;
     padding: 15px;
     border-radius: 3px;
     color: #ffffff;
@@ -39,4 +39,4 @@
 .toast.info {
     background-color: rgba(53, 184, 224, 0.8);
     border: 2px solid #35b8e0;
-}
+}

+ 73 - 53
app/public/js/change_set.js

@@ -45,6 +45,20 @@ $(document).ready(() => {
 
     // 上报时按钮点击
     $('a[data-target="#sub-ap"]').on('click', function () {
+        let category = $(this).attr('data-category');
+        if (category === 'save_change') {
+            // 保存修改modal
+            $('.up-change').hide();
+            $('.save-change').show();
+        } else {
+            // 上报审批modal
+            $('.up-change').show();
+            $('.save-change').hide();
+        }
+    });
+
+    // 重新上报时按钮点击
+    $('a[data-target="#sub-sp2"]').on('click', function () {
         let category = $(this).data('category');
         if (category === 'save_change') {
             // 保存修改modal
@@ -55,6 +69,7 @@ $(document).ready(() => {
             $('.up-change').show();
             $('.save-change').hide();
         }
+        $('#hideSp').attr('data-category', category);
     });
 
     // 提交表单
@@ -126,70 +141,75 @@ $(document).ready(() => {
         }
     });
 
-    // 搜索用户
-    $('#search_audit_btn').click(function () {
-        const keyword = $('#search_audit_input').val();
-        if (keyword === '') {
-            toastr.error('请输入姓名进行检索!');
-        } else {
-            $('#search_audit_list').html('');
-            const data = { keyword: keyword, type: 'more' };
-            postData('/search/user', data, function (result) {
-                const searchList = result;
-                $('#search_audit_list').html('');
-                if (searchList.length === 0) {
-                    toastr.error('不存在该用户姓名!');
-                } else {
-                    let search_audit_html = '';
-                    for (const s of searchList) {
-                        search_audit_html += '<div class="card border-primary" style="margin-bottom: 5px;">' +
-                            '<div class="card-body">' +
-                            '<h5 class="card-title">' +
-                            '<a class="btn btn-primary btn-sm pull-right add_audit_btn" ' +
-                            'href="javascript:void(0);" data-user="' + s.id + '/%/' + s.name + '/%/' + s.role + '/%/' + s.company + '">添加</a>' + s.name +
-                            '</h5>' +
-                            '<h6 class="card-subtitle mb-2 text-muted">' + s.role + '</h6>' +
-                            '<p class="card-text">' + s.company + '</p>' +
-                            '</div>' +
-                            '</div>';
-                    }
-                    $('#search_audit_list').html(search_audit_html);
-                }
-            });
+    // 审批人分组选择
+    $('#account_group').change(function () {
+        let account_html = '<option value="0">选择审批人</option>';
+        for (const account of accountList) {
+            if (parseInt($(this).val()) === 0 || parseInt($(this).val()) === account.account_group) {
+                const role = account.role !== '' ? '(' + account.role + ')' : '';
+                const company = account.company !== '' ? ' -' + account.company : '';
+                account_html += '<option value="' + account.id + '">' + account.name + role + company + '</option>';
+            }
         }
+        $('#account_list').html(account_html);
+    });
+
+    $('#hideSp').click(function () {
+       $('#sub-sp2').modal('hide');
     });
 
     // 添加到审批流程中
-    $('body').on('click', '.add_audit_btn', function () {
-        const userData = $(this).data('user').split('/%/');
-        // 先判断是否已存在该审批流程的审批人,再添加(不包括上报)
-        let auditListIdData = [];
-        $('#auditList li').each(function () {
-           let aid = $(this).data('auditid');
-           auditListIdData.push(aid);
-        });
-        if (!in_array(auditListIdData, userData[0])) {
-            // 令其它的审批人流程图标转换
-            $('#auditList li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
-            // 添加新审批人
-            const addhtml = '<li class="list-group-item" data-auditmsg="' + $(this).data('user') + '"' +
-                'data-auditid="' + userData[0] + '" >' +
-                '<a href="javascript:void(0);" class="text-danger pull-right remove_audit_btn">移除</a>' +
-                '<i class="fa fa-stop-circle"></i>' +
-                userData[1] + '<small class="text-muted">' + userData[2] + '</small>' +
-                '</li>';
-            $('#auditList').append(addhtml);
-        } else {
-            toastr.error('审批流程中已存在该用户!');
+    $('body').on('change', '#account_list', function () {
+        let id = $(this).val();
+        id = parseInt(id);
+        if (id !== 0) {
+            let auditListIdData = [];
+            $('#auditList li').each(function () {
+                let aid = $(this).data('auditid');
+                auditListIdData.push(aid);
+            });
+            if (!in_array(auditListIdData, id)) {
+                const accountInfo = accountList.find(function (item) {
+                    return item.id === id;
+                });
+                const user = accountInfo.id + '/%/' + accountInfo.name + '/%/' + accountInfo.role + '/%/' + accountInfo.company;
+                const addhtml = '<li class="list-group-item" data-auditmsg="' + user + '"' +
+                    'data-auditid="' + accountInfo.id + '" >' +
+                    '<a href="javascript:void(0);" class="text-danger pull-right remove_audit_btn">移除</a>' +
+                    '<span>' + (auditListIdData.length+1) + '</span> ' + accountInfo.name + '  <small class="text-muted">' + accountInfo.role + '</small>' +
+                    '<p class="m-0 ml-2"><small class="text-muted">' + accountInfo.company + '</small></p>' +
+                    '</li>';
+                $('#auditList').append(addhtml);
+
+                // 重新上报时。令其它的审批人流程图标转换
+                $('#shenpi-audit-list li i').removeClass('fa-stop-circle').addClass('fa-chevron-circle-down');
+                // 添加新审批人
+                const addhtml2 = '<li class="list-group-item" data-auditid="' + accountInfo.id + '" >' +
+                    '<i class="fa fa-stop-circle"></i>' +
+                    accountInfo.name + ' <small class="text-muted">' + accountInfo.role + '</small>' +
+                    '</li>';
+                $('#shenpi-audit-list').append(addhtml2);
+            } else {
+                toastr.error('审批流程中已存在该用户!');
+            }
         }
     });
 
     // 移除审批流程的审批人
     $('body').on('click', '.remove_audit_btn', function () {
+        const uid = $(this).parents('li').attr('data-auditid');
         $(this).parents('li').remove();
+        let index = 1;
+        $('#auditList li').each(function () {
+            $(this).children('span').text(index);
+            index++;
+        });
+
+        // 重新上报时。移除审批流程
         // 令最后一个图标转换
-        if ($('#auditList li').length !== 0 && !$('#auditList li i').hasClass('fa-stop-circle')) {
-            $('#auditList li').eq($('#auditList li').length-1).children('i')
+        $('#shenpi-audit-list li[data-auditid="' + uid + '"]').remove();
+        if ($('#shenpi-audit-list li').length !== 0 && !$('#shenpi-audit-list li i').hasClass('fa-stop-circle')) {
+            $('#shenpi-audit-list li').eq($('#shenpi-audit-list li').length-1).children('i')
                 .removeClass('fa-chevron-circle-down').addClass('fa-stop-circle');
         }
     });

+ 186 - 0
app/public/js/setting.js

@@ -0,0 +1,186 @@
+'use strict';
+
+/**
+ * 项目信息js
+ *
+ * @author EllisRan.
+ * @date 2019/3/19
+ * @version
+ */
+
+$(document).ready(() => {
+    // 启用和停用账号
+    $('.account-switch-btn').on('click', function () {
+        const data = {
+            enable: $(this).hasClass('btn-outline-success') ? 1 : 0,
+            id: $(this).data('account'),
+        };
+        postData('/setting/user/switch', data, function () {
+            window.location.href = '/setting/user';
+        });
+    });
+
+    // 编辑账号
+    $('a[data-target="#edit-user"]').on('click', function () {
+        const account = $(this).data('account');
+        $('#edit-user input[name="account"]').val(account.account);
+        $('#edit-user input[name="name"]').val(account.name);
+        $('#edit-user input[name="company"]').val(account.company);
+        $('#edit-user input[name="role"]').val(account.role);
+        $('#edit-user input[name="mobile"]').val(account.mobile);
+        $('#edit-user input[name="telephone"]').val(account.telephone);
+        $('#edit-user input[name="id"]').val(account.id);
+        $('#edit-user select[name="account_group"]').val(account.account_group);
+        $('#edit-user input[class="account-check"]').val(account.account);
+    });
+
+    // 分配随机密码
+    $("#rand-password").click(function() {
+        const password = randPassword();
+        $(this).parent().parent().find('input').val(password);
+    });
+
+    // 重置密码
+    let isChange = false;
+    $("#reset-password-btn").click(function() {
+        try {
+            if (isChange) {
+                throw '稍后再操作';
+            }
+            const resetPassword = $("#reset-password").val();
+            const id = $("#user-id").val();
+            if (resetPassword.length < 6) {
+                throw '密码长度不能小于6';
+            }
+            if (!/^[0-9a-zA-Z*~!@&%$^\\(\\)#_\[\]\-\+={}|?'":,<>.`]+$/.test(resetPassword)) {
+                throw '密码只支持英文数字及符号';
+            }
+            const btn = $(this);
+            $.ajax({
+                url: '/setting/user/reset/password',
+                type: 'post',
+                data: { id: id, reset_password: resetPassword },
+                dataType: 'json',
+                error: function() {
+                    isChange = false;
+                    btn.html('重置密码');
+                    throw '网络错误!';
+                },
+                beforeSend: function(xhr) {
+                    let csrfToken = Cookies.get('csrfToken');
+                    xhr.setRequestHeader('x-csrf-token', csrfToken);
+                    isChange = true;
+                    btn.html('<i class="fa fa-spinner fa-pulse"></i>');
+                },
+                success: function(response) {
+                    isChange = false;
+                    btn.html('重置密码');
+                    if (response.err !== 0) {
+                        throw response.msg;
+                    }
+                    $("#reset-password").val('');
+                    toast('重置成功', 'success', 'check');
+                }
+            });
+        } catch (error) {
+            toast(error, 'error', 'exclamation-circle');
+            console.log(error);
+        }
+    });
+
+    // 账号查重
+    $('input[name="account"]').on('blur', function () {
+        const self = $(this);
+        if (self.val() !== self.siblings('input').val()) {
+            const data = {account: $(this).val()};
+            postData('/setting/user/exist', data, function (data) {
+                if (data === null) {
+                    self.removeClass('is-invalid');
+                } else {
+                    self.addClass('is-invalid');
+                }
+            })
+        } else {
+            self.removeClass('is-invalid');
+        }
+    });
+});
+
+
+/**
+ * 表单检测
+ */
+function checkUserForm(status) {
+    try {
+        if (status === 'add') {
+            if ($('#add-user select[name="account_group"]').val() == 0) {
+                throw '请选择账号组';
+            }
+            if ($('#add-user input[name="account"]').val() == '' || $('#add-user input[name="account"]').hasClass('is-invalid')) {
+                throw '账号不能为空或已存在';
+            }
+            if ($('#add-user input[name="password"]').val() == '' || $('#add-user input[name="password"]').val().length < 6) {
+                throw '密码不能为空或不能小于6位';
+            }
+            if (!/^[0-9a-zA-Z*~!@&%$^\\(\\)#_\[\]\-\+={}|?'":,<>.`]+$/.test($('#add-user input[name="password"]').val())) {
+                throw '密码只支持英文数字及符号';
+            }
+            if ($('#add-user input[name="name"]').val() == '') {
+                throw '姓名不能为空';
+            }
+            if ($('#add-user input[name="company"]').val() == '') {
+                throw '单位名称不能为空';
+            }
+            if ($('#add-user input[name="role"]').val() == '') {
+                throw '职位名称不能为空';
+            }
+            if ($('#add-user input[name="mobile"]').val() == '') {
+                throw '手机号不能为空';
+            }
+        } else {
+            if ($('#edit-user select[name="account_group"]').val() == 0) {
+                throw '请选择账号组';
+            }
+            if ($('#edit-user input[name="account"]').val() == '' || $('#add-user input[name="account"]').hasClass('is-invalid')) {
+                throw '账号不能为空或已存在';
+            }
+            if ($('#edit-user input[name="name"]').val() == '') {
+                throw '姓名不能为空';
+            }
+            if ($('#edit-user input[name="company"]').val() == '') {
+                throw '单位名称不能为空';
+            }
+            if ($('#edit-user input[name="role"]').val() == '') {
+                throw '职位名称不能为空';
+            }
+            if ($('#edit-user input[name="mobile"]').val() == '') {
+                throw '手机号不能为空';
+            }
+        }
+    } catch (err) {
+        toast(err, 'error', 'exclamation-circle');
+        return false;
+    }
+}
+
+/**
+ * 随机密码
+ */
+function randPassword() {
+    let result = '';
+    // 随机6-10位
+    const length = Math.ceil(Math.random() * 2 + 8);
+    let numberSeed = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+    let stringSeed = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+        'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
+
+    const randSeed = stringSeed.concat(numberSeed);
+    const seedLength = randSeed.length - 1;
+    for (let i = 0; i < length; i++) {
+        const index = Math.ceil(Math.random() * seedLength);
+        result += randSeed[index];
+    }
+
+    return result;
+}

+ 7 - 0
app/router.js

@@ -33,6 +33,13 @@ module.exports = app => {
     app.post('/setting/updateinfo/:id', sessionAuth, 'settingController.updateinfo');
     // 账号设置
     app.get('/setting/user', sessionAuth, 'settingController.user');
+    // 账号停用和启用
+    app.post('/setting/user/switch', sessionAuth, 'settingController.userSwitch');
+    app.post('/setting/user/add', sessionAuth, 'settingController.addUser');
+    app.post('/setting/user/update', sessionAuth, 'settingController.updateUser');
+    app.post('/setting/user/reset/password', sessionAuth, 'settingController.resetUserPassword');
+    app.post('/setting/user/exist', sessionAuth, 'settingController.accountExist');
+
     // 标段自定义类别
     app.get('/setting/category', sessionAuth, 'settingController.category');
     app.post('/setting/category/add', sessionAuth, 'settingController.addCategory');

+ 20 - 19
app/service/change.js

@@ -333,26 +333,27 @@ module.exports = app => {
                 changeList.push.apply(changeList, changeWhiteList);
                 const insertCL = [];
                 let total_price = 0;
-                for (const cl of changeList) {
-                    const clInfo = cl.split(';');
-                    const clArray = {
-                        tid: tenderId,
-                        cid: changeInfo.cid,
-                        lid: clInfo[7],
-                        code: clInfo[0],
-                        name: clInfo[1],
-                        unit: clInfo[2],
-                        unit_price: clInfo[3],
-                        oamount: clInfo[4],
-                        camount: clInfo[5],
-                        samount: '',
-                        detail: clInfo[6],
-                    };
-                    insertCL.push(clArray);
-                    total_price += this.ctx.helper.accMul(clArray.unit_price, clArray.camount);
+                if (changeList.length > 0) {
+                    for (const cl of changeList) {
+                        const clInfo = cl.split(';');
+                        const clArray = {
+                            tid: tenderId,
+                            cid: changeInfo.cid,
+                            lid: clInfo[7],
+                            code: clInfo[0],
+                            name: clInfo[1],
+                            unit: clInfo[2],
+                            unit_price: clInfo[3],
+                            oamount: clInfo[4],
+                            camount: clInfo[5],
+                            samount: '',
+                            detail: clInfo[6],
+                        };
+                        insertCL.push(clArray);
+                        total_price = this.ctx.helper.accAdd(total_price, this.ctx.helper.accMul(clArray.unit_price, clArray.camount));
+                    }
+                    await this.transaction.insert(this.ctx.service.changeAuditList.tableName, insertCL);
                 }
-                await this.transaction.insert(this.ctx.service.changeAuditList.tableName, insertCL);
-
                 // 修改变更令基本数据
                 const cArray = {
                     code: postData.code,

+ 22 - 0
app/service/change_audit.js

@@ -223,6 +223,28 @@ module.exports = app => {
             const list = await this.db.query(sql, sqlParam);
             return list;
         }
+
+        /**
+         * 获取除当前次数的审批人列表
+         * @param {Object} cid - 变更令id
+         * @param {int} times - 次数
+         * @return {object} 返回结果
+         */
+        async getListByBack(cid, times) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('cid', {
+                value: this.db.escape(cid),
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('times', {
+                value: times,
+                operate: '!=',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const result = await this.db.query(sql, sqlParam);
+
+            return result;
+        }
     }
 
     return ChangeAudit;

+ 101 - 11
app/service/project_account.js

@@ -71,6 +71,25 @@ module.exports = app => {
                         auth_mobile: { type: 'mobile', allowEmpty: false },
                     };
                     break;
+                case 'add':
+                    rule = {
+                        account: { type: 'string', required: true },
+                        password: { type: 'string', required: true, min: 6 },
+                        name: { type: 'string', required: true },
+                        company: { type: 'string', required: true },
+                        role: { type: 'string', required: true },
+                        mobile: { type: 'mobile', required: true },
+                    };
+                    break;
+                case 'modify':
+                    rule = {
+                        account: { type: 'string', required: true },
+                        name: { type: 'string', required: true },
+                        company: { type: 'string', required: true },
+                        role: { type: 'string', required: true },
+                        mobile: { type: 'mobile', required: true },
+                    };
+                    break;
                 default:
                     break;
             }
@@ -162,6 +181,7 @@ module.exports = app => {
                         name: accountData.name,
                         accountId: accountData.id,
                         loginTime: currentTime,
+                        is_admin: accountData.is_admin,
                         sessionToken,
                         loginType,
                         permission,
@@ -276,25 +296,38 @@ module.exports = app => {
          * 修改用户数据
          *
          * @param {Object} data - post过来的数据
-         * @param {Number} accountId - 账号id
          * @return {Boolean} - 返回修改结果
          */
-        async save(data, accountId) {
+        async save(data) {
             if (data._csrf !== undefined) {
                 delete data._csrf;
             }
-            accountId = parseInt(accountId);
-            accountId = isNaN(accountId) ? 0 : accountId;
-            let result = false;
+            const id = data.id !== undefined ? parseInt(data.id) : 0;
+            if (id > 0) {
+                // 修改操作时
+                delete data.create_time;
+                data.id = id;
+            } else {
+                // 重名检测
+                const accountData = await this.db.select(this.tableName, {
+                    where: {
+                        account: data.account,
+                        project_id: data.enterprise_id,
+                    },
+                });
+                if (accountData.length > 0) {
+                    throw '已存在对应的帐户名';
+                }
+
+                // 加密密码
+                data.password = crypto.createHmac('sha1', data.account).update(data.password)
+                    .digest().toString('base64');
 
-            if (accountId <= 0) {
-                return result;
             }
-            data.id = accountId;
+            const operate = id === 0 ? await this.db.insert(this.tableName, data) :
+                await this.db.update(this.tableName, data);
 
-            // 更新数据
-            const operate = await this.db.update(this.tableName, data);
-            result = operate.affectedRows > 0;
+            const result = operate.affectedRows > 0;
 
             return result;
         }
@@ -384,6 +417,63 @@ module.exports = app => {
 
             return this.save(updateData, accountId);
         }
+
+        /**
+         * 重置密码
+         *
+         * @param {Number} accountId - 账号id
+         * @param {String} password - 重置的密码
+         * @return {Boolean} - 重置结果
+         */
+        async resetPassword(accountId, password) {
+            // 初始化事务
+            this.transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                // 查找对应账号数据
+                const accountData = await this.getDataByCondition({ id: accountId });
+                if (accountData.account === undefined) {
+                    throw '不存在对应账号';
+                }
+                // 加密密码
+                const encryptPassword = crypto.createHmac('sha1', accountData.account).update(password)
+                    .digest().toString('base64');
+                // 更新账号密码
+                const sql = 'UPDATE ?? SET password=? WHERE id=? AND password != ?;';
+                const sqlParam = [this.tableName, encryptPassword, accountId, 'SSO password'];
+                const operate = await this.transaction.query(sql, sqlParam);
+                result = operate.affectedRows > 0;
+
+                if (!result) {
+                    throw '更新密码失败';
+                }
+
+                // 发送短信
+                if (accountData.auth_mobile !== '') {
+                    const sms = new SMS(this.ctx);
+                    const content = '【纵横计量支付】账号:' + accountData.account + ',密码重置为:' + password;
+                    sms.send(accountData.auth_mobile, content);
+                }
+
+                this.transaction.commit();
+            } catch (error) {
+                this.transaction.rollback();
+            }
+
+            return result;
+        }
+
+        /**
+         * 判断是否存在对应的账号
+         *
+         * @param {String} account - 账号名称
+         * @param {Number} projectId - 项目id
+         * @return {Boolean} - 返回是否存在
+         */
+        async isAccountExist(account, projectId) {
+            const accountData = await this.db.get(this.tableName, { account, project_id: projectId });
+            return accountData;
+        }
     }
 
     return ProjectAccount;

+ 2 - 0
app/view/change/index.ejs

@@ -13,10 +13,12 @@
                     </select>
                 </div>
             </div>
+            <% if (tender.user_id === uid) { %>
             <div>
                 <a href="#add-bj" data-toggle="modal" data-target="#add-bj" class="btn btn-sm btn-primary pull-right">新建变更令</a>
                 <a href="#setting" data-toggle="modal" data-target="#setting" class="btn btn-sm btn-outline-primary pull-right"><i class="fa fa-cog"></i></a>
             </div>
+            <% } %>
         </div>
     </div>
     <div class="content-wrap">

+ 7 - 2
app/view/change/info.ejs

@@ -1,6 +1,10 @@
 <link rel="stylesheet" type="text/css" href="/public/css/datatable/dataTables.bootstrap4.min.css">
 <link rel="stylesheet" type="text/css" href="/public/css/datatable/fixedColumns.bootstrap4.min.css">
 <style type="text/css" class="init">
+    #tablist li .active {
+        background: #e4e7ea;
+        font-weight: 600;
+    }
     .qd-table .input-sm{
         height:30px;
         padding:2px;
@@ -138,8 +142,8 @@
             </div>
             <% } else if (auditStatus === 2) { %>
             <div>
-                <a href="#sub-ap" data-category="save_change" data-toggle="modal" data-target="#sub-ap" class="btn btn-sm btn-success pull-right">保存修改</a>
-                <a href="#sub-ap" data-category="up_change" data-toggle="modal" data-target="#sub-ap" class="btn btn-primary btn-sm pull-right">重新上报</a>
+                <a href="#sub-sp2" data-category="save_change" data-toggle="modal" data-target="#sub-sp2" class="btn btn-sm btn-success pull-right">保存修改</a>
+                <a href="#sub-sp2" data-category="up_change" data-toggle="modal" data-target="#sub-sp2" class="btn btn-primary btn-sm pull-right">重新上报</a>
                 <!--<a href="#" class="btn btn-outline-danger btn-sm pull-right text-truncate" style="max-width: 100px;" title="删除 <%- change.code %>">删除 <%- change.code %></a>-->
             </div>
             <% } else if (auditStatus === 3) { %>
@@ -735,6 +739,7 @@
 <script>
     const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
     const changeUnits = JSON.parse('<%- JSON.stringify(changeUnits) %>');
+    const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     const billsTable = {
         columnDefs: [
             { className: 'allwidth1', width: 100, targets: 0 },

+ 132 - 33
app/view/change/info_modal.ejs

@@ -41,16 +41,23 @@
             </div>
             <form method="post" action="/tender/<%- tender.id %>/change/<%- change.cid %>/save">
                 <div class="modal-body">
-                    <!--如未创建清单-->
-                    <!--<h5>还没添加任何变更清单,无法提交。</h5>-->
-                    <!--可以提交审批 但需要设置审批流程-->
                     <div class="form-group">
-                        <label>搜索审批人</label>
+                        <label>选择审批人</label>
                         <div class="input-group">
-                            <input class="form-control" placeholder="请输入姓名进行检索" type="text" id="search_audit_input">
-                            <div class="input-group-append">
-                                <button class="btn btn-outline-secondary" type="button" id="search_audit_btn"><i class="fa fa-search"></i></button>
+                            <div class="input-group-prepend">
+                                <select class="form-control" id="account_group">
+                                    <option value="0">所有分组</option>
+                                    <% for (const dw in accountGroup) { %>
+                                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
+                                    <% } %>
+                                </select>
                             </div>
+                            <select class="form-control" id="account_list">
+                                <option value="0">选择审批人</option>
+                                <% for (const account of accountList) { %>
+                                    <option value="<%= account.id %>"><%= account.name %><% if (account.role !== '') { %>(<%= account.role %>)<% } %><% if (account.company !== '') { %> -<%= account.company %><% } %></option>
+                                <% } %>
+                            </select>
                         </div>
                     </div>
                     <div id="search_audit_list">
@@ -59,26 +66,14 @@
                         <div class="card-header">
                             审批流程
                         </div>
-                        <ul class="list-group list-group-flush">
-                            <% for (const audit of auditList) { %>
-                                <% if (audit.usite === 0) { %>
-                                <li class="list-group-item">
-                                    <i class="fa fa-play-circle fa-rotate-90"></i>
-                                    <%= audit.name %><small class="text-muted"><%= audit.jobs %></small>
-                                </li>
-                                <% } %>
-                            <% } %>
-                        </ul>
                         <ul class="list-group list-group-flush" id="auditList">
-                            <% for (const audit of auditList) { %>
+                            <% for (const [index, audit] of auditList.entries()) { %>
                                 <% if (audit.usite !== 0) { %>
                                 <li class="list-group-item" data-auditmsg="<%= audit.uid + '/%/' + audit.name + '/%/' + audit.jobs + '/%/' + audit.company %>"
                                     data-auditid="<%= audit.uid %>">
                                     <a href="javascript:void(0)" class="text-danger pull-right remove_audit_btn">移除</a>
-                                    <% if (audit.usite === auditList.length-1) { %><i class="fa fa-stop-circle"></i>
-                                    <% } else { %><i class="fa fa-chevron-circle-down"></i>
-                                    <% } %>
-                                    <%= audit.name %><small class="text-muted"><%= audit.jobs %></small>
+                                    <span><%= index %></span> <%= audit.name %>  <small class="text-muted"><%= audit.jobs %></small>
+                                    <p class="m-0 ml-2"><small class="text-muted"><%= audit.company %></small></p>
                                 </li>
                                 <% } %>
                             <% } %>
@@ -126,11 +121,6 @@
                                 <tr><th>项目节编号</th><th>名称</th><th>部位明细</th><th>选择</th></tr>
                                 </thead>
                                 <tbody id="code-list" data-index="">
-                                <!--<tr><td colspan="3">自行编辑变更详情</td><td><input type="checkbox"></td></tr>-->
-                                <!--<tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台1#桩</td><td><input type="checkbox"></td></tr>-->
-                                <!--<tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台2#桩</td><td><input type="checkbox"></td></tr>-->
-                                <!--<tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台3#桩</td><td><input type="checkbox"></td></tr>-->
-                                <!--<tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台4#桩</td><td><input type="checkbox"></td></tr>-->
                                 </tbody>
                             </table>
                         </div>
@@ -145,6 +135,106 @@
         </div>
     </div>
 </div>
+
+<% if (auditStatus === 2) { %>
+    <!--重新上报-->
+    <div class="modal fade" id="sub-sp2" 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="row">
+                        <div class="col-4">
+                            <a href="#sub-ap" data-toggle="modal" data-category="" data-target="#sub-ap" id="hideSp">修改审批流程</a>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <% for (const audit of auditList) { %>
+                                        <% if (audit.usite === 0) { %>
+                                            <li class="list-group-item">
+                                                <i class="fa fa-play-circle fa-rotate-90"></i>
+                                                <%= audit.name %>  <small class="text-muted"><%= audit.jobs %></small>
+                                            </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                                <ul class="list-group list-group-flush" id="shenpi-audit-list">
+                                    <% for (const [index,a] of auditList.entries()) { %>
+                                        <% if (a.usite !== 0 ) { %>
+                                            <li class="list-group-item" data-auditid="<%= a.uid %>">
+                                                <% if (index+1 !== auditList.length) { %>
+                                                    <i class="fa fa-chevron-circle-down"></i>
+                                                <% } else if (index+1 === auditList.length) { %>
+                                                    <i class="fa fa-stop-circle"></i>
+                                                <% } %>
+                                                <%= a.name %>  <small class="text-muted"><%= a.jobs %></small>
+                                            </li>
+                                        <% }%>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        </div>
+                        <div class="col-8 modal-height-500" style="overflow: auto">
+                            <% for (const time in auditList3) { %>
+                            <% if (auditList3[time].length > 0) { %>
+                                <div class="card mt-3">
+                                    <ul class="list-group list-group-flush">
+                                        <% for (const [aindex,al] of auditList3[time].entries()) { %>
+                                            <li class="list-group-item">
+                                                <% if (al.usite === 0 && al.status === 2) { %>
+                                                    <span class="pull-right">重新上报中</span>
+                                                <% } else if (al.usite === 0 && al.status === 3) { %>
+                                                    <span class="text-success pull-right">上报</span>
+                                                <% } else if (al.usite !== 0 && al.status === 2) { %>
+                                                    <span class="pull-right">审批中</span>
+                                                <% } else if (al.usite !== 0 && al.status === 3) { %>
+                                                    <span class="text-success pull-right">审批通过</span>
+                                                <% } else if (al.usite !== 0 && al.status === 4) { %>
+                                                    <span class="text-danger pull-right">审批终止</span>
+                                                <% } else if (al.usite !== 0 && (al.status === 5 || al.status === 6)) { %>
+                                                    <span class="text-warning pull-right">审批退回 </span>
+                                                <% } %>
+                                                <h5 class="card-title">
+                                                    <% if (al.usite === 0 && al.status === 2 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90"></i>
+                                                    <% } else if (al.usite === 0 && al.status === 3 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90 text-success"></i>
+                                                    <% } else if (al.status === 1 || al.status === 2) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList3[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> "></i>
+                                                    <% } else if (al.status === 3) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList3[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-success"></i>
+                                                    <% } else if (al.status === 4) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList3[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-danger"></i>
+                                                    <% } else if (al.status === 5 || al.status === 6) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList3[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-warning"></i>
+                                                    <% } %>
+                                                    <%= al.name %>&nbsp;<small class="text-muted"><%= al.jobs %></small>
+                                                </h5>
+                                                <% if (al.sdesc !== '' && al.sdesc !== null) { %>
+                                                    <p class="card-text mb-1"><%= al.sdesc %></p>
+                                                <% } %>
+                                                <% if (al.sin_time !== null) { %>
+                                                    <p class="card-text"><small class="text-muted"><%= moment(al.sin_time).format('YYYY-MM-DD') %></small></p>
+                                                <% } %>
+                                            </li>
+                                        <% } %>
+                                    </ul>
+                                </div>
+                            <% } %>
+                            <% } %>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                    <button type="button" data-sumbit="sumbit_change" data-category="up_change" class="btn btn-primary up-change">确认上报</button>
+                    <button type="button" data-sumbit="sumbit_change" data-category="save_change" class="btn btn-success save-change">保存修改</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
 <% } %>
 
 <% if (auditStatus === 3 || auditStatus === 4 || auditStatus === 5 || auditStatus === 7) { %>
@@ -183,8 +273,10 @@
                                 <li class="list-group-item">
                                     <% if (al.usite === 0 && al.status === 2) { %>
                                     <span class="pull-right">重新上报中</span>
-                                    <% } else if (al.usite === 0 && al.status === 3) { %>
+                                    <% } else if (al.usite === 0 && al.status === 3 && a.times === 1) { %>
                                     <span class="text-success pull-right">上报</span>
+                                    <% } else if (al.usite === 0 && al.status === 3 && a.times !== 1) { %>
+                                    <span class="text-success pull-right">重新上报</span>
                                     <% } else if (al.usite !== 0 && al.status === 2) { %>
                                     <span class="pull-right">审批中</span>
                                     <% } else if (al.usite !== 0 && al.status === 3) { %>
@@ -210,9 +302,12 @@
                                         <% } %>
                                         <%= al.name %>&nbsp;<small class="text-muted"><%= al.jobs %></small>
                                     </h5>
-                                    <% if (al.sin_time !== null || (al.sdesc !== '' && al.sdesc !== null)) { %>
-                                        <p class="card-text"><%= al.sdesc %>&nbsp;<%= moment(al.sin_time).format('YYYY-MM-DD') %></p>
-                                    <% } %>
+                                        <% if (al.sdesc !== '' && al.sdesc !== null) { %>
+                                            <p class="card-text mb-1"><%= al.sdesc %></p>
+                                        <% } %>
+                                        <% if (al.sin_time !== null) { %>
+                                            <p class="card-text"><small class="text-muted"><%= moment(al.sin_time).format('YYYY-MM-DD') %></small></p>
+                                        <% } %>
                                 </li>
                                 <% } %>
                             </ul>
@@ -243,8 +338,10 @@
                         <ul class="list-group list-group-flush">
                             <% for (const [index,a] of auditList.entries()) { %>
                             <li class="list-group-item">
-                                <% if (a.status === 3 && a.usort === 0) { %>
+                                <% if (a.status === 3 && a.usort === 0 && a.times === 1) { %>
                                 <span class="text-success pull-right">上报</span>
+                                <% } else if (a.status === 3 && a.usort === 0 && a.times !== 1) { %>
+                                <span class="text-success pull-right">重新上报</span>
                                 <% } else if (a.status === 3 && a.usort !== 0) { %>
                                 <span class="text-success pull-right">审批通过</span>
                                 <% } else if (a.status === 2) { %>
@@ -309,8 +406,10 @@
                         <ul class="list-group list-group-flush">
                             <% for (const [index,a] of auditList.entries()) { %>
                                 <li class="list-group-item">
-                                    <% if (a.status === 3 && a.usort === 0) { %>
+                                    <% if (a.status === 3 && a.usort === 0 && a.times === 1) { %>
                                         <span class="text-success pull-right">上报</span>
+                                    <% } else if (a.status === 3 && a.usort === 0 && a.times !== 1) { %>
+                                        <span class="text-success pull-right">重新上报</span>
                                     <% } else if (a.status === 3 && a.usort !== 0) { %>
                                         <span class="text-success pull-right">审批通过</span>
                                     <% } else if (a.status === 2) { %>

+ 2 - 0
app/view/change/modal.ejs

@@ -1,3 +1,4 @@
+<% if (tender.user_id === uid) { %>
 <!--首次使用-->
 <div class="modal" id="first">
     <div class="modal-dialog" role="document" >
@@ -178,3 +179,4 @@
     let connectorRule = '<%- tender.c_connector %>';
 </script>
 <script src="/public/js/moment/moment.min.js"></script>
+<% } %>

+ 3 - 3
app/view/layout/menu.ejs

@@ -4,7 +4,7 @@
         <ul class="nav nav-pills nav-stacked bg-nav">
             <% for (const index in ctx.menuList) { %>
             <% if (ctx.menuList[index].display === undefined || !ctx.menuList[index].display) { continue } %>
-            <li <% if(ctx.controllerName === index) { %>class="active"<% } %>>
+            <li <% if(ctx.controllerName === index || (ctx.controllerName === 'list' && index === 'tender')) { %>class="active"<% } %>>
                 <a href="<%- ctx.menuList[index].url %>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- ctx.menuList[index].name %>">
                     <i class="fa <%- ctx.menuList[index].icon %>"></i>
                     <% if (ctx.menuList[index].caption) { %>
@@ -17,7 +17,7 @@
     </div>
     <div class="nav-bottom mt-auto">
         <ul class="nav nav-pills nav-stacked bg-nav">
-            <li><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="项目设置"><i class="fa fa-cogs"></i><span>项目设置</span></a></li>
+            <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="项目信息"><i class="fa fa-cogs"></i><span>项目信息</span></a></li>
         </ul>
         <div class="dropright mb-1 ml-1">
             <a href="" class="btn btn-sm btn-light" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
@@ -32,4 +32,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>

+ 1 - 1
app/view/layout/sub_menu.ejs

@@ -23,4 +23,4 @@
         </div>
         <% } %>
     </div>
-</div>
+</div>

+ 7 - 5
app/view/setting/info.ejs

@@ -3,7 +3,7 @@
     <div class="panel-title">
         <div class="title-main">
             <h2>项目信息
-                <a href="#" class="btn btn-primary btn-sm pull-right" onclick="updateinfo()">保存修改</a>
+                <% if (projectData.user_account === ctx.session.sessionUser.account) { %><a href="#" class="btn btn-primary btn-sm pull-right" onclick="updateinfo()">保存修改</a><% } %>
             </h2>
         </div>
     </div>
@@ -11,14 +11,14 @@
         <div class="c-body">
             <div class="row">
                 <div class="col-5">
-                    <form id="info-form" action="/<%= ctx.controllerName %>/updateinfo/<%= projectData.id === undefined ? 0 : projectData.id %>?_csrf=<%= ctx.csrf %>" method="post">
+                    <% if (projectData.user_account === ctx.session.sessionUser.account) { %><form id="info-form" action="/<%= ctx.controllerName %>/updateinfo/<%= projectData.id === undefined ? 0 : projectData.id %>?_csrf=<%= ctx.csrf %>" method="post"><% } %>
                         <div class="form-group">
                             <label>项目编号</label>
                             <input class="form-control" value="<%= projectData.code%>" type="text" readonly>
                         </div>
                         <div class="form-group">
                             <label>项目名称</label>
-                            <input class="form-control" name="name" id="name" placeholder="请输入项目名称" value="<%= projectData.name%>" type="text">
+                            <input class="form-control" value="<%= projectData.name%>" type="text" <% if (projectData.user_account !== ctx.session.sessionUser.account) { %>readonly<% } else { %>name="name" id="name" placeholder="请输入项目名称"<% } %>>
                         </div>
                         <div class="form-group">
                             <label>管理员</label>
@@ -42,15 +42,17 @@
                             <label>创建时间</label>
                             <input class="form-control" value=<%= dateStr%> type="text" readonly>
                         </div>
-                    </form>
+                        <% if (projectData.user_account === ctx.session.sessionUser.account) { %></form><% } %>
                 </div>
             </div>
         </div>
     </div>
 </div>
+<% if (projectData.user_account === ctx.session.sessionUser.account) { %>
 <script type="text/javascript">
     function updateinfo()
     {
         $("#info-form").submit();
     }
-</script>
+</script>
+<% } %>

+ 10 - 2
app/view/setting/sub_menu.ejs

@@ -1,12 +1,13 @@
 <div class="panel-sidebar">
     <div class="panel-title">
         <div class="title-bar">
-            <h2>项目设置</h2>
+            <h2>项目信息</h2>
         </div>
     </div>
     <div class="scrollbar-auto">
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
+                <% if (projectData.user_account === ctx.session.sessionUser.account) { %>
                 <% for (const index in ctx.subMenu) { %>
                 <li <% if (ctx.url === ctx.subMenu[index].url) { %>class="active"<% } %>>
                     <a href="<%- ctx.subMenu[index].url %>">
@@ -14,7 +15,14 @@
                     </a>
                 </li>
                 <% } %>
+                <% } else { %>
+                <li <% if (ctx.url === ctx.subMenu.info.url) { %>class="active"<% } %>>
+                    <a href="<%- ctx.subMenu.info.url %>">
+                        <span><%- ctx.subMenu.info.caption %></span>
+                    </a>
+                </li>
+                <% } %>
             </ul>
         </div>
     </div>
-</div>
+</div>

+ 24 - 40
app/view/setting/user.ejs

@@ -3,7 +3,11 @@
     <div class="panel-title">
         <div class="title-main">
             <h2>账号管理
+                <% if (projectData.max_user !== accountData.length) { %>
                 <a href="#ver" data-toggle="modal" data-target="#add-user" class="btn btn-primary btn-sm pull-right">添加账号</a>
+                <% } else { %>
+                <a href="#add-unpass" data-toggle="modal" data-target="#add-unpass" class="btn btn-primary btn-sm pull-right">添加账号(受限)</a>
+                <% } %>
             </h2>
         </div>
     </div>
@@ -27,46 +31,25 @@
                             <th class="text-center">操作</th></tr>
                         </thead>
                         <tbody>
-                        <tr>
-                            <td>chente</td>
-                            <td>陈特</td>
-                            <td>珠海纵横创新软件有限公司</td>
-                            <td>产品经理</td>
-                            <td>15812644017</td>
-                            <td>0765-3850891</td>
-                            <td class="text-center"><a href="#edit-user" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                <a href="" class="btn btn-sm btn-outline-danger">停用</a></td>
-                        </tr>
-                        <tr class="table-danger">
-                            <td>chente2</td>
-                            <td>陈特2</td>
-                            <td>珠海纵横创新软件有限公司</td>
-                            <td>产品经理</td>
-                            <td>15812644017</td>
-                            <td>0765-3850891</td>
-                            <td class="text-center"><a href="#edit-user" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                <a href="" class="btn btn-sm btn-outline-success">启用</a></td>
-                        </tr>
-                        <tr>
-                            <td>chente3</td>
-                            <td>陈特3</td>
-                            <td>珠海纵横创新软件有限公司</td>
-                            <td>产品经理</td>
-                            <td>15812644017</td>
-                            <td>0765-3850891</td>
-                            <td class="text-center"><a href="#edit-user" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                <a href="" class="btn btn-sm btn-outline-danger">停用</a></td>
-                        </tr>
-                        <tr>
-                            <td>chente4</td>
-                            <td>陈特4</td>
-                            <td>珠海纵横创新软件有限公司</td>
-                            <td>产品经理</td>
-                            <td>15812644017</td>
-                            <td>0765-3850891</td>
-                            <td class="text-center"><a href="#edit-user" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
-                                <a href="" class="btn btn-sm btn-outline-danger">停用</a></td>
+                        <% for (const account of accountData) { %>
+                        <tr <% if (account.enable !== 1) { %> class="table-danger"<% } %>>
+                            <td><%= account.account %><% if (account.is_admin === 1) { %> <span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="管理员"><i class="fa fa-user-circle-o"></i></span><% } %></td>
+                            <td><%= account.name %></td>
+                            <td><%= account.company %></td>
+                            <td><%= account.role %></td>
+                            <td><%= account.mobile %></td>
+                            <td><%= account.telephone %></td>
+                            <td class="text-center"><a href="#edit-user" data-account="<%= JSON.stringify(account) %>" data-toggle="modal" data-target="#edit-user" class="btn btn-sm btn-outline-primary">编辑</a>
+                                <% if (account.is_admin !== 1) { %>
+                                    <% if (account.enable !== 1) { %>
+                                        <a href="" class="btn btn-sm btn-outline-success account-switch-btn" data-account="<%= account.id %>">启用</a>
+                                    <% } else { %>
+                                        <a href="" class="btn btn-sm btn-outline-danger account-switch-btn" data-account="<%= account.id %>">停用</a>
+                                    <% } %>
+                                <% } %>
+                            </td>
                         </tr>
+                        <% } %>
                         </tbody>
                     </table>
                 </div>
@@ -101,4 +84,5 @@
             </div>
         </div>
     </div>
-</div>
+</div>
+<script src="/public/js/setting.js"></script>

+ 94 - 24
app/view/setting/user_modal.ejs

@@ -5,36 +5,60 @@
             <div class="modal-header">
                 <h5 class="modal-title">添加账号</h5>
             </div>
+            <form method="post" action="/setting/user/add?_csrf=<%= ctx.csrf %>" onsubmit="return checkUserForm('add');">
             <div class="modal-body">
                 <div class="form-group">
+                    <label><b class="text-danger">*</b>账号组</label>
+                    <select class="form-control" name="account_group">
+                        <option value="0">请选择</option>
+                        <% for (const dw in accountGroup) { %>
+                        <option value="<%= dw %>"><%= accountGroup[dw] %></option>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group">
                     <label>登录账号<b class="text-danger">*</b></label>
-                    <input class="form-control"  placeholder="支持英文数字组合" type="text">
+                    <input class="form-control" name="account" placeholder="支持英文数字组合" type="text">
+                    <input value="" class="account-check" type="hidden">
+                    <div class="invalid-feedback">
+                        该账号已存在。
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>登录密码<b class="text-danger">*</b></label>
+                    <div class="input-group">
+                        <input type="text" name="password" class="form-control" placeholder="密码支持英文数字及符号">
+                        <div class="input-group-append">
+                            <button id="rand-password" class="btn btn-outline-secondary" type="button">随机密码</button>
+                        </div>
+                    </div>
                 </div>
                 <div class="form-group">
                     <label>姓名<b class="text-danger">*</b></label>
-                    <input class="form-control" value="" type="text">
+                    <input class="form-control" name="name" value="" type="text">
                 </div>
                 <div class="form-group">
-                    <label>单位名称</label>
-                    <input class="form-control" value="" type="text">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input class="form-control" name="company" value="" type="text">
                 </div>
                 <div class="form-group">
-                    <label>职位名称</label>
-                    <input class="form-control" value="" type="text">
+                    <label>职位名称<b class="text-danger">*</b></label>
+                    <input class="form-control" name="role" value="" type="text">
                 </div>
                 <div class="form-group">
-                    <label>手机</label>
-                    <input class="form-control" value="" type="">
+                    <label>手机<b class="text-danger">*</b></label>
+                    <input class="form-control" name="mobile" value="" type="number">
                 </div>
                 <div class="form-group">
                     <label>电话</label>
-                    <input class="form-control" placeholder="格式000-0000000" type="">
+                    <input class="form-control" name="telephone" placeholder="格式000-0000000" type="text">
                 </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-primary">确定添加</button>
+                <button type="submit" class="btn btn-primary">确定添加</button>
             </div>
+            </form>
         </div>
     </div>
 </div>
@@ -45,38 +69,64 @@
             <div class="modal-header">
                 <h5 class="modal-title">编辑账号</h5>
             </div>
+            <form method="post" action="/setting/user/update?_csrf=<%= ctx.csrf %>" onsubmit="return checkUserForm('update');">
             <div class="modal-body">
                 <div class="form-group">
-                    <label>登录账号</label>
-                    <input class="form-control" value="chente"  placeholder="支持英文数字组合" type="text">
+                    <label>账号组<b class="text-danger">*</b></label>
+                    <select class="form-control" name="account_group">
+                        <option value="0">请选择</option>
+                        <% for (const dw in accountGroup) { %>
+                            <option value="<%= dw %>"><%= accountGroup[dw] %></option>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label>登录账号<b class="text-danger">*</b></label>
+                    <input class="form-control" value="" name="account" placeholder="支持英文数字组合" type="text">
+                    <input value="" class="account-check" type="hidden">
+                    <div class="invalid-feedback">
+                        该账号已存在。
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label>登录密码</label>
+                    <div class="input-group">
+                        <input type="text" id="reset-password" class="form-control" placeholder="密码支持英文数字及符号">
+                        <div class="input-group-append">
+                            <button id="reset-password-btn" class="btn btn-outline-secondary" type="button">修改密码</button>
+                        </div>
+                    </div>
+                    <span class="form-text text-success">如果账号已有认证手机,密码也将发送至该手机。</span>
                 </div>
                 <div class="form-group">
-                    <label>姓名</label>
-                    <input class="form-control" value="陈特" type="text">
+                    <label>姓名<b class="text-danger">*</b></label>
+                    <input class="form-control" value="" name="name" type="text">
                     <small class="form-text text-muted">修改姓名,将影响所有该账号参与数据</small>
                 </div>
                 <div class="form-group">
-                    <label>单位名称</label>
-                    <input class="form-control" value="珠海纵横创新软件有限公司" type="text">
+                    <label>单位名称<b class="text-danger">*</b></label>
+                    <input class="form-control" value="" name="company" type="text">
                 </div>
                 <div class="form-group">
-                    <label>职位名称</label>
-                    <input class="form-control" value="产品经理" type="text">
+                    <label>职位名称<b class="text-danger">*</b></label>
+                    <input class="form-control" value="" name="role" type="text">
                 </div>
                 <div class="form-group">
-                    <label>手机</label>
-                    <input class="form-control" value="1812644017" type="">
+                    <label>手机<b class="text-danger">*</b></label>
+                    <input class="form-control" value="" name="mobile" type="number">
                 </div>
                 <div class="form-group">
                     <label>电话</label>
-                    <input class="form-control" value="0756-3850891" placeholder="格式000-0000000" type="">
+                    <input class="form-control" value="" name="telephone" placeholder="格式000-0000000" type="text">
                 </div>
             </div>
             <div class="modal-footer">
-                <button type="button" class="btn btn-outline-danger" data-dismiss="modal">删除账号</button>
+                <input type="hidden" name="id" id="user-id" value="">
+                <!--<button type="button" class="btn btn-outline-danger" data-dismiss="modal">删除账号</button>-->
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-primary">提交修改</button>
+                <button type="submit" class="btn btn-primary">提交修改</button>
             </div>
+            </form>
         </div>
     </div>
 </div>
@@ -152,4 +202,24 @@
             </div>
         </div>
     </div>
-</div>
+</div>
+<!--弹出账号受限-->
+<div class="modal" tabindex="-1" role="dialog" id="add-unpass">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">无法添加账号</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <p>账号已超过最大账号数,无法添加新账号。</p>
+                <p>当前限制账号总数:<b><%= projectData.max_user %></b></p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 1 - 1
config/menu.js

@@ -31,7 +31,7 @@ const menu = {
         display: true,
         url: '/sum',
         children: null,
-        caption: '',
+        caption: '总分包',
     },
 };