vue学习

最近在前后端分离的学习中逐步找到了乐趣,并且不知不觉对前端也产生了一些学习的冲动,经过一系列的咨询,最终决定从学习vue开始。感谢在本文中涉及到的各位博客大神的指导(可以通过本文中的链接进一步学习大神们的思想),站在巨人的肩膀上学习,是一种莫大的幸福。以下内容时基于vue 2.x版本学习。

一、准备知识 

1) vscode快捷键

https://www.cnblogs.com/weihe-xunwu/p/6687000.html

https://blog.csdn.net/ss515896745/article/details/85651912

 2) ES6知识

https://blog.csdn.net/qq_36838191/article/details/87776369

https://www.cnblogs.com/crazymagic/p/9714153.html

注意:相关的几篇博客都要看一下,这位大神写的,就在后面

https://www.jianshu.com/p/7cb40e8d145a

http://www.cnblogs.com/chrischjh/p/4848934.html

3) 基础知识

https://www.cnblogs.com/ppJuan/p/7151000.html

https://www.cnblogs.com/xiaotanke/p/7448383.html

https://www.jianshu.com/p/d9be7c410e10

https://www.cnblogs.com/beyrl-blog/p/8625145.html

https://blog.csdn.net/ccc1929969359/article/details/80798593

https://blog.csdn.net/zbl744949461/article/details/80476888

https://blog.csdn.net/qq_34182808/article/details/86690193

https://blog.csdn.net/qq_32963841/article/details/86063941

https://blog.csdn.net/luoyu6/article/details/80098145

https://www.runoob.com/w3cnote/vue2-start-coding.html

https://www.cnblogs.com/rik28/p/6024425.html

4) 生命周期

http://baijiahao.baidu.com/s?id=1603406094025961442&wfr=spider&for=pc

https://blog.csdn.net/blueblueskyhua/article/details/78024836

5) vue前端核心语法

https://blog.csdn.net/transformer_wsz/article/details/80245962

http://www.cnblogs.com/keepfool/p/5619070.html

https://www.jianshu.com/p/4bd5e745ce95

6) 动态切换组件

https://www.cnblogs.com/tugenhua0707/p/8722003.html

https://www.cnblogs.com/x123811/p/8329289.html

https://blog.csdn.net/shenzhen_zsw/article/details/81128271

https://blog.csdn.net/succuba_ckr/article/details/82817872

7) 前后端交互

https://blog.csdn.net/joyce_lcy/article/details/78873733

https://blog.csdn.net/joyce_lcy/article/details/78871204

https://www.cnblogs.com/daijinxue/p/8309476.html

https://blog.csdn.net/m0_37836194/article/details/80370315

https://www.jianshu.com/p/ba2e92b8b6bd

8)render函数

https://www.cnblogs.com/weichen913/p/9676210.html

举例:

// The Vue build version to load with the `import` command

// (runtime-only or standalone) has been set in webpack.base.conf with an alias.

import Vue from 'vue'

import App from './App'

import router from './router'

import myPage from './components/firstPage'

 

Vue.config.productionTip = false

 

/* eslint-disable no-new */

// 创建vue入口实例

 

// 可行方式1:需要vue将虚拟dom(template)转换成真实dom

// new Vue({

//   el: '#app',//这个#app指的是index.html文件中id为app的div,将组件App加载到这个div下面,这就是mvvm的含义。el就是指向view的。而data就是指向module的

//   router,//使用路由

//   components: { App },

//   template: '<App/>'

// })

 

//可行方式2,使用render函数,不需要vue将虚拟dom转换成真实dom

// new Vue({

//   el: "#app",

//   router,

//   render: h => h(App)

// });

 

//可行方式3:与方式2基本一样,只是箭头函数使用了正规返回格式;方式2中不用{}时代表返回箭头后面的结果

new Vue({

  el: "#app",

  router,

  render: h => { return h(App) }

});

 

//这个并不是一个新的方式,只是用如下的方式指明el和template或者h(组件名)到底加载什么,加载到哪里

// new Vue({

//   el: "#myApp",//这个#app指的是index.html文件中id为myApp的div

//   router,

//   render: h => { return h(myPage) }//加载组件myPage到index.html的id为myApp的div下面

// });

9)watch方法--监听值变化的方法,如果值没有变化,则不会触发

https://www.imooc.com/article/70010

举例:

<template>

  <div>

    <!-- <router-link to="/test">test</router-link> -->

 

    <button @click="changeA">切换到页面A</button>

    <button @click="changeB">切换到页面B</button>

    <button @click="changeC">切换到页面C</button>

    <!-- 这里就是动态加载组件,使用:is方法 -->

    <!-- 一般使用component标签即可 -->

    <!-- is后面的值就是组件名和原来的组件标签用法一致(重点就是大写和-等价问题) -->

    <!-- :is后面的值就是加载对应的组件 -->

    <component :is="changePageIndex"></component>

    <p @click="changeValue">{{myObj}}</p>

    <router-view/>

  </div>

</template>

 

<script>

import ATEst from "./itemPage/APage";

import Btest from "./itemPage/BPage";

import cTest from "./itemPage/CPage";

export default {

  data() {

    return {

      changePageIndex: "A-t-Est",

      myObj: {

        id: "1",

        name: "张三"

      }

    };

  },

  methods: {

    changeA() {

      this.changePageIndex = "A-tEst";

    },

    changeB() {

      this.changePageIndex = "Btest";

    },

    changeC() {

      this.changePageIndex = "c-test";

    },

    changeValue() {

      this.myObj = { id: "2", name: "李四" };

    },

    showNewAndOldValue(newVaule, oldValue) {}

  },

  components: {

    ATEst,

    Btest,

    cTest

  },

  watch: {

    changePageIndex(newVaule, oldVuale) {

      alert(newVaule);

      alert(oldVuale);

    },

    myObj(newValue, oldValue) {

      console.log(newValue);

      alert(newValue.id);

      console.log(oldValue);

      alert(oldValue.name);

    }

  }

};

</script>

 

<style>

/* @import url("../css/myCss.css"); */

</style>

10)this的理解

https://blog.csdn.net/wojiaomaxiaoqi/article/details/78030548

普通函数和简化版函数的this是调用者,如果没有明显的调用者,那么this就是全局对象。

箭头函数的this是当前定义时上下文的对象。

尤其是axios前后端交互的时候一定要注意this的使用。如果使用普通函数或者简化版函数,由于异步的问题,this一般是undefined(异步时不知道调用者是谁)。如果使用箭头函数就是当前这个方法所在的vue组件(文件)对象。如果非要使用普通函数或者简化版函数,那么在定义的时候将this赋值给一个局部变量,作为当前vue组件(文件)的对象。

11)父子组件传值

https://blog.csdn.net/qq_40259641/article/details/81265950

12)计算属性

https://cn.vuejs.org/v2/guide/computed.html

13)状态管理:vuex的简单使用

http://obkoro1.com/2019/01/01/Vuex-%E7%9A%84%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8-%E6%9E%81%E7%AE%80%E4%BD%BF%E7%94%A8/

https://baijiahao.baidu.com/s?id=1618794879569468435&wfr=spider&for=pc

https://blog.csdn.net/Jasons_xie/article/details/89402662

https://vuex.vuejs.org/zh/guide/actions.html

https://www.cnblogs.com/wenbronk/p/9738031.html

https://blog.csdn.net/qq_39523111/article/details/79638614

https://www.cnblogs.com/yeziTesting/p/7182904.html

 

 二、安装与启动(vue 2.x版本,可以自行网上搜索3.x安装与启动方法)

1、 首先去node的官方网站下载稳定版的node,里面已经集成了npm

2、 使用命令行安装vue-cli

1) 切换成淘宝镜像路径

2) 安装vue-cli

3、 下载vscode编辑器,并在该软件内安装vue所需插件,以便用于编写vue工程

4、 创建一个空的文件夹,用于存放vue工程

5、 通过命令行的方式切换到该空的文件夹中,使用命令创建项目

https://www.cnblogs.com/ylboke/p/8393216.html

项目名字不要有大写字母

该回车就回车,回车表示yes继续。

创建过程可能有点慢,要有耐心。

6、 在项目的根目录下,通过命令行的方式启动项目

ps:

至于有哪些命令,可以去package.json文件中查看。

npm run XXX命令

注意:

1) 如果不需要创建新项目,而是已经有的项目,最好打开根目录,通过npm install的方式导入一下依赖;

2) 如果需要其他的依赖,也可以通过npm install XXX的方式导入

3) 依赖都是放在node_modules这个文件夹中

4) 想打开页面看效果,必须使用npm启动了项目

5) Node就是一个用js编写的后端服务,通过nmp启动,给vue工程提供一个可以运行的环境

F12断点调试:可以在事件处理程序中添加断点(debugger)或者在页面调试处打断点(一般vue文件在webpack的src下面)

三、ES6语法

一、ES6基本语法

1、 let和const声明

1) 二者仅在{}块内有效,不具备var同样的变量升级功能

2)let用于定义变量,必须先定义变量后续才可使用(例如赋值等);const用于定义常量,必须直接赋值,不可后续赋值。

2、 模板字符串

3、 箭头函数

类似于lambda表达式,简化方法定义

4、 function的缩写方式

类似于java定义函数

 

 

5、 import和export以及export default

import导入依赖,export对外提供依赖,export default也是对外提供依赖。

export需要通过{变量}的方式对外提供依赖,export default 变量直接对外提供依赖。同样import时也要看是export还是export default提供的,前者需要{},后者不需要{}

6、 解构和…

https://www.cnblogs.com/chrischjh/p/4848934.html

7、 遍历数组(2个方法)和对象map(3个方法)

forArray() {

      const a = [1, "2张三", 3, 4, 5, "6"];

      a.forEach(function(value, index, arr) {

        console.log(`数组下标:${index}`);

        console.log(`元素值:${value}`);

        console.log(`整个数组:${arr}`);

        console.log("================");

      });

      for (let index in a) {

        console.log(`数组下标:${index}`);

        console.log(`元素值:${a[index]}`);

        console.log("================");

      }

    },

    forMap() {

      const map = { a: "A", b: "B", d: "D" };

      for (let key in map) {

        console.log(`键${key}`);

        console.log(`值${map[key]}`);

        console.log("================");

      }

      Object.keys(map).forEach(function(key, index, arr) {

        //本质获取键数组,然后遍历键数组获取结果

        console.log(`键:${key}`);

        console.log(`值:${map[key]}`);

        console.log(`键在数组中的下标:${index}`);

        console.log(`整个键数组:${arr}`);

        console.log("================");

      });

      Object.entries(map).forEach(function(entry, index, arr) {

        console.log(`entry:${entry }`);

        console.log(`键:${entry[0]}`);

        console.log(`值:${entry[1]}`);

        console.log(`entry在entry数组中的下标:${index}`);

        console.log(`整个entry数组:${arr}`);

        console.log("================");

      });

    }

 

四、vue的简单使用

注意:vue工程都是单页面工程,需要的组件按需加载进这个页面里面!!!

1、MVVM模型:双向绑定的理解,vue实例的理解。

Vue实例中el就是指向真实domdom元素,template中的内容就是加载到el指向的地方(view);

Vue实例中的data就是数据(module);

Vue实例就是view-moudle

2、this的使用

https://blog.csdn.net/wojiaomaxiaoqi/article/details/78030548

普通函数和简化版函数的this是调用者,如果没有明显的调用者,那么this就是全局对象。

箭头函数的this是当前定义时上下文的对象。

尤其是axios前后端交互的时候一定要注意this的使用。如果使用普通函数或者简化版函数,由于异步的问题,this一般是undefined(异步时不知道调用者是谁)。如果使用箭头函数就是当前这个方法所在的vue组件(文件)对象。如果非要使用普通函数或者简化版函数,那么在定义的时候将this赋值给一个局部变量,作为当前vue组件(文件)的对象。

 

3、通过vue-cli创建的工程的结构认识,加载顺序如下:

注意:代码一般写在src下面

1)     index.html

2)     main.js

3)     App.vue

4)     Router文件夹下面的index.js

4、组件的使用要点

1)一个组件可以理解成就是一个vue文件,所以需要使用前需要先有这个组件。

2)使用方式

A)script标签下import

B)export defaultcomponents中声明

C)使用该组件

方式一:直接通过组件名标签的方式在template标签中使用

方式二:通过component标签和:is的方式动态加载所需的组件

注意:无论是哪种方式,importcomponents中的组件名字A必须一致(随便起),并且“组件名标签或者:is的动态值”与组件名字A必须相协调(这里指的是大写和-具有互换能力)

3)一个vue组件的常见结构

<template>

<!-- template中只允许有一个标签 -->

<div>

<!-- 这个里面就是该组件需要显示的内容 -->

<!-- 使用vue语法的地方的变量直接写名字即可,标签体使用{{数据变量}}的形式 -->

<!-- 该标签用于展示在router文件夹的index.js中定义的路由 -->

<route-view></route-view>

</div>

</template>

 

<script>

// 在export default的上方可以通过import A from 'vue组件路径'的方式导入所需组件

 

// 一个vue文件中只允许有一个export default

export default {

name:"该vue标签的额名字,其实可以不写",

data(){

return{

message1:"template中需要的数据变量必须在这里定义",

message2:"template中需要的数据变量必须在这里定义"

}

},

methods:{//template中的事件处理程序都要在这里定义

method1(){

 

},

method2(){

 

}

},

components:{

// 这个地方声明需要的组件

},

//下面就是八大常见声明周期函数

beforeCreate(){

//创建vue实例之前

},

created(){

//创建vue实例之后

},

beforeMount(){

//生成真实dom树之前

},

mounted(){

//生成真实dom树之后

//类似window.onloaded和$(function(){});

},

beforeUpdate(){

//数据更新之前

//数据已经改变,只是没有在真实的dom树加载

},

updated(){

//数据更新完毕,真实dom树已经加载

},

beforeDestroy(){

//销毁实例之前

},

destroyed(){

//销毁实例之后

}

}

</script>

 

<style>

/* 这个地方就是用于样式设计,可以在这里直接写,也可以写到一个css文件中,通过@import的方式加载 */

/* 只要是当前vue和其已经加载的组件,都能够使用这里的css样式;更一般的是,该组价真正加载的真实dom所在的文件内的各个dom元素都可以使用这里的css样式*/

</style>

 

对于style更一般情况的举例:

在index.html文件中创建了一个p标签,这个标签使用了组件App.vue中导入的样式,p标签也会显示对应的样式。原因就是App.vue组件其实最终进入了index.html之中,所以其内容(当然包括css样式)也会一并加载到index.html文件中。这样一来,index.html中的dom元素就可以使用App.vue中导入的样式了。

5、routerindex.js文件

注意1:由于vue是单页面,所以route-view就是一个在此页面根据不同路径显示不同内容的标签,路径是什么就在route-view的位置显示对应的组件内容。

注意2:如果创建项目的时候没有创建路由,则可以按照如下方式添加路由。

首先:在项目的根目录cmd后npm install vue-router

然后:在main.js中添加路由内容

接着:在src下创建router文件夹,并创建index.js文件

最后:在index.js文件中添加路由内容

import Vue from 'vue'

import Router from 'vue-router'

import zero from "@/components/RoutePage/zero"

import one from "@/components/RoutePage/one"

 

Vue.use(Router)

 

export default new Router({

    routes: [{

            path: '/',

            name: 'zero',

            component: zero //名字随便起,在route-view标签处显示

        },

        {

            path: '/one',

            name: 'one',

            component: one //名字随便起,在route-view标签处显示

        }

    ]

})

1)它就是需要在route-view标签中显示的导航,根据不同的路径,显示不同的内容。

注意,路由也不一定非要使用在#app这个地方,只要导入route组件,放到创建vue实例中即可在对应的组件内使用路由。

例如:

//这个并不是一个新的方式,只是用如下的方式指明el和template或者h(组件名)到底加载什么,加载到哪里

// new Vue({

// el: "#myApp",//这个#app指的是index.html文件中id为myApp的div

// router,

// render: h => { return h(myPage) }//加载组件myPage到index.html的id为myApp的div下面

// });

2)至于路径可以通过两种方式实现:

A)直接在浏览器输入:ip:端口/#/路径

B)template中添加route-link to=”路径,会在页面生成一个超链接

3)route-view不一定非要在App.vue中,可以在其他组件中。只要有route-view的地方就都会显示(前提该组件已经加载,并且route的路径是正确的(可以通过浏览器查看路径))

4)基本结构

importVuefrom'vue'

importRouterfrom'vue-router'

// 在这里通过import和from的方式导入需要的组件,import 组件名(随便起) from '组件路径'

Vue.use(Router)

export default newRouter({

  routes: [

    {

      path:"路径,例如/表示根路径,再例如/myPage等,这个路径是虚拟的,不是真实的,类似后端的接口路径一样",

      name:"组件名,要和最上面import中的一致",

      component:要和name值一致(不是字符串)

    },

    {

      path:"路径,例如/表示根路径,再例如/myPage等,这个路径是虚拟的,不是真实的,类似后端的接口路径一样",

      name:"组件名,要和最上面import中的一致",

      component:要和name值一致(不是字符串)

    }

  ]

})

6、watch--监听数据变量的变化。如果有变化,进行相应的操作;如果没有变化,不会调用其内部对应的监听方法。

https://www.imooc.com/article/70010

格式:

写在export default

watch:{

       数据变量1(参数1,参数2){//参数1用于自动存储新值,参数2用于存储旧值

       数据变化时的操作方法

}

数据变量2(参数1,参数2){//参数1用于自动存储新值,参数2用于存储旧值

       数据变化时的操作方法

}

}

举例:

<template>

  <div>

    <!-- <router-link to="/test">test</router-link> -->

    <button @click="changeA">切换到页面A</button>

    <button @click="changeB">切换到页面B</button>

    <button @click="changeC">切换到页面C</button>

    <!-- 这里就是动态加载组件,使用:is方法 -->

    <!-- 一般使用component标签即可 -->

    <!-- is后面的值就是组件名和原来的组件标签用法一致(重点就是大写和-等价问题) -->

    <!-- :is后面的值就是加载对应的组件 -->

    <component :is="changePageIndex"></component>

    <p @click="changeValue">{{myObj}}</p>

    <router-view/>

  </div>

</template>

<script>

import ATEst from  "./itemPage/APage";

import Btest from  "./itemPage/BPage";

import cTestfrom  "./itemPage/CPage";

export default {

  data() {

    return {

      changePageIndex:"A-t-Est",

      myObj: {

        id:"1",

        name:"张三"

      }

    };

  },

  methods: {

    changeA() {

      this.changePageIndex = "A-tEst";

    },

    changeB() {

      this.changePageIndex = "Btest";

    },

    changeC() {

      this.changePageIndex = "c-test";

    },

    changeValue() {

      this.myObj = { id:"2", name:"李四" };

    },

    showNewAndOldValue(newVaule, oldValue) {}

  },

  components: {

    ATEst,

    Btest,

    cTest

  },

  watch: {

    changePageIndex(newVaule, oldVuale) {

      alert(newVaule);

      alert(oldVuale);

    },

    myObj(newValue, oldValue) {

      console.log(newValue);

      alert(newValue.id);

      console.log(oldValue);

      alert(oldValue.name);

    }

  }

};

</script>

<style>

/* @import url("../css/myCss.css"); */

</style>

7、计算属性

https://cn.vuejs.org/v2/guide/computed.html

1)目的

{{A}}A最好是一个数据变量,而不是一个关于数据变量表达式。希望有一个数据变量B(成为计算属性)可以作为数据变量表达式,并且数据变量发生变化时,B也同步更新。此时就用到了计算属性。

2)用法

export default中写

computed:{

         计算属性1function(){//这个方法就像是计算属性1getter方法

       return this.数据变量1的运算结果;

}

计算属性2function(){//这个方法就像是计算属性2getter方法

       return this.数据变量2的运算结果;

}

}

 

数据变量1或者数据变量2发生变化,那么计算属性1或者2也会同步更新。计算属性1和计算属性2也可以当成数据变量使用,即{{计算属性1}}{{计算属性2}}

 

举例:

<template>

  <div>

    <h1>这个是普通数据变量apple的个数:{{appleCount}}</h1>

    <h1>这个是普通数据变量apple的个数的2倍:{{beishu}}</h1>

    <h1>这个是普通数据变量apple的个数的3倍:{{beishu2}}</h1>

    <inputtype="text"v-model="appleCount">

  </div>

</template>

<script>

export default {

  data() {

    return {

      appleCount:1

    };

  },

  computed: {

    beishu:function() {

      let a;

      a = this.appleCount * 2;

      return a;

    },

    beishu2:function() {

      let a;

      a = this.appleCount * 3;

      return a;

    }

  }

};

</script>

<style>

</style>

8、使用js方法

1)创建一个js文件,在这个js文件中定义相应的方法,并对外提供接口

2)在需要的js文件中的方法的地方导入该组件

3)使用导入的组件方法

9、父子组件传值

https://blog.csdn.net/qq_40259641/article/details/81265950

1)概念

A)    父组件:导入(import)其他组件的组件就是父组件

B)    子组件:被import的组件就是子组件

2)基本步骤

A)父组件给子组件传值

前提:子组件已经被父组件导入,且components中声明

步骤1:子组件在export default中的props:[“要接收父组件的值的名称A]

步骤2:在父组件中使用该子组件的标签中添加属性A的值

B)子组件给父组件传值

前提:子组件已经被父组件导入,且components中声明

步骤1:子组件在methods中定义事件处理程序,主要使用this.$emit(“父组件的监听方法F”,要传递给父组件的值);

步骤2:子组件绑定这个事件处理程序

步骤3:在父组件使用该子组件的标签中@F=“处理方法B

步骤4:在父组件的methods中定义处理方法B(data){处理子组件传递值data的方法}

              举例:

              (父组件:myInit.vue

<template>

  <div>

    <h1>父子页面传值测试页面</h1>

    <button @click="changeColor('blue')">blue</button>

    <button @click="changeColor('red')">red</button>

    <button @click="changeColor('green')">green</button>

    <br>

    <button @click="changeSonPage('son1')">son1</button>

    <button @click="changeSonPage('son2')">son2</button>

    <button @click="changeSonPage('son3')">son3</button>

<component :is="sonName" :zdyColor1="parentDataWithColor1" :zdyColor2="parentDataWithColor2" :zdyColor3="parentDataWithColor3" :zdyId="parentDataWithId" @mySon1Function="mySonFunction" @mySon2Function="mySonFunction"  @mySon3Function="mySonFunction" @allSonFunction="mySonFunction"></component>

  </div>

</template>

<script>

import son1 from  "./son/son1";

import son2 from  "./son/son2";

import son3 from  "./son/son3";

export default {

  data() {

    return {

      parentDataWithColor1:"",

      parentDataWithColor2:"",

      parentDataWithColor3:"",

      sonName:"son1",

      parentDataWithId:""

    };

  },

  components: {

    son1,

    son2,

    son3

  },

  methods: {

    changeColor(color) {

      constidValue = `${color}_id`;

      if (this.sonName === "son1") {

        this.parentDataWithId = newDate();

        this.parentDataWithColor1 = { color:color, id:idValue };

      } elseif (this.sonName === "son2") {

        this.parentDataWithColor2 = { color:color, id:idValue };

      } elseif (this.sonName === "son3") {

        this.parentDataWithColor3 = { color:color, id:idValue };

      }

    },

    changeSonPage(sonNum) {

      this.sonName = sonNum;

    },

    mySonFunction(data) {

      alert(data.id);

      alert(data.name);

    }

  }

};

</script>

<style>

</style>

(子组件1son1.vue

<template>

  <div>

    <h1 :style="{color:zdyColor1.color}" :id="zdyId">这是第一个子组件页面</h1>

    <button @click="son1ToParent">son1ToParent</button>

  </div>

</template>

<script>

export default {

  props: ["zdyColor1", "zdyId"],

  watch: {

    zdyColor1(v1, v2) {

      if (!!v1) {

        alert(`${v1.color},${v1.id}`);

      }

      if (!!v2) {

        alert(`${v2.color},${v2.id}`);

      }

    },

    zdyId(v1, v2) {

      alert(`zdyId=${v1}`);

    }

  },

  methods: {

    son1ToParent() {

      constdataWithSon1 = { id:1, name:"son1" };

      this.$emit("mySon1Function", dataWithSon1);

      this.$emit("allSonFunction", dataWithSon1);

    }

  }

};

</script>

<style>

</style>

(子组件2son2.vue

<template>

  <div>

    <h1 :style="{color:zdyColor2.color}">这是第二个子组件页面</h1>

    <button @click="son2ToParent">son2ToParent</button>

  </div>

</template>

<script>

export default {

  props: ["zdyColor2"],

  watch: {

    zdyColor2(v1, v2) {

      if (!!v1) {

        alert(`${v1.color},${v1.id}`);

      }

      if (!!v2) {

        alert(`${v2.color},${v2.id}`);

      }

    }

  },

  methods: {

    son2ToParent() {

      constdataWithSon2 = { id:2, name:"son2" };

      this.$emit("mySon2Function", dataWithSon2);

      this.$emit("allSonFunction", dataWithSon2);

    }

  }

};

</script>

<style>

</style>

(子组件3son3.vue

<template>

  <div>

    <h1 :style="{color:zdyColor3.color}">这是第三个子组件页面</h1>

    <button @click="son3ToParent">son3ToParent</button>

  </div>

</template>

<script>

export default {

  props: ["zdyColor3"],

  watch: {

    zdyColor3(v1, v2) {

      if (!!v1) {

        alert(`${v1.color},${v1.id}`);

      }

      if (!!v2) {

        alert(`${v2.color},${v2.id}`);

      }

    }

  },

  methods: {

    son3ToParent() {

      constdataWithSon3 = { id:3, name:"son3" };

      this.$emit("mySon3Function", dataWithSon3);

      this.$emit("allSonFunction", dataWithSon3);

    }

  }

};

</script>

<style>

</style>

10、         状态管理:Vuex的简单使用

http://obkoro1.com/2019/01/01/Vuex-%E7%9A%84%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8-%E6%9E%81%E7%AE%80%E4%BD%BF%E7%94%A8/

https://baijiahao.baidu.com/s?id=1618794879569468435&wfr=spider&for=pc

https://blog.csdn.net/Jasons_xie/article/details/89402662

https://vuex.vuejs.org/zh/guide/actions.html

https://www.cnblogs.com/wenbronk/p/9738031.html

https://blog.csdn.net/qq_39523111/article/details/79638614

https://www.cnblogs.com/yeziTesting/p/7182904.html

1)目的:全局管理变量,方便各组件之间传值

2)使用方法(不使用module

A)在项目根目录下,通过命令行的方式安装vuex插件

npm install vuex --save

B)src下面创建store文件夹,在store下面创建index.js文件

C)store/index.js文件中编写需要的内容

// 引入vue 和 vuex

import Vue from 'vue';

import Vuex from 'vuex';

Vue.use(Vuex); // 使用vuex插件,跟router一样

const store = new Vuex.Store({

    state: {//这里存放全局变量,要是想改变其值,只能使用actions或者mutations,没有setters方法

        countPage1:1,

        countPage2:2

    },

    getters: {//这里存放state的对应属性的计算属性,state就是全局变量,必须这样写方法

        getDoubleCountWithPage1(state) {

            let a;

            a = state.countPage1 * 2;

            return a ;

        },

        getDoubleCountWithPage2(state) {

            let a;

            a = state.countPage2 * 2;

            return a;

        }

    },

    actions: {//这里用于其他组件调用方法

        addPage1(obj, data) {//obj是一个json对象,我们主要使用其属性commit,而data就是参数值

            obj.commit("realAddPage1", data);

        },

        dePage1({ commit }, data) {//与写成obj原理一样,只是这里通过解构思想,直接获取到了commit

            commit("realDePage1", data);

        },

        addPage2(obj, data) {

            obj.commit("realAddPage2", data);

        },

        dePage2({ commit }, data) {

            commit("realDePage2", data);

        }

    },

    mutations: {//操作state中属性的真正方法

        realAddPage1(state, data) {//state就是全局变量,data就是参数值

            state.countPage1 += data;

        },

        realDePage1(state, data) {

            state.countPage1 -= data;

        },

        realAddPage2(state, data) {

            state.countPage2 += data;

        },

        realDePage2(state, data) {

            state.countPage2 -= data;

        }

    }

});

export default store;

D)main.js中导入这个组件,放到vue实例中。

此时其他组件就可以通过this.$store.XXX.XXX的方式使用这个全局变量了。

// The Vue build version to load with the `import` command

// (runtime-only or standalone) has been set in webpack.base.conf with an alias.

import Vue from 'vue'

import App from './App'

import router from './router'

import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */

newVue({

  el:'#app',

  router,

  store,

  components: { App },

  template:'<App/>'

})

E)如果觉得每次都通过this.$store.XXX的方式很麻烦,可以在组件内通过映射别名的方式实现。主要就是使用mapStatemapGettersmapActions

      需要先import三个对象

注意原名要用引号引起来;

并且必须保证使用别名的时候按照如下规则:

方法内使用别名,前面加this.,否则会报未定义的异常;

标签体内使用别名(例如事件处理程序直接调用别名方法),不要加this.,否则会报未定义的异常;

 

 

       例如:

<template>

  <div>

    <!-- 方式一:直接使用$store -->

    <!-- <h1>页面2的变量为{{this.$store.state.countPage2}}</h1> -->

    <!-- 方式二:使用映射别名的方法 -->

    <h1>页面2的变量为{{twoVal}}</h1>

    <!-- <h1>页面1的变量的二倍为{{this.$store.getters.getDoubleCountWithPage1}}</h1> -->

    <h1>页面1的变量的二倍为{{beishuWithPage1}}</h1>

    <!-- <button @click="addPgaeOne">增加页面1的变量值,默认写死为10</button> -->

    <button @click="finalAddPage1(10)">增加页面1的变量值,默认写死为10</button>

    <!-- <button @click="dePgaeOne">减少页面1的变量值,默认写死为5</button> -->

    <button @click="finalDePage1(5)">减少页面1的变量值,默认写死为5</button>

  </div>

</template>

<script>

import { mapState, mapGetters, mapActions } from"vuex";

export default {

  methods: {

    addPgaeOne() {

      this.$store.dispatch("addPage1", 10); //参数1是actions中的方法名,参数2是actions中需要的data参数值

    },

    dePgaeOne() {

      this.$store.dispatch("dePage1", 5);

    },

    ...mapActions({

      //this.finalAddPage1(5)等价于this.$store.dispatch("addPage1",5)

      finalAddPage1:"addPage1",

      finalDePage1:"dePage1"

    })

  },

  computed: {

    ...mapState({

      //方式一::this.twoVal等价于this.$store.state.countPage2

      //   twoVal: state => state.countPage2

      //方式二:this.twoVal等价于this.$store.state.countPage2

      twoVal:"countPage2"

    }),

    ...mapGetters({

      //this.beishuWithPage1等价于this.$store.getters.getDoubleCountWithPage1

      beishuWithPage1:"getDoubleCountWithPage1"

    })

  }

};

</script>

<style>

</style>

3)使用module--store/index.js太过于冗余了,而且明显就是有几个不同的模块各自使用各自的状态(全局变量),此时就可以考虑通过module的方式解决冗余问题。

A)    安装vuex

B)    编写模块js--可以先创建一个文件夹,然后在文件夹下面创建一个index.js。在这个index.js里面写stategettersactionsmutations并导出,且注意命名空间为true

        C)    将这个模块导入store/index.js之中

             

        D)    store/index.js导入main.js

            

E)    在需要的组件只用即可

使用变量

this.$store.state.模块名key.模块变量值

使用getters

this.$store.getters[‘模块名key/模块的getters中的方法名’]

使用actions

this.$store.dispatch(‘模块名key/模块的actions中的方法名’,参数值)

F)    如果需要别名,和2)中不同的就是需要给出是哪个模块的,mapStatemapGettersmapActions可以写多组

注意原名要用引号引起来

                     

11、前后端交互—axios组件的使用

https://www.cnblogs.com/chjlxxc/p/frontend001.html

https://blog.csdn.net/joyce_lcy/article/details/78873733

https://blog.csdn.net/joyce_lcy/article/details/78871204

https://www.cnblogs.com/daijinxue/p/8309476.html

https://blog.csdn.net/m0_37836194/article/details/80370315

https://www.jianshu.com/p/ba2e92b8b6bd

https://www.cnblogs.com/ylyw/p/7692071.html

1)     安装axios组件

npm install axios –save –dev

    2)   配置axios全局参数

      //注意:axios组件的defaults的相关配置是全局配置,一旦一个地方配置了,无论哪里引入这个axios组件,则默认的情况都是使用如下配置;

      //这也就是这个Axios配置文件存在的原因;

      //但是一定注意这个配置文件需要引用到可以用的地方,否则不起作用;例如在App.vue中导入

      //如果不配置如下内容,那么对于post和put请求而言前端将传递json字符串,后端的post和put接口需要通过@RequestBody接收;对于get和delete无所谓。

 

      //建议进行如下配置

 

      //使用@RequestBody接收的好处是最后传给后端的是一个json字符串,所以后端可以直接解析自定义的类型

      //而不使用@RequestBody的时候,后端是不识别前端传递的自定义类型,需要将自定义的类型转换成字符串(JSON.stringify(json对象)),后端在方法体中对字符串进行json转换成自定义类型对象

 

      import MyAxios from 'axios';

      import qs from 'qs';

 

      //一个通用的前缀,用于设置请求URL中相同的部分,所有axios的方法就会自动加上这个前缀url

      MyAxios.defaults.baseURL = "http://10.17.66.19:7070/vue-sb";

 

      //以下三个配置就是用于设置请求方式和内容

      MyAxios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';//必须要配置一下

 

      MyAxios.defaults.transformRequest = [function (data, headers) {//data是请求数据,headers是请求头

      //上传Excel文件到后端

      if (!!headers['Content-Type'] && (headers['Content-Type'].indexOf('multipart/form-data') > -1)) {

              return data;

       }

      //上传json字符串到后端,后端需要用@RequestBody接收请求参数

       if (!!headers['Content-Type'] && headers['Content-Type'].indexOf("application/json") > -1) {

        return JSON.stringify(data);

      }

      //后端可以按照不同的json对象的方式直接接收,不需要@RequestBody修饰请求参数

      return qs.stringify(data);

 

       //对比qs.stringify与JSON.stringify两个方法的对比

      // var a = { name: 'hehe', age: 10 };

      // qs.stringify(a) 结果:'name=hehe&age=10';理解:qs.stringify将json对象序列化成URL的形式,以&进行拼接

      // JSON.stringify(a) 结果:'{"name":"hehe","age":10}';理解:JSON.stringify将json对象转换成json字符串

      }];

      //对于post和put请求,请求参数的格式默认就是application/x-www-form-urlencoded;charset=UTF-8;后端按照json对象的方式接收,不需要@RequestBody

      //如果需要修改参数格式,可以在具体的请求中传入配置参数,例如上传Excel文件的方法

      //无论是否配置请求参数格式类型,对于get和delete请求,直接参数接收或者POJO类接收都是可以的,因为参数在URL的后面通过?和&连接,不在请求体内

      MyAxios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; 

    3)  在一个可用的地方引入配置js文件

    

    4)  在需要使用axios组件的地方import组件

         import 自定义组件名 from ‘axios’

5)    使用axiosgetpostputdeleteaxios等相关方法即可(前后请求参数名称要一致

     A)  json字符串版--对于postput需要使用@RequestBody,可以解析自定义类型对象。

      (jsonStrParam.vue)

<template>

<div>

<button @click="get()">get_str</button>

<button @click="post()">post_str</button>

<button @click="put()">put_str</button>

<button @click="del()">delete_str</button>

</div>

</template>

<script>

import MyAxiosWithStrParam from 'axios';

export default {

methods: {

get() {

const url = "/jsonstr/get.do?id=5&name=刘德华&address=九龙";

MyAxiosWithStrParam.get(url).then((res) => {

let { data } = res;

console.log(data);

});

},

post() {

const myParams = {};

myParams["id"] = "6";

myParams["name"] = "张学友";

myParams["address"] = "新界";

const myParams2 = {};

myParams2["id"] = "6_2";

myParams2["name"] = "张学友2";

myParams2["address"] = "新界2";

const myParams3 = { "id": "6_3", "name": "张学友3", "address": "新界3" };

myParams2["jsonParam3"] = myParams3;

myParams["jsonStrParam2"] = myParams2;

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonstr/post.do";

let config = {

headers: {

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

}

}

MyAxiosWithStrParam.post(url, myParams, config).then((res => {

let { data } = res;

console.log(data);

}));

},

put() {

const myParams = {};

myParams["id"] = "7";

myParams["name"] = "黎明";

myParams["address"] = "将军澳";

const myParams2 = {};

myParams2["id"] = "7_2";

myParams2["name"] = "黎明2";

myParams2["address"] = "将军澳2";

const myParams3 = { "id": "7_3", "name": "黎明3", "address": "将军澳3" };

myParams2["jsonParam3"] = myParams3;

myParams["jsonStrParam2"] = myParams2;

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonstr/put.do";

let config = {

headers: {

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

}

}

MyAxiosWithStrParam.put(url, myParams, config).then((res => {

let { data } = res;

console.log(data);

}));

},

del() {

const url = "/jsonstr/delete.do?id=8&name=郭富城&address=油麻地";

MyAxiosWithStrParam.delete(url).then((res) => {

let { data } = res;

console.log(data);

});

}

},

}

</script>

<style></style>  

      (jsonStrParamByOneAxios.vue)

<template>

<div>

<button @click="get()">get_str</button>

<button @click="post()">post_str</button>

<button @click="put()">put_str</button>

<button @click="del()">delete_str</button>

</div>

</template>

<script>

import MyAxiosWithStrParam from 'axios';

export default {

methods: {

get() {

const url = "/jsonstr/get.do?id=5&name=刘德华&address=九龙";

MyAxiosWithStrParam({

method: "get",

url: url

}).then((res) => {

let { data } = res;

console.log(data);

});

},

post() {

const myParams = {};

myParams["id"] = "6";

myParams["name"] = "张学友";

myParams["address"] = "新界";

const myParams2 = {};

myParams2["id"] = "6_2";

myParams2["name"] = "张学友2";

myParams2["address"] = "新界2";

const myParams3 = { "id": "6_3", "name": "张学友3", "address": "新界3" };

myParams2["jsonParam3"] = myParams3;

myParams["jsonStrParam2"] = myParams2;

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonstr/post.do";

let headers = { 'Content-Type': 'application/json;charset=UTF-8' };

MyAxiosWithStrParam({

method: "post",

url: url,

data: myParams,

headers: headers

}).then((res => {

let { data } = res;

console.log(data);

}));

},

put() {

const myParams = {};

myParams["id"] = "7";

myParams["name"] = "黎明";

myParams["address"] = "将军澳";

const myParams2 = {};

myParams2["id"] = "7_2";

myParams2["name"] = "黎明2";

myParams2["address"] = "将军澳2";

const myParams3 = { "id": "7_3", "name": "黎明3", "address": "将军澳3" };

myParams2["jsonParam3"] = myParams3;

myParams["jsonStrParam2"] = myParams2;

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonstr/put.do";

let headers = { 'Content-Type': 'application/json;charset=UTF-8' };

MyAxiosWithStrParam({

method: "put",

url: url,

data: myParams,

headers: headers

}).then((res => {

let { data } = res;

console.log(data);

}));

},

del() {

const url = "/jsonstr/delete.do?id=8&name=郭富城&address=油麻地";

MyAxiosWithStrParam({

method: "delete",

url: url

}).then((res) => {

let { data } = res;

console.log(data);

});

}

},

}

</script>

<style></style>

        (jsonStrParam.java)

package com.lennar.testvuespringboot.JsonStrParam;

import lombok.Data;

import java.util.List;

import java.util.Map;

@Data

public class JsonStrParam {

private String id;

private String name;

private String address;

private JsonStrParam2 jsonStrParam2;

private List<String> testList;

private Map<String,String> testMap;

}

        (jsonStrParam2.java)

package com.lennar.testvuespringboot.JsonStrParam;

import lombok.Data;

@Data

public class JsonStrParam2 {

private String id;

private String name;

private String address;

private JsonStrParam3 jsonParam3;

 

}

        (jsonStrParam3.java)

package com.lennar.testvuespringboot.JsonStrParam;

import lombok.Data;

@Data

public class JsonStrParam3 {

private String id;

private String name;

private String address;

}

        (jsonStrController.java)

package com.lennar.testvuespringboot.JsonStrParam;

import com.lennar.testvuespringboot.JsonObjectParam.JsonParam;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

import java.util.Map;

@RestController

@RequestMapping("/jsonstr")

public class JsonStrController {

@GetMapping("/get.do")

public Map<String, Object> get(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("get_直接获取的id", id);

map.put("get_直接获取的name", name);

map.put("get_从JsonParam中获取的Address", jsonParam.getAddress());

return map;

}

@PostMapping("/post.do")

public Map<String, Object> post(@RequestBody JsonStrParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("post_从JsonStrParam中获取的id", jsonParam.getId());

map.put("post_从JsonStrParam中获取的name", jsonParam.getName());

map.put("post_从JsonStrParam中获取的Address", jsonParam.getAddress());

map.put("post_从JsonStrParam中获取的jsonParam2", jsonParam.getJsonStrParam2());

map.put("post_从JsonStrParam中获取的testMap", jsonParam.getTestMap());

map.put("post_从JsonStrParam中获取的testList", jsonParam.getTestList());

return map;

}

@PutMapping("/put.do")

public Map<String, Object> put(@RequestBody JsonStrParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("put_从JsonStrParam中获取的id", jsonParam.getId());

map.put("put_从JsonStrParam中获取的name", jsonParam.getName());

map.put("put_从JsonStrParam中获取的Address", jsonParam.getAddress());

map.put("put_从JsonStrParam中获取的jsonParam2", jsonParam.getJsonStrParam2());

map.put("put_从JsonStrParam中获取的testMap", jsonParam.getTestMap());

map.put("put_从JsonStrParam中获取的testList", jsonParam.getTestList());

return map;

}

@DeleteMapping("/delete.do")

public Map<String, Object> delete(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("delete_直接获取的id", id);

map.put("delete_直接获取的name", name);

map.put("delete_从JsonParam中获取的Address", jsonParam.getAddress());

return map;

}

}

     B) json对象版--不需要使用@RequestBody,但是自定义类型对象需要在前端通过JSON.stringify(json对象)转换成json字符串,然后后端使用String类型接收,进而后端在方法体内通过AlibabaJSONObject方法解析json字符串为json对象。

      (jsonObjParam.vue)

<template>

<div>

<button @click="get()">get_obj</button>

<button @click="post()">post_obj</button>

<button @click="put()">put_obj</button>

<button @click="del()">delete_obj</button>

</div>

</template>

<script>

import MyAxios from 'axios';

export default {

methods: {

get() {

const url = "/jsonobj/get.do?id=1&name=张三&address=唐山";

MyAxios.get(url).then((res) => {

let { data } = res;

console.log(data);

});

},

post() {

const myParams = {};

myParams["id"] = "2";

myParams["name"] = "李四";

myParams["address"] = "北京";

const myParams2 = {};

myParams2["id"] = "2_2";

myParams2["name"] = "李四2";

myParams2["address"] = "北京2";

myParams["jsonParam2"] = JSON.stringify(myParams2);//对于json对象,后端无法直接解析成对象,需要转换成json字符串,后端方法中解析

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonobj/post.do";

MyAxios.post(url, myParams).then((res => {

let { data } = res;

console.log(data);

}));

},

put() {

const myParams = {};

myParams["id"] = "3";

myParams["name"] = "王五";

myParams["address"] = "上海";

const myParams2 = {};

myParams2["id"] = "3_2";

myParams2["name"] = "王五2";

myParams2["address"] = "上海2";

myParams["jsonParam2"] = JSON.stringify(myParams2);//对于json对象,后端无法直接解析成对象,需要转换成json字符串,后端方法中解析

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonobj/put.do";

MyAxios.put(url, myParams).then((res => {

let { data } = res;

console.log(data);

}));

},

del() {

const url = "/jsonobj/delete.do?id=4&name=赵六&address=杭州";

MyAxios.delete(url).then((res) => {

let { data } = res;

console.log(data);

});

}

},

}

</script>

<style></style>

      (jsonObjParamByOneAxios.vue)

<template>

<div>

<button @click="get()">get_obj</button>

<button @click="post()">post_obj</button>

<button @click="put()">put_obj</button>

<button @click="del()">delete_obj</button>

</div>

</template>

<script>

import MyAxios from 'axios';

export default {

methods: {

get() {

const url = "/jsonobj/get.do?id=1&name=张三&address=唐山";

MyAxios({

method: "get",

url: url

}).then((res) => {

let { data } = res;

console.log(data);

});

},

post() {

const myParams = {};

myParams["id"] = "2";

myParams["name"] = "李四";

myParams["address"] = "北京";

const myParams2 = {};

myParams2["id"] = "2_2";

myParams2["name"] = "李四2";

myParams2["address"] = "北京2";

myParams["jsonParam2"] = JSON.stringify(myParams2);//对于json对象,后端无法直接解析成对象,需要转换成json字符串,后端方法中解析

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonobj/post.do";

MyAxios({

method: "post",

url: url,

data: myParams

}).then((res => {

let { data } = res;

console.log(data);

}));

},

put() {

const myParams = {};

myParams["id"] = "3";

myParams["name"] = "王五";

myParams["address"] = "上海";

const myParams2 = {};

myParams2["id"] = "3_2";

myParams2["name"] = "王五2";

myParams2["address"] = "上海2";

myParams["jsonParam2"] = JSON.stringify(myParams2);//对于json对象,后端无法直接解析成对象,需要转换成json字符串,后端方法中解析

myParams["testMap"] = { "A": "a", "B": "b" };

myParams["testList"] = ["id1", "id2", "id3"];

 

const url = "/jsonobj/put.do";

MyAxios({

method: "put",

url: url,

data: myParams

}).then((res => {

let { data } = res;

console.log(data);

}));

},

del() {

const url = "/jsonobj/delete.do?id=4&name=赵六&address=杭州";

MyAxios({

method: "delete",

url: url

}).then((res) => {

let { data } = res;

console.log(data);

});

}

},

}

</script>

<style></style>

      (jsonParam.java)

package com.lennar.testvuespringboot.JsonObjectParam;

import lombok.Data;

import java.util.List;

import java.util.Map;

@Data

public class JsonParam {

private String address;

private String jsonParam2;//json对象的方式是无法直接获取自定义对象的

private Map<String,String> testMap;

private List<String> testList;

}

      (jsonParam2.java)

package com.lennar.testvuespringboot.JsonObjectParam;

import lombok.Data;

@Data

public class JsonParam2 {

private String id;

private String name;

private String address;

}

      (jsonObjectController.java)

package com.lennar.testvuespringboot.JsonObjectParam;

import com.alibaba.fastjson.JSONObject;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

import java.util.Map;

@RestController

@RequestMapping("/jsonobj")

public class JsonObjectController {

@GetMapping("/get.do")

public Map<String, Object> get(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("get_直接获取的id", id);

map.put("get_直接获取的name", name);

map.put("get_从JsonParam中获取的Address", jsonParam.getAddress());

return map;

}

@PostMapping("/post.do")

public Map<String, Object> post(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("post_直接获取的id", id);

map.put("post_直接获取的name", name);

map.put("post_从JsonParam中获取的Address", jsonParam.getAddress());

map.put("post_从JsonParam中获取的jsonParam2", JSONObject.parseObject(jsonParam.getJsonParam2(), JsonParam2.class));

map.put("post_从JsonParam中获取的testMap", jsonParam.getTestMap());

map.put("post_从JsonParam中获取的testList", jsonParam.getTestList());

return map;

}

@PutMapping("/put.do")

public Map<String, Object> put(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("put_直接获取的id", id);

map.put("put_直接获取的name", name);

map.put("put_从JsonParam中获取的Address", jsonParam.getAddress());

map.put("put_从JsonParam中获取的jsonParam2", JSONObject.parseObject(jsonParam.getJsonParam2(), JsonParam2.class));

map.put("put_从JsonParam中获取的testMap", jsonParam.getTestMap());

map.put("put_从JsonParam中获取的testList", jsonParam.getTestList());

return map;

}

@DeleteMapping("/delete.do")

public Map<String, Object> delete(String id, String name, JsonParam jsonParam) {

Map<String, Object> map = new HashMap<>();

map.put("delete_直接获取的id", id);

map.put("delete_直接获取的name", name);

map.put("delete_从JsonParam中获取的Address", jsonParam.getAddress());

return map;

}

}

     C) Excel文件版--使用MutipartFile接收

      (ExcelParam.vue)

<template>

  <div>

<form>

  <input type="file" name="fileup" id="uploadEventFile" v-on:change="fileChange($event)" style="display:none" />

</form>

<button @click="importData($event)">导入excel</button>

<button><a href="http://127.0.0.1:7070/vue-sb/excel/writeExcel.do" style="text-decoration: none">下载</a></button>

  </div>

</template>

 

<script>

  import $ from "jquery";

  import excelAxios from "axios";

 

  export default {

methods: {

  // 导入excel文件

  importData: function (event) {

event.preventDefault();

$("#uploadEventFile").trigger("click")

  },

  fileChange: function (el) {

el.preventDefault();//取消默认行为

let uploadEventFile = $("#uploadEventFile").val();

this.file = el.target.files[0];

if (uploadEventFile == '') {

  alert("请择excel,再上传");

} else if (uploadEventFile.lastIndexOf(".xls") > 0 || uploadEventFile.lastIndexOf(".XLS") > 0) {

  let formData = new FormData();

  // 向 formData 对象中添加文件

  formData.append('file', this.file);//Excel文件,key必须是file,后端接收的时候也必须是file

  formData.append('sheetIndex', 1);//自定义的属性,读取Excel的哪个sheet表,sheetIndex从1开始

  formData.append('startReadRowNumber', 1);//自定义的属性,从第几行开始读取,startReadRowNumber从0开始

  // url为对应的后端接口

  const url = "/excel/readExcel.do";

  let config = {

headers: {

  'Content-Type': 'multipart/form-data'

}

  }

  excelAxios.post(url, formData, config).then((res) => {

let { data } = res;

console.log(data);

  }).catch();

} else {

  alert("只能上传excel文件");

}

  }

}

  }

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->

<style scoped>

</style>

      (ExcelParamByOneAxios.vue)

<template>

  <div>

<form>

  <input type="file" name="fileup" id="uploadEventFile" v-on:change="fileChange($event)" style="display:none" />

</form>

<button @click="importData($event)">导入excel</button>

<button><a href="http://127.0.0.1:7070/vue-sb/excel/writeExcel.do" style="text-decoration: none">下载</a></button>

  </div>

</template>

 

<script>

  import $ from "jquery";

  import excelAxios from "axios";

 

  export default {

methods: {

  // 导入excel文件

  importData: function (event) {

event.preventDefault();

$("#uploadEventFile").trigger("click")

  },

  fileChange: function (el) {

el.preventDefault();//取消默认行为

let uploadEventFile = $("#uploadEventFile").val();

this.file = el.target.files[0];

if (uploadEventFile == '') {

  alert("请择excel,再上传");

} else if (uploadEventFile.lastIndexOf(".xls") > 0 || uploadEventFile.lastIndexOf(".XLS") > 0) {

  let formData = new FormData();

  // 向 formData 对象中添加文件

  formData.append('file', this.file);//Excel文件,key必须是file,后端接收的时候也必须是file

  formData.append('sheetIndex', 1);//自定义的属性,读取Excel的哪个sheet表,sheetIndex从1开始

  formData.append('startReadRowNumber', 1);//自定义的属性,从第几行开始读取,startReadRowNumber从0开始

  // url为对应的后端接口

  const url = "/excel/readExcel.do";

  let headers = { 'Content-Type': 'multipart/form-data' };

 

  excelAxios({

method: "post",

url: url,

data: formData,

headers: headers

  }).then((res) => {

let { data } = res;

console.log(data);

  });

} else {

  alert("只能上传excel文件");

}

  }

}

  }

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->

<style scoped>

</style>

      (MyOutPutExcel.java)

package com.lennar.testvuespringboot.ExcelParam.model2excel;

import com.alibaba.excel.annotation.ExcelProperty;

import com.alibaba.excel.metadata.BaseRowModel;

import lombok.Data;

import lombok.EqualsAndHashCode;

//读写不要共用一个,这样很不友好,分着比较好

@EqualsAndHashCode(callSuper = true)

@Data

public class MyOutPutExcel extends BaseRowModel {

@ExcelProperty(value = "序号", index = 0)

private String id;//数值型最好不要用Integer或者Double,避免出错,格式转化

@ExcelProperty(value = "姓名", index = 1)

private String name;

@ExcelProperty(value = "地址", index = 2)

private String address;

@ExcelProperty(value = "性别", index = 3)

private String sax;

@ExcelProperty(value = "生日", index = 4)

private String birthday;

@ExcelProperty(value = "备注", index = 5)

private String remark;

}

      (Student.java)

package com.lennar.testvuespringboot.ExcelParam.model2excel;

import com.alibaba.excel.annotation.ExcelProperty;

import com.alibaba.excel.metadata.BaseRowModel;

import com.lennar.testvuespringboot.ExcelParam.utils.ExcelUtils;

import lombok.Data;

import lombok.EqualsAndHashCode;

//由于格式问题,最好提供模板,不要随意修改Excel模板的格式,否则可能读不到

//一个excel文件中可能对应多个sheet表,最好一个sheet对应一个类,并且这个类写在单独的一个Java文件中

/**

* @ClassName Student

* @Description Excel文件对应的Java模型,必须继承BaseRowModel,所有属性类型建议都适用String,避免由于格式转换出现的异常;读写可以使用一个Java模型;读的时候根据index(列号)进行获取;写的时候由index(列号)和value(列名成)进行写入

*/

@EqualsAndHashCode(callSuper = true)

@Data

public class Student extends BaseRowModel {

@ExcelProperty(index = 0, value = "学生ID")

private String id;//为了防止类型转换异常,最好数值型也用String接收;例如1.0转换不成1

@ExcelProperty(index = 1, value = "学生姓名")

private String name;

@ExcelProperty(index = 2, value = "学生所读小学")

private String school;

@ExcelProperty(index = 3, value = "学生生日")//不要少index,不能index=4,不要index3了;index从0开始,表示第几列;value为列名称(写入时起作用)

private String birthday;

 

//读Excel文件到属性,调用的就是setter方法

//虽然@Data注解了,但是如果有自己想特殊处理的setter或者getter方法,则直接类中定义自己的setter/getter方法即可(哪个属性需要,就搞哪个属性即可,不用全部属性的setter、getter写出来的),此后调用时,调用的是自己的setter/getter方法

//@Data的作用就是没有的,它提供,有的,它不提供

//注意日期类读到的就是Excel中设置单元格那里的格式,尤其是2003版(xls)很有可能默认只有年月日,因此读错,此时需要Excel设置单元格设置自定义格式

public void setBirthday(String birthday) {

this.birthday = ExcelUtils.getCorrectDateFromExcel(birthday);

}

}

      (Worker.java)

package com.lennar.testvuespringboot.ExcelParam.model2excel;

import com.alibaba.excel.annotation.ExcelProperty;

import com.alibaba.excel.metadata.BaseRowModel;

import lombok.Data;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)

@Data

public class Worker extends BaseRowModel {

@ExcelProperty(index = 0, value = "工人编号")

private String id;

@ExcelProperty(index = 1, value = "工人职责")

private String function;

}

      (ExcelUtils.java)

package com.lennar.testvuespringboot.ExcelParam.utils;

import com.alibaba.excel.ExcelReader;

import com.alibaba.excel.ExcelWriter;

import com.alibaba.excel.context.AnalysisContext;

import com.alibaba.excel.event.AnalysisEventListener;

import com.alibaba.excel.metadata.BaseRowModel;

import com.alibaba.excel.metadata.Sheet;

import com.alibaba.excel.support.ExcelTypeEnum;

import lombok.Data;

import lombok.EqualsAndHashCode;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.multipart.MultipartFile;

 

import javax.servlet.http.HttpServletResponse;

import java.io.*;

import java.text.SimpleDateFormat;

import java.util.*;

 

public class ExcelUtils {

 

private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtils.class);

 

/**

* 用于标识2003版Excel文件的后缀

*/

private static final String XLS_FORMAT = "xls";

 

 

/**

* @param sheetIndex         一个Excel可能有多个sheet表,sheetIndex表示第几个,从1开始

* @param startReadRowNumber 一个sheet表有多行,startReadRowNumber表示从第几行开始读取,从0开始

* @param path               Excel文件完整路径

* @param clazz              Excel对应的Java模型,可以为null;建议使用Java模型,防止类型转换异常,且方便处理空值问题等,强烈建议使用Java模型!!!

* @return List<Object>      返回的一个sheet中的所有记录,List中的每一个元素都是sheet中的一行数据,通过Java模型对象或者List<Object>存储

* @method readExcel

* @description 从本地读取Excel文件,返回sheet中的所有记录;对于空行,xlsx版也会获取到(每列对应的值为null);而xls版只会获取非空行

*/

public static List<Object> readExcel(int sheetIndex, int startReadRowNumber, String path, Class clazz) {

return readExcel(sheetIndex, startReadRowNumber, path, null, clazz);

}

 

/**

* @param sheetIndex

* @param startReadRowNumber

* @param excel              读取前端上传的Excel文件

* @param clazz

* @return java.util.List<java.lang.Object>

* @method readExcel

* @description 读取前端上传的Excel文件

*/

public static List<Object> readExcel(int sheetIndex, int startReadRowNumber, MultipartFile excel, Class clazz) {

return readExcel(sheetIndex, startReadRowNumber, null, excel, clazz);

}

 

private static List<Object> readExcel(int sheetIndex, int startReadRowNumber, String path, MultipartFile excelFile, Class clazz) {

//用于存放读取的单个sheet表的结果

List<Object> allDatasWithOneSheetFromAnyRows = null;

//表示Excel文件的版本:2003版(xls)或者2007版(xlsx)

ExcelTypeEnum excelVersionFlag = null;

//用于读取Excel文件

InputStream inputStream = null;

//用于处理Excel文件的核心对象

ExcelListener listener = null;

//创建能够实现读取Excel文件的管理对象

ExcelReader excelReader = null;

try {

if (StringUtils.isNotBlank(path)) {

//获取Excel文件的版本:2003版(xls)或者2007版(xlsx)

excelVersionFlag = getExcelVersion(path);

//获取Excel文件

inputStream = getInputStream(path, null);

} else if (null != excelFile) {

//获取Excel文件的版本:2003版(xls)或者2007版(xlsx)

excelVersionFlag = getExcelVersion(excelFile.getOriginalFilename());

//获取Excel文件

inputStream = getInputStream(null, excelFile);

}

//解析每行结果在listener中处理

listener = new ExcelListener();

//创建读取Excel文件的管理对象

excelReader = new ExcelReader(inputStream, excelVersionFlag, null, listener);

//读取Excel文件,结果存放于listener的excelDatasWithRow这个List集合中

//一旦调用了read方法,就自动进入了listener之中,每一行读取后调用listener的invoke方法,整个sheet读取后调用listener的doAfterAllAnalysed方法,最后读取的所有结果存放到excelDatasWithRow这个List集合中

//读取每一行且使用Java模型时,其实使用的是Java模型类中各个属性的setter方法,因此有个性化需求时,可以在对应setter方法中进行赋值等

excelReader.read(new Sheet(sheetIndex, startReadRowNumber, clazz));

allDatasWithOneSheetFromAnyRows = new LinkedList<>();

allDatasWithOneSheetFromAnyRows.addAll(listener.getExcelDatasWithRow());

if (null != listener) {

listener.getExcelDatasWithRow().clear();

LOGGER.info("清空当次listener的缓存资源已经完成");

}

} catch (Exception e) {

LOGGER.error("获取excel内容失败,具体原因:" + e.toString());

} finally {

if (null != inputStream) {

try {

inputStream.close();

} catch (IOException e) {

LOGGER.error("获取Excel后关闭InputStream流异常,具体原因:" + e.toString());

}

}

}

return allDatasWithOneSheetFromAnyRows;

}

 

 

/**

* @param sheetNameAndDatasMap key是每一个sheet表的名字,value是对应sheet表的数据

* @param path                 写入本地Excel文件的完整路径

* @return java.lang.Boolean

* @method writeExcel

* @description 向本地写入Excel文件;Excel文件写入之前可有可无,没有就创建,有就先删除原文件再创建并写入

*/

public static Boolean writeExcel(Map<String, List<? extends BaseRowModel>> sheetNameAndDatasMap, String path) {

return writeExcel(sheetNameAndDatasMap, path, null, null);

}

 

/**

* @param sheetNameAndDatasMap

* @param response 返回前端下载Excel文件的核心对象

* @param fileName 返回的Excel文件的名字

* @return java.lang.Boolean

* @method writeExcel

* @description 前端点击下载,网页直接下载获得Excel文件

public static Boolean writeExcel(Map<String, List<? extends BaseRowModel>> sheetNameAndDatasMap, HttpServletResponse response, String fileName) {

return writeExcel(sheetNameAndDatasMap, null, response, fileName);

}

 

private static Boolean writeExcel(Map<String, List<? extends BaseRowModel>> sheetNameAndDatasMap, String path, HttpServletResponse response, String fileName) {

//标识是否成功写入本地Excel文件

Boolean success = true;

//表示Excel文件的版本:2003版(xls)或者2007版(xlsx)

ExcelTypeEnum excelVersionFlag = null;

//输出流,写入本地的通道

OutputStream outputStream = null;

try {

if (StringUtils.isNotBlank(path)) {

//获取Excel文件的版本:2003版(xls)或者2007版(xlsx)

excelVersionFlag = getExcelVersion(path);

//获取输出流

outputStream = getOutputStream(path, null, null);

} else if (null != response) {

//返回给前端默认使用2007版(xlsx)

excelVersionFlag = ExcelTypeEnum.XLSX;

fileName += excelVersionFlag.getValue();

//获取输出流

outputStream = getOutputStream(null, response, fileName);

}

//获取写入本地Excel文件管理对象

ExcelWriter writer = new ExcelWriter(outputStream, excelVersionFlag);

//当前创建的sheet表编号,从1开始

Integer sheetIndex = 1;

for (Map.Entry<String, List<? extends BaseRowModel>> entry : sheetNameAndDatasMap.entrySet()) {

//sheet表名字

String sheetName = entry.getKey();

//sheet表数据

List<? extends BaseRowModel> datas = entry.getValue();

//获取Excel文件对应的Java模型的类型

Class clazz = null;

if (null != datas && datas.size() > 0) {

clazz = datas.get(0).getClass();

}

//创建sheet表

Sheet sheet = new Sheet(sheetIndex, 0, clazz, sheetName, null);

//将sheet表的数据写入sheet表中

writer.write(datas, sheet);

sheetIndex++;

}

//当所有的sheet写入完成之后调用该finish方法,完成写入Excel文件工作

writer.finish();

} catch (Exception e) {

success = false;

LOGGER.error("写入本地Excel异常,具体原因:" + e.toString());

} finally {

if (null != outputStream) {

try {

outputStream.close();

} catch (IOException e) {

success = false;

LOGGER.error("写入本地Excel完成后关闭输出流异常,具体原因:" + e.toString());

}

}

}

return success;

 

}

 

/**

* @param path

* @return com.alibaba.excel.support.ExcelTypeEnum

* @method getExcelVersion

* @description 获取Excel文件的版本

*/

private static ExcelTypeEnum getExcelVersion(String path) {

//默认是2007版,即文件名以xlsx结束

ExcelTypeEnum excelVersionFlag = ExcelTypeEnum.XLSX;

//获取文件的版本后缀:xls或者xlsx

if (StringUtils.isNotBlank(path)) {

String excelVersion = path.toLowerCase().substring(path.lastIndexOf(".") + 1, path.length());

if (XLS_FORMAT.equals(excelVersion)) { //如果是office2003

excelVersionFlag = ExcelTypeEnum.XLS;

}

}

return excelVersionFlag;

}

 

/**

* @return java.io.InputStream

* @method getInputStream

* @description 获取读取Excel时的输入流

* @param path 通过本地路径path获取输入流

* @param excel 通过前端传入后端的Excel文件获取输入流

*/

private static InputStream getInputStream(String path, MultipartFile excel) throws IOException {

InputStream inputStream = null;

if (StringUtils.isNotBlank(path)) {

inputStream = new FileInputStream(path);

} else if (null != excel) {

inputStream = new BufferedInputStream(excel.getInputStream());

}

return inputStream;

}

 

/**

* @return java.io.OutputStream

* @method getOutputStream

* @description 获取写入Excel文件时的输出流

* @param path 根据本地路径path获取输出流

* @param response 根据返回响应对象response获取输出流,以便直接在网页下载

* @param fileName

*/

private static OutputStream getOutputStream(String path, HttpServletResponse response, String fileName) throws IOException {

OutputStream outputStream = null;

if (StringUtils.isNotBlank(path)) {

outputStream = new FileOutputStream(path);

} else if (null != response) {

response.setContentType("utf-8");

response.setContentType("application/msexcel");

response.setHeader("Content-Disposition", "attachment; filename=" + new String((fileName).getBytes("gb2312"), "iso8859-1"));

outputStream = response.getOutputStream();

}

return outputStream;

}

 

/**

* @param dateStrFromExcel

* @return java.lang.String

* @method getCorrectDateFromExcel

* @description 对从Excel文件中获取的日期进行格式化处理;注意Excel文件中每个单元格的格式是怎样的,程序就会读成什么样子的字符串值,所以注意格式(自定义格式),尤其是日期,特别注意xls版本

*/

public static String getCorrectDateFromExcel(String dateStrFromExcel) {

String finalDateValue;

try {

Double dateDoubleValue = Double.valueOf(dateStrFromExcel);

String dateFormat = "yyyy-MM-dd HH:mm:ss";//24进制,建议使用

// String dateFormat = "yyyy-MM-dd hh:mm:ss";//12进制

finalDateValue = parseDateFromExcel2DateString(false, dateDoubleValue, dateFormat);

} catch (Exception e) {

finalDateValue = dateStrFromExcel;

}

return finalDateValue;

}

 

/**

* @param use1904windowing 一般设置成false即可

* @param value

* @param dateFormat

* @return java.lang.String

* @method parseDateFromExcel2DateString

* @description 将Excel那种时间浮点数转化成可识别的时间字符串,多见于xlsx文件中。

*/

private static String parseDateFromExcel2DateString(boolean use1904windowing, double value, String dateFormat) {

Date date = parseDateFromExcel2DateObject(use1904windowing, value);

if (StringUtils.isNotBlank(dateFormat)) {

SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);

return simpleDateFormat.format(date);

}

return date.toString();

}

 

/**

* @param use1904windowing 一般设置成false即可

* @param value

* @return java.util.Date

* @method parseDateFromExcel2DateObject

* @description 将Excel那种时间浮点数转化成可识别的时间对象Date,多见于xlsx文件中。

*/

private static Date parseDateFromExcel2DateObject(boolean use1904windowing, double value) {

int wholeDays = (int) Math.floor(value);

int millisecondsInDay = (int) ((value - (double) wholeDays) * 8.64E7D + 0.5D);

Calendar calendar = new GregorianCalendar();

short startYear = 1900;

byte dayAdjust = -1;

if (use1904windowing) {

startYear = 1904;

dayAdjust = 1;

} else if (wholeDays < 61) {

dayAdjust = 0;

}

calendar.set(startYear, 0, wholeDays + dayAdjust, 0, 0, 0);

calendar.set(Calendar.MILLISECOND, millisecondsInDay);

if (calendar.get(Calendar.MILLISECOND) == 0) {

calendar.clear(Calendar.MILLISECOND);

}

Date date = calendar.getTime();

return date;

}

 

}

 

 

/**

* 处理Excel文件的核心类,继承了AnalysisEventListener,并实现了其invoke和doAfterAllAnalysed方法;

* invoke方法是每读取一行记录后触发的函数;

* doAfterAllAnalysed方法是读取完整个sheet表后触发的函数

*/

@EqualsAndHashCode(callSuper = true)

@Data

class ExcelListener extends AnalysisEventListener {

 

//用于存放当前读取的sheet表中的所有记录,每一个记录是一个Object类型对象;

//而这个Object类型对象可能是Excel文件对应的Java模型或者List<Object>,总之存放的是一行记录

private List<Object> excelDatasWithRow = new LinkedList<>();

 

/**

* @param oneRowData 当前读取的一行记录

* @param context    读取Excel的管理对象

* @return void

* @method invoke    读取每一行且使用Java模型时,其实使用的是Java模型类中各个属性的setter方法,因此有个性化需求时,可以在对应setter方法中进行赋值等

* @description

*/

public void invoke(Object oneRowData, AnalysisContext context) {

//当前sheet表的序号

int currentSheetNumber = context.getCurrentSheet().getSheetNo();

//读取当前记录的行号

int currentRowNumber = context.getCurrentRowNum();

//数据存放到list,以便后续的批量处理或者业务逻辑处理

excelDatasWithRow.add(oneRowData);

//是否使用Java模型,true使用,false未使用

Boolean useJavaModel = true;

if (oneRowData instanceof List) {//未使用Java模型来解析Excel,此时每一行解析出来就是一个List集合

useJavaModel = false;

}

//读取一行后的处理操作

dealDataWithOneRow(currentSheetNumber, currentRowNumber, oneRowData, useJavaModel, context);

}

 

/**

* @param context

* @return void

* @method doAfterAllAnalysed

* @description 整个sheet表读取结束后执行本方法

*/

public void doAfterAllAnalysed(AnalysisContext context) {

dealAllDatas(excelDatasWithRow, context);

}

 

/**

* @param sheetNumber

* @param rowNumber

* @param oneRowData

* @param useJavaModel

* @param context

* @return void

* @method dealDataWithOneRow

* @description 读取一行记录后的处理方法

*/

private void dealDataWithOneRow(int sheetNumber, int rowNumber, Object oneRowData, boolean useJavaModel, AnalysisContext context) {

}

 

/**

* @param excelDatasWithRow

* @param context

* @return void

* @method dealAllDatas

* @description 读取所有行之后的处理方法

*/

private void dealAllDatas(List<Object> excelDatasWithRow, AnalysisContext context) {

}

}

      (ExcelController.java)

package com.lennar.testvuespringboot.ExcelParam;

import com.alibaba.excel.metadata.BaseRowModel;

import com.lennar.testvuespringboot.ExcelParam.model2excel.MyOutPutExcel;

import com.lennar.testvuespringboot.ExcelParam.model2excel.Student;

import com.lennar.testvuespringboot.ExcelParam.model2excel.Worker;

import com.lennar.testvuespringboot.ExcelParam.utils.ExcelUtils;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

import java.util.*;

@RestController

@RequestMapping("/excel")

public class ExcelController {

 

/**

* @param file               前端上传的Excel文件,file这个名字不要变(前后端都适用这个即可)

* @param sheetIndex         读取Excel文件中的第几个sheet表(前后端名称自定义,但要一致)

* @param startReadRowNumber 从第几行开始读取内容(前后端名称自定义,但要一致)

* @return java.util.Map<java.lang.String   ,   java.lang.Object>

* @method readExcel

* @description 读取前端上传的Excel文件

*/

@PostMapping("/readExcel.do")

public Map<String, Object> readExcel(MultipartFile file, Integer sheetIndex, Integer startReadRowNumber) {

Map<String, Object> map = new HashMap<>();

//获取上传的内容

List<Object> list = ExcelUtils.readExcel(sheetIndex, startReadRowNumber, file, Student.class);

map.put("list", list);

return map;

}

 

 

/**

* @param response 通过它获取输出流,以便前端网页下载Excel文件

* @return void 返回类型是void

* @method wirteExcel

* @description 前端网页下载Excel文件

*/

@GetMapping("/writeExcel.do")

public void wirteExcel(HttpServletResponse response) {

//创建数据,将来可以通过读取数据库等动态创建数据

Map<String, List<? extends BaseRowModel>> sheetNameAndDatasMap = new LinkedHashMap<>();

List<MyOutPutExcel> datas = new LinkedList<>();

List<Student> students = new LinkedList<>();

List<Worker> workers = new LinkedList<>();

for (int i = 1; i <= 5; i++) {

MyOutPutExcel myOutPutExcel1 = new MyOutPutExcel();

myOutPutExcel1.setId(i + "");

myOutPutExcel1.setName("名字_" + i);

myOutPutExcel1.setBirthday((new Date()).toString());

myOutPutExcel1.setAddress("住址_" + i);

myOutPutExcel1.setSax("性别_" + i);

myOutPutExcel1.setRemark("备注_" + i);

datas.add(myOutPutExcel1);

Student student = new Student();

student.setId(i + "");

student.setBirthday((new Date()).toString());

student.setName("学生姓名_" + i);

student.setSchool("小学_" + i);

students.add(student);

Worker worker = new Worker();

worker.setId(i + "");

worker.setFunction("功能_" + i);

workers.add(worker);

}

sheetNameAndDatasMap.put("学生表", students);

sheetNameAndDatasMap.put("输出表", datas);

sheetNameAndDatasMap.put("工人表", workers);

//实现网页下载Excel文件

ExcelUtils.writeExcel(sheetNameAndDatasMap, response, "下载");

}

 

}

注意事项:

1)注意谷歌浏览器跨域问题;

2)一般是在vuex中进行前后端交互,结果存放到状态变量中,便于全局管理使用;

3)then方法中如果使用到了this,最好使用箭头函数,而非普通方法;

4)返回结果res.data是后端返回的数据,res.status是系统返回的状态码

    5)   前后端请求参数必须一致

12、常见命令:就当成html标签的一些属性使用即可。类比jsp标签的各个属性。

1)v-model

双向绑定思想,尤其是在input标签中使用,不在理会value中的值,仅根据v-model绑定的数据变量的值在input中显示内容。

2)v-ifv-elsev-else-if

决定标签是否加载

3)v-show

标签始终存在,就是样式display是否是none(隐藏)

4)v-for

v-for = “(item,index) in items”  :key=”唯一标示”

{{item}}或者{{item.XXX}}

5)v-bind

v-bind:html标签的属性=””或者:html标签的属性=””

 

用于绑定属性,使得属性可以根据数据变量动态变化

例如:

<h1 v-bind:style="{'color':zdyColor,'font-size':zdySize}">这个测试一波</h1>

<h1 class="zdyFontFamily" :class="{'red':isRed,'disn':isDisn,'font-size':isSize}">这个测试二波</h1>

<h1 class="red font-size">这个测试三波</h1>

<a :href="zdyUrl">跳转</a>

6)v-on

v-on:事件=“方法名(加参数,可以有字符串(只要和外面的引号区分开即可))”或者:事件“方法名(加参数,可以有字符串(只要和外面的引号区分开即可))

 

用于声明触发事件

方法在export defaultmethods中定义

7)获取dom元素

方式一:ref

 

方式二:原生dom元素编程,通过document获取

 

常见命令整体演练:

<template>

  <div>

    <hr>

    <hr>

    <button @click="testModelAndView">理解双向的含义(修改inputp值变,修改p值时input值也变)-顺便测试v-model</button>

    <p>{{message}}</p>

    <input type="text" name id v-model="message" value="123">

    <hr>

    <hr>

    <button @click="testIfAndShow">测试v-ifv-show</button>

    <p v-if="one">if_1</p>

    <p v-if="two">if_2</p>

    <p v-if="three">if_3</p>

    <p v-show="one">show_1</p>

    <p v-show="two">show_2</p>

    <p v-show="three">show_3</p>

    <hr>

    <hr>

    <hr>

    <hr>

    <button @click="testIfAndShow">测试v-ifv-else(-show不支持)</button>

    <p v-if="one">if_1</p>

    <p v-else>if_1_else</p>

    <hr>

    <hr>

    <button @click="testIfAndShow">测试v-ifv-else-if(-show不支持)</button>

    <p v-if="if_one">if_1</p>

    <p v-else-if="if_two">if_1_else_if</p>

    <hr>

    <hr>

    <hr>

    <hr>

    <button @click="testIfAndShow">测试v-ifv-else-if(-show不支持)v-else(-show不支持)</button>

    <p v-if="if_one">if_1</p>

    <p v-else-if="if_two">if_1_else_if</p>

    <p v-else>if_1_else</p>

    <hr>

    <hr>

    <ul>

      <!-- key属性必须要加上,并且是唯一不可变的值;index就是索引编号,从0开始 -->

      <li v-for="(user,index) in users" :key="user.id">

        <input type="checkbox">

        {{index+1}}个用户:{{user.name}},来自{{user.address}}

      </li>

    </ul>

    <!-- 提供增加列表的功能,并且新增加的对象放到最前面 -->

    <input type="text" v-model="userName">

    <br>

    <input type="text" v-model="userCity">

    <button @click="addUser">addUser</button>

    <hr>

    <hr>

    <button @click="changeColorAndUrl">修改颜色和url</button>

    <h1 v-bind:style="{'color':zdyColor,'font-size':zdySize}">这个测试一波</h1>

    <h1 class="zdyFontFamily" :class="{'red':isRed,'disn':isDisn,'font-size':isSize}">这个测试二波</h1>

    <h1 class="red font-size">这个测试三波</h1>

    <a :href="zdyUrl">跳转</a>

    <hr>

    <hr>

    <!-- 获取dom元素,进而获取值 -->

    <button @click="getDom(domIndex)">获取dom元素</button>

    <p id="dom1" data="1" ref="testDom1">dom1文本</p>

    <p id="dom2" data="2" ref="testDom2">dom2文本</p>

    <p id="dom3" data="3" ref="testDom3">dom3文本</p>

    <hr>

    <hr>

  </div>

</template>

 

<script>

export default {

  data() {

    return {

      message: "helloworld",

      one: true,

      two: false,

      three: true,

      if_one: false,

      if_two: false,

      userName: "",

      userCity: "",

      users: [

        { name: "张三", address: "唐山", id: 1 },

        { name: "李四", address: "北京", id: 2 },

        { name: "王五", address: "上海", id: 3 }

      ],

      zdyColor: "red",

      zdySize: "100px",

      isRed: false,

      isDisn: false,

      isSize: true,

      zdyUrl: "https://www.baidu.com",

      domIndex: "dom2"

    };

  },

  methods: {

    testModelAndView() {

      this.message = this.message === "testModel" ? "helloworld" : "testModel";

    },

    testIfAndShow() {

      this.one = this.message === "testModel" ? true : false;

      this.two = !this.one;

      this.three = !!this.one;

    },

    addUser() {

      const user = {

        name: this.userName,

        address: this.userCity,

        id: this.users.length + 1

      };

      this.users.unshift(user);

      (this.userName = ""), (this.userCity = "");

    },

    changeColorAndUrl() {

      this.isRed = !this.isRed;

      this.zdyColor = this.zdyColor === "red" ? "blue" : "red";

      this.zdyUrl =

        this.zdyUrl === "https://www.baidu.com"

          ? "http://www.cnblogs.com/keepfool/p/5619070.html"

          : "https://www.baidu.com";

    },

    getDom(domId) {

      //方法一,使用ref

      const domNode = this.$refs.testDom1;

      console.log(domNode.getAttribute("data")); //自定义属性

      console.log(domNode.id); //固有属性

      console.log(domNode.innerHTML);

      domNode.innerHTML = "123";

      //方法二:使用dom元素编程思想

      const domNode2 = document.getElementById(domId);

      alert(domNode2.id);

    }

  }

};

</script>

 

<style>

/* 标识符./表示当前目录,而../表示父目录,而/表示下级 */

@import "../../css/myCss.css";

</style>

 

13、打包,并与后端合并

    1)  修改如下2个文件

                   

 

 

                   

 

 

     2最好先删除原来的dist然后在前端项目所在的根目录,运行命令npm run build

 

注意:打包命令是不是build,可以通过package.json查看。

 

3)获取打包出来的dist文件夹内的所有内容(除了单页面html)放入后端的静态文件夹中。例如resources/static下面,如果后端的static文件夹没有,可以自己创建。

 

注意:前后端的static不要混为一谈。

 

4)由于vue最后的项目其实只有一个单页面html页面,这个页面最好通过后端接口的方式找到。所以把dist里面的单页面html放到resource/后端接口路径下面即可。

    导入所需依赖放到控制器的pom文件下

    <dependency>

       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-thymeleaf</artifactId>

    </dependency>  

        (在application.properties中配置如下信息)

    spring.thymeleaf.cache=false
    
spring.thymeleaf.mode=HTML5
    
spring.thymeleaf.prefix=classpath:/

       (在controller中定义转向单页面的路径接口函数)

注意使用@Controller注解,而不是@RestController注解

返回值类型是String

 

返回的是路径,开头不需要/,且结尾处不需要.html

 

 

     5最后启动后端服务,输入ip:端口/context-path/接口路径URL

        例如:ip:端口/context-path/index.do

                   

 

 

 

五、技巧

1、前端各个组件间传值

一般就是通过vuex这个状态管理(全局变量管理)实现的,具体可以通过两种方式进行传值。

1)直接调用store中的actions的方法,将参数直接传递到方法参数中

例如:delete方法就可以直接把主键id传递给actions中的方法。

2)将修改storestate的变量值

例如:update方法就可以把各个参数方法state的变量中,以便可以在其他组件中显示。

2、前后端在哪里进行交互

一般就是在vuexmutations中实现。便于全局管理变量。

3、结果排序输出

一般后端返回的数据可以通过v-for的方式实现动态显示,但是要注意的是:key并不是能够实现自动排序的。需要通过如下两种方式实现排序输出:

方式1:后端排序后返回数据

方式2:前端对后端返回的数据进行排序(sort+自定义排序方法)后使用v-for动态显示。注意:sort修改本身结果。一般可以按照创建时间排序,因此后端需要有一个createTime属性。

例如:

 

 

 

4、图片问题

1)图片一般放到assets下面,可以在此文件夹下自己创建新的文件夹

2)通过vscode是无法直接存储图片的,可以打开路径,直接去文件夹里面存放图片,此后vscode就会加载了。

5、vue使用jQuery的方法

https://www.jianshu.com/p/8118f7f5298e

1)安装jQuery组件依赖

npm install jquery --save

2)build/webpack.base.conf.js中添加如下内容

 

3)在需要使用jQuery库的地方使用jQuery即可

A)导入jqueryimport $ from “jquery”

B)正常使用jQuery即可

6、导入第三方组件的一般步骤(例如使用axios组件)

1)如果当前项目的本地库node_modules中没有这个组件,需要先安装导入这个组件

2)在需要的地方通过import的方式引入这个组件

        3)使用这个组件即可 

类似于java定义函数

 

 

posted @ 2019-10-22 19:27  lennar92  阅读(343)  评论(0编辑  收藏  举报