vue,vuex的后台管理项目架子structure-admin,后端服务nodejs,前端vue页面
2019-11-29

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

1、vuex来实现状态管理2、静态页面,未引入后端服务3、组件是用的是element-ui4、页面布局是上左右,左右布局使用的弹性和布局flex,左边定宽,右边计算宽度5、左右的滚动条是相互独立的,去掉body上的滚动条6、没有业务代码,仅仅是一个静态的vuex的架子说明:之前使用左侧menu的fixed布局,发现element-ui的弹窗组件不能正常显示,考虑换成flex布局

 

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

欢迎访问github源码:https://github.com/saucxs/structure-admin

项目地址演示地址:http://120.27.109.67:8089/#/departmentManage,测试的账号:test,密码: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 语句中的 limitfield / fieldReverse 指定 SQL 语句中的 fieldorder 指定 SQL 语句中的 ordergroup 指定 SQL 语句中的 groupjoin 指定 SQL 语句中的 joinunion 指定 SQL 语句中的 unionhaving 指定 SQL 语句中的 havingcache 设置查询缓存

添加数据

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

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/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/440.html

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)登陆页

(2)写周报页面

 

 9、欢迎fork和start该项目

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

 不懂的地方可以提issue,欢迎提出来共同探讨

 

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

在PC端,欢迎访问:http://120.27.109.67:8089/#/departmentManage,这个账号和密码,给出一个测试的,账号:test,密码:123456