浏览代码

Merge branch 'master' of http://smartcost.f3322.net:3000/caiaolin/Calculation

Conflicts:
	package.json
MaiXinRong 7 年之前
父节点
当前提交
13d0ba251e

+ 2 - 0
app/base/base_controller.js

@@ -11,6 +11,7 @@ const moment = require('moment');
 const messageType = require('../const/message_type');
 const Controller = require('egg').Controller;
 const menuList = require('../../config/menu');
+const JsValidator = require('../lib/js_validator');
 class BaseController extends Controller {
 
     /**
@@ -22,6 +23,7 @@ class BaseController extends Controller {
     constructor(ctx) {
         super(ctx);
         this.messageType = messageType;
+        this.jsValidator = new JsValidator(ctx);
         // 当前菜单
         ctx.menu = menuList[ctx.controllerName] === undefined ? {} : menuList[ctx.controllerName];
         // 菜单列表

+ 2 - 2
app/controller/boot_controller.js

@@ -19,10 +19,10 @@ module.exports = app => {
          */
         async index(ctx) {
             const rule = ctx.service.customer.rule('boot');
-            const frontRule = ctx.helper.validateConvert(rule);
+            const jsValidator = await this.jsValidator.convert(rule).build();
 
             const renderData = {
-                rule: JSON.stringify(frontRule),
+                jsValidator,
             };
             await ctx.render('boot/index.ejs', renderData);
         }

+ 6 - 6
app/controller/profile_controller.js

@@ -26,17 +26,17 @@ module.exports = app => {
             const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId });
 
             // 获取基础数据的字段规则
-            let baseRule = ctx.service.projectAccount.rule('profileBase');
-            baseRule = ctx.helper.validateConvert(baseRule);
+            const baseRule = ctx.service.projectAccount.rule('profileBase');
+            const baseJsValidator = await this.jsValidator.convert(baseRule).setSelector('#base-form').build();
 
             // 获取修改密码的字段规则
-            let passwordRule = ctx.service.projectAccount.rule('modifyPassword');
-            passwordRule = ctx.helper.validateConvert(passwordRule);
+            const passwordRule = ctx.service.projectAccount.rule('modifyPassword');
+            const passwordJsValidator = await this.jsValidator.convert(passwordRule).setSelector('#password-form').build();
 
             const renderData = {
                 accountData,
-                baseRule: JSON.stringify(baseRule),
-                passwordRule: JSON.stringify(passwordRule),
+                baseJsValidator,
+                passwordJsValidator,
             };
             await this.layout('profile/info.ejs', renderData);
         }

+ 2 - 2
app/controller/project_controller.js

@@ -44,13 +44,13 @@ module.exports = app => {
 
                 // 数据规则
                 const rule = ctx.service.project.rule('saveInfo');
-                const frontRule = ctx.helper.validateConvert(rule);
+                const jsValidator = await this.jsValidator.convert(rule).build();
 
                 const renderData = {
                     projectData,
                     salesmanData,
                     officeList,
-                    rule: JSON.stringify(frontRule),
+                    jsValidator,
                 };
                 await this.layout('project/info.ejs', renderData);
             } catch (error) {

+ 2 - 3
app/controller/tender_controller.js

@@ -34,15 +34,14 @@ module.exports = app => {
         async index(ctx) {
             // 获取新增标段的规则
             const rule = ctx.service.tender.rule('add');
-            const frontRule = ctx.helper.validateConvert(rule);
+            const jsValidator = await this.jsValidator.convert(rule).build();
 
             // 根据项目id获取标段数据
             const tenderList = await ctx.service.tender.getList();
-            console.log(tenderConst.type);
             const renderData = {
                 tenderList,
-                rule: JSON.stringify(frontRule),
                 tenderConst,
+                jsValidator,
             };
             await this.layout('tender/index.ejs', renderData);
         }

+ 36 - 61
app/extend/helper.js

@@ -8,67 +8,6 @@
  * @version
  */
 module.exports = {
-    /**
-     * 内置验证器转换为前端jquery验证器
-     *
-     * @param {Object} rule - 后端service中的rule
-     * @return {Object} - 返回转换后的数据
-     */
-    validateConvert(rule) {
-        const result = {};
-        if (Object.keys(rule).length <= 0) {
-            return rule;
-        }
-
-        for (const index in rule) {
-            result[index] = {};
-            const type = rule[index].type !== undefined && rule[index].type !== '' ? rule[index].type : '';
-            const stringType = ['string', 'password'];
-            // 是否必填
-            if (rule[index].required !== undefined) {
-                result[index].required = rule[index].required;
-            }
-
-            // 最小长度
-            if (stringType.indexOf(type) >= 0 && rule[index].min !== undefined) {
-                result[index].minlength = rule[index].min;
-            }
-
-            // 最大长度
-            if (stringType.indexOf(type) >= 0 && rule[index].max !== undefined) {
-                result[index].maxlength = rule[index].max;
-            }
-
-            // 密码相关
-            if (type === 'password' && rule[index].compare !== undefined) {
-                result[index].equalTo = '#' + rule[index].compare;
-            }
-
-            // 最小值
-            const integerType = ['integer', 'int', 'Number'];
-            if (integerType.indexOf(type) >= 0 && rule[index].min !== undefined) {
-                result[index].min = rule[index].min;
-            }
-
-            // 最大值
-            if (integerType.indexOf(type) >= 0 && rule[index].max !== undefined) {
-                result[index].max = rule[index].max;
-            }
-
-            // 自定义判断
-            const customType = ['mobile', 'ip'];
-            // 自定义且带参数
-            if (customType.indexOf(type) >= 0) {
-                result[index][type] = true;
-                if (rule[index].allowEmpty !== undefined) {
-                    result[index].required = !rule[index].allowEmpty;
-                }
-            }
-
-        }
-
-        return result;
-    },
 
     /**
      * 生成随机字符串
@@ -219,4 +158,40 @@ module.exports = {
         return result;
     },
 
+    /**
+     * 判断当前用户是否有指定权限
+     *
+     * @param {Number|Array} permission - 权限id
+     * @return {Boolean} - 返回判断结果
+     */
+    hasPermission(permission) {
+        let result = false;
+        try {
+            const sessionUser = this.ctx.session.sessionUser;
+            if (sessionUser.permission === undefined) {
+                throw '不存在权限数据';
+            }
+            let currentPermission = sessionUser.permission;
+            if (currentPermission === '') {
+                throw '权限数据为空';
+            }
+            // 管理员则直接返回结果
+            if (currentPermission === 'all') {
+                return true;
+            }
+            currentPermission = currentPermission.split(',');
+            permission = permission instanceof Array ? permission : [permission];
+            let counter = 0;
+            for (const tmp of permission) {
+                if (currentPermission[tmp] !== undefined) {
+                    counter++;
+                }
+            }
+            result = counter === permission.length;
+        } catch (error) {
+            result = false;
+        }
+
+        return result;
+    },
 };

+ 135 - 0
app/lib/js_validator.js

@@ -0,0 +1,135 @@
+'use strict';
+
+/**
+ * egg内置验证器与jquery验证器结合
+ *
+ * @author CaiAoLin
+ * @date 2018/2/7
+ * @version
+ */
+
+class JSValidator {
+
+    /**
+     * 构造函数
+     *
+     * @param {Object} ctx - egg全局变量
+     * @return {void}
+     */
+    constructor(ctx) {
+        this.rule = {};
+        // 模板
+        this.template = 'layout/validator_template.ejs';
+        // 表单元素
+        this.selector = 'form';
+        this.ctx = ctx;
+        this.message = {};
+    }
+
+    /**
+     * 设置选择器
+     *
+     * @param {String} selectorName - 选择器名称
+     * @return {Object} - 返回自身类以便链式操作
+     */
+    setSelector(selectorName) {
+        this.selector = selectorName;
+        return this;
+    }
+
+    /**
+     * 转换格式
+     *
+     * @param {Object} rule - egg中的规则
+     * @return {Object} - 返回自身类以便链式操作
+     */
+    convert(rule) {
+        const result = {};
+        const messageResult = {};
+        if (Object.keys(rule).length <= 0) {
+            return rule;
+        }
+
+        for (const index in rule) {
+            result[index] = {};
+            const type = rule[index].type !== undefined && rule[index].type !== '' ? rule[index].type : '';
+            const stringType = ['string', 'password'];
+            // 是否必填
+            if (rule[index].required !== undefined) {
+                result[index].required = rule[index].required;
+            }
+
+            // 最小长度
+            if (stringType.indexOf(type) >= 0 && rule[index].min !== undefined) {
+                result[index].minlength = rule[index].min;
+            }
+
+            // 最大长度
+            if (stringType.indexOf(type) >= 0 && rule[index].max !== undefined) {
+                result[index].maxlength = rule[index].max;
+            }
+
+            // 密码相关
+            if (type === 'password' && rule[index].compare !== undefined) {
+                result[index].equalTo = '#' + rule[index].compare;
+            }
+
+            // 最小值
+            const integerType = ['integer', 'int', 'Number'];
+            if (integerType.indexOf(type) >= 0 && rule[index].min !== undefined) {
+                result[index].min = rule[index].min;
+            }
+
+            // 最大值
+            if (integerType.indexOf(type) >= 0 && rule[index].max !== undefined) {
+                result[index].max = rule[index].max;
+            }
+
+            // 自定义判断
+            const customType = ['mobile', 'ip'];
+            // 自定义且带参数
+            if (customType.indexOf(type) >= 0) {
+                result[index][type] = true;
+                if (rule[index].allowEmpty !== undefined) {
+                    result[index].required = !rule[index].allowEmpty;
+                }
+            }
+
+            // 密码值判断
+            if (type === 'password' && rule[index].compare !== undefined) {
+                result[index].equalTo = '#' + rule[index].compare;
+            }
+
+            // 提示语
+            if (rule[index].message !== undefined) {
+                messageResult[index] = rule[index].message;
+            }
+
+        }
+
+        this.rule = result;
+        this.message = messageResult;
+        return this;
+    }
+
+    /**
+     * 构建js
+     *
+     * @return {String} - 返回js
+     */
+    async build() {
+        if (Object.keys(this.rule).length <= 0) {
+            return '';
+        }
+
+        const renderData = {
+            selector: this.selector,
+            rule: this.rule,
+            message: this.message,
+        };
+        return await this.ctx.renderView(this.template, renderData);
+    }
+
+}
+
+module.exports = JSValidator;

+ 0 - 16
app/public/js/profile.js

@@ -26,21 +26,6 @@ $(document).ready(function() {
             },
         };
 
-        options.rules = baseRule;
-        $("#base-form").validate(options);
-
-        // 基础信息提交
-        $("#base-submit").click(function() {
-            $("#base-form").valid();
-        });
-
-        options.rules = passwordRule;
-        // 修改密码验证
-        $("#password-form").validate(options);
-        $("#modify-password").click(function() {
-            $("#password-form").valid();
-        });
-
         options.rules = {
             auth_mobile: {
                 mobile: true,
@@ -59,7 +44,6 @@ $(document).ready(function() {
             }
 
             const mobile = $("input[name='auth_mobile']").val();
-            console.log(mobile);
             const btn = $(this);
 
             $.ajax({

+ 7 - 3
app/service/project_account.js

@@ -60,9 +60,9 @@ module.exports = app => {
                     break;
                 case 'modifyPassword':
                     rule = {
-                        password: { type: 'string', required: true, min: 6 },
-                        new_password: { type: 'string', required: true, min: 6 },
-                        confirm_password: { type: 'string', required: true, min: 6 },
+                        password: { type: 'password', required: true, min: 6 },
+                        new_password: { type: 'password', required: true, min: 6 },
+                        confirm_password: { type: 'password', required: true, min: 6, compare: 'new_password' },
                     };
                     break;
                 case 'bindMobile':
@@ -96,6 +96,7 @@ module.exports = app => {
                 let accountData = {};
                 let projectInfo = {};
                 let projectList = [];
+                let permission = '';
                 if (loginType === 2) {
                     // 查找项目数据
                     const projectData = await this.ctx.service.project.getProjectByCode(data.project.toString());
@@ -126,11 +127,13 @@ module.exports = app => {
                         // 管理员则用sso通道判断
                         const sso = new SSO(this.ctx);
                         result = await sso.loginValid(data.account, data.project_password.toString());
+                        permission = 'all';
                     } else {
                         // 加密密码
                         const encryptPassword = crypto.createHmac('sha1', data.account).update(data.project_password)
                             .digest().toString('base64');
                         result = encryptPassword === accountData.password;
+                        permission = accountData.permission;
                     }
                 } else {
                     // sso登录(演示版)
@@ -161,6 +164,7 @@ module.exports = app => {
                         loginTime: currentTime,
                         sessionToken,
                         loginType,
+                        permission,
                     };
                     this.ctx.session.sessionProject = projectInfo;
                     this.ctx.session.sessionProjectList = projectList;

+ 1 - 7
app/view/boot/index.ejs

@@ -91,14 +91,8 @@
 <script type="text/javascript" src="/public/js/popper/popper.min.js"></script>
 <script type="text/javascript" src="/public/js/bootstrap/bootstrap.min.js"></script>
 <script type="text/javascript" src="/public/js/global.js"></script>
-<script type="text/javascript">
-    let rule = '<%- rule %>';
-    rule = JSON.parse(rule);
-    let message = {};
-</script>
+<%- jsValidator %>
 <script type="text/javascript" src="/public/js/jquery/jquery.validate.js"></script>
-<script type="text/javascript" src="/public/js/form_validate.js"></script>
-<script type="text/javascript" src="/public/js/validate.extend.js"></script>
 </body>
 
 </html>

+ 22 - 0
app/view/layout/validator_template.ejs

@@ -0,0 +1,22 @@
+<script type="text/javascript">
+$(document).ready(function() {
+    $("<%= selector %>").validate({
+        rules: <%- JSON.stringify(rule) %>,
+        messages: <%- JSON.stringify(message) %>,
+        errorPlacement: function(error, element) {
+            $(element).addClass('is-invalid');
+            $(element).after(error);
+        },
+        errorClass: "invalid-feedback",
+        errorElement: "div",
+        highlight: false,
+        success: function(element) {
+            $(element).prev('input').removeClass('is-invalid');
+            $(element).remove();
+        },
+    });
+
+});
+
+</script>
+<script src="/public/js/validate.extend.js"></script>

+ 3 - 11
app/view/profile/info.ejs

@@ -58,20 +58,12 @@
         </div>
     </div>
 </div>
+<%- baseJsValidator %>
+<%- passwordJsValidator %>
 <script type="text/javascript">
     new Vue({
         el: '#app',
     });
-    let baseRule = '<%- baseRule %>'
-    baseRule = JSON.parse(baseRule);
-
-    let passwordRule = '<%- passwordRule %>';
-    passwordRule = JSON.parse(passwordRule);
-    if (passwordRule.confirm_password !== undefined) {
-        passwordRule.confirm_password.equalTo = '#new_password';
-    }
-
     const csrf = '<%= ctx.csrf %>';
 </script>
-<script type="text/javascript" src="/public/js/profile.js"></script>
-<script type="text/javascript" src="/public/js/validate.extend.js"></script>
+<script type="text/javascript" src="/public/js/profile.js"></script>

+ 1 - 5
app/view/project/info.ejs

@@ -42,12 +42,8 @@
         </div>
     </div>
 </div>
+<%- jsValidator %>
 <script type="text/javascript">
-    let rule = '<%- rule %>';
-    rule = JSON.parse(rule);
-
-    let message = {
-    };
     new Vue({
         el: '#save-form',
     });

+ 1 - 7
app/view/tender/index.ejs

@@ -106,17 +106,11 @@
         </div>
     </div>
 </div>
+<%- jsValidator %>
 <script type="text/javascript">
     let tenderList = '<%- JSON.stringify(tenderList) %>';
     tenderList = JSON.parse(tenderList);
 
-    let rule = '<%- rule %>';
-    rule = JSON.parse(rule);
-
-    let message = {
-    };
 </script>
-<script src="/public/js/form_validate.js"></script>
-<script src="/public/js/validate.extend.js"></script>
 <script src=/public/js/echarts/echarts.min.js></script>
 <script src="/public/js/tender.js"></script>

+ 1 - 1
config/config.local.js

@@ -19,7 +19,7 @@ module.exports = appInfo => {
             // 用户名
             user: 'root',
             // 密码
-            password: 'root',
+            password: 'admin',
             // 数据库名
             database: 'calculation',
         },

+ 1 - 1
package.json

@@ -33,7 +33,7 @@
   "scripts": {
     "start": "egg-scripts start --daemon",
     "stop": "egg-scripts stop",
-    "dev": "egg-bin dev --port 7002",
+    "dev": "set EGG_SERVER_ENV=local&&egg-bin dev --port 7002",
     "dev-local": "set EGG_SERVER_ENV=qa&&egg-bin dev --port 7002",
     "dev-qa": "set EGG_SERVER_ENV=qa&&egg-bin dev --port 7002",
     "test": "npm run lint -- --fix && npm run test-local",

+ 17 - 26
test/app/extend/helper.test.js

@@ -11,32 +11,6 @@ const { app, assert } = require('egg-mock/bootstrap');
 
 describe('test/app/extend/helper.test.js', () => {
 
-    it('validateConvert test', function* () {
-        // 创建 ctx
-        const ctx = app.mockContext();
-        const rule = {
-            username: { type: 'string', required: true, allowEmpty: false, min: 4, max: 10 },
-            group_id: { type: 'integer', required: true, allowEmpty: false, min: 4, max: 10 },
-            password: { type: 'password', required: true, allowEmpty: false, min: 4 },
-            confirm_password: { type: 'password', required: true, allowEmpty: false, min: 4, compare: 'password' },
-            ip: { type: 'ip', allowEmpty: false },
-            telephone: { type: 'mobile', allowEmpty: false },
-        };
-        // 转换为json验证
-        let convertRule = ctx.helper.validateConvert(rule);
-        convertRule = JSON.stringify(convertRule);
-        let expectRule = {
-            username: { required: true, minlength: 4, maxlength: 10 },
-            group_id: { required: true, min: 4, max: 10 },
-            password: { required: true, minlength: 4 },
-            confirm_password: { required: true, minlength: 4, equalTo: '#password' },
-            ip: { ip: true, required: true },
-            telephone: { mobile: true, required: true },
-        };
-        expectRule = JSON.stringify(expectRule);
-        assert(convertRule === expectRule);
-    });
-
     it('generateRandomString test', function* () {
         // 创建 ctx
         const ctx = app.mockContext();
@@ -98,4 +72,21 @@ describe('test/app/extend/helper.test.js', () => {
         }
         assert(result);
     });
+
+    it('test permission', function* () {
+        // 创建 ctx
+        const ctx = app.mockContext();
+        ctx.session = {
+            sessionUser: {
+                permission: '1,2,3,4',
+            },
+        };
+        const permission = require('../../../config/permission');
+        let result = ctx.helper.hasPermission(permission.permission.VIEW_STAGE_MEASURE);
+        assert(!result);
+
+        // 多个权限判断
+        result = ctx.helper.hasPermission([permission.permission.CREATE_TENDER, permission.permission.VIEW_ALL_TENDER]);
+        assert(result);
+    });
 });

+ 43 - 0
test/app/lib/js_validator.test.js

@@ -0,0 +1,43 @@
+'use strict';
+
+/**
+ * jsvalidator单元测试
+ *
+ * @author CaiAoLin
+ * @date 2018/2/7
+ * @version
+ */
+
+const JsValidator = require('../../../app/lib/js_validator');
+const { app, assert } = require('egg-mock/bootstrap');
+
+describe('test/app/lib/js_validator.test.js', () => {
+
+    it('test convert', function* () {
+        // 创建 ctx
+        const ctx = app.mockContext();
+        const jsValidator = new JsValidator(ctx);
+        const rule = {
+            username: { type: 'string', required: true, allowEmpty: false, min: 4, max: 10 },
+            group_id: { type: 'integer', required: true, allowEmpty: false, min: 4, max: 10 },
+            password: { type: 'password', required: true, allowEmpty: false, min: 4 },
+            confirm_password: { type: 'password', required: true, allowEmpty: false, min: 4, compare: 'password' },
+            ip: { type: 'ip', allowEmpty: false },
+            telephone: { type: 'mobile', allowEmpty: false },
+        };
+        // 转换为json验证
+        jsValidator.convert(rule);
+        const convertRule = JSON.stringify(jsValidator.rule);
+        let expectRule = {
+            username: { required: true, minlength: 4, maxlength: 10 },
+            group_id: { required: true, min: 4, max: 10 },
+            password: { required: true, minlength: 4 },
+            confirm_password: { required: true, minlength: 4, equalTo: '#password' },
+            ip: { ip: true, required: true },
+            telephone: { mobile: true, required: true },
+        };
+        expectRule = JSON.stringify(expectRule);
+        assert(convertRule === expectRule);
+    });
+
+});