0
点赞
收藏
分享

微信扫一扫

Vue详解及综合案例

香小蕉 2022-11-01 阅读 181

一、Vue简介

1.1 简介

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

1.3 其它 MVVM 实现者

1.4 为什么要使用 Vue.js

1.5 Vue.js 的两大核心要素

1.5.1 数据驱动

1.5.2 组件化

二、Vue的初体验

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

 <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>

2.2 在页面中绑定vue元素

<div id="app"></div>

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

<script>
        new Vue({
            el:"#app",
            data:{
                title:"hello vue!",
                  args1:"hi!",
                 age:18,
                flag:true
            }
        });
</script>
 #  el: element的简称,也就是Vue实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
 #  data: 用于提供数据的对象,里面存放键值对数据。

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

<div id="app">
    {{ title }}
</div>

三、 插值表达式

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

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

<div id="app">
    {{title}}
</div>

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

<div id="app">
    {{[1,2,3,4][2]}}
</div>

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

<div id="app">
    {{ {"name":"xiaoyu","age":20}.age }}
</div>

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

<div id="app">
        {{ sayHello()}}
</div>

3.5Vue对象总结 

四、Vue的分支 v-if

4.1 v-if

<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="app">
            <p v-if="flag">
                今天天气很舒服!
            </p>
            <p v-else-if="rich">
                今天天气很燥热!晚上要去放松一下!
            </p>
            <p v-else="rich">
                晚上只能自嗨!
            </p>
        </div>
        
    </body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el:'#app',
            data:{
                flag:false,
                rich:false
            },
            methods:{
            }
        });
    </script>
</html>

4.2 v-show

<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
            <p v-show="rich">
                有钱!
            </p>
            <p v-if="rich">
                有钱!
            </p>
            <button type="button" @click="rich=!rich">今晚彩票开奖</button>
        </div>
        
    </body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el:'#app',
            data:{
                flag:false,
                rich:false
            },
            methods:{
                
            }
        });     
    </script>
</html>

五、Vue的循环 v-for

5.1 普通的for循环

<body>
<div id="app">
    <ul>
        <li v-for="item in items">{{item.date}}</li>
    </ul>
</div>
</body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            items:[1,2,3,4,5,6]
        }
    });
</script>

5.2 带着索引的for

<body>
<div id="app">
    <ul>
        <li v-for=" (a,i)  in  args" :key='i'>{{i}}{{a}}</li>
    </ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            args:[1,2,3,4,5,6]
        }
    });
</script>

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

<body>
<div id="app">
    <ul>
        <li v-for="(value,name,index) in student">{{name}}--{{value}}--{{index}}</li>
    </ul>
</div>
</body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            student:{
                username:'小鱼',
                age:20,
                girl:'如花'
            }
        }
    });
</script>
页面效果如下:

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

<body>
<div id="app">
 <table style="border: 1px solid black">
            <tr>
                <th>姓名</th>
                <th>年龄</th>
                <th>联系方式</th>
            </tr>
            <tr v-for="(stu,index) in students" v-bind:key="index">
                <td>{{stu.name}}</td>
                <td>{{stu.age}}</td>
                <td>{{stu.phone}}</td>
            </tr>
        </table>
</div>
</body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script></script>
<script>
    new Vue({
        el:'#app',
        data:{
             students:[
                {
                    name:'zs',
                    age:18,
                    phone:'111'
                },
                {
                    name:'ls',
                    age:18,
                    phone:'222'
                },{
                    name:'ww',
                    age:18,
                    phone:'333'
                }
            ]
        }
    });
</script>

六、Vue的属性绑定

6.1 v-model

<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <input type="text" v-model="title">
            {{title}}
        </div>
    </body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
        new Vue({
            el:'#app',
            data:{
                title:"hello vue"
            }
            
        })
    </script>
</html>
页面效果如下:

 6.2 v-bind

<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <a v-bind:href="link"></a>
        </div>
    </body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
        new Vue({
            el:'#app',
            data:{
                link:'http://www.baidu.com'
            }
        })
    </script>
</html>
<a v-bind:href='link'></a>  等价于  <a :href='link'>

七、Vue的事件绑定 

<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            sum={{sum}}<br/>
            {{sum>10?'总数大于10':'总数不大于10'}}<br/>
            <button type="button" @click="increase(2)">增加</button>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                sum:0
            },
            methods:{
                increase:function(s){
                    this.sum+=s
                }
            }
        })
    </script>
</html>

从这里例子中:

<button type="button" @click="increase(2)">增加</button>
increase:function(s)
this.sum+=s

7.1 v-on

<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <input type="text" v-on:click="changeMajor"  />
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el:'#app',
            data:{
                major:'java'
            },
            methods:{
                sayHi(){
                    alert("HELLO VUE!");
                },
                changeMajor(){
                    console.log("change Title")
                }
            }
    </script>
</html>
<input type="text" @click="changeMajor"  />

7.2 事件修饰符

<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div id="app">
            <p @mousemove="mm">
                x:{{x}}
                y:{{y}}
                <span @mousemove.stop>鼠标移动到此即停止</span>
            </p>
            <div @click = "doDivClick" style="width: 100px;height: 100px;border: 1px solid black">
            <a @click.stop="doThis" href="https://www.baidu.com">
                百度
            </a>
        </div>
        </div>
    </body>
    <script   <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
        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");
                }
            }
        })
    </script>
</html>

7.3计算属性:computed

7.3.1 什么是计算属性

7.3.2 计算属性与方法的区别

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>布局篇 计算属性</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
</head>
<body>
​
<div id="vue">
    <p>调用当前时间的方法:{{currentTime1()}}</p>
    <p>当前时间的计算属性:{{currentTime2}}</p>
</div>
​
<script type="text/javascript">
    var vm = new Vue({
        el: '#vue',
        data: {
            message: 'Hello Vue'
        },
        methods: {
            currentTime1: function () {
                return Date.now();
            }
        },
        computed: {
            currentTime2: function () {
                this.message;
                return Date.now();
            }
        }
    });
</script>
</body>
</html>

7.3.3 测试效果 

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

7.3.4 结论

八、Vue的组件化

8.1 什么是“组件化”

8.1.1 组件的全局注册

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue组件的全局注册</title>
</head>
<body>
    <div id="app">
        <model1></model1>
        <model1></model1>
        <model1></model1>
    </div>
        <hr/>
    <div id="app1">
        <model1></model1>
        <model1></model1>
        <model1></model1>
    </div>
</body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script>
    //通过Vue.component实现组件的全局注册,全局注册后的组件可以被重复使用。
    Vue.component("model1",{
​
        template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
        data:function(){
            return {
                title:"hello vue"
            }
        },
        methods:{
            btnfn:function(){
                alert("hello !!!");
            }
        }
    });
    new Vue({
        el:'#app'
    })
    new Vue({
        el:'#app1'
    })
</script>
​
</html>

8.1.2 组件的本地注册

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue组件的本地(局部)注册</title>
</head>
<body>
    <div id="app">
        <model11></model11>
    </div>
    <hr/>
    <!--在这里使用组件model11会报错-->
    <div id="app1">
        <model11></model11>
    </div>
</body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        components:{
            "model11":{
​
                template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
                data:function(){
                    return {
                        title:"hello vue"
                    }
                },
                methods:{
                    btnfn:function(){
                        alert("hello !!!");
                    }
                }
            }
        }
    })
    new Vue({
        el:'#app1'
    })
</script>

8.1.3 小结

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

8.2 组件的生命周期

组件的生命周期钩子
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
</head>
<body>
    <pre>每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、
        将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,
        这给了用户在不同阶段添加自己的代码的机会。</pre>
    <div id="app">
        <p>{{name}}</p>
        <button @click="update">update</button>
        <button @click="destroy">destroy</button>
    </div>
<script type="text/javascript">
    new Vue({
        el:"#app",
        data:{
            a:1,
            name:"zs"
        },
        methods:{
            update(){
                this.name = Math.random() +"---";
            },
            destroy(){
                this.$destroy();
            }
        },
        beforeCreate(){
          console.log("创建vue实例之前");
        },
        created(){
            //
            console.log("vue实例创建成功");
            console.log(this.a);
        }
       /* created: () => console.log(this.a)*/
        ,
        beforeMount(){
            console.log("vue对象挂载之前");
        },
        mounted(){
            // 只执行一次    页面加载完之后执行       比如 咱们在 页面加载完毕 发送ajax请求 获取数据
            console.log(" mounted   挂载完毕")
        },
        beforeUpdate(){
            // 可执行多次
            console.log("data更新之前执行");
        },
        updated(){
            // 可执行多次
            console.log("data更新之后执行");
        },
        beforeDestroy(){
            // 只执行一次
            console.log("vue实例销毁之前执行");
        },
        destroyed(){
            // 只执行一次
            console.log("vue实例销毁之后执行");
        }
​
    });
</script>
</body>
</html>
​

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

9.1 什么是vue-cli

9.2 node.js的介绍及安装

node.js的介绍

node.js的安装

测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。

9.3 使用node.js安装vue-cli

使用如下命令安装vue-cli

npm install -g vue-cli   安装的是2.9.6 版本     -------   不装这个
npm install  -g   @vue/cli     安装的是新版本   新版本  支持 vue ui 
当出现以下界面,表示正在安装:
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install vue-cli -g
cnpm install
cnpm run dev

9.4创建vue项目

9.4.1配置vue片段

{
	"Print to console": {
			"prefix": "vue",
			"body": [
					"<!-- $1 -->",
					"<template>",
					"  <div class='$2'>$5</div>",
					"</template>",
					"",
					"<script>",
					"//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
					"//例如:import 《组件名称》 from '《组件路径》';",
					"",
					"  export default {",
					"    //import引入的组件需要注入到对象中才能使用",
					"    components: {},",
					"    props: {},",
					"    data() {",
					"      //这里存放数据",
					"      return {}",
					"    },",
					"    //监听属性 类似于data概念",
					"    computed: {},",
					"    //监控data中的数据变化",
					"    watch: {},",
					"    //方法集合",
					"    methods: {},",
					"    //生命周期 - 创建完成(可以访问当前this实例)",
					"    created() {},",
					"    //生命周期 - 挂载完成(可以访问DOM元素)",
					"    mounted() {},",
					"    beforeCreate() {}, //生命周期 - 创建之前",
					"    beforeMount() {}, //生命周期 - 挂载之前",
					"    beforeUpdate() {}, //生命周期 - 更新之前",
					"    updated() {}, //生命周期 - 更新之后",
					"    beforeDestroy() {}, //生命周期 - 销毁之前",
					"    destroyed() {}, //生命周期 - 销毁完成",
					"    activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发",
					"  }",
					"</script>",
					"<style scoped>",
					" /* @import url(); 引入公共css类 */",
					"$4",
					"</style>"
			],
			"description": "生成vue模板"
	}
}

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

vue list

9.6vue工程的组件使用

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

消息订阅与发布组件PubSub

9.8 消息订阅与发布组件PubSub

npm install --save pubsub-js

在别的组件中 发布消息

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

9.9插槽 slot

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

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

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

常用命令:

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

vue init webpack my-project1
过程中会出现如下界面,需要手动操作。
出现如下界面,表示安装成功。
npm run dev

访问http://localhost:8081,页面效果如下:

 十、Vue-router 路由

10.1 安装路由模块

npm install vue-router --save 

10.2 引入路由模块并使用

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: '<App/>'
})

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
    }
  ]
})
​
 <div class="menu">
      <ul>
        <li>
          <!--<a href="">首页</a>-->
          <router-link to="/index">首页</router-link>
        </li>
        <li>
          <!--<a href="">菜单1</a>-->
          <router-link to="/menu1">菜单1</router-link>
        </li>
      </ul>
    </div>
    <div class="content">
      <router-view></router-view>
    </div>

10.3嵌套路由

Menu1.vue

<div>
      {{msg}}
      <ul>
          <li>
            <router-link to="/menu1/submenu1">子菜单1</router-link>
          </li>
          <li>
            <router-link to="/menu1/submenu2">子菜单2</router-link>
          </li>
        </ul>
​
        <div class="content">
                <router-view></router-view>
      </div>
​
  </div>
{
      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
        }
      ]
    },
<template>
  <div>子菜单1
      <input type="text" name="sub1input">
  </div>
</template>
<template>
  <div>子菜单2
      <input type="text" name="sub2input">
  </div>
</template>

10.4路由缓存

10.5路由传参

<template>
  <div id="app">
    <div class="menu">
      <ul>
        <li>
          <router-link to="/">首页</router-link>
        </li>
          <li>
          <router-link to="/menu1">菜单1</router-link>
        </li>
        <li>
          <router-link to="/admin">账户信息</router-link>
        </li>
      </ul>
    </div>
    <div class="content">
      <router-view></router-view>
    </div>
  </div>
</template>
<script>
export default {
  mounted(){
    this.axios.defaults.headers.common['token'] = 6666;
  }
}
</script>
<style>
ul li{
  float: left;
  margin-right: 20px;
  list-style: none;
}
.content{
  clear: both;
}
</style>
{
      //path对应app.vue文件中账户信息标签to="/admin"
      path: '/admin', 
      name: 'index',
      component: () => import ('@/views/Admin.vue'),
      children: [ // 子路由,点击姓名显示详细信息
        {
          path:'/admin/adminDetail/:id',
          component: () => import ('@/views/AdminDetail.vue')
        }
      ]
    },
<template>
  <div class="">
    <table>
      <tr>
        <th>编号</th>
        <th>账号</th>
        <th>密码</th>
      </tr>
      <tr v-for="(admin, index) in admins" v-bind:key="index">
        <td>{{ admin.id }}</td>
        <router-link :to='`/admin/adminDetail/${admin.id}`'><td>{{ admin.username }}</td></router-link>
        <td>{{ admin.password }}</td>
      </tr>
    </table>
    <hr>
    <div class="content">
        <router-view></router-view>
    </div>
  </div>
</template>
​
<script>
​
export default {
    //这里存放数据
  data() {
    return {
      admins: [],
    };
  },
​
  //方法集合
  methods: {
    getAdmins() {
      this.axios({
        url: "http://localhost:8081/admin/list",
        params: {
          page: 1,
          limit: 10,
        },
      })
        .then((res) => {
          this.admins = res.data.data;
        })
        .catch(function (error) {
          //处理错误情况
          console.log(error);
        })
        .then(function () {
          //总是会执行
        });
    },
  },
​
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    this.axios.defaults.headers.common['token'] = 6666;
    this.getAdmins(); // 页面加载完成后调用这个函数
  },
};
</script>
<style>
table {
  border: 1px solid black;
}
th,
td,
tr {
  border: 1px solid black;
  text-align: center;
  width: 150px;
}
/* @import url(); 引入公共css类 */
</style>
<template>
  <div class="">
    Id:{{ $route.params.id }}<br />
    {{ admin.username }}<br />
    {{ admin.password }}<br />
    {{ admin.type }}<br />
    {{ admin.create_time }}<br />
  </div>
</template>
​
<script>
export default {
  data() {
    //这里存放数据
    return {
      admin: {},
    };
  },
  //监控data中的数据变化
  watch: { 
    $route(value) {
      let id = value.params.id;
      this.getAdminById(id); // 每次点击姓名都会调用该方法
    },
  },
  //方法集合
  methods: {
    getAdminById(id) {
      console.log(id);
      this.axios({
        url: `http://localhost:8081/admin/adminDetail/${id}`,
      })
        .then((res) => {
          console.log("响应的id详情");
          console.log(res.data.data.admin);
          this.admin = res.data.data.admin;
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    let id = this.$route.params.id;
    this.getAdminById(id);
  },
};
</script>

10.6 编程式路由传参

<template>
  <div class="">
    <table>
      <tr>
        <th>编号</th>
        <th>账号</th>
        <th>密码</th>
        <th>测试编程式路由</th>
      </tr>
      <tr v-for="(admin, index) in admins" v-bind:key="index">
        <td>{{ admin.id }}</td>
        <router-link :to="`/admin/adminDetail/${admin.id}`"
          ><td>{{ admin.username }}</td></router-link
        >
        <td>{{ admin.password }}</td>
        <td>
          <button @click="pushShow(admin.id)">push</button>
          <button @click="replaceShow(admin.id)">replace</button>
        </td>
      </tr>
    </table>
    <button @click="$router.back()">回退</button>
    <hr />
    <div class="content">
      <router-view></router-view>
    </div>
  </div>
</template>
​
<script>
export default {
  //这里存放数据
  data() {
    return {
      admins: [],
    };
  },
​
  //方法集合
  methods: {
    getAdmins() {
      this.axios({
        url: "http://localhost:8081/admin/list",
        params: {
          page: 1,
          limit: 10,
        },
      })
        .then((res) => {
          this.admins = res.data.data;
        })
        .catch(function (error) {
          //处理错误情况
          console.log(error);
        })
        .then(function () {
          //总是会执行
        });
    },
    pushShow(id) {
      console.log(id);
      // 点击时路由带id,通过$router获得id
      this.$router.push(`/admin/adminDetail/${id}`);
    },
    replaceShow(id) {
      this.$router.replace(`/admin/adminDetail/${id}`);
    },
  },
​
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    this.axios.defaults.headers.common["token"] = 6666;
    this.getAdmins(); // 页面加载完成后调用这个函数
  },
};
</script>
<style>
table {
  border: 1px solid black;
}
th,
td,
tr {
  border: 1px solid black;
  text-align: center;
  width: 150px;
}
/* @import url(); 引入公共css类 */
</style>

 十一、使用Axios发送请求

11.1 什么是 Axios

11.2 为什么要使用 Axios

11.3 Axios的使用

11.3.1 安装vue axios

npm install --save axios vue-axios

11.3.2 在main.js中引入

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

11.4 发送axios请求

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'
        ...//其他相关配置
      })

11.4.3axios的简化写法

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

11.4.4axios请求案例

 mounted(){
    this.axios.defaults.headers.common['token'] = 6666;
  }
<template>
  <div class="">
    <table>
      <tr>
        <th>编号</th>
        <th>账号</th>
        <th>密码</th>
      </tr>
      <tr v-for="(admin, index) in admins" v-bind:key="index">
        <td>{{ admin.id }}</td>
        <td>{{ admin.username }}</td>
        <td>{{ admin.password }}</td>
      </tr>
    </table>
  </div>
</template>
​
<script>
export default {
  data(){
      data() {
    //这里存放数据
    return {
      admins: [],
    };
  },
​
  },
  methods:{
    getAdmins() {
      this.axios({
        methods:"get",
        url: "http://localhost:8081/admin/list",
        params: {
          page: 1,
          limit: 10,
        },
      })
        .then((res) => {
          this.admins = res.data.data;
        })
        .catch(function (error) {
          //处理错误情况
          console.log(error);
        })
        .then(function () {
          //总是会执行
        });
    } 
    },
      mounted() {
    this.axios.defaults.headers.common['token'] = 6666;
    this.getAdmins(); // 页面加载完成后调用这个函数
  },  
  }
}
</script>

11.4.5 服务端解决跨域问题

<mvc:cors>  
    <mvc:mapping path="/"
        allowed-origins="*"
        allowed-methods="POST, GET, OPTIONS, DELETE, PUT,PATCH"
        allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
        allow-credentials="true" />
</mvc:cors>

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

Content-Type:application/json;charset=UTF-8
Content-Type:application/x-www-form-urlencoded
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

12.2步骤

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

<template>
  <div class=''>
    vuex
    <p>当前数字: {{number}} is {{evenOrOdd}}</p>
    <button v-on:click="jia()">加</button>
    <button @click="jian()">减</button>
  </div>
</template>
<script>
  export default {
    data() {
      //这里存放数据
      return {
        number:0
      }
    },
    //监听属性 类似于data概念
    computed: {
        evenOrOdd(){
            return this.number%2==0?'偶数':'奇数'
        }
    },
    //方法集合
    methods: {
        jia(){
            this.number++;
        },
        jian(){
            this.number--;
        }
    },
  }
</script>

 12.3 安装

 

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

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

import Vuex from 'vuex'
Vue.use(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: {
​
    }
})
import Vue from 'vue'
import Vuex from 'vuex'
import store from './store'
​
Vue.use(Vuex);
​
new Vue({
  el: '#app',
  store
});
<p>当前数字: {{$store.state.number}} is {{$store.getters.evenOrOdd}}</p>
    <button v-on:click="jia()">加</button>
    <button @click="jian()">减</button>
    methods: {
        jia(){
            // this.$store.commit('jia'); // 同步调用
            this.$store.dispatch('jia'); // 异步调用
        },
        jian(){
          // this.$store.commit('jian'); // 同步调用
          this.$store.dispatch('jian'); // 异步调用
        }
    },
// 不想每次都写 $store.state.xxx    就导入组件然后计算属性    
//  第一步:在MyVuex.vue中的<script>标签下导入
import { mapState, mapGetters, mapActions } from "vuex";
//计算属性 类似于data概念
computed: {
    // evenOrOdd(){
    //     return this.number%2==0?'偶数':'奇数'
    // }
​
    // 第二步
    ...mapState(['number']),
    ...mapGetters(['evenOrOdd'])  // 如果 名字不一致
​
    //...mapGetters({'evenOrOdd':'evenOrOdd222'})    // 如果 名字不一致  vuex 中的 getters 内部的方法名 为 evenOrOdd222   不建议这样写
},
methods: {
    
    // jia(){
    //     this.number++
    // },
    // jian(){
    //     this.number--
    // }
​
    //jia(){
    //    this.$store.dispatch('jia')
    //},
    //jian(){
    //     this.$store.dispatch('jian')
    //}
    ...mapActions(['jia','jian'])
},
<div>
     <!-- <p>当前数字 {{$store.state.number}} is {{$store.getters.evenOrOdd}}</p>      -->
​
    <!-- // 第三步  -->
    <p>当前数字: {{number}} is {{evenOrOdd}}</p>
    <button v-on:click="jia()">加</button>
    <button @click="jian()">减</button>
</div>

12.4解决浏览器刷新Vuex 数据消失问题

  export default {
    name: 'App',
    mounted() {
      window.addEventListener('unload', this.saveState);
    },
    methods: {
      saveState() {
        sessionStorage.setItem('state', JSON.stringify(this.$store.state));
      }
    }
  }
// 设置刷新浏览器number值不改变
state: sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')) : {
  user: {
    name: ''
  }
},

十三、Vue实战增删改查上传文件

工具:前端VSCode,后端Idea,数据库MySql

13.1前端vue的js配置文件

13.1.1src > man.js文件

import Vue from 'vue'
import App from './App'
import router from './router'  //引入路由模块
import axios from 'axios'
import VueAxios from 'vue-axios'
import Vuex from 'vuex'
import store from './store'
import ElementUI from 'element-ui'; // 引入element-ui
import 'element-ui/lib/theme-chalk/index.css';  // element-ui的css样式要单独引入

// Vue.prototype.$axios = axios
Vue.config.productionTip = false
Vue.use(VueAxios, axios)
Vue.use(Vuex)
Vue.use(ElementUI);   // 这种方式引入了ElementUI中所有的组件

/* eslint-disable no-new */
new Vue({
  store,
  el: '#app',
  router, //使用路由模块
  components: { App },
  template: '<App/>'
})

13.1.2src > router > index.js文件

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

export default new Router({
  routes: [
    // 默认跳转登录页面
    {
      path: '/',
      name: 'login',
      component: () => import('@/pages/Login.vue')
    },
    
    // 跳转home主页面
    {
      path: '/home',
      name: 'home',
      component: () => import('@/pages/Home.vue'),
      children: [

        // 跳转用户信息页面
        {
          path: '/home/adminList',
          component: () => import('@/pages/AdminList.vue'),
          children: [

            // 跳转增加用户页面
            {
              path: '/home/adminAdd',
              component: () => import('@/pages/AdminAdd.vue')
            },

            // 跳转更新用户页面
            {
              path: '/home/adminEdit',
              component: () => import('@/pages/AdminEdit.vue')
            },

            //跳转详情页面
            {
              path: '/home/adminDetail',
              component: () => import('@/pages/AdminDetail.vue')
            }
          ]
        },
      ]
    },
  ]
})

13.1.3src > store > index.js文件

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

export default new Vuex.Store({
    // 全局 state 对象,用于保存所有组件的公共数据
    // 设置刷新浏览器username值不改变
    state: sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')) : {
        admin:{username:'',}
      },

    // 实时监听state值的最新状态,注意这里的getters可以理解为计算属性
    getters: {
        //上面的username一个计算属性
        getAdmin(state){
            return state.admin;
        }
    },

    // 定义改变state初始值的方法,这里是唯一可以改变state的地方,缺点是只能同步执行
    mutations: {
        // 在组件中是通过this.$store.commit('updateAdmin')方法来调用mutations
        updateAdmin(state,admin){
            //把admin用户放到state中,也就是放到全局里面
            state.admin = admin;
        },
    },

    // 定义触发mutations里函数的方法,可以异步执行mutations里的函数
    actions: {
        // 在组件中是通过 this.$store.dispatch('asyncUpdateAdmin')来调用 actions
        //取出登录成功时存进去的当前用户信息
        asyncUpdateAdmin(context,admin){
            context.commit("updateAdmin",admin)
        }, 
    },
})

13.1.4src > components >

upload >policy.js文件

// import request from '@/utils/request'
/**
 * 上传文件时请求后台获取凭证
 */
export function policy(_self){
  return new Promise((resolve,reject) => {

    //请求后台签名接口,拿到签名数据
    _self.axios.get("http://localhost:8080/oss/policy",{}).then(({data}) => {
      resolve(data);
    })
  })
}

13.2前端Vue页面及代码

13.2.1App.vue代码

<template>
  <div id="app">
    <div class="content">
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
//导入组件
export default {
  mounted(){
    // // 监听页面属性 页面刷新调用
    // window.addEventListener('unload', this.saveState);
  },
}
</script>
<style>
</style>

13.2.2登录页面及代码

<template>
  <div class="login-box">
    <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <h3 style="font-family:楷体">欢迎登录</h3>
        <el-form-item label="用户名" prop="username">
            <el-input v-model="form.username" placeholder="请输入用户名"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
            <el-input type="password" v-model="form.password" placeholder="请输入密码"></el-input>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="onSubmit('form')">登录</el-button>
        </el-form-item>
    </el-form>
  </div>
</template>

<script>
  export default {
    //import引入的组件需要注入到对象中才能使用
    components: {},
    props: {},
    data() {
      //这里存放数据
      return {
        form: {
            username:"",
            password:"",
        },
        
        // 表单校验
        rules: {
            username:[
                { required:true,message:"请输入用户名",trigger:"blur"},
                {
                    min:3,
                    max:20,
                    message:"长度在3到20个字符",
                    trigger:"blur",
                },
            ],
            password:[
                {required:true,message:"请输入密码",trigger:"blur"},
                {
                    min:3,
                    max:20,
                    message:"长度在3到20个字符",
                    trigger:"blur",
                },
            ],
        },
      };
      
    },
    //监听属性 类似于data概念
    computed: {},
    //监控data中的数据变化
    watch: {},
    //方法集合
    methods: {
        onSubmit(formName) {
            this.$refs[formName].validate((valid) => {
                var vm = this;
                console.log("这是vm")
                console.log(vm.form.username)
                if(valid) {
                    console.log("表单验证通过");
                    // 发送axios请求
                    this.axios({
                        method:"get",
                        url:"http://localhost:8080/admin/login",
                    
                        params:{
                            // 获取的输入框的username值
                            username:vm.form.username, 
                            
                            // 获取的输入框的password值
                            password:vm.form.password, 
                        }
                    }).then(function (resp){
                        console.log(resp.data)
                        // debugger
                        if(resp.data.code == 200){
                            //登录成功,要向vuex中存放user对象
                            var admin = resp.data.data.currentAdmin;
                            
                            //登录成功将token放入本地存储
                            var token = resp.data.data.token;
                            window.localStorage.setItem("token",token);

                            //把登陆成功用户信息存到state中
                            console.log(admin)
                            vm.$store.dispatch("asyncUpdateAdmin",admin);

                            vm.$message({
                                message:"登录成功",
                                type:"success",
                            });
                            setTimeout(function(){
                                //跳转到首页
                                vm.$router.push("/home");
                            },2000);
                        } else {
                            vm.$message.error("用户名或密码错误");
                        }
                    });
                } else {
                    console.log("表单验证失败");
                }
            })
        }
    
    },
  }
</script>
<style scoped>
 /* @import url(); 引入公共css类 */
 .login-box {
    width: 500px;
    height: 300px;
    border:1px solid #dcdfe6;
    margin: 150px auto;
    padding: 20px 50px 20px 30px;
    border-radius: 20px;
    box-shadow: 0px 0px 20px #dcdfe6;
 }
</style>

13.2.3主页面及代码

<template>
  <el-container style="height: 703px; border: 1px solid #eee">
    <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
      <el-menu :default-openeds="['1', '3']">
        <el-submenu index="1">
          <template slot="title"
            ><i class="el-icon-message"></i>导航一</template
          >
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="1-1">
              <router-link to="/home/adminList">用户管理</router-link>
            </el-menu-item>
            <el-menu-item index="1-2">
              <router-link to="/home/roomList">房屋管理</router-link>
            </el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="1-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="1-4">
            <template slot="title">选项4</template>
            <el-menu-item index="1-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
        <el-submenu index="2">
          <template slot="title"><i class="el-icon-menu"></i>导航二</template>
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="2-1">选项1</el-menu-item>
            <el-menu-item index="2-2">选项2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="2-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="2-4">
            <template slot="title">选项4</template>
            <el-menu-item index="2-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
        <el-submenu index="3">
          <template slot="title"
            ><i class="el-icon-setting"></i>导航三</template
          >
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="3-1">选项1</el-menu-item>
            <el-menu-item index="3-2">选项2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="3-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="3-4">
            <template slot="title">选项4</template>
            <el-menu-item index="3-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
      </el-menu>
    </el-aside>

    <el-container>
      <el-header style="text-align: right; font-size: 12px">
        <el-dropdown>
          <i class="el-icon-setting" style="margin-right: 15px"></i>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>查看</el-dropdown-item>
            <el-dropdown-item>新增</el-dropdown-item>
            <el-dropdown-item>删除</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
        <!-- 取出在store中index.js设置的全局属性admin{username:''} -->
        <!-- <span>{{this.$store.state.admin.username}}</span> -->
        <!-- 因为才计算属性计算了,所以通过getters也可以去到 -->
        <span>{{ this.$store.getters.getAdmin.username }}</span>
      </el-header>

      <el-main>
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>
<script>
export default {
  mounted() {
    //定时刷新token
    setInterval(this.refreshToken, 1000*60*60*24);
  },

  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    //这里存放数据
    return {};
  },

  methods: {
    refreshToken() {
      //发送axios请求,获取新的token
      this.axios({
        url:"http://localhost:8080/admin/refreshToken",
      }).then((res) => {
        // 后端生成新的token,响应回来再存起来
        window.localStorage.setItem("token",res.data.data.token)
      })
    },
  },

  //生命周期 创建之前
  beforeCreate() {
 //从本地存储拿到登录成功之后写回来的token,设置全局的headers携带token
    this.axios.defaults.headers.common["token"] = `${localStorage.getItem("token")}`;
  }
};
</script>
<style scoped>
/* @import url(); 引入公共css类 */
.el-header {
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;
}
.el-aside {
  color: #333;
}
</style>

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

<template>
  <div class="">
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
      <el-form-item label="账号名称">
        <el-input v-model="formInline.username" placeholder="用户名"></el-input>
      </el-form-item>
      <el-form-item label="类型">
        <el-select v-model="type" clearable placeholder="请选择">
          <el-option
            v-for="item in types"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          >
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="状态">
        <el-select v-model="flag" clearable placeholder="请选择">
          <el-option
            v-for="item in flags"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          >
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="success" @click="getAdmins()">查询</el-button>
        <el-button type="success" @click="handleClickAdd()">新增</el-button>
      </el-form-item>
    </el-form>
    <el-table :data="admins" stripe style="width: 100%; height: 100%">
      <el-table-column prop="id" label="编号" width="110"></el-table-column>
      <el-table-column
        prop="username"
        label="用户名"
        width="140"
      ></el-table-column>
      <el-table-column prop="password" label="密码" width="140">
      </el-table-column>
      <el-table-column
        prop="flag"
        label="状态"
        width="112"
        :formatter="formatterFlag"
      >
      </el-table-column>
      <el-table-column prop="type" label="类型" width="140"> </el-table-column>
      <el-table-column prop="createTime" label="创建时间" width="150">
      </el-table-column>
      <el-table-column prop="updateTime" label="更新时间" width="150">
      </el-table-column>
      <el-table-column prop="picture" label="头像" width="150">
        <!-- 图片的显示 -->
        <template slot-scope="scope">
          <el-image
            style="width: 100px; height: 100px"
            :src="scope.row.picture"
            @click="previewPic(scope.row.picture)"
            :preview-src-list="srcList"
          >
          </el-image>
        </template>
      </el-table-column>
      <el-table-column fixed="right" label="操作" width="200">
        <template slot-scope="scope">
          <!-- 查看详情 -->
          <el-button
            @click="handleClickInfo(scope.row)"
            icon="el-icon-search"
            circle
          ></el-button>
          <!-- 编辑 -->
          <el-button
            @click="handleClickEdit(scope.row)"
            type="primary"
            icon="el-icon-edit"
            circle
          ></el-button>
          <!-- 删除 -->
          <el-button
            @click="handleClickDelete(scope.row)"
            type="danger"
            icon="el-icon-delete"
            circle
          ></el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="currentPage"
      :page-sizes="[10, 15, 20, 25, 30]"
      :page-size="pageSize"
      :total="total"
      layout="total,sizes,prev,pager,next,jumper"
    >
    </el-pagination>
    <!-- 编辑和添加对话框路由显示 -->
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    //这里存放数据,渲染表格
    return {
      srcList: [
        "https://fuss10.elemecdn.com/8/27/f01c15bb73e1ef3793e64e6b7bbccjpeg.jpeg",
      ], // 图片预览集合,里面放链接
      formInline: {
        username: "", //条件查询用户名
        // type: "", // 查询类型
        // flag: "", // 查询状态
      },
      currentPage: 1, // 默认第一页
      pageSize: 3, // 默认每页显示10条
      total: 0, // 总条目数,默认0
      admins: [],

      //下拉框方式搜索账号类型
      types: [
        {
          value: "",
          label: "全部",
        },
        {
          value: "管理员",
          label: "管理员",
        },
        {
          value: "用户",
          label: "用户",
        },
      ],
      type: "",

      //下拉框方式搜索账号状态
      flags: [
        {
          value: "",
          label: "全部",
        },
        {
          value: "1",
          label: "有效",
        },
        {
          value: "2",
          label: "无效",
        },
        {
          value: "3",
          label: "冻结",
        },
      ],
      flag: "",
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    // 点击图片预览,
    previewPic(url) {
      this.srcList = []; // 清空srcList数组

      //push():在末尾追加一个或多个元素返回新的长度
      //pop():删除并返回数组的最后一个元素
      //shift():删除并返回数组的第一个元素
      this.srcList.push(url);
    },
    //查询用户信息
    getAdmins() {
      console.log(this.flag);
      console.log(this.type);
      this.axios({
        methods: "get",
        url: "http://localhost:8080/admin/list",
        params: {
          page: this.currentPage, // 获取当前页码传给后台
          limit: this.pageSize, // 获取每页条数传给后台
          username: this.formInline.username, // 搜索关键字
          // type: this.formInline.type, // 搜索关键字
          // flag: this.formInline.flag, // 搜索关键字
          type: this.type, // 下拉框方式选择搜索类型
          flag: this.flag, // 下拉框方式选择搜索状态
        },
      })
        .then((res) => {
          if (res.data.code == 0) {
            // 将查询出来的admin集合和总条数赋值给data里面的admins
            this.admins = res.data.data;
            this.total = res.data.count;
            // console.log("查询用户所有信息");
            // console.log(res.data.data);
          } else {
            console.log("用户信息查询失败了!!!");
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    },

    //选择每页条数时触发
    handleSizeChange(val) {
      //每页的size条数变化的时候触发
      console.log(val);
      this.pageSize = val; // 更新pageSize
      this.currentPage = 1;

      //每次改变都再次发送axios方法,请求后台
      this.getAdmins();
    },

    // 点击上/下页时触发
    handleCurrentChange(val) {
      //当前页面变化的时候触发
      this.currentPage = val; // 更新currentPage

      //每次改变都再次发送axios方法,请求后台
      this.getAdmins();
    },

    //数字类型与字符类型转换
    formatterFlag(rows) {
      // 1表示有效  2表示无效  3表示冻结
      return rows.flag == 1 ? "有效" : 2 ? "无效" : "冻结";
    },

    //增加用户
    handleClickAdd() {
      this.$router.push({
        path: "/home/adminAdd",
      });

      //每次改变都再次发送axios方法,请求后台
      this.getAdmins();
    },

    //查看详情
    handleClickInfo(row) {
      this.$router.push({
        path: "/home/adminDetail",
        query: {
          id: row.id,
        },
      });
    },

    //编辑
    handleClickEdit(row) {
      console.log(row);
      //打开编辑页面 前端的页面 跳转的时候可以携带参数
      this.$router.push({
        path: "/home/adminEdit",
        query: {
          id: row.id, // 参数id
        },
      });

      //每次改变都再次发送axios方法,请求后台
      this.getAdmins();
    },

    //删除
    handleClickDelete(row) {
      this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          //点击确定删除进入此处
          this.axios({
            methods: "post",
            url: "http://localhost:8080/admin/delete",
            params: {
              id: row.id,
            },
          }).then((res) => {
            if (res.data.code == 200) {
              this.$message({
                type: "success",
                message: "删除成功!",
              });
              this.getAdmins();
            } else {
              this.$message({
                type: "error",
                message: "删除失败!",
              });
            }
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },
  },

  //生命周期 - 创建完成(可以访问当前this实例)
  created() {},
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    //页面加载完成,查询列表信息
    this.getAdmins();
  },
};
</script>
<style scoped>
/* @import url(); 引入公共css类 */
</style>

13.2.5单个文件上传代码

<template>
  <div class=''>
    <!--:file-list="fileList"带这个参数图片上就不显示文件名,不能点击查看了 -->
    <el-upload 
    action="https://ymk1.oss-cn-hangzhou.aliyuncs.com"
    :data="dataObj"
    list-type="picture"
    :multiple="false"
    :show-file-list="showFileList"
    :before-upload="beforeUpload"
    :on-remove="handleRemove"
    :on-success="handleUploadSuccess"
    :on-preview="handlePreview"
    :on-progress="uploadProgress"
    >
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload_tip">
        只能上传jpg/png文件,且不超过10MB
      </div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
        <!-- 计算属性中fileList第一个元素的url -->
    <img width="100%" :src="fileList[0].url" alt=""/>
    </el-dialog>
  </div>
</template>

<script>
import { policy } from '@/components/upload/policy';
import { getUUID } from '@/utils';
  export default {
    //import引入的组件需要注入到对象中才能使用
    components: {},
    props: {
        //value是AdminAdd.vue中的singleUpload标签里的v-model传过来的
        // v-model是双向绑定的
        value: String, 
    },
    data() {
      //这里存放数据
      return {
        dataObj:{
            policy: "",
            signature: "",
            key: "",
            ossaccessKeyId: "",
            dir: "",
            host: "",
        },
        dialogVisible: false,
      }
    },
    //监听属性 类似于data概念
    computed: {
        
        imageUrl(){
            // value是AdminAdd.vue中的singleUpload标签里的v-model传过来的
            return this.value; 
        },

        //图片名字
        imageName(){
            if(this.value != null && this.value !== ""){
                return this.value.substr(this.value.lastIndexOf("/") + 1);
            } else {
                return null;
            }
        },

        //图片列表 返回是个数组
        fileList(){
            return[
                {
                    // name: this.imageName,
                    url: this.imageUrl,
                },
            ];
        },

        showFileList: {
            get: function (){
                return(
                    this.value !== null && this.value !== "" && this.value !== undefined
                );
            },
            set: function (newValue){},
        },
    },
    //监控data中的数据变化
    watch: {},
    //方法集合
    methods: {

        emitInput(val){
            console.log("上传之后的图片地址:"+val)
            this.$emit("input",val); 
        },

        //移除图片
        handleRemove(file,fileList){
            this.emitInput("");
        },

        //点击文件列表中已上传的文件时的钩子
        handlePreview(file){
            this.dialogVisible = true;
        },

        // 上传文件之前触发
        beforeUpload(file){
            console.log("......")

            let _self = this;
            return new Promise((resolve,reject) => {
                policy(_self) //调的是
                .then((res) => {
                    // policy 策略
                    _self.dataObj.policy = res.data.policy;
                    // signature 签名
                    _self.dataObj.signature = res.data.signature;
                    // oss acess-key 
                    _self.dataObj.ossaccessKeyId = res.data.accessid;
                    // 上传之后的文件 路径+文件名   上传日期/uuid + 文件名
                    _self.dataObj.key = res.data.dir + getUUID() + "_${filename}";
                    // dir 是上传到oss存放的路径
                    _self.dataObj.dir = res.data.dir;
                    // host:http://localhost:8080/oss/policy
                    _self.dataObj.host = res.data.host;
                    resolve(true);
                })
                .catch((err) => {
                    reject(false);
                });
            })
        },

        //上传成功触发 拿到上传的文件的路径,然后回显
        handleUploadSuccess(res,file) {
            console.log("上传成功...");
            console.log(file.name)
            this.showFileList = true;
            this.fileList.pop();
            this.fileList.push({
                name: "success",
                url:
                  this.dataObj.host+
                  "/" +
                  this.dataObj.key.replace("${filename}",file.name),
            });
            // 调用methods中的emitInput(val)方法,将上传后的地址赋值
            this.emitInput(this.fileList[0].url);
        },
    },
  }
</script>
<style scoped>
 /* @import url(); 引入公共css类 */

</style>

13.2.6添加用户页面及代码

<template>
  <div class="">
    <!-- 添加用户对话框 -->
    <el-dialog
      title="添加用户"
      :close-on-click-modal="false"
      :visible.sync="addDialogVisible"
      width="40%"
    >
      <!-- 内容主题 带有变单验证-->
      <el-form
        :model="admin"
        :rules="addAdminFormRules"
        ref="addAdminForm"
        label-position="left"
        label-width="80px"
      >
        <el-form-item label="用户名" prop="username">
          <el-input v-model="admin.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="admin.password"></el-input>
        </el-form-item>
        <el-form-item label="状态" prop="flag">
          <el-select v-model="admin.flag" clearable placeholder="请选择">
            <el-option
              v-for="item in flags"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="类型" prop="type">
          <el-select v-model="admin.type" clearable placeholder="请选择">
            <el-option
              v-for="item in types"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="头像" prop="picture">
          <!-- 调用singleUpload.vue页面 -->
          <!-- v-model将上传成功后的地址传给admin.picture -->
          <single-upload v-model="admin.picture"></single-upload>
        </el-form-item>
        <el-form-item label="操作">
          <el-button type="primary" @click="submitForm('addAdminForm')"
            >提交</el-button
          >
          <el-button @click="cancel()">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>

<script>
import SingleUpload from "../components/upload/singleUpload.vue";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: { SingleUpload },
  props: {},
  data() {
    //这里存放数据
    return {
      //添加用户的表单数据
      admin: {},
      imageUrl: "", //图片地址
      //控制添加用户对话框的显示与隐藏
      addDialogVisible: true,

      //添加表单的验证规则对象
      addAdminFormRules: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
          {
            min: 1,
            max: 10,
            message: "用户名长度在1~10个字符",
            trigger: "blur",
          },
        ],
        password: [
          { required: true, message: "请输入密码", trigger: "blur" },
          { min: 4, max: 15, message: "密码长度在4~15个字符", trigger: "blur" },
        ],
      },

      //下拉框方式添加时选择账号状态
      flags: [
        {
          value: "1",
          label: "有效",
        },
        {
          value: "2",
          label: "无效",
        },
        {
          value: "3",
          label: "冻结",
        },
      ],
      flag: "",

      //下拉框添加时选择账号类型
      types: [
        {
          value: "管理员",
          label: "管理员",
        },
        {
          value: "用户",
          label: "用户",
        },
      ],
      type: "",
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    //formName可用来校验表单信息
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        //当表单验证通过之后才能进入if语句中
        if (valid) {
          this.axios({
            method: "post",
            url: "http://localhost:8080/admin/add",
            data: this.admin,
          })
            .then((res) => {
              debugger;
              console.log("添加用户信息响应数据");
              console.log(res);
              debugger;
              if (res.data.code == 200) {
                // 添加成功给出提示
                this.$message(
                  {
                    type: "success",
                    message: "添加成功!",
                  },
                  1000
                );

                // 通过路由跳转页面
                this.$router.push({
                  path: "/home/adminList",
                });
              } else if (res.data.code == 444) {
                //添加失败给出提示
                this.$message(
                  {
                    type: "error",
                    message: "添加失败",
                  },
                  1000
                );
              }

              // 通过路由跳转页面
              this.$router.push({
                path: "/home/adminList",
              });
            })
            .catch((error) => {
              console.log(error);
            });
        }
      });
    },

    //点击取消路由到adminList页面
    cancel() {
      this.$router.push({
        path: "/home/adminList",
      });
    },
  },
};
</script>
<style scoped>
</style>

13.2.7删除动作及代码

13.2.8编辑页面及代码

<!--  -->
<template>
  <div class="">
     <!-- 添加用户对话框 -->
     <el-dialog title="修改用户" :close-on-click-modal="false"  :visible.sync="editDialogVisible" width="40%">
      <!-- 内容主题 带有变单验证-->
    <el-form
      ref="editAdminForm"
      label-position="left"
      label-width="80px"
      :model="admin"
    >
      <el-form-item label="用户名">
        <el-input v-model="admin.username"></el-input>
      </el-form-item>
      <el-form-item label="密码">
        <el-input v-model="admin.password"></el-input>
      </el-form-item>
      <el-form-item label="状态">
        <!-- <el-input v-model="admin.flag"></el-input> -->
        <el-select v-model="admin.flag" clearable placeholder="请选择">
          <el-option
            v-for="item in flags"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          >
          </el-option>
          </el-select>
      </el-form-item>
      <el-form-item label="类型">
        <!-- <el-input v-model="admin.type"></el-input> -->
        <el-select v-model="admin.type" clearable placeholder="请选择">
          <el-option
            v-for="item in types"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          >
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="头像">
         <!-- 调用singleUpload.vue页面 -->
          <!-- v-model将上传成功后的地址传给admin.picture -->
          <single-upload v-model="admin.picture"></single-upload>

      </el-form-item>
      <el-form-item label="操作">
        <el-button type="primary" @click="submitForm('editAdminForm')"
          >提交</el-button
        >
        <el-button @click="cancel()">取消</el-button>
      </el-form-item>
    </el-form>
  </el-dialog>
  </div>
</template>

<script>
// 这里需要引入singleUpload.vue组件,不然<single-upload>用不了
import SingleUpload from '../components/upload/singleUpload.vue';
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {SingleUpload},
  props: {},
  data() {
    //这里存放数据
    return {
      admin: {},

      //控制修改用户对话框的显示与隐藏
      editDialogVisible: true,

      //下拉框方式回显账号状态
      flags: [
        {
          value: "1",
          label: "有效",
        },
        {
          value: "2",
          label: "无效",
        },
        {
          value: "3",
          label: "冻结",
        },
      ],
      flag: "",
      //下拉框回显账号类型
      types: [
        {
          value: "管理员",
          label: "管理员",
        },
        {
          value: "用户",
          label: "用户",
        },
      ],
      type: "",

    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    //通过用户id查询信息回显
    getAdminById(id) {
      this.axios({
        methods: "get",
        url: "http://localhost:8080/admin/adminDetail",
        params: {
          id: id,
        },
      })
        .then((res) => {
          console.log("通过id查用户信息回显");
          console.log(res);
          //要看懂 后台响应回来的数据格式,拿到咱们要取得的数据
          if (res.data.code == 200) {
            this.admin = res.data.data.admin;
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },

    //提交表单信息触发
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          //发送axios请求
          this.axios.post('http://localhost:8080/admin/update',this.admin)
            .then((res) => {
              console.log("更新提交后台响应的数据");
              console.log(res);
              //要看懂 后台响应回来的数据格式,拿到咱们要取得的数据
              if (res.data.code == 200) {

                // 修改成功给出提示
                this.$message({
                type: "success",
                message: "修改成功!",
              },1000);
                
                // 通过路由跳转页面
                this.$router.push({
                  path:'/home/adminList'
                })

              } else if(res.data.code == 444){
                this.$message({
                type: "error",
                message: "修改失败!",
              },1000);

              // 通过路由跳转页面
              this.$router.push({
                  path:'/home/adminList'
                })

              }
            })
            .catch((error) => {
              console.log(error);
            });
        } else {
          console.log("error submit!");
          return false;
        }
      });
    },
    cancel() {
      this.$router.push({
        path: "/home/adminList",
      });
    },

  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    //获取 路由跳转携带的参数
    let id = this.$route.query.id;

    //获取用户详情
    this.getAdminById(id);
  },
};
</script>
<style scoped>
/* @import url(); 引入公共css类 */
</style>

13.3后端相关配置代码

13.3.1pom.xml文件

<!--—————web开发环境,springMvc相关的依赖 内置tomcat—————-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--—————自带热部署插件—————-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!--—————配置类相关的依赖—————-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--—————lombok可以自动生成 get set—————-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <optional>true</optional>
        </dependency>

        <!--—————测试—————-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--—————mysql依赖—————-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--————— mybatis-plus代码生产器 —————-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!--—————模板 代码生成器使用模板进行生产—————-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>

        <!--—————mybatis-plus 扩展插件 比如 分页插件依赖—————-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--—————mysql依赖—————-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--—————fastJson依赖—————-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>

        <!--————— mybatis-plus插件依赖 —————-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--————— mybatis-plus代码生产器 —————-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!--—————knife4j 依赖—————-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.7</version>
        </dependency>

        <!--—————aop依赖—————-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--——————新版本整合oss文件上传——————-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>aliyun-oss-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!--————————整合jwt token————————-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

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拦截器及跨域配置

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也渲染了对应的视图了,此时可以做一些清理工作");
    }
}
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("/**"); // 拦截所有路径
    }
}

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全局异常和自定义异常

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("系统发生异常,请联系管理员");
    }
}

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;
    }
}

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认证

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;
}

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;
    }
}

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后端响应前端封装的格式

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;
    }
}

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<Object,Object> 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<Object,Object> map){
        this.setData(map);
        return this;
    }
}

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<T> {

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

    private List<T> data;
}

13.3.7AOP切面配置

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配置

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("xx@qq.com")
                        .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为空数据变空串

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<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }
}

13.3.10MybatisPlus

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

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();
    }
}

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

 

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

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<Object,Object> 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<Object, Object>();
            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);
    }
}

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<Object, Object> 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<Admin> adminList(@RequestParam Map<String,Object> map){
        System.out.println(map);
        LayuiPageVo<Admin> 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<Object, Object> 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

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<Admin> adminList(Map<String,Object> 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);
}

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<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username).eq("password", password);
        return adminMapper.selectOne(queryWrapper);
    }

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

        Page<Admin> adminPage = new Page<>(page,limit);
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        // 通过遍历的方式将map集合中不参加模糊查询的键值过滤,将模糊查询放到条件构造器中
        for (Map.Entry<String, Object> 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<Admin> 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<Admin> 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

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<Admin> {
}
举报

相关推荐

0 条评论