Vue详解及综合案例

一、Vue简介

1.1 简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式的js框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router,vue-resource,vuex)或既有项目整合。

1.2 MVVM 模式的实现者——双向数据绑定模式

  • Model:模型层,在这里表示 JavaScript 对象

  • View:视图层,在这里表示 DOM(HTML 操作的元素)

  • ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者 

Vue详解及综合案例_第1张图片

这里 核心就是 ViewModel 里面有DOM监听以及数据绑定,View是页面数据展示 Model也就是前面data里定义的,通过Vue来实现各种快捷功能,我们用普通js写的话 得写一大串Js代码;

mvvm设计模式 这里的

第一个m是 model 也就是vm的data属性

第二个v是 view 视图 网页模版

最后vm就是中间vue的 viewmodel 代码体现就是vm对象或者vm实例;

1.3 其它 MVVM 实现者

  • AngularJS

    简单介绍一下,AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。

  • ReactJS

    React引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。

  • 微信小程序

    微信小程序的视图层和数据层就是通过MVVM进行绑定的。

1.4 为什么要使用 Vue.js

  • 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)

  • 移动优先。更适合移动端,比如移动端的 Touch 事件

  • 易上手,学习曲线平稳,文档齐全

  • 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性

  • 开源,社区活跃度高

1.5 Vue.js 的两大核心要素

1.5.1 数据驱动

Vue详解及综合案例_第2张图片

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

1.5.2 组件化

  • 页面上每个独立的可交互的区域视为一个组件

  • 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护

  • 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面

二、Vue的初体验

2.1在页面引入vue的js文件即可。

注意:cdn是一种加速策略,能够快速的提供js文件

 

2.2 在页面中绑定vue元素

创建一个div,id是app

2.3 创建vue对象,设计对象的内容

其中该vue对象,绑定了页面中id是app的那个div

 #  el: element的简称,也就是Vue实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
 #  data: 用于提供数据的对象,里面存放键值对数据。

说明:

这里有几点重要说明:

new Vue 我们创建了Vue对象;

el 指定了绑定DOM,接管DOM操作;

data:用于提供数据的对象,里面存放键值对数据

v-model 重点 可以实现数据双向绑定,改变了这里的值,其他地方也根据改变;

{{xxxxx}} 显示数据;

2.4 在页面的元素中使用插值表达式来使用vue对象中的内容

{{ title }}

三、 插值表达式

插值表达式的作用是在View中获得Model中的内容

Model中的内容如下:

new Vue({
        el:"#app",
        data:{
            title:"hello world!"
        },
        methods:{
            sayHello:function(){
                return "hello vue";
            }
        }
    });

3.1 简单使用插值表达式获取数据

{{title}}

此时,页面上将会显示"Hello world!"

3.2 在插值表达式中获取数组中的内容

{{[1,2,3,4][2]}}

此时,页面上会显示“3”,也就是数组中的第三个元素被获取。

3.3 使用插值表达式获取对象中的属性

{{ {"name":"xiaoyu","age":20}.age }}

此时,页面上会显示“20”,也就是对象中age属性的值。

3.4 使用插值表达式调用Vue中的方法

{{ sayHello()}}

此时,页面上会显示“hello vue”,也就是调用了vue对象中的sayHello方法,并展示了方法的返回值。

3.5Vue对象总结 

Vue.js通过加载js,实现对页面的快速渲染。vue封装的js该如何使用? 就必须了解MVVM双向数据绑定模式。Vue将视图层和数据层分离,通过MVVM建立视图层和数据层的连接。其中,插值表达式是一种连接方式,可以通过插值表达式以多种方式,快速的从数据层获取数据并展示在视图层上。数据层Vue对象,也是由很多部分组成,比如之前介绍的el,data,methods等,以及之后要介绍的mount,computed等。

四、Vue的分支 v-if

4.1 v-if

Vue中的分支语句v-if非常好理解,逻辑跟Java中的if-else相同。v-if语句块包含以下内容:

  • v-if

  • v-else

  • v-else-if

接下来以一个简单例子即可理解:


    
        
        
    
    
        

今天天气很舒服!

今天天气很燥热!晚上要去放松一下!

晚上只能自嗨!

 

从这个例子可以看出,vue对象中的data提供了分支的条件。根据条件,如果是true,则v-if的内容就会显示,反之不显示。

4.2 v-show

v-if和v-show之间有着看似相同的效果,但优化上却有区别。先看下面这个例子:


    
        
        
    
    
            

有钱!

有钱!

 

通过点击“今晚彩票开奖”按钮,能切换rich的值,此时发现,v-if和v-show的显示状态都会来回切换。看起来是一样的,但通过查看控制台代码发现,v-show实际会将p标签的css样式的display属性设为none来达到隐藏的效果。 

Vue详解及综合案例_第3张图片

而v-if是直接在页面上添加和删除p标签来达到效果,因此v-show在反复切换的应用场景下,效率比v-if更高。

五、Vue的循环 v-for

Vue中的循环关键字并没有Java的那么多,只有v-for,但用法上有多种。接下来我们来逐一介绍。

5.1 普通的for循环

我们需要定义数据源,然后通过v-for来遍历数据源,再使用差值表达式输出数据。


   
           
  • {{item.date}}
  •    
 

在这个例子中,数据源提供了一个数组。视图层通过v-for来循环输出多个li标签,非常简单。

5.2 带着索引的for


   
           
  • {{i}}{{a}}
  •    

此时的i就是每次循环的循环变量 ,从0开始一直到元素个数-1

5.3 遍历一个对象中的信息: value、name、index


   
           
  • {{name}}--{{value}}--{{index}}
  •    

value、name、index 这几个字符可以自己定义,分别表示每次循环内容的值、键、序号。

  • value: 循环中每条数据的值 小鱼、20、如花

  • name: 循环中每天数据的键 username、age、girl

  • index: 循环的序号,从0开始

页面效果如下:

5.4 遍历对象数组:嵌套的for循环


                                                                                                                                                       
姓名年龄联系方式
{{stu.name}}{{stu.age}}{{stu.phone}}
 

可以清楚的看到,此时数据源是一个student数组,通过两层v-for循环,外层遍历数组中的每个student对象,内层v-for遍历每个对象的v、k、i。

六、Vue的属性绑定

Vue提供了多个关键字,能快速的将数据对象中的值绑定在视图层中。

6.1 v-model

通过v-model将标签的value值与vue对象中的data属性值进行绑定。


    
        
        
    
    
        
{{title}}
  new Vue({ el:'#app', data:{ title:"hello vue" } })

此时input标签中加入了“v-model='title'”,表示input的value值与vue对象中的title属性绑定,当在input输入框中输入内容会实时修改title的值。于是{{title}}插值表达式能实时输出input输入框内的值。

页面效果如下:
Vue详解及综合案例_第4张图片

 6.2 v-bind

我们知道插值表达式是不能写在html的标签的属性内的,那如果一定要用vue中的属性作为html标签的属性的内容,就可以通过v-bind进行属性绑定。


    
        
        
    
    
        
  new Vue({ el:'#app', data:{ link:'http://www.baidu.com' } })

这样,a标签内的href属性就可以使用vue对象中的属性值。

注意: v-bind也可以简写,使用冒号“:”来代替。

  等价于  

七、Vue的事件绑定 

关于事件,要把握好三个步骤:设参、传参和接参。


    
        
        
    
    
        
sum={{sum}}
{{sum>10?'总数大于10':'总数不大于10'}}

从这里例子中:

设参:

传参: 

increase:function(s)

 接参:

this.sum+=s

注意:increase:function(s){ }函数可以写成increase(s){ }

接下来我们来看一下VUE中如何进行事件绑定。

7.1 v-on

通过配合具体的事件名,来绑定vue中定义的函数


    
        
        
    
    
        

此时,该按钮,在点击时将会调用Vue对象中定义的changeMajor方法。

注意: v-on也可以简写,使用"@"替代。

7.2 事件修饰符

可以使用Vue中定义好的事件修饰符,快速达到效果。查看以下例子:


    
        
        
    
    
        
    
    
        new Vue({
            el:'#app',
            data:{
                x:0,
                y:0
            },
            methods:{
                mm(event){
                    this.x = event.clientX;
                    this.y = event.clientY;
                },
                stopm(event){
                    event.stopPropagation();
                },
                doThis(){
                alert("执行到了a的click");
            },
                doDivClick(){
                alert("执行到了div的click");
                }
            }
        })
    

当鼠标经过P标签区域内时,区域内就会显示X和Y轴的坐标,如果经过P标签内的Span标签内时,此时会调用事件属性mousemove.stop预定的效果,鼠标移动的效果将会被取消,X和Y不再显示信息。

7.3计算属性:computed

7.3.1 什么是计算属性

计算属性的重点突出在 属性 两个字上(属性是名词),首先它是个 属性 其次这个属性有 计算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;

7.3.2 计算属性与方法的区别




    
    布局篇 计算属性
    
   

调用当前时间的方法:{{currentTime1()}}

   

当前时间的计算属性:{{currentTime2}}

说明

  • methods:定义方法,调用方法使用 currentTime1(),需要带括号

  • computed:定义计算属性,调用属性使用 currentTime2,不需要带括号;this.message 是为了能够让 currentTime2 观察到数据变化而变化

注意:methods 和 computed 里不能重名

7.3.3 测试效果 

仔细看图中说明,观察其中的差异

Vue详解及综合案例_第5张图片

7.3.4 结论

调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销

八、Vue的组件化

8.1 什么是“组件化”

Vue的组件化设计思想借鉴了Java的面向对象思想。Java认为万物皆对象,在Vue中,万物皆组件。

也就是说,在实际的vue项目中,以及使用了Vue框架的项目中,Vue的对象都会以组件的形式出现,能被反复使用。

要想实现组件化,需要在页面中注册组件:关于注册的方式有两种,分别是全局注册和本地注册。

8.1.1 组件的全局注册




    
    vue组件的全局注册


    
                           
       
   
                           
 

8.1.2 组件的本地注册

vue的全局注册,也就意味着在页面的任意一个被vue绑定过的div中,都可以使用全局注册了的vue组件。

但是,如果是对vue组件进行本地注册,那么在其他被vue绑定的div中,不能使用该组件。




    
    vue组件的本地(局部)注册


    
           

       
           
 

8.1.3 小结

这是一个完整的Vue组件。该组件包含了三个部分:template(html视图层内容)、script(Model层)、style(CSS样式)。这样封装好的组件可以被复用,也可以作为其他组件的组成部分而被封装——Java的面向对象再次体现。

  • 特点1: template标签内,必须有且只能有一个根标签。

  • 特点2: componet中注册的组件中的data,必须是已函数的形式。

    如下:

data:function(){
     return {
           title:"hello vue"
     }
 }

8.2 组件的生命周期

Vue中的组件也是有生命周期的。一个Vue组件会经历:初始化、创建、绑定、更新、销毁等阶段,不同的阶段,都会有相应的生命周期钩子函数被调用。

组件的生命周期钩子
Vue详解及综合案例_第6张图片

    生命周期主要有三个阶段:

  • 一,初始化显示;(重要勾子 mounted 网页加载完毕触发)
  • 二,更新显示;(重要勾子beforeUpdate 数据属性更新前)
  • 三,死亡;(重要勾子beforeDestroy vm死亡前)

   每个生命周期都会有对应的生命周期的函数,或者叫做勾子函数;



    
    Title
    


    
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、
        将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,
        这给了用户在不同阶段添加自己的代码的机会。
   
       

{{name}}

                   

九、使用Vue-Cli搭建Vue项目

9.1 什么是vue-cli

cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js。

9.2 node.js的介绍及安装

node.js的介绍

node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。

node.js的安装

从官网下载安装即可: 下载 | Node.js 中文网

测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。
Vue详解及综合案例_第7张图片

9.3 使用node.js安装vue-cli

使用如下命令安装vue-cli

npm install -g vue-cli   安装的是2.9.6 版本     -------   不装这个
npm install  -g   @vue/cli     安装的是新版本   新版本  支持 vue ui 
  • npm: 使用node.js的命令

  • install: 安装

  • vue-cli: 要安装的vue-cli

  • -g: 全局安装

注意 这里的安装可能需要管理员权限

安装完了之后 可能被windows的安全策略限制 参考

VSCode报错:vue : 无法加载文件 D:\nodejs\node_global\vue.ps1,因为在此系统上禁止运行脚本。_没有咸鱼的梦想的博客-CSDN博客

当出现以下界面,表示正在安装:

如果使用npm官方镜像速度比较慢,可以使用淘宝镜像来安装:

npm install -g cnpm --registry=https://registry.npm.taobao.org

之后使用npm命令时就可以替换成cnpm

cnpm install vue-cli -g
cnpm install
cnpm run dev

9.4创建vue项目

dos命令输入vue ui会进入如下图:

Vue详解及综合案例_第8张图片

Vue详解及综合案例_第9张图片

Vue详解及综合案例_第10张图片

Vue详解及综合案例_第11张图片

Vue详解及综合案例_第12张图片

创建项目 不保存预设 然后 后台就开始下载文件

Vue详解及综合案例_第13张图片

创建好之后 页面跳转到仪表盘

Vue详解及综合案例_第14张图片

可以在这里 安装一些插件 比如 axios element-ui 等等

Vue详解及综合案例_第15张图片

这是已经安装的插件

点 添加插件

Vue详解及综合案例_第16张图片

安装 ajax 插件 axios

然后 可以关掉仪表盘,使用vscode 打开 刚才创建的工程 使用 vue run serve 启动工程,

Vue详解及综合案例_第17张图片

9.4.1配置vue片段

打开VSCode,文件—>首选项—>配置用户代码片段 

Vue详解及综合案例_第18张图片

Vue详解及综合案例_第19张图片

输入一下代码:配置完成后新建.vue文件输入vue回车就会生成以下代码。

{
	"Print to console": {
			"prefix": "vue",
			"body": [
					"",
					"",
					"",
					"",
					""
			],
			"description": "生成vue模板"
	}
}

9.5 使用vue-cli下载项目骨架搭建我们的项目

就像maven一样,vue为我们提供了一些官方项目骨架。使用vue list命令可以列出当前官方提供的骨架,可以使用这些骨架来快速搭建出项目。

vue list

完事 用vscode 打开项目文件夹

Vue详解及综合案例_第20张图片

9.6vue工程的组件使用

Vue详解及综合案例_第21张图片

9.7父组件向子组件传递数据---父子组件之间

Vue详解及综合案例_第22张图片

父组件

Vue详解及综合案例_第23张图片

消息订阅与发布组件PubSub

9.8 消息订阅与发布组件PubSub

我们前面讲了父子组件之间通过prop来实现消息传递;但是再其他情况,比如兄弟组件,爷孙组件消息传递时候,就要用到高级的消息订阅与发布;

1.首先我们安装下消息订阅与发布pubsub组件;

npm install --save pubsub-js

2.把 pubsub 配置成全局组件 在 main.js 中配置

Vue详解及综合案例_第24张图片

3.在 父组件中 订阅消息 可以在 mounted 钩子函数中 监听消息

Vue详解及综合案例_第25张图片

在这里 使用了 箭头函数 => ,在箭头函数中 的 this 代表 当前 vue 对象,如果 使用一般函数 ,那么在这个函数内 this 就代表PubSub对象,所以 在这里 咱们使用 =>箭头函数

在别的组件中 发布消息

Vue详解及综合案例_第26张图片

注意: 执行上面的案例 会发现 订阅消息的回调函数 执行了两次 , 我们需要在我们每次接受数据pubsub.subscribe的时候,先执行pubsub.unsubscribe操作就好了,就完美解决了,这样你接收以后的callback只执行一次

PubSub.unsubscribe();
PubSub.subscribe(eventName, callback);   //这样  回调就只执行一次

9.9插槽 slot

父组件向子组件传递标签,通过slot 插槽实现

主要作用:

某一个区块,先占位,然后可以动态的搞个标签进去,方便切换该位置的内容,无需再搞多个页面。

父组件 直接定义标签p 注意 slot 属性

子组件 使用 slot 标签 占位置 注意 slot 标签的name 属性,当父组件没有传对应的插槽(属性值对应)内容时,会显示插槽默认内容

9.10 使用Webpack骨架快速创建项目

常用命令:

  • npm install

    在运行和调试项目前,一般都需要先执行该命令,目的是安装项目运行所需要的环境。

  • npm run dev

    以调试的方式运行项目

  • npm run build

    生成用于项目部署所需的最小资源,生成的内容存放在build文件夹内。

Webpack是一个前端资源的打包工具,它可以将js、image、css等资源当成一个模块进行打包。

Vue详解及综合案例_第27张图片

从图中我们可以看出,Webpack可以将js、css、png等多种静态资源进行打包,使用webpack有什么好处呢?

1、模块化开发程序员在开发时可以分模块创建不同的js、css等小文件方便开发,最后使用webpack将这些小文件打包成一个文件,减少了http的请求次数。webpack可以实现按需打包,为了避免出现打包文件过大可以打包成多个文件。

2、编译typescript、ES6等高级js语法随着前端技术的强大,开发中可以使用javascript的很多高级版本,比如:typescript、ES6等,方便开发,webpack可以将打包文件转换成浏览器可识别的js语法。

3、CSS预编译webpack允许在开发中使用Sass和Less等原生CSS的扩展技术,通过sass-loader、less-loader将Sass和Less的语法编译成浏览器可识别的css语法

4.使用vue.js开发大型应用需要使用webpack打包工具

vue init webpack my-project1
  • webpack: 骨架名称

  • my-project1: 项目名称 

过程中会出现如下界面,需要手动操作。
Vue详解及综合案例_第28张图片
出现如下界面,表示安装成功。
Vue详解及综合案例_第29张图片

进入到my-project1文件夹内后,使用以下命令来运行项目。

npm run dev

Vue详解及综合案例_第30张图片

访问http://localhost:8081,页面效果如下:
Vue详解及综合案例_第31张图片

 十、Vue-router 路由

10.1 安装路由模块

npm install vue-router --save 

10.2 引入路由模块并使用

创建views 文件夹 其下面创建Index.vue 和 Menu1.vue

在main.js中引入路由模块并使用

import Vue from 'vue'
import App from './App'
import router from './router'  //引入路由模块
​
Vue.config.productionTip = false
​
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router, //使用路由模块
  components: { App },
  template: ''
})

 在 router文件夹下 的index.js 是 路由的配置文件

index.js中:

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Index from '@/views/Index'
import Menu1 from '@/views/Menu1'
Vue.use(Router)
​
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/index',
      name: 'Index',
      component: Index
    },
    {
      path: '/menu1',
      name: 'Menu1',
      component: Menu1
    }
  ]
})
​

在App.vue 中的路由写法

 
    
         

通过 router-link 向路由配置发送请求 ,结果会在 router-view 显示

10.3嵌套路由

Menu1.vue是在前面创建的views文件夹下 继续嵌套 下级页面 配置嵌套路由,其他代码有vue片段生成

Menu1.vue

    {{msg}}      
             
  •            子菜单1          
  •          
  •            子菜单2          
  •        
​        
                     
​  

在路由配置文件src文件夹下router文件夹下的index.js中

{
      path: '/menu1',
      name: 'Menu1',
      component: Menu1,
      children:[   // 子路由
        {
          path:'/menu1/submenu1',    // 当然  对应的 子组件 得创建好
          component: () => import('@/views/SubMenu1.vue')   // 这样就不用在文件上面配置了
        },
        {
          path:'/menu1/submenu2',
          component: () => import('@/views/SubMenu2.vue')
        },
        {
          path:'',
          redirect:'/menu1/submenu1'     // 默认显示submenu1
        }
      ]
    },

在views文件夹下创建SubMenu1.vue,其他代码vue片段生成

SubMenu2.vue,其他代码vue片段生成

10.4路由缓存

当我们 在 子菜单1 和 子菜单2 的 文本输入框输入内容时,发现 跳转之后内容没有了,这种情况也经常遇到

Vue详解及综合案例_第32张图片

我们切换不同的组件时,希望 组件内输入的内容不要丢失 ,怎么解决?只需要 使用keep-alive 标签

Vue详解及综合案例_第33张图片

Vue详解及综合案例_第34张图片

10.5路由传参

Vue详解及综合案例_第35张图片

app.vue显示页面的配置:



注意:上述路由需要再定义个div并在里面写标签

router文件下index.js中路由配置,这里只设置了账户信息路由

{
      //path对应app.vue文件中账户信息标签to="/admin"
      path: '/admin', 
      name: 'index',
      component: () => import ('@/views/Admin.vue'),
      children: [ // 子路由,点击姓名显示详细信息
        {
          path:'/admin/adminDetail/:id',
          component: () => import ('@/views/AdminDetail.vue')
        }
      ]
    },

用户信息组件views文件夹下创建Admin.vue


当点击名字时会显示该用户信息,用到嵌套路由,在views下创建AdminDetail.vue文件,代码如下:

10.6 编程式路由传参

Vue详解及综合案例_第36张图片

只需要在上面的Admin.vue文件中添加一些代码就行。


 十一、使用Axios发送请求

11.1 什么是 Axios

Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:

  • 从浏览器中创建 XMLHttpRequests

  • 从 node.js 创建 http 请求

  • 支持 Promise API

  • 拦截请求和响应

  • 转换请求数据和响应数据

  • 取消请求

  • 自动转换 JSON 数据

  • 客户端支持防御 XSRF(跨站请求伪造)

GitHub:GitHub - axios/axios: Promise based HTTP client for the browser and node.js

11.2 为什么要使用 Axios

由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架

11.3 Axios的使用

11.3.1 安装vue axios

npm install --save axios vue-axios

11.3.2 在main.js中引入

在项目中使用axios模块

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
​
Vue.use(VueAxios, axios)

11.4 发送axios请求

在使用axios时,要注意到配置选项中包含params和data两者

1.params是添加到url的请求字符串中的,用于get请求;

2.而data是添加到请求体(body)中的, 用于post请求;

11.4.1axios使用方法-post

this.$axios({  //这种格式,是需要每个参数都弄成对象的
        methods: 'POST',
        url: '#',
        data: {
          key1: val1,
          key2: val2
        },
        timeout: 1000,
        baseURL:'xxxx'
        ...//其他相关配置
      })

11.4.2axios使用方法-get

  this.$axios({
        methods: 'get',
        url: '#',
        params: {  //注意是params,get请求必须写上params,不能写data
          key1: val1,
          key2: val2
        },
        timeout: 1000,
        baseURL:'xxxx'
        ...//其他相关配置
      })

注意:

在get请求下,参数需要使用【params】来设置,

而post请求中,是使用data来传递的

11.4.3axios的简化写法

this.$axios.get('url')
this.$axios.post('url')

注意:这种直接没有参数的,就写个url就行了

get请求直接写参数是错误写法,会导致请求无法携带参数

如果get请求有参数,必须使用params:{}括起来

11.4.4axios请求案例

如果带token的话可以在app.vue中的mounted里面设置全局的

 mounted(){
    this.axios.defaults.headers.common['token'] = 6666;
  }

创建一个.vue文件书写一下代码:

11.4.5 服务端解决跨域问题

  
    

在spring-mvc.xml中加入上述这一段。其中,allowed-origins指的是允许的访问源的域名,"*"表示任何人都可以访问,也可以指明具体的域名

11.4.6解决axios无法传递data中的参数问题

原因:默认情况下发送axios时请求头中的内容类型为: (后端没有使用@RequestBody)

Content-Type:application/json;charset=UTF-8

而实际服务端需要的是:

Content-Type:application/x-www-form-urlencoded

因此,使用axios的qs内置库中的方法进行内容类型的转换。

import Qs from 'qs'
​
this.axios({
    method:'post',
    url:'http://localhost:8081/regist',
    transformRequest: [function (data) {
        return Qs.stringify(data)
    }],
    data:{
        email:this.email
    }
})
.then(function (response) {
    alert(response.data.message)
});

十二、 Vuex状态集中式管理

12.1 什么是Vuex

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

12.2步骤

1.先在创建一个没有使用 vuex的组件 MyVuex.vue


Vue详解及综合案例_第37张图片

 12.3 安装

1.如果不想安装可以在构建项目时勾选此选项: 

 Vue详解及综合案例_第38张图片

2.也可以在项目根目录执行如下命令来安装 Vuex

若失败,可使用cnpm

npm install vuex --save

修改 main.js 文件,导入 Vuex,关键代码如下:

import Vuex from 'vuex'
Vue.use(Vuex);
npm install vuex --save

但是 如果项目里 需要多个组件 共享这个 number 的值 ,也就是 number 的变化 很多组件都能感知到 怎么实现,vuex 状态集中式管理 就是解决这个问题的。

首先 安装

修改 main.js 文件,导入 Vuex,关键代码如下:

import Vuex from 'vuex'
Vue.use(Vuex);

创建vuex配置文件 在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
​
export default new Vuex.Store({
    // 全局 state 对象,用于保存所有组件的公共数据
    state: {
        //在组件中是通过 this.$store.state.number 来获取
        number: 0
    },
    // 实时监听 state 值的最新状态,注意这里的 getters 可以理解为计算属性
    getters: {
        // 在组件中是通过 this.$store.getters.evenOrOdd 来获取
        evenOrOdd(state) {
            return state.number % 2 == 0 ? '偶数' : '奇数'
        }
    },
    // 定义改变 state 初始值的方法,这里是唯一可以改变 state 的地方,缺点是只能同步执行
    mutations: {
        // 在组件中是通过 this.$store.commit('jia'); 方法来调用 mutations
        jia(state) {
            state.number++
        },
        jian(state) {
            state.number--
        }
    },
    // 定义触发 mutations 里函数的方法,可以异步执行 mutations 里的函数
    actions: {
​
        // 在组件中是通过 this.$store.dispatch('jia'); 来调用 actions
        jia(context) {
            context.commit('jia')
        },
        // 在组件中是通过 this.$store.dispatch('jian'); 来调用 actions
        jian({ commit, state }) {    //解构 context 对象  可以拿到对象属性  commit  和  state
            if (state.number > 0) {
                commit('jian')
            }
        }
    },
    modules: {
​
    }
})

修改 main.js 增加刚才配置的 store/index.js,关键代码如下:

import Vue from 'vue'
import Vuex from 'vuex'
import store from './store'
​
Vue.use(Vuex);
​
new Vue({
  el: '#app',
  store
});

修改MyVuex.vue

当前数字: {{$store.state.number}} is {{$store.getters.evenOrOdd}}

           methods: {        jia(){            // this.$store.commit('jia'); // 同步调用            this.$store.dispatch('jia'); // 异步调用       },        jian(){          // this.$store.commit('jian'); // 同步调用          this.$store.dispatch('jian'); // 异步调用       }   },

依然 可以实现之前的效果

但是感觉 每次 都写 $.store.xxx.xxx 太长了 ,怎么办?

// 不想每次都写 $store.state.xxx    就导入组件然后计算属性    
//  第一步:在MyVuex.vue中的

13.2.2登录页面及代码

Vue详解及综合案例_第41张图片

在src下创建pages文件夹,然后创建Login.vue 




13.2.3主页面及代码

Vue详解及综合案例_第42张图片

在src下的pages文件夹下面创建Home.vue 



注意:当用户登录成功后,后端响应一个token,前端接受token将token存到本地在 beforeCreate()里设置了全局的token,这样就不用每次请求都设置token请求头了。

 13.2.4条件和分页查询页面及代码

Vue详解及综合案例_第43张图片

在src下的pages文件夹下面创建AdminList.vue

根据需要自行设置每页显示条数,只需更改data里面的pageSize属性。



13.2.5单个文件上传代码

Vue详解及综合案例_第44张图片

 在src文件夹下创建components文件夹,如果前面policy.js配置文件时已经创建就直接在component是下创建singleUpload.vue.




13.2.6添加用户页面及代码

Vue详解及综合案例_第45张图片

在src下的pages文件夹下面创建AdminAdd.vue

该页面是通过点击新增寻找/home/adminAdd路径的路由,可以看到改页面弹出在home页面之上,这是因为在home的路由配置通过children[{}]里面设置了嵌套路由




13.2.7删除动作及代码

Vue详解及综合案例_第46张图片

该功能使用的弹出框代码在Home.vue中。

methods中handleClickDelete()方法里.

13.2.8编辑页面及代码

Vue详解及综合案例_第47张图片

 在src下的pages文件夹下面创建AdminEdit.vue





13.3后端相关配置代码

目录 

Vue详解及综合案例_第48张图片

13.3.1pom.xml文件


        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        

        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
        
            org.projectlombok
            lombok
            1.18.20
            true
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            mysql
            mysql-connector-java
            runtime
        

        
        
            com.baomidou
            mybatis-plus-generator
            3.3.2
        

        
        
            org.apache.velocity
            velocity
            1.7
        

        
        
            com.baomidou
            mybatis-plus-extension
            3.4.2
        

        
        
            mysql
            mysql-connector-java
            runtime
        

        
        
            com.alibaba
            fastjson
            1.2.56
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
        

        
        
            com.baomidou
            mybatis-plus-generator
            3.3.2
        

        
        
            com.github.xiaoymin
            knife4j-spring-boot-starter
            2.0.7
        

        
        
            org.springframework.boot
            spring-boot-starter-aop
        

        
        
            com.alibaba.cloud
            aliyun-oss-spring-boot-starter
            1.0.0
        

        
        
            com.auth0
            java-jwt
            3.4.0
        
        
            io.jsonwebtoken
            jjwt
            0.7.0
        

13.3.2application.yml文件


# 服务端口号
server:
  port: 8080


spring:
  datasource: # 数据库配置
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/haiweiaicloud?serverTimezone=GMT%2B8
    username: root
    password: 123456

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看sql输出日志

  global-config:
    db-config:
      logic-delete-value: 1  # 逻辑删除   1 代表已被逻辑删除
      logic-not-delete-value: 0 # 逻辑删除   0 代表未被逻辑删除
      id-type: auto  # 整个项目中设置主键自增
  mapper-locations: classpath:mapper/*.xml  # 映射mapper中xml文件

#OSS文件上传配置
alibaba:
  cloud:
    access-key: LTAI5tPsayZbQq9DNzGztFPL
    secret-key: bDWiypa4C2qrUmT8w2zfDsCiJ5ZbUC
    oss:
      endpoint: oss-cn-hangzhou.aliyuncs.com
      bucket: ymk1

13.3.3拦截器及跨域配置

全局拦截器,此代码在interceptor包下,类名MyInterceptor

作用:

        进行了前置拦截的相关操作,根据token判断用户登录

package com.ymk.hotelsystem.interceptor;

import com.alibaba.fastjson.JSON;
import com.ymk.hotelsystem.annotation.UnInterception;
import com.ymk.hotelsystem.common.CheckResult;
import com.ymk.hotelsystem.common.R;
import com.ymk.hotelsystem.common.SystemConstant;
import com.ymk.hotelsystem.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * @Desc 拦截器
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    /**
     * 前置拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器前置拦截"); // 可以在此方法中解决跨域问题

        //当含有自定义请求头时必须进行域减请求
        if ("OPTIONS".equals(request.getMethod())) {//处理预检请求
            System.out.println("preflight 请求");
            return true;
        }


        System.out.println(request.getRequestURI()); // 获取请求的路径
        /**
         * 判断是否登录
         */
        // 方法处理器
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 为防止类转换异常可以在这做个判断
        if (handler instanceof HandlerMethod) {

            Method method = handlerMethod.getMethod();
            String methodName = method.getName();
            log.info("====拦截到了方法:{},在该方法执行之前执行====", methodName);

            // 通过方法,可以获取该方法上的自定义注解,然后通过注解来判断该方法是否要被拦截
            // @UnInterception 是自定义注解
            UnInterception unInterception = method.getAnnotation(UnInterception.class);
            if (null != unInterception) {
                return true;
            }

            //判断用户有没有登录,一般登录之后的用户都有一个对应的token
            String token = request.getHeader("token");
            System.out.println("获取请求头token:" + token);
            if (null == token || "".equals(token)) {
                log.info("用户未登录,没有权限执行...请登录");

                //token为空时将信息响应到前端
                R r = R.error().message(SystemConstant.JWT_ERRCODE_NULL + ":用户未登录");
                print(response, r);
                return false;
            } else {
                // 校验token 的逻辑
                CheckResult checkResult = JwtUtils.validateJWT(token);

                if (checkResult.isSuccess()) {
                    //token令牌校验通过
                    return true;
                } else {
                    // 解析失败时将分情况通过switch响应前端
                    switch (checkResult.getErrCode()) {
                        case SystemConstant.JWT_ERRCODE_FAIL: {
                            System.out.println("签名校验不通过");
                            R r = R.error().message(SystemConstant.JWT_ERRCODE_FAIL + ":签名校验不通过");
                            print(response, r);
                            break;
                        }
                        case SystemConstant.JWT_ERRCODE_EXPIRE: {
                            System.out.println("签名已过期");
                            R r = R.error().message(SystemConstant.JWT_ERRCODE_EXPIRE + ":签名已过期");
                            print(response, r);
                            break;
                        }

                    }
                    // token校验失败返回false 拦截
                    return false;
                }
            }
        }
        // 返回true才会继续执行,返回false则取消了当前请求
        return true;
    }

    /**
     * 当token解析失败时向前端响应
     *
     * @param response
     * @param r
     */
    public void print(HttpServletResponse response, R r) {
        // 响应json数据
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            // 把java 对象转为 json串
            String jsonString = JSON.toJSONString(r);
            // 响应回去的数据是json 格式
            writer.print(jsonString);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 后置拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("执行完方法后执行(Controller方法调用之后),但是此时还没进行视图渲染");
    }

    /**
     * 最终拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("整个请求都处理完毕,DispatcherServlet也渲染了对应的视图了,此时可以做一些清理工作");
    }
}

下面代码在config包下,类名MyMvcConfig

作用: 

        解决跨域,设置不需要拦截的路径

package com.ymk.hotelsystem.config;

import com.ymk.hotelsystem.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Desc MVC相关的一个配置
 */
@Configuration // 声明配置类
public class MyMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域解决
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // registry 解决跨域问题
        registry.addMapping("/**")
                .allowedOrigins("*") // 允许任何的域
                .allowCredentials(true) // 允许携带cookie
                .allowedMethods("GET","POST","PUT","DELETE","HEAD","PATCH","OPTIONS","TRACE","CONNECT") // 允许的请求方法
                .allowedHeaders("*"); // 允许头

    }

    /**
     * 配置拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                //访问doc.html时有关路径不拦截
                .excludePathPatterns("/webjars/**","/swagger-resources/**","/doc.html") // 不用拦截的路径
                .addPathPatterns("/**"); // 拦截所有路径
    }
}

下面代码在annotation包下,注解名UnInterception,该自定义注解是用来指定某个方法不需要被拦截。只能写在方法上

package com.ymk.hotelsystem.annotation;

/**
 * @Desc 自定义注解
 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 该注解用来指定某个方法不用拦截
 */
@Target(ElementType.METHOD) // 只能写在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface UnInterception {
}

13.3.4全局异常和自定义异常

下面代码在exception包下,类名GlobalExceptionHandler

作用:

        全局异常处理

package com.ymk.hotelsystem.exception;

import com.ymk.hotelsystem.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @Desc 全局异常处理器
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    /**
     * 拦截业务异常,返回业务异常信息
     * @param ex
     * @return
     */
    @ExceptionHandler(BusinessErrorException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public R handleBusinessError(BusinessErrorException ex){
        String code = ex.getCode();
        String message = ex.getMessage();
        return R.error().code(Integer.parseInt(code)).message(message);
    }

    /**
     * 空指针异常
     * @param ex NullPointerException
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public R handleTypeMismatchException(NullPointerException ex) {
        log.error("空指针异常,{}", ex.getMessage());
        return R.error().message("空指针异常了");
    }

    /**
     * 缺少请求参数异常
     * @param ex MissingServletRequestParameterException
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R handleHttpMessageNotReadableException(MissingServletRequestParameterException ex){
        log.error("缺少请求参数,{}", ex.getMessage());
        return R.error().message("缺少必要的请求参数");
    }

    /**
     * 系统异常 预期以外异常
     * @param ex
     * @return
     * 项目中,我们一般都会比较详细的去拦截一些常见异常,拦截Exception 虽然可以一劳永逸,
     * 但是不利于我们去排查或者定位问题。实际项目中,可以把拦截 Exception 异常写在 GlobalExceptionHandler
     * 最下面,如果都没有找到,最后再拦截一下 Exception 异常,保证输出信息友好。
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public R handleUnexpectedServer(Exception ex){
        log.error("系统异常:", ex);
        return R.error().message("系统发生异常,请联系管理员");
    }
}

 下面代码在common包下,枚举类名BusinessMsgEnum

作用:        

        自定义异常状态码及提示信息

package com.ymk.hotelsystem.common;

/**
 * @Desc
 */

/**
 * 业务异常提示信息枚举类
 */
public enum BusinessMsgEnum {
    /** 参数异常 */
    PARMETER_EXCEPTION("102","参数异常"),
    /** 等待超时 */
    SERVICE_TIME_OUT("103","服务调用超时"),
    /** 参数过大 */
    PARMETER_BIG_EXCEPTION("102","输入的图片数量不能超过50张"),
    /** 500 :一劳永逸的提示也可以在这定义 */
    UNEXPECTED_EXCEPTION("500","异常发生异常,请联系管理员!");

    // 还可以定义更多的也无异常
    /**
     * 消息码
     */
    private String code;
    /**
     * 消息内容
     */
    private String msg;

    private BusinessMsgEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

下面代码在exception包下,类名BusinessErrorException

作用:

        自定义异常

package com.ymk.hotelsystem.exception;

import com.ymk.hotelsystem.common.BusinessMsgEnum;

/**
 * @Desc 自定义异常
 */
public class BusinessErrorException extends RuntimeException{
    /**
     * 异常码
     */
    private String code;
    /**
     * 异常提示信息
     */
    private String message;

    public BusinessErrorException(BusinessMsgEnum businessMsgEnum){
        this.code = businessMsgEnum.getCode();
        this.message = businessMsgEnum.getMsg();
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

13.3.5JWT生成及Token认证

下面代码在common包下,类名SystemConstant

作用:

        JWTUtil工具类解析token设置的状态码,和JWT相关属性

package com.ymk.hotelsystem.common;

/**
 * @Desc  jwtUtil工具类用到的
 */
public class SystemConstant {
    /**
     * token
     */
    public static final int JWT_ERRCODE_NULL = 4000;  // token不存在
    public static final int JWT_ERRCODE_EXPIRE = 4001; // token已过期
    public static final int JWT_ERRCODE_FAIL= 4002;    //验证不通过

    /**
     * JWT
     */
    public static final String JWT_SECERT = "8677df7fc3a34e26a61c034d5ec8245d";
    public static final long JWT_TTL = 60 * 60 * 1000;
}

 下面代码在common包下,类名CheckResult

作用:

        用来验证JWT

package com.ymk.hotelsystem.common;

import io.jsonwebtoken.Claims;

/**
 * @Desc 验证JWT用到的
 */
public class CheckResult {

    private int errCode;

    private boolean success;

    private Claims claims;

    public int getErrCode(){
        return errCode;
    }

    public void setErrCode(int errCode){
        this.errCode = errCode;
    }

    public boolean isSuccess(){
        return success;
    }

    public void setSuccess(boolean success){
        this.success = success;
    }

    public Claims getClaims(){
        return claims;
    }

    public void setClaims(Claims claims){
        this.claims = claims;
    }
}

下面代码在utils包下,类名JwtUtils

作用:

        前端发送认证请求,比如用户名密码,认证成功后后台生成一个token 发送给前端,前端存储到localstorage,以后每次请求都带这个token发送到后台去验证

        

package com.ymk.hotelsystem.utils;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.ymk.hotelsystem.common.CheckResult;
import com.ymk.hotelsystem.common.SystemConstant;
import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;

/**
 * @Desc 前端发送认证请求,比如用户名密码,认证荣国后后台生成一个token
 * 发送给前端,前端存储到localstorage,以后每次请求都带这个token发送到后台去验证
 */
public class JwtUtils {

    /**
     * 签发JWT
     *
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJwt(String id, String subject, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                // 主题 一般是用户名
                .setSubject(subject)
                // 签发者
                .setIssuer("ymk")
                // 签发日期
                .setIssuedAt(now)
                // 签发算法以及密钥
                .signWith(signatureAlgorithm, secretKey);

        if (ttlMillis >= 0) {
            // 当前时间+有效时间
            long expMillis = nowMillis + ttlMillis;
            // 过期时间
            Date expDate = new Date(expMillis);
            // 设置过期时间
            builder.setExpiration(expDate);
        }
        return builder.compact();
    }

    /**
     * 生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodeKey = Base64.decode(SystemConstant.JWT_SECERT);
        SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
        return key;
    }

    /**
     * 验证JWT
     * @param jwtStr
     * @return
     */
    public static CheckResult validateJWT(String jwtStr) {
        CheckResult checkResult = new CheckResult();
        Claims claims = null;
        try {
            claims = parseJwt(jwtStr);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setErrCode(SystemConstant.JWT_ERRCODE_EXPIRE);
            checkResult.setSuccess(false);
        } catch (SignatureException e) {
            checkResult.setErrCode(SystemConstant.JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        } catch (Exception e) {
            checkResult.setErrCode(SystemConstant.JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        }
        return checkResult;
    }

    /**
     * 解析JWT字符串
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJwt(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }


    public static void main(String[] args) {
        // 后端生成token
        String sc = createJwt("1", "jack", SystemConstant.JWT_TTL);
        System.out.println(sc);

        //后端验证token
        CheckResult checkResult = validateJWT(sc);
        System.out.println(checkResult.isSuccess());
        System.out.println(checkResult.getErrCode());
        Claims claims = checkResult.getClaims();
        System.out.println(claims);
        System.out.println(claims.getId());
        System.out.println(claims.getSubject());

        // 刷新token 重新生成token
        Claims claims2 = validateJWT(sc).getClaims();
        String sc2 = createJwt(claims2.getId(), claims2.getSubject(), SystemConstant.JWT_TTL);
        System.out.println(sc2);
    }
}

13.3.6后端响应前端封装的格式

下面代码在common包下,枚举类名ResultCodeEnum

作用:

        设置响应后端的一些操作是否成功和状态码以及信息

package com.ymk.hotelsystem.common;

import lombok.Getter;

@Getter // get方法
public enum ResultCodeEnum {
    //枚举值
    SUCCESS(true,"操作成功",200),
    UNKNOWN_REASON(false,"操作失败",999),
    BAD_SQL_GRAMMAR(false,"sql语法错误",520),
    ERROR(false,"操作失败",444);

    private Boolean success;
    private String message;
    private Integer code;

    ResultCodeEnum(Boolean success, String message, Integer code) {
        this.success = success;
        this.message = message;
        this.code = code;
    }
}

下面代码在common包下,类名R

作用:

        后端返回给前端的 一个封装的数据结构

package com.ymk.hotelsystem.common;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * 后端返回给前端的 一个 封装的 数据结构
 */
@Data
public class R {
    private Integer code; // 响应的状态码

    private String message; // 响应的信息

    private Boolean success; // 是否成功

    //封装响应的数据
    private Map data = new HashMap<>();

    public R() {}

    // 返回成功的结果
    public static R ok(){
        R r = new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }
    // 返回失败的结果
    public static R error(){
        R r = new R();
        r.setSuccess(ResultCodeEnum.ERROR.getSuccess());
        r.setCode(ResultCodeEnum.ERROR.getCode());
        r.setMessage(ResultCodeEnum.ERROR.getMessage());
        return r;
    }

    public static R setResult(ResultCodeEnum resultCodeEnum){
        R r = new R();
        r.setSuccess(resultCodeEnum.getSuccess());
        r.setCode(resultCodeEnum.getCode());
        r.setMessage(resultCodeEnum.getMessage());
        return r;
    }

    //创建一个R对象去调success方法,然后把失败或成功的值传进去
    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMessage(message);
        return this;
    }

    public R code(Integer code){
        this.setCode(code);
        return this;
    }

    public R data(Object key,Object value){
        this.data.put(key,value);
        return this;
    }

    public R data(Map map){
        this.setData(map);
        return this;
    }
}

下面的代码在common包下,类名LayuiPageVo

作用:

        用来封装Layui渲染表格时接受后台响应的信息

package com.ymk.hotelsystem.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @Desc
 */
@Data//set,get,toString方法
@AllArgsConstructor//有参构造
@NoArgsConstructor//无参构造
public class LayuiPageVo {

    private Integer code; // 0 表示成功
    private String msg;
    private Long count;

    private List data;
}

13.3.7AOP切面配置

下面代码在aop包下,类名LogAspectHandler

作用:

        通过JointPoint 对象来获取一个签名,然后利用签名可以获取请求的包名、方法名,也可以用来记录一些信息,比如获取请求的url和ip

package com.ymk.hotelsystem.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @Desc
 */
@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * 定义一个切面,拦截com.ymk.controller包和子包下的所用方法
     */
    @Pointcut("execution(* com.ymk.hotelsystem.controller..*.*(..))")
    public void pointCut() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void annotationCut() {
    }

    /**
     * 在上面定义的切面方法之前执行该方法
     * @param joinPoint jointPoint
     * JointPoint 对象很有用,可以用它来获取一个签名,然后利用签名可以获取请求的包名、方法名,
     * 包括参数(通过 `joinPoint.getArgs()` 获取)等等。
     */
    @Before("pointCut()") // 前置通知
    public void doBefore(JoinPoint joinPoint) {
        log.info("====doBefore方法进入了====");

        // 获取签名
        Signature signature = joinPoint.getSignature();
        // 获取切入的包名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取即将执行的方法名
        String funcName = signature.getName();
        log.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);

        // 也可以用来记录一些信息,比如获取请求的url和ip
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取请求url
        String url = request.getRequestURL().toString();
        // 获取请求ip
        String ip = request.getRemoteAddr();
        log.info("用户请求的url为:{},ip地址为:{}", url, ip);
    }
}


13.3.8Knife4j配置

下面代码在config包下,类名Knife4jConfiguration

作用:

        前端和后端的联系,通过访问localhost:8080/doc.html可展示在线的 API 接口文档,进行测试接口数据

package com.ymk.hotelsystem.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

/**
 * @Desc Knife4j配置
 */
@Configuration // 声明配置类
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        //.title("swagger-bootstrap-ui-demo RESTful APIs")
                        .description("# swagger-bootstrap-ui-demo RESTful APIs")
                        .termsOfServiceUrl("http://www.xx.com/")
                        .contact("[email protected]")
                        .version("1.0")
                        .build())
                //分组名称
                .groupName("2.X版本")
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.ymk.demo22.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

13.3.9Jackson为空数据变空串

下面代码在config包下,类名JacksonConfig

作用:

        向前端响应json数据的时候将为空的数据会变成空串。

package com.ymk.hotelsystem.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;

/**
 * @Desc Jackson配置
 */
@Configuration //声明配置类
public class JacksonConfig {
    //此配置 向前端响应json数据的时候 为空的数据会变成空串
    @Bean
    @Primary // 简单的说 就是 当Spring 容器扫描到某个接口有多个bean时,如果某个bean上有@Primary 注解 则这个bean 会被优先选用
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }
}
 
  

13.3.10MybatisPlus

乐观锁、逻辑删除、分页插件

下面代码在config包下,类名MybatisPlusConfig

作用:

        配置一些插件,该类配置了分页插件、乐观锁插件、逻辑删除相关的bean

package com.ymk.hotelsystem.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Desc 配置一些插件
 */
@Configuration
@MapperScan("com.ymk.hotelsystem.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor optimisticLockerInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInnerInterceptor); // 添加分页拦截器 --- 分页插件

        // 乐观锁插件
        OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor();
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor); // 添加乐观锁拦截器 --- 乐观锁插件

        return interceptor;
    }

    //逻辑删除相关的bean
    @Bean
    public ISqlInjector sqlInjector(){
        return new DefaultSqlInjector();
    }
}

下面代码在handler包下,类名MyMetaObjectHandler

作用:

        设置插入修改时的填充自动填充某些字段的值、添加逻辑删除的默认值逻辑上未删除的默认值。

package com.ymk.hotelsystem.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @Desc 自动填充 某些 字段的值
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill....");// 添加时填充
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
//        this.setFieldValByName("createTime", LocalDate.now(),metaObject);
//        this.setFieldValByName("updateTime",LocalDate.now(),metaObject);

        // 添加 乐观锁的 默认值是1
        this.setFieldValByName("version",1,metaObject);

        // 0 表示逻辑上未删除的
        this.setFieldValByName("deleted", 0, metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill....");// 修改时填充
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

13.4后端操作代码

13.4.1实体类pojo

下面代码在pojo包下,类名Admin

作用:

        对应数据库中的t_admin表,字段与Admin属性一一对应。

 

package com.ymk.hotelsystem.pojo;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @Desc
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_admin") //指定数据库中的表名
public class Admin {

    @TableId(type = IdType.AUTO) //使用数据库中的自增策略
    private Integer id;

    private String username;

    private String password;

    private Integer flag;

    private String type;

    private String picture;


    @JsonFormat(pattern="yyyy-MM-dd")
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;

    @JsonFormat(pattern="yyyy-MM-dd")
    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE) // 添加和修改都会赋值
    private Date updateTime;

    @TableField(fill = FieldFill.INSERT)
    @Version
    private Integer Version;

    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    private Integer deleted;

}

13.4.2controller

下面代码在controller包下,类名OssController

作用:

        因为不再需要后端去上传到OSS上了,现在交给了前端,前端上传oss请求,后端响应凭证,然后前端拿着凭证上传OSS。

package com.ymk.hotelsystem.controller;

import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.ymk.hotelsystem.annotation.UnInterception;
import com.ymk.hotelsystem.common.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Desc 前端上传oss请求,后端响应凭证
 */
@RestController
public class OssController {
    @Resource
    OSS ossClient;

    @Value("${alibaba.cloud.oss.endpoint}")
    private String endpoint;

    @Value("${alibaba.cloud.oss.bucket}")
    private String bucket;

    @Value("${alibaba.cloud.access-key}")
    private String accessId;

    @RequestMapping("/oss/policy")
    @UnInterception
    public R policy() {

        //host的格式为bucketname.endpoint
        String host = "https://" + bucket + "." + endpoint;
        //callbackUrl为上传回调服务器的URL,将下面的IP和Port配置为自己的真实信息
//    String callbackUrl = "http://88.88.88.8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

        //用户上传文件是指定的前缀
        String dir = format + "/";

        Map respMap = null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            respMap = new LinkedHashMap();
            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return R.ok().data(respMap);
    }
}

下面代码在controller包下,类名AdminController

作用:

        接受前端用户请求

package com.ymk.hotelsystem.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ymk.hotelsystem.annotation.UnInterception;
import com.ymk.hotelsystem.common.CheckResult;
import com.ymk.hotelsystem.common.LayuiPageVo;
import com.ymk.hotelsystem.common.SystemConstant;
import com.ymk.hotelsystem.utils.JwtUtils;
import com.ymk.hotelsystem.utils.OssUtil;
import com.ymk.hotelsystem.common.R;
import com.ymk.hotelsystem.pojo.Admin;
import com.ymk.hotelsystem.service.AdminService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author 闫梦坤
 * @Date 2022/10/16 23:43
 * @Desc
 */
@RestController
@Slf4j
@RequestMapping("/admin")
public class AdminController {
    @Autowired
    private AdminService adminService;

    /**
     * 管理员登录
     * @param username
     * @param password
     * @return
     */
    @UnInterception
    @GetMapping("/login")
    public R login(String username, String password){
        log.info("控制层接受账号密码:"+username+":"+password);
        Admin admin = adminService.findByLogin(username, password);

        Map map = new HashMap<>();
        if (admin != null){
            //生成token发送给前端
            String token = JwtUtils.createJwt(String.valueOf(admin.getId()),admin.getUsername(), SystemConstant.JWT_TTL);
            map.put("currentAdmin",admin);
            map.put("token",token);
            return R.ok().data(map);
        } else {
            return R.error().message(SystemConstant.JWT_ERRCODE_NULL+"");
        }
    }

    /**
     * 分页加模糊查询
     * @param map
     * @return
     */
    @RequestMapping("/list")
    public LayuiPageVo adminList(@RequestParam Map map){
        System.out.println(map);
        LayuiPageVo adminLayuiPageVo = adminService.adminList(map);
        return  adminLayuiPageVo;
    }

    /**
     * 新增账号
     * @param admin
     * @return
     */
    @PostMapping("/add")
    public R adminAdd(@RequestBody Admin admin){
        log.info("添加用户接受的值:"+admin);
        Integer result = adminService.adminAdd(admin);
        log.info("增加受影响行数:"+result);
        if (result > 0){
            return R.ok().message("新增成功");
        } else {
            return R.error().message("系统繁忙,请您稍后尝试");
        }
    }

    @PostMapping("/update")
    public R adminEdit(@RequestBody Admin admin){
        log.info("修改用户接受的值:"+admin);
        Integer result = adminService.adminEdit(admin);
        if (result>0){
            return R.ok().message("修改成功");
        } else {
            return R.error().message("修改失败,请重试");
        }
    }

    /**
     * 删除用户
     * @param id
     * @return
     */
    @PostMapping("/delete")
    public R adminDelete(Integer id){
        System.out.println("删除账号的id:"+id);
        Integer result = adminService.adminDelete(id);
        if (result>0){
            return R.ok().message("删除成功");
        } else {
            return R.error().message("删除失败");
        }
    }


    /**
     * 通过id查询用户
     */
    @GetMapping("/adminDetail")
    public R findAdminById(Integer id){
        log.info("接受的查询id:"+id);
        Admin admin = adminService.findAdminById(id);
        Map map = new HashMap<>();
        map.put("admin",admin);
        return R.ok().data(map);
    }

    /**
     * 重新生成token
     * 前端页面设置每一段时间刷新token,
     * 并向后端发送axios请求来获取新的token
     * @return
     */
    @RequestMapping("/refreshToken")
    public R refreshToken(HttpServletRequest request){
        String token = request.getHeader("token");
        System.out.println("刷新获得旧的token:"+token);
        CheckResult checkResult = JwtUtils.validateJWT(token);
        Claims claims = checkResult.getClaims();
        //生成一个新的token
        String newToken = JwtUtils.createJwt(claims.getId(), claims.getSubject(), SystemConstant.JWT_TTL);
        return R.ok().data("token",newToken);
    }
}

13.4.3service

AdminService接口

package com.ymk.hotelsystem.service;

import com.ymk.hotelsystem.common.LayuiPageVo;
import com.ymk.hotelsystem.pojo.Admin;

import java.util.Map;

/**
 * @Author 闫梦坤
 * @Date 2022/10/16 23:41
 * @Desc
 */
public interface AdminService {
    /**
     * 管理员登录
     * @param username
     * @param password
     * @return
     */
    Admin findByLogin(String username, String password);

    /**
     * 分页加条件查询
     * @param map
     * @return
     */
    LayuiPageVo adminList(Map map);

    /**
     * 增加账号
     * @param admin
     * @return
     */
    Integer adminAdd(Admin admin);

    /**
     * 修改账号信息
     * @param admin
     * @return
     */
    Integer adminEdit(Admin admin);

    /**
     * 删除用户
     * @param id
     * @return
     */
    Integer adminDelete(Integer id);


    /**
     * 通过id查询用户
     * @param id
     * @return
     */
    Admin findAdminById(Integer id);
}

AdminServiceImpl实现类

package com.ymk.hotelsystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ymk.hotelsystem.common.LayuiPageVo;
import com.ymk.hotelsystem.mapper.AdminMapper;
import com.ymk.hotelsystem.pojo.Admin;
import com.ymk.hotelsystem.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * @Desc
 */
@Service("adminService")
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    /**
     * 管理员登录
     * @param username
     * @param password
     * @return
     */
    @Override
    public Admin findByLogin(String username, String password) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username).eq("password", password);
        return adminMapper.selectOne(queryWrapper);
    }

    /**
     * 分页加条件查询
     * @param map
     * @return
     */
    @Override
    public LayuiPageVo adminList(Map map) {
        //加上空字符串变成字符串再转成Integer类型
        Integer page = Integer.parseInt(map.get("page")+"");
        Integer limit = Integer.parseInt(map.get("limit")+"");

        Page adminPage = new Page<>(page,limit);
        QueryWrapper queryWrapper = new QueryWrapper<>();
        // 通过遍历的方式将map集合中不参加模糊查询的键值过滤,将模糊查询放到条件构造器中
        for (Map.Entry entry : map.entrySet()) {
            if (!entry.getKey().equals("token") && !entry.getKey().equals("page") && !entry.getKey().equals("limit")){
                queryWrapper.like(entry.getKey(), entry.getValue());
            }
        }

        adminPage = adminMapper.selectPage(adminPage, queryWrapper);
        //把mybatis的分页数据封装到 layui 的分页数据结构中
        LayuiPageVo layuiPageVo = new LayuiPageVo<>();
        layuiPageVo.setCode(0);// layui 默认0 是正确的状态码
        layuiPageVo.setMsg("分页列表数据"); //提示信息
        layuiPageVo.setCount(adminPage.getTotal()); // 总记录数
        layuiPageVo.setData(adminPage.getRecords()); // 分页的列表数据
        return layuiPageVo;
    }

    /**
     * 新增用户
     * @param admin
     * @return
     */
    @Override
    public Integer adminAdd(Admin admin) {
        int insert = adminMapper.insert(admin);
        return insert;
    }

    /**
     * 修改用户信息
     * @param admin
     * @return
     */
    @Override
    public Integer adminEdit(Admin admin) {
        UpdateWrapper updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id",admin.getId());
        int update = adminMapper.update(admin, updateWrapper);
        return update;
    }

    /**
     * 删除用户
     * @param id
     * @return
     */
    @Override
    public Integer adminDelete(Integer id) {
        int i = adminMapper.deleteById(id);
        return i;
    }


    /**
     * 通过id查询用户
     * @param id
     * @return
     */
    @Override
    public Admin findAdminById(Integer id) {
        Admin admin = adminMapper.selectById(id);
        return admin;
    }
}

13.4.4mapper

AdminMapper接口

package com.ymk.hotelsystem.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ymk.hotelsystem.pojo.Admin;
import org.springframework.stereotype.Repository;

/**
 * @Desc
 */
@Repository
public interface AdminMapper extends BaseMapper {
}

你可能感兴趣的:(javascript,开发语言,ecmascript)