zoukankan      html  css  js  c++  java
  • vue,vuex的后台管理项目架子structure-admin,后端服务nodejs

    之前写过一篇vue初始化项目,构建vuex的后台管理项目架子,这个structure-admin-web所拥有的功能

    接下来,针对structure-admin-web的不足,进行了补充,开发了具有登陆的structure-admin项目,技术站:主要是node+vue+redis+mysql+es6

    欢迎访问structure-admin源码:structure-admin

    欢迎访问weekly源码:weekly

    项目地址演示地址:http://weekly.mwcxs.top

    • (公司负责人)账号:testadmin,密码:123456
    • (部门经理)账号:test,密码:123456
    • (成员)账号:teststaff,密码:123456

    PS:添加的成员的默认密码是123456

    接下来:

    一、后端服务nodejs,thinjs的redis配置,操作数据库

    二、前端vue+vuex全局路由守卫,axios请求拦截

    三、项目启动必读

    一、后端服务nodejs,thinjs的redis配置,操作数据库

    我使用的是thinkjs,一个nodejs的框架。

    1、首先介绍登陆的控制

    逻辑:

    (1)已经登陆的,直接根据的路由跳到相应页面;

    (2)已经登陆的,不能跳到登陆页面,跳到首页;

    (3)没有登陆的,输入url必须跳到登陆页;

    (4)退出系统的,必须回到登陆页,状态是未登录

    1.1 thinkjs的redis的配置文件adapter.js

    exports.session = {
        type: 'redis',
        common: {
            cookie: {
                name: 'thinkjs',
                keys: ['werwer', 'werwer'],
                signed: true
            }
        },
        redis: {
            handle: redisSession,
            host: '127.0.0.1',
            port: 6379,
            password: 'a123456'
        }
    };

    设置的由redis的服务地址host,端口,以及redis的密码,redis的搭建和配置,参考安装window下的redis,redis可视化管理工具(Redis Desktop Manager)安装,基础使用,实例化项目这篇文章。

    1.2  在每一次前端请求的路由的时候,都会去redis服务器中去取userInfo的信息

    如果为空,返回前端data为空,前端在路由过滤中,跳到登陆页,如果有值就正常返回。 

    async __before() {
            let user = await this.session("userInfo");
            if(user) {
                this.user = user;
            } else {
                this.user = '';
            }
        }

    这个在nodejs的控制器controller里,在每一次前端的请求发送到后端的时候,都会去redis的取userInfo的值,

    let user = await this.session("userInfo");

    这个userInfo的值也是自己在登陆的时候,把登陆成功之后的个人信息加入到redis服务中

    1.3 在登陆成功的时候讲个人信息加到redis服务中

    async loginAction() {
            let {username, password} = this.post();try {
                let user = await this.model('user').where({
                    username,
                }).find();
                if(user.password && user.password == password) {
                    // login success
                    await this.session('userInfo',{username, userId:user.id});
                    return this.success("登陆成功");
                } else {
                    return this.fail("用户名或密码错误")
                }
            }
            catch(e) {
                console.log(e);
                return this.fail("登录失败")
            }

    这个就是将个人信息加入到redis中

    await this.session('userInfo',{username, userId:user.id});

    WEB 请求中经常通过 session 来维持会话的,框架通过 think-session 和 Adapter 来支持 session 功能。

    2、介绍登出(退出)的控制

     async logoutAction() {
            try {
                await this.session(null);
                return this.success("登出成功");
            } catch(e) {
                return this.fail(`登出失败${e}`)
            }
        }

    这个就是前端发的请求登出,直接将redis的置空,根据前端路由跳转到登陆页,这时候redis的服务中没有值,就不会跳转到其他页面

    3、数据库的配置adapter.js

    exports.model = {
      type: 'mysql',
      common: {
        logConnect: true,
        logSql: true,
        logger: msg => think.logger.info(msg)
      },
      mysql: {
        handle: mysql,
        database: 'example',
        prefix: 'example_',
        encoding: 'utf8',
        host: '127.0.0.1',
        port: '3306',
        user: 'root',
        password: '123456',
        dateStrings: true
      }
    };

    common部分是配置是否将sql的语句的操作日志打出来,这样便于我们在开发的时候的调试和修改bug

    4、操作数据库

    项目开发中,经常需要操作数据库(如:增删改查等功能),手工拼写 SQL 语句非常麻烦,同时还要注意 SQL 注入等安全问题。为此框架提供了模型功能,方便操作数据库。

    Mysql 的 Adapter 为 think-model-mysql,底层基于 mysql 库实现,使用连接池的方式连接数据库,默认连接数为 1。

    登陆的接口来说明:this.model说明使用封装好的model,find是查找单条数据,在user的这张表中查找username值为前端传来的username的值,返回的值赋给user中。

    async loginAction() {
            let {username, password} = this.post();
            try {
                let user = await this.model('user').where({
                    username,
                }).find();
                if(user.password && user.password == password) {
                    // login success
                    await this.session('userInfo',{username, userId:user.id});
                    return this.success("登陆成功");
                } else {
                    return this.fail("用户名或密码错误")
                }
            }
            catch(e) {
                console.log(e);
                return this.fail("登录失败")
            }

    think.Model 基类提供了丰富的方法进行 CRUD 操作,下面来一一介绍。

    查询数据

    模型提供了多种方法来查询数据,如:

    • find 查询单条数据
    • select 查询多条数据
    • count 查询总条数
    • countSelect 分页查询数据
    • max 查询字段的最大值
    • avg 查询字段的平均值
    • min 查询字段的最小值
    • sum 对字段值进行求和
    • getField 查询指定字段的值

    同时模型支持通过下面的方法指定 SQL 语句中的特定条件,如:

    • where 指定 SQL 语句中的 where 条件
    • limit / page 指定 SQL 语句中的 limit
    • field / fieldReverse 指定 SQL 语句中的 field
    • order 指定 SQL 语句中的 order
    • group 指定 SQL 语句中的 group
    • join 指定 SQL 语句中的 join
    • union 指定 SQL 语句中的 union
    • having 指定 SQL 语句中的 having
    • cache 设置查询缓存

    添加数据

    模型提供了下列的方法来添加数据:

    • add 添加单条数据
    • thenAdd where 条件不存在时添加
    • addMany 添加多条数据
    • selectAdd 添加子查询的结果数据

    更新数据

    模型提供了下列的方法来更新数据:

    删除数据

    模型提供了下列的方法来删除数据:

    用项目的代码举栗子:

    (1)查询单条数据,用find(),条件为:工号(usernum)为180909,用户名(username)为saucxs ,并且填写时间(time)为这周的时间范围的时间戳,返回的是对象object

     let weekly = await this.model('week').where({
         usernum: '180909', 
       username: 'saucxs',
       time: {
    '>': startWeekStamp, '<': endWeekStamp} }).find();

    解读:model('week')的意思,取得是week的数据表

    (2)查询多条数据,用select(),条件:公司id(company_id)为data的数据,返回的是数组array

    let department = await this.model('department').where({company_id: 'data'}).select();

    (3)查询表中的具体的列数据,用field()

    departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
              company_id: this.user.company_id,
              role: {'>=': this.user.role}
            }).find();

    解读:this.user.company_id取的是登陆用户的公司id,{'>=': this.user.role}为比登陆用户的角色

    (4)分页查询,用page(page, pagesize)和countSelect(),返回的数据是对象

    departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
              company_id: this.user.company_id,
              role: {'>=': this.user.role}
            }).order("department_id asc , role asc").page(page, pagesize).countSelect();

    解读:返回的对象,如下图所示:(count是总条数,currentPage为当前页,data是数据的数组,pageSize为每一页展示几条,totalPages为总共有多少页)

    (5)排序,倒序(desc)和正序(asc),用order("参数1 asc,参数2 desc”)

    departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
              company_id: this.user.company_id,
              role: {'>=': this.user.role}
            }).order("department_id asc , role asc").page(page, pagesize).countSelect();

    (6)删除,用delete(),条件用where

     await this.model('department').where({company_id, department_id}).delete();

    (7)新增,用add(),没有where

      await this.model('department').add({
              company_id: this.user.company_id, company_name: this.user.company_name, department_id, department_name
            });

    (8)改,用update(),条件where

     await this.model('user').where({id}).update({
                    usernum, username, telephone, role, role_name,email, company_id, company_name, department_id, department_name
                  });

    手动执行 SQL 语句

    有时候模型包装的方法不能满足所有的情况,这时候需要手工指定 SQL 语句,可以通过下面的方法进行:

    • query 手写 SQL 语句查询
    • execute 手写 SQL 语句执行

    具体的可以参考thinkJS的官方文档的数据操作这块:https://thinkjs.org/zh-cn/doc/3.0/relation_model.html

    二、前端vue+vuex全局路由守卫,axios请求拦截

    刚才简单的说了一下nodejs的后端启动的服务,封装的接口,而前端调用这个接口使用的是url是:模块名/控制器名/方法名,这个可以在配置文件中修改定义的方法

    1、全局路由守卫

    全局路由守卫是每一次都会判断是否登陆(也就是判断redis服务中是否有值)。已经登陆(后端返回的用户权限信息),则判断当前要跳转的路由,用户是否有权限访问,可以考虑在用户登陆之后将用户权限把路由过滤一遍生成菜单,菜单保存到vuex中。

    /*路由处理*/
    router.beforeEach((to, from, next) => {
      let menuId;
      let auditResult;
      let applicationVerifyFlag;
      let key = to.meta.key;
      if (key) {
        store.dispatch("getUserInfo", {}).then(response => {if(!response.data){
            if (to.path !== '/login') {
              return next('/login');
            }
            next();
          }else{
            if (to.path == '/login') {
              return next('/writeWeekly');
            }
            store.commit("USER_INFO", response.data);
            next();
          }
        });
      } else {
       next();
      }
    });

    根据这个key来判断是否有权限,取得是路由中meta的key的值。

      routes: [
        {
          path: '/login',
          name: 'login',
          meta: {
            key: '0'
          },
          component: login
        },
        {
          path: '/',
          name: 'home',
          component: home,
          children: [{
            path: '/writeWeekly',
            name: 'writeWeekly',
            meta: {
              key: '1'
            },
            component: writeWeekly
          }]
        }
      ]

    2、axios请求拦截

    统一处理所有的http请求和响应的,通过配置http request interceptors为http头部增加Authorization字段,其内容为Token,通过配置http response interceptors,当后端接口返回401 Unauthorized(未授权),让用户重新登录。

    // 开发环境调试用户信息
    axios.interceptors.request.use(config => {
        if (process.env.NODE_ENV === 'development') {
          config.headers["username"] = "189090909";
        }
        return config;
    });
    
    axios.interceptors.response.use(
      response => {
        let data = response.data;
        console.log(data, 'data');
        if (!data.data) {
          //   登陆成功的回调地址
          return data;
        } else {
          return data;
        }
      },
      error => ({
        code: -1,
        msg: "网络异常"
      })
    );

    对所有的请求进行了封装。

    // get请求配置
    let getConfig = {
        url: '',
        baseURL: serveUrl,
        headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      paramsSerializer(params) {
        return Qs.stringify(params, {
          arrayFormat: 'brackets'
        })
      },
      timeout: 5000
    }
    
    // post请求配置
    let postConfig = {
        url: '',
        baseURL: serveUrl,
        headers: {
          'Content-Type': 'application/json',
          'X-Requested-With': 'XMLHttpRequest'
        },
        transformRequest: [function (data) {
          return JSON.stringify(data.params || {})
        }],
        timeout: 5000
    }
    
    export {
        serveUrl,
        getConfig,
        postConfig,
      }

    三、项目启动必读

    1、首先你的环境是nodejs,不会安装配置参考:http://www.mwcxs.top/page/420.html

    2、clone下来项目

    git clone https://github.com/saucxs/structure-admin.git

    3、分别针对前端vuestructure-admin-web的文件夹和node后端structure-admin-node,安装相应依赖

    npm install

    4、安装redis(可以考虑安装RedisDesktopManager)

    参考:安装window下的redis,redis可视化管理工具(Redis Desktop Manager)安装,基础使用,实例化项目

    5、安装mysql,这个就不赘述

    6、修改nodejs的后端的配置文件adapter.js,config.js这两个文件中

    adapter.js

    exports.cache = {
        type: 'redis',
        common: {
            timeout: 24 * 60 * 60 * 1000 // millisecond
        },
        redis: {
            handle: redisCache,
            host: '127.0.0.1',
            port: 6379,
            password: 'a123456'  //redis安装时候设置的秘密
        }
    };
    //
    //
    exports.model = {
    type: 'mysql',
    common: {
    logConnect: true,
    logSql: true,
    logger: msg => think.logger.info(msg)
    },
    mysql: {
    handle: mysql,
    database: 'weekly',
    prefix: 'week_',
    encoding: 'utf8',
    host: '127.0.0.1', //本地数据库
    port: '3306',     //数据库端口
    user: 'root', //数据库的用户名
    password: '123456', //数据库该用户名的密码
    dateStrings: true
    }
    };

    7、分别对前后端分离的项目启动

    (1)前端vuestructure-admin-web的启动

    npm run dev

    (2)和node后端structure-admin-node的启动

    npm start

    8、这样就可以启动

    (1)登陆页

    (2)写周报页面

    9、该项目架子搭的周报企业管理系统

    在PC端,欢迎访问:http://weekly.mwcxs.top

    • (公司负责人)账号:testadmin,密码:123456
    • (部门经理)账号:test,密码:123456
    • (成员)账号:teststaff,密码:123456
  • 相关阅读:
    [LeetCode] 16. 3Sum Closest 解题思路
    [LeetCode] 28. Implement strStr() 解题思路
    我所理解的 KMP(Knuth–Morris–Pratt) 算法
    [LeetCode] 86. Partition List 解题思路
    [LeetCode] 61. Rotate List 解题思路
    [LeetCode] 11. Container With Most Water My Submissions Question 解题思路
    如何强制卸载阿里云盾(安骑士)监控及屏蔽云盾IP检测&附带教程
    让Nginx支持pathinfo
    linux下解压rar文件
    Linux查看物理CPU个数、核数、逻辑CPU个数
  • 原文地址:https://www.cnblogs.com/chengxs/p/9465482.html
Copyright © 2011-2022 走看看