1

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

接下来,针对structure-admin-web的不足,进行了补充,开发了具有登陆的structure-admin项目,

技术站:主要是node+vue+redis+mysql+es6

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

同步更新到sau交流学习社区:https://www.mwcxs.top/page/42...

接下来:

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

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

三、项目启动必读

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

我使用的是thinkjs3,一个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 添加子查询的结果数据
更新数据
模型提供了下列的方法来更新数据:

update 更新单条数据
updateMany 更新多条数据
thenUpdate 条件式更新
increment 字段增加值
decrement 字段减少值
删除数据
模型提供了下列的方法来删除数据:

delete 删除数据
用项目的代码举栗子:

(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...

二、前端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...

2、clone下来项目

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

3、分别针对前端vue的structure-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)前端vue的structure-admin-web的启动

npm run dev

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

npm start

8、这样就可以启动

(1)登陆页

clipboard.png

(2)写周报页面

clipboard.png

structure-admin架子线上地址:http://structure_admin.mwcxs.top
测试账号:test,密码:123456

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

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

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


松宝写代码
515 声望47 粉丝

昵称:saucxs | songEagle | 松宝写代码