哪有什么岁月静好,不过是有人替你负重前行!

第3章:Vue-cli3 开发单文件组件

1、快速原型开发

// App.vue里面的内容

<template>
  <div>
    <h3>{{msg}}</h3>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "vue-cli3开发单文件组件"
    };
  },
  created () {
      ;
  },
  methods: {
      name() {
          
      }
  },
  computed: {
      name() {
          return this.data 
      }
  },
  components: {
      
  },
};
</script>
<style scoped>
    h3{
        color: red;
    }
</style>

执行结果为:

 2、购物车项目搭建

// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <ul>
      <li v-for="item in carList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
      </li>
    </ul>
    <my-cart :cart='carList' :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
export default {
  name: 'app',
  data(){
    return{
      carList:[
        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        {id:2,title:"React实战开发",price:288,active:true,count:1}
      ],
      title:"购物车"
    };
  },
  components:{
    MyCart
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="c in cart" :key="c.id">
                <td>
                    <input type="checkbox" v-model="c.active"> <!-- //active的作用默认都是选中 -->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button>-</button>
                    {{c.count}}
                    <button>+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title','cart']//父组件通过props向子组件传递数据
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。
</script>

<style scoped>

</style>

运行结果:

 3、购物车项目操作

// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <ul>
      <li v-for="item in carList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
      </li>
    </ul>
    <my-cart :cart='carList' :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
export default {
  name: 'app',
  data(){
    return{
      carList:[
        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        {id:2,title:"React实战开发",price:288,active:true,count:1}
      ],
      title:"购物车"
    };
  },
  components:{
    MyCart
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> <!-- //active的作用默认都是选中 -->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title','cart'], //父组件通过props向子组件传递数据
        methods:{
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.cart.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>

</style>

  

结果:

 4、Mock模拟数据      模拟后端数据

// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <ul>
      <li v-for="item in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
      </li>
    </ul>
    <my-cart :cart='cartList' :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
export default {
  name: 'app',
  data(){
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {
      const res = await this.$http.get('/api/cartList')
      this.cartList = res.data.result;
    } catch (error) {
      console.log(error);
    }
    
    
  },
  components:{
    MyCart
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> <!-- //active的作用默认都是选中 -->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title','cart'], //父组件通过props向子组件传递数据
        methods:{
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.cart.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>

</style>
//vue.config.js里面的内容

module.exports = {
    devServer:{
        //每次修改完这个文件,一定要重新启动才能生效
        //mock数据模拟
        before(app,server){
            //接口
            app.get('/api/cartList',(req,res)=>{
                res.json({
                    result:[
                        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, 
                        {id:2,title:"React实战开发",price:288,active:true,count:1}
                    ]
                })
            })
        }
    }
}
// main.js里面的内容

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false

Vue.prototype.$http = axios;
new Vue({
  render: h => h(App),
}).$mount('#app')

结果:

 5、如何做数据持久化     刷新网页,购物车里面的数据还在

// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <!-- //展示购物车的列表 -->
    <ul>
      <li v-for="(item,index) in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
        <button @click="addCart(index)">添加购物车</button>
      </li>
    </ul>
    <my-cart :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
export default {
  name: 'app',
  methods:{
    addCart(i){
      const good = this.cartList[i];
      this.$bus.$emit('addCart',good);   //分发事件
    }
  },
  data(){  //这里的data存储着本地数据
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')   //这里是请求数据
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {  //try是用来处理接口错误的,成功的时候走try里面的内容,失败的时候走error。
      const res = await this.$http.get('/api/cartList')  //await等待结果
      this.cartList = res.data.result;  //这里接收到的数据传递到my-cart这个文件中。
    } catch (error) {
      console.log(error);
    }
    
    
  },
  components:{
    MyCart
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> 
                    <!-- //active的作用默认都是选中。vue使用v-model实现这些标签数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。-->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并,值是合并的格数,这里为两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title'], //父组件通过props向子组件传递数据
        data() {
            return {
                cart: JSON.parse(localStorage.getItem('cart')) || []
            };
        },
        watch:{  //监听数组cart
            cart:{
                handler(n){  //handler(n,o) n=new  o=old 意思是新值和旧值
                    this.setLocalData(n);
                },
                deep:true  //值true,是深度监听
            }
        },
        created () {
            this.$bus.$on('addCart',good=>{  //接收事件,传递的数据good
                const ret = this.cart.find(v=>v.id===good.id);
                if(!ret){  //这里是如果没有数据!ret是没有数据的意思
                    //购物车没有数据
                    this.cart.push(good);  //给购物车添加数据
                } else{
                    ret.count += 1;  //购物车里有数据,直接数量加1
                }
            });
        },
        methods:{
            setLocalData(n){
                //计算总课数
                localStorage.setItem('cart',JSON.stringify(n));  // 传进来的数组转换成字符串存储起来
            },
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.cart.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>

</style>
// main.js里面的内容

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' //axios是网上下载下来的
Vue.config.productionTip = false //阻止启动生产消息,Vue有两个模式,生产模式和开发模式。
                                  //开发模式:npm run dev是前端自己开发用的 ,生产模式:npm run build 打包之后给后端放在服务端上用的

Vue.prototype.$http = axios;  //挂载axios。
Vue.prototype.$bus = new Vue();  //添加事件总线,将
new Vue({
  render: h => h(App),
}).$mount('#app')

结果:刷新网页后,购物车里面的数据还在。

 6、Vue中使用第三方组件(element-ui)

// App.vue里面的内容
 
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <!-- //展示购物车的列表 -->
    <ul>
      <li v-for="(item,index) in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
        <el-button @click="addCart(index)" type='success'>添加购物车</el-button>
      </li>
    </ul>
    <my-cart :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
  </div>
</template>
 
<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
export default {
  name: 'app',
  methods:{
    addCart(i){
      const good = this.cartList[i];
      this.$bus.$emit('addCart',good);   //分发事件
    }
  },
  data(){  //这里的data存储着本地数据
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')   //这里是请求数据
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {  //try是用来处理接口错误的,成功的时候走try里面的内容,失败的时候走error。
      const res = await this.$http.get('/api/cartList')  //await等待结果
      this.cartList = res.data.result;  //这里接收到的数据传递到my-cart这个文件中。
    } catch (error) {
      console.log(error);
    }
     
     
  },
  components:{
    MyCart
  }
}
</script>
 
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
            <el-table
                ref="multipleTable"
                :data="cart"
                border
                tooltip-effect="dark"
                style="width: 100%"
                @selection-change="handleSelectionChange">

                <el-table-column type="selection" width="55"></el-table-column>
                <el-table-column prop="title" label="课程" width="120"></el-table-column>
                <el-table-column prop="price" label="价格" width="120"></el-table-column>
                <el-table-column label="数量" width="200">
                    <template slot-scope="scope">
                        <el-input-number v-model="scope.row.count" :min="1" :max="100" label="描述文字"></el-input-number>
                    </template>
                </el-table-column>
                <el-table-column label="总价" width="120">
                    <template slot-scope="scope">
                        {{scope.row.count * scope.row.price}}
                    </template>
                </el-table-column>
            </el-table>


        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> 
                    <!-- //active的作用默认都是选中。vue使用v-model实现这些标签数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。-->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并,值是合并的格数,这里为两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title'], //父组件通过props向子组件传递数据
        data() {
            return {
                cart: JSON.parse(localStorage.getItem('cart')) || [],   //刷新网页的时候,获取对应的数据,格式化一下,如果没有值,返回空值
                multipleSelection:[]
            };
        },
        watch:{  //监听数组cart,cart是数组,需要深度监听
            cart:{
                handler(n){  //handler(n,o) n=new  o=old 意思是新值和旧值
                    this.setLocalData(n);
                },
                deep:true  //值true,是深度监听
            }
        },
        created () {
            this.$bus.$on('addCart',good=>{  //接收事件,传递的数据good
                const ret = this.cart.find(v=>v.id===good.id);
                if(!ret){  //这里是如果没有数据!ret是没有数据的意思
                    //购物车没有数据
                    this.cart.push(good);  //给购物车添加数据
                } else{
                    ret.count += 1;  //购物车里有数据,直接数量加1
                }
            });
        },
        methods:{
            toggleSelection(rows) {
                if (rows) {
                rows.forEach(row => {
                    this.$refs.multipleTable.toggleRowSelection(row);
                });
                } else {
                this.$refs.multipleTable.clearSelection();
                }
            },
            handleSelectionChange(val) {
                this.multipleSelection = val;
                console.log(this.multipleSelection);
            },
            setLocalData(n){
                //计算总课数
                localStorage.setItem('cart',JSON.stringify(n));  // 传进来的数组转换成字符串存储起来
            },
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.multipleSelection.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>

</style>
// main.js里面的内容

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' //axios是网上下载下来的
import './plugins/element.js'
Vue.config.productionTip = false //阻止启动生产消息,Vue有两个模式,生产模式和开发模式。
//开发模式:npm run dev是前端自己开发用的 ,生产模式:npm run build 打包之后给后端放在服务端上用的

Vue.prototype.$http = axios;  //挂载axios。
Vue.prototype.$bus = new Vue();  //添加事件总线,将
new Vue({
  render: h => h(App),
}).$mount('#app')
// element.js里面的内容

import Vue from 'vue'
import { Button,Table,TableColumn,InputNumber} from 'element-ui'

Vue.use(Button)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(InputNumber)
//vue.config.js里面的内容

module.exports = {
    devServer:{
        //每次修改完这个文件,一定要重新启动才能生效
        //mock数据模拟   模拟后端数据
        before(app){
            //接口
            app.get('/api/cartList',(req,res)=>{  //req是请求,res是响应
                res.json({
                    result:[
                        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, 
                        {id:2,title:"React实战开发",price:288,active:true,count:1}
                    ]
                })
            })
        }
    }
}

结果:

 7、Element 表单组件分析

// FormItem.vue里面的内容

<template>
    <div>
        <h3>element表单</h3>
        <el-form      
            :model="ruleForm" 
            status-icon 
            :rules="rules"  
            ref="ruleForm" 
            label-width="100px" 
            class="demo-ruleForm"
        >
            <el-form-item label="用户名" prop="name">
                <el-input type="text" v-model="ruleForm.name"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop="pwd">
                <el-input type="password" v-model="ruleForm.pwd"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
//Form 定义数据模型 负责定义校验规则
//FormItem 绑定label prop校验和显示错误信息
//Input 负责双向数据绑定 通知FormItem做检验

//provide inject 内部共享数据
    export default{
        name:'FormElement',
        data(){
            return{
                ruleForm:{
                    name:'',
                    pwd:''
                },
                rules:{
                    name:[
                        {required:true,message:'请输入名称'},
                        {min:6,max:10,message:'请输入6~10位用户名'}
                    ],
                    pwd:[{require:true,message:'请输入密码'}],
                }
            }
        },
        methods:{
            submitForm(name){
                this.$refs[name].validate(valid=>{
                    console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            }
        }
    }
</script>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
            <el-table
                ref="multipleTable"
                :data="cart"
                border
                tooltip-effect="dark"
                style="width: 100%"
                @selection-change="handleSelectionChange">

                <el-table-column type="selection" width="55" prop='active'></el-table-column>
                <el-table-column prop="title" label="课程" width="120"></el-table-column>
                <el-table-column prop="price" label="价格" width="120"></el-table-column>
                <el-table-column label="数量" width="200">
                    <template slot-scope="scope">
                        <el-input-number v-model="scope.row.count" :min="1" :max="100" label="描述文字"></el-input-number>
                    </template>
                </el-table-column>
                <el-table-column label="总价" width="120">
                    <template slot-scope="scope">{{scope.row.count * scope.row.price}}</template>
                </el-table-column>
            </el-table>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> 
                    <!-- //active的作用默认都是选中。vue使用v-model实现这些标签数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。-->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并,值是合并的格数,这里为两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title'], //父组件通过props向子组件传递数据
        data() {
            return {
                cart: JSON.parse(localStorage.getItem('cart')) || [],   //刷新网页的时候,获取对应的数据,格式化一下,如果没有值,返回空值
                multipleSelection:[]
            };
        },
        watch:{  //监听数组cart,cart是数组,需要深度监听
            cart:{
                handler(n){  //handler(n,o) n=new  o=old 意思是新值和旧值
                    this.setLocalData(n);
                },
                deep:true  //值true,是深度监听
            }
        },
        created () {
            this.$bus.$on('addCart',good=>{  //接收事件,传递的数据good
                const ret = this.cart.find(v=>v.id===good.id);
                if(!ret){  //这里是如果没有数据!ret是没有数据的意思
                    //购物车没有数据
                    this.cart.push(good);  //给购物车添加数据
                } else{
                    ret.count += 1;  //购物车里有数据,直接数量加1
                }
            });
        },
        methods:{
            toggleSelection(rows) {
                if (rows) {
                rows.forEach(row => {
                    this.$refs.multipleTable.toggleRowSelection(row);
                });
                } else {
                this.$refs.multipleTable.clearSelection();
                }
            },
            handleSelectionChange(val) {
                this.multipleSelection = val;
                console.log(this.multipleSelection);
            },
            setLocalData(n){
                //计算总课数
                localStorage.setItem('cart',JSON.stringify(n));  // 传进来的数组转换成字符串存储起来
            },
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.multipleSelection.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>  /*//在vue组件中,在style标签上添加scoped属性,以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的*/

</style>
// element.js里面的内容

import Vue from 'vue'
import { Button,Table,TableColumn,InputNumber,Form,FormItem,Input} from 'element-ui'  //分批安装element插件。

Vue.use(Button)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(InputNumber)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <!-- //展示购物车的列表 -->
    <ul>
      <li v-for="(item,index) in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
        <el-button @click="addCart(index)" type='success'>添加购物车</el-button>
      </li>
    </ul>
    <my-cart :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
    <FormItem></FormItem>
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
//@相当于src目录
import FormItem from '@/components/FormItem.vue';
import Formltem from './components/Formltem.vue';

export default {
  name: 'app',
  methods:{
    addCart(i){
      const good = this.cartList[i];
      this.$bus.$emit('addCart',good);   //分发事件
    }
  },
  data(){  //这里的data存储着本地数据
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')   //这里是请求数据
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {  //try是用来处理接口错误的,成功的时候走try里面的内容,失败的时候走error。
      const res = await this.$http.get('/api/cartList')  //await等待结果
      this.cartList = res.data.result;  //这里接收到的数据传递到my-cart这个文件中。
    } catch (error) {
      console.log(error);
    }
    
  },
  components:{
    MyCart,  //MyCart是一个局部模板,因为在<div id:'app'></div>下面挂载着,只能在这下面使用。
    FormItem
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
//vue.config.js里面的内容

module.exports = {
    devServer:{
        //每次修改完这个文件,一定要重新启动才能生效
        //mock数据模拟   模拟后端数据
        before(app){
            //接口
            app.get('/api/cartList',(req,res)=>{  //req是请求,res是响应
                res.json({
                    result:[
                        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, 
                        {id:2,title:"React实战开发",price:288,active:true,count:1}
                    ]
                })
            })
        }
    }
}
// main.js里面的内容

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' //axios是网上下载下来的
import './plugins/element.js'  //安装的插件element
Vue.config.productionTip = false //阻止启动生产消息,Vue有两个模式,生产模式和开发模式。
//开发模式:npm run dev是前端自己开发用的 ,生产模式:npm run build 打包之后给后端放在服务端上用的

Vue.prototype.$http = axios;  //挂载axios。
Vue.prototype.$bus = new Vue();  //添加事件总线
new Vue({
  render: h => h(App),
}).$mount('#app')

 分析问题:

 8、表单组件设计-Input实现双向数据绑定

// Input.vue里面的内容

<template>
    <div>
        <!-- v-model是 v-bind:value v-on:input 的合并 -->
        <input type="type" :value="inputVal" @input="handleInput">
    </div>
</template>

<script>
    export default {
        props: {
            type: {
                type: String,
                default: 'text'
            },
            value:{
                type:String,
                default:''
            }
        },
        data() {
            return {
                inputVal: this.value
            }
        },
        methods: {
            handleInput(e) {
                // 赋值
                // 实现双向数据绑定
                this.inputVal = e.target.value
                this.$emit('input', this.inputVal);
                // 通知父组件值的更新
                this.$parent.$emit('validate',this.inputVal)
            }
        },
    }
</script>

<style scoped>

</style>
// FormElement.vue里面的内容

<template>
    <div>
        <h3>element表单</h3>
        <!-- 自己的组件 -->
        <m-input v-model="ruleForm.name"></m-input>
        <m-input type='password' v-model="ruleForm.pwd"></m-input>
        <!-- <MInput v-model="ruleForm.name"></MInput> -->
        <!-- <MInput type='password' v-model="ruleForm.pwd"></MInput> -->
        <el-form      
            :model="ruleForm" 
            status-icon 
            :rules="rules"  
            ref="ruleForm" 
            label-width="100px" 
            class="demo-ruleForm"
        >
            <el-form-item label="用户名" prop="name">
                <el-input type="text" v-model="ruleForm.name"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop="pwd">
                <el-input type="password" v-model="ruleForm.pwd"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
//Form 定义数据模型 负责定义校验规则
//FormItem 绑定label prop校验和显示错误信息
//Input 负责双向数据绑定 通知FormItem做检验

//provide inject 内部共享数据
import MInput from './Input';
    export default{
        name:'FormElement',
        components: {
            MInput,
        },
        data(){
            return{
                ruleForm:{   //ref:'ruleForm'绑定了用户名和密码。绑定from组件,获取组件对象。
                    name:'',
                    pwd:''
                },
                rules:{  //绑定了规则
                    name:[
                        {required:true,message:'请输入名称'},  //必须的,值为true。
                        {min:6,max:10,message:'请输入6~10位用户名'}
                    ],
                    pwd:[{require:true,message:'请输入密码'}],
                }
            }
        },
        methods:{
            submitForm(name){
                this.$refs[name].validate(valid=>{
                    console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            }
        }
    }
</script>
// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <!-- //展示购物车的列表 -->
    <ul>
      <li v-for="(item,index) in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
        <el-button @click="addCart(index)" type='success'>添加购物车</el-button>
      </li>
    </ul>
    <my-cart :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
    <FormElement></FormElement>
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
//@相当于src目录
import FormElement from '@/components/FormElement.vue';

export default {
  name: 'app',
  methods:{
    addCart(i){
      const good = this.cartList[i];
      this.$bus.$emit('addCart',good);   //分发事件
    }
  },
  data(){  //这里的data存储着本地数据
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')   //这里是请求数据
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {  //try是用来处理接口错误的,成功的时候走try里面的内容,失败的时候走error。
      const res = await this.$http.get('/api/cartList')  //await等待结果
      this.cartList = res.data.result;  //这里接收到的数据传递到my-cart这个文件中。
    } catch (error) {
      console.log(error);
    }
    
  },
  components:{
    MyCart,  //MyCart是一个局部模板,因为在<div id:'app'></div>下面挂载着,只能在这下面使用。
    FormElement
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
// Cart.vue里面的内容

<template>
    <div>
        <h2>{{title}}</h2>
            <el-table
                ref="multipleTable"
                :data="cart"
                border
                tooltip-effect="dark"
                style="width: 100%"
                @selection-change="handleSelectionChange">

                <el-table-column type="selection" width="55" prop='active'></el-table-column>
                <el-table-column prop="title" label="课程" width="120"></el-table-column>
                <el-table-column prop="price" label="价格" width="120"></el-table-column>
                <el-table-column label="数量" width="200">
                    <template slot-scope="scope">
                        <el-input-number v-model="scope.row.count" :min="1" :max="100" label="描述文字"></el-input-number>
                    </template>
                </el-table-column>
                <el-table-column label="总价" width="120">
                    <template slot-scope="scope">{{scope.row.count * scope.row.price}}</template>
                </el-table-column>
            </el-table>
        <table border="1">
            <tr>
                <th>#</th>
                <th>课程</th>
                <th>单价</th>
                <th>数量</th>
                <th>总价</th>
            </tr>
            <tr v-for="(c,index) in cart" :key="c.id"> <!-- //index是当前的索引 -->
                <td>
                    <input type="checkbox" v-model="c.active"> 
                    <!-- //active的作用默认都是选中。vue使用v-model实现这些标签数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。-->
                </td>
                <td>{{c.title}}</td>
                <td>{{c.price}}</td>
                <td>
                    <button @click="substract(index)">-</button> <!-- //substract是减去的意思 -->
                    {{c.count}}
                    <button @click="add(index)">+</button>
                </td>
                <td>¥{{c.price*c.count}}</td>
            </tr>
            <tr>
                <td></td>
                <td colspan="2">{{activeCount}}/{{count}}</td> <!-- //colspan意思是横向合并,值是合并的格数,这里为两格 -->
                <td colspan="2">¥{{total}}</td>
            </tr>
        </table>
        
    </div>
    
</template>

<script>
    export default {
        name:'cart',
        props:['title'], //父组件通过props向子组件传递数据
        data() {
            return {
                cart: JSON.parse(localStorage.getItem('cart')) || [],   //刷新网页的时候,获取对应的数据,格式化一下,如果没有值,返回空值
                multipleSelection:[]
            };
        },
        watch:{  //监听数组cart,cart是数组,需要深度监听
            cart:{
                handler(n){  //handler(n,o) n=new  o=old 意思是新值和旧值
                    this.setLocalData(n);
                },
                deep:true  //值true,是深度监听
            }
        },
        created () {
            this.$bus.$on('addCart',good=>{  //接收事件,传递的数据good
                const ret = this.cart.find(v=>v.id===good.id);
                if(!ret){  //这里是如果没有数据!ret是没有数据的意思
                    //购物车没有数据
                    this.cart.push(good);  //给购物车添加数据
                } else{
                    ret.count += 1;  //购物车里有数据,直接数量加1
                }
            });
        },
        methods:{
            toggleSelection(rows) {
                if (rows) {
                rows.forEach(row => {
                    this.$refs.multipleTable.toggleRowSelection(row);
                });
                } else {
                this.$refs.multipleTable.clearSelection();
                }
            },
            handleSelectionChange(val) {
                this.multipleSelection = val;
                console.log(this.multipleSelection);
            },
            setLocalData(n){
                //计算总课数
                localStorage.setItem('cart',JSON.stringify(n));  // 传进来的数组转换成字符串存储起来
            },
            remove(i){
                if(window.confirm('确定是否要删除?')){  //弹窗让用户确认是否真的要删除数据。
                    this.cart.splice(i,1);  //从这条数据开始,删除一条数据。
                }
            },
            substract(i){
                let count = this.cart[i].count;
                count > 1 ? (this.cart[i].count -= 1) : this.remove(i);
            },
            add(i){
                this.cart[i].count++;
            }
        },
        computed: {  //computed是计算属性。
            count() {
                return this.cart.length;
            },
            activeCount(){
                return this.multipleSelection.filter(v=>v.active).length; //filter是过滤的意思,过滤出来每个对象
            },
            total(){
                // let sum = 0;
                // this.cart.forEach(c => {  //forEach遍历的意思
                    // if(c.active){
                        // sum += c.price*c.count;
                    // }
                // });
                // return sum;
                return this.cart.reduce((sum,c)=>{  //reduce是一个回调函数,有两个参数,第一个参数是回调函数,第二个是起始值。
                    if (c.active){
                        sum += c.price * c.count;
                    }
                    return sum;
                },0);
            }
        },
    } //在创建JavaScript模块时,export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。

</script>

<style scoped>  /*//在vue组件中,在style标签上添加scoped属性,以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的*/

</style>
//vue.config.js里面的内容

module.exports = {
    devServer:{
        //每次修改完这个文件,一定要重新启动才能生效
        //mock数据模拟   模拟后端数据
        before(app){
            //接口
            app.get('/api/cartList',(req,res)=>{  //req是请求,res是响应
                res.json({
                    result:[
                        {id:1,title:"Vue实战开发",price:188,active:true,count:1}, 
                        {id:2,title:"React实战开发",price:288,active:true,count:1}
                    ]
                })
            })
        }
    }
}
// main.js里面的内容

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' //axios是网上下载下来的
import './plugins/element.js'  //安装的插件element
Vue.config.productionTip = false //阻止启动生产消息,Vue有两个模式,生产模式和开发模式。
//开发模式:npm run dev是前端自己开发用的 ,生产模式:npm run build 打包之后给后端放在服务端上用的

Vue.prototype.$http = axios;  //挂载axios。
Vue.prototype.$bus = new Vue();  //添加事件总线
new Vue({
  render: h => h(App),
}).$mount('#app')
// element.js里面的内容

import Vue from 'vue'
import { Button,Table,TableColumn,InputNumber,Form,FormItem,Input} from 'element-ui'  //分批安装element插件。

Vue.use(Button)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(InputNumber)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)

结果:

 9、表单组件-设计FormItem组件

此节添加了FormItem.vue组件,更改了FormElement.vue组件,其它的都没有更改。

// FormItem.vue里面的内容

<template>
    <div>
        <label v-if='label'>{{label}}</label>   <!-- //判断,v-if。如果有label显示label。 -->
        <!-- 槽的作用 -->
        <slot></slot>
        <!-- 显示检验的错误信息 -->
        <p v-if="validateStatus==='error'" class="error">{{errorMessage}}</p>
    </div>
</template>

<script>
    //0.label 和 prop实行
    //1.获取当前输入框的规则
    //2.如果输入框和rule不匹配,显示错误信息
    //3.Input组件中涌入输入内容时,通知FormItem做校验(校验的方法)
    //4.使用async-validator做检验
    export default {
        data() {
            return {
                validateStatus: '', //检验的状态值
                errorMessage:'' //显示的错误信息
            };
        },
        props:{
            label:{
                type:String,
                default:''
            },
            prop:{
                type:String,
                default:''
            }
        }
    }
</script>

<style scoped>
    .error{
        color: red;
    }
</style>
// FormElement.vue里面的内容

<template>
    <div>
        <h3>element表单</h3>
        <!-- 自己的组件 -->
        <m-form-item label="用户名" prop="name">
            <m-input v-model="ruleForm.name"></m-input>
        </m-form-item>
        <m-form-item label='密码' prop='pwd'>
            <m-input type='password' v-model="ruleForm.pwd"></m-input>
        </m-form-item>
        
        <el-form      
            :model="ruleForm" 
            status-icon 
            :rules="rules"  
            ref="ruleForm" 
            label-width="100px" 
            class="demo-ruleForm"
        >
            <el-form-item label="用户名" prop="name">
                <el-input type="text" v-model="ruleForm.name"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop="pwd">
                <el-input type="password" v-model="ruleForm.pwd"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
//Form 定义数据模型 负责定义校验规则
//FormItem 绑定label prop校验和显示错误信息
//Input 负责双向数据绑定 通知FormItem做检验

//provide inject 内部共享数据
import MInput from './Input';
import MFormItem from './FormItem.vue'
    export default{
        name:'FormElement',
        components: {
            MInput,
            MFormItem
        },
        data(){
            return{
                ruleForm:{   //ref:'ruleForm'绑定了用户名和密码。绑定from组件,获取组件对象。
                    name:'',
                    pwd:''
                },
                rules:{  //绑定了规则
                    name:[
                        {required:true,message:'请输入名称'},  //必须的,值为true。
                        {min:6,max:10,message:'请输入6~10位用户名'}
                    ],
                    pwd:[{require:true,message:'请输入密码'}],
                }
            }
        },
        methods:{
            submitForm(name){
                this.$refs[name].validate(valid=>{
                    console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            }
        }
    }
</script>

 10、表单组件设计-如何正确设计表单校验规则

// FormItem.vue里面的内容

<template>
    <div>
        <label v-if='label'>{{label}}</label>   <!-- //判断,v-if。如果有label显示label,如果没有,不显示。 -->
        <!-- 槽的作用 Input输入框插入进来,即FormItem的子组件插入进来-->
        <slot></slot>
        <!-- 显示检验的错误信息 判断错误状态,如果相等,显示错误信息-->
        <p v-if='validateStatus==="error"' class="error">{{errorMessage}}</p>
    </div>
</template>

<script>
import schema from 'async-validator'
    //0.label 和 prop实行
    //1.获取当前输入框的规则
    //2.如果输入框和rule不匹配,显示错误信息
    //3.Input组件中涌入输入内容时,通知FormItem做校验(校验的方法)
    //4.使用async-validator做检验
    // npm i async-validator -S
    export default {
        name:'FormItem',
        data() {
            return {
                validateStatus: '', //检验的状态值
                errorMessage:'' //显示的错误信息
            };
        },
        inject:['form'],
        methods: {
            validate(value) {
                console.log(value); //输入框的值
                // let descriptor = {};  //此对象要存储校验规则
                //获取校验对象  =>Form组件对象  =>rules[this.prop]
                // descriptor[this.prop] = this.form.rules[this.prop];
                const descriptor = {    //上面的两行代码等价于这一句话。
                    [this.prop]: this.form.rules[this.prop]
                }
                const validator = new schema(descriptor)

                // let obj = {};
                // obj[this.prop] = value;
                validator.validate({[this.prop]:value},error=>{
                    if(error){
                        //显示错误
                        this.validateStatus = 'error';
                        this.errorMessage = error[0].message;
                    }else{
                        //错误置空
                        this.validateStatus = '';
                        this.errorMessage = '';
                    }
                })
            }
        },
        created () {
            this.$on('validate',this.validate);
        },
        props:{
            label:{
                type:String,
                default:''
            },
            prop:{  //name或者是pwd
                type:String,
                default:''
            }
        }
    }
</script>

<style scoped>
    .error{
        color: red;
    }
</style>
// FormElement.vue里面的内容

<template>
    <div>
        <h3>element表单</h3>
        <!-- //自己的组件 -->
        <m-form :model="ruleForm" :rules="rules">
            <m-form-item label="用户名" prop="name">    <!-- //绑定label用户名 -->
                <m-input v-model="ruleForm.name"></m-input>
            </m-form-item>
            <m-form-item label='密码' prop='pwd'>
                <m-input type='password' v-model="ruleForm.pwd"></m-input>
            </m-form-item>
        </m-form>
        
        <el-form      
            :model="ruleForm" 
            status-icon 
            :rules="rules"  
            ref="ruleForm" 
            label-width="100px" 
            class="demo-ruleForm"
        >
            <el-form-item label="用户名" prop="name">
                <el-input type="text" v-model="ruleForm.name"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop="pwd">
                <el-input type="password" v-model="ruleForm.pwd"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
//Form 定义数据模型 负责定义校验规则
//FormItem 绑定label prop校验和显示错误信息
//Input 负责双向数据绑定 通知FormItem做检验

//provide inject 内部共享数据
import MInput from './Input';
import MFormItem from './FormItem.vue';
import MForm from './Form.vue';
    export default{
        name:'FormElement',
        components: {
            MInput,
            MFormItem,
            MForm
        },
        data(){
            return{
                ruleForm:{   //ref:'ruleForm'绑定了用户名和密码。绑定from组件,获取组件对象。
                    name:'',
                    pwd:''
                },
                rules:{  //绑定了规则
                    name:[
                        {required:true,message:'请输入名称'},  //必须的,值为true。
                        {min:6,max:10,message:'请输入6~10位用户名'}
                    ],
                    pwd:[{require:true,message:'请输入密码'}],
                }
            }
        },
        methods:{
            submitForm(name){
                this.$refs[name].validate(valid=>{
                    console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            }
        }
    }
</script>
// Form.vue里面的内容

<template>
    <div>
        <slot></slot>
    </div>
</template>

<script>
    export default {
        provide(){
            return{
                form:this
            }
        },
        props: {
            model: {
                type: Object,
                required:true
            },
            rules:{
                type:Object
            }
        },
    }
</script>

<style lang="scss" scoped>

</style>
// App.vue里面的内容

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{title}}</h1>
    <!-- //展示购物车的列表 -->
    <ul>
      <li v-for="(item,index) in cartList" :key="item.index"> <!--//v-for循环遍历对象数组 -->
        <h2>{{item.title}}</h2>
        <p>¥{{item.price}}</p>
        <el-button @click="addCart(index)" type='success'>添加购物车</el-button>
      </li>
    </ul>
    <my-cart :title="title"></my-cart> <!--//这里的my-cart标签,同等于MyCart标签 -->
    <FormElement></FormElement>
  </div>
</template>

<script>
import MyCart from './components/Cart'; //MyCart自定义的称名,也可以起其它名称
//@相当于src目录
import FormElement from '@/components/FormElement.vue';

export default {
  name: 'app',
  methods:{
    addCart(i){
      const good = this.cartList[i];
      this.$bus.$emit('addCart',good);   //分发事件
    }
  },
  data(){  //这里的data存储着本地数据
    return{
      cartList:[],
      // carList:[
        // {id:1,title:"Vue实战开发",price:188,active:true,count:1}, //active的值为true,默认选中,为false,默认不选中
        // {id:2,title:"React实战开发",price:288,active:true,count:1}
      // ],
      title:"购物车"
    };
  },
  async created (){
    // this.$http.get('/api/cartList')   //这里是请求数据
    // .then(res=>{
      // this.cartList = res.data.result;
    // }).catch(err=>{
      // console.log(err);
    // })
    try {  //try是用来处理接口错误的,成功的时候走try里面的内容,失败的时候走error。
      const res = await this.$http.get('/api/cartList')  //await等待结果
      this.cartList = res.data.result;  //这里接收到的数据传递到my-cart这个文件中。
    } catch (error) {
      console.log(error);
    }
    
  },
  components:{
    MyCart,  //MyCart是一个局部模板,因为在<div id:'app'></div>下面挂载着,只能在这下面使用。
    FormElement
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;  /*/font-family为段落设置字体*/
  -webkit-font-smoothing: antialiased;  /*/字体抗锯齿渲染*/
  -moz-osx-font-smoothing: grayscale;  /*/字体抗锯齿渲染*/
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

 警告:

11、Form组件检验方法完结

只记录更改的内容:

// Form.vue里面的内容

<template>
    <div>
        <slot></slot>
    </div>
</template>

<script>
    export default {
        provide(){
            return{
                form:this
            }
        },
        props: {
            model: {
                type: Object,
                required:true
            },
            rules:{
                type:Object
            }
        },
        created () {
            //缓存需要校验的表单项
            this.fileds = [];
            this.$on('formItemAdd',item=>{
                this.fileds.push(item);
            });
        },
        methods: {
            validate(callback) {
                //获取所有的验证结果统一处理,只要有一个失败就失败

                //tasks保存着验证之后的多个promise对象
                const tasks = this.fileds.map(item=>tiem.validate());
                let ret = true;
                Promise.all(tasks).then(results=>{
                    results.forEach(valid=>{
                        if(!valid){
                            ret = false
                        }
                    })
                    callback(ret)
                })
            }
        },
    }
    //1.声明props 获取数据模型(model)和校验规则对象
</script>

<style lang="scss" scoped>

</style>
// FormItem.vue里面的内容

<template>
    <div>
        <label v-if='label'>{{label}}</label>   <!-- //判断,v-if。如果有label显示label,如果没有,不显示。 -->
        <!-- 槽的作用 Input输入框插入进来,即FormItem的子组件插入进来-->
        <slot></slot>
        <!-- 显示检验的错误信息 判断错误状态,如果相等,显示错误信息-->
        <p v-if='validateStatus==="error"' class="error">{{errorMessage}}</p>
    </div>
</template>

<script>
import schema from 'async-validator'
    //0.label 和 prop实行
    //1.获取当前输入框的规则
    //2.如果输入框和rule不匹配,显示错误信息
    //3.Input组件中涌入输入内容时,通知FormItem做校验(校验的方法)
    //4.使用async-validator做检验
    // npm i async-validator -S
    export default {
        name:'FormItem',
        data() {
            return {
                validateStatus: '', //检验的状态值
                errorMessage:'' //显示的错误信息
            };
        },
        inject:['form'],
        mounted () {
            if(this.prop){  //必须判断,因为Form组件的子组件可能不是FormItem
                this.$paren.$emit('formItemAdd', this);
            };
        },
        methods: {
            validate(value) {
                return new Promise(resolve=>{
                    // let descriptor = {};  //此对象要存储校验规则
                    //获取校验对象  =>Form组件对象  =>rules[this.prop]
                    // descriptor[this.prop] = this.form.rules[this.prop];
                    const descriptor = {    //上面的两行代码等价于这一句话。
                        [this.prop]: this.form.rules[this.prop]
                    }
                    const validator = new schema(descriptor)

                    // let obj = {};
                    // obj[this.prop] = value;
                    validator.validate({[this.prop]:value},error=>{
                        if(error){
                            //显示错误
                            this.validateStatus = 'error';
                            this.errorMessage = error[0].message;
                            resolve(false);
                        }else{
                            //错误置空
                            this.validateStatus = '';
                            this.errorMessage = '';
                            resolve(true);
                        }
                    })
                })
            }
        },
        created () {
            this.$on('validate',this.validate);
        },
        props:{
            label:{
                type:String,
                default:''
            },
            prop:{  //name或者是pwd
                type:String,
                default:''
            }
        }
    }
</script>

<style scoped>
    .error{
        color: red;
    }
</style>
// FormElement.vue里面的内容

<template>
    <div>
        <h3>element表单</h3>
        <!-- //自己的组件 -->
        <m-form :model="ruleForm" :rules="rules">
            <m-form-item label="用户名" prop="name">    <!-- //绑定label用户名 -->
                <m-input v-model="ruleForm.name"></m-input>
            </m-form-item>
            <m-form-item label='密码' prop='pwd'>
                <m-input type='password' v-model="ruleForm.pwd"></m-input>
            </m-form-item>
            <m-form-item>
                <el-button type="primary" @click="submitForm2('ruleForm')">提交</el-button>
            </m-form-item>
        </m-form>
        
        <el-form      
            :model="ruleForm" 
            status-icon 
            :rules="rules"  
            ref="ruleForm" 
            label-width="100px" 
            class="demo-ruleForm"
        >
            <el-form-item label="用户名" prop="name">
                <el-input type="text" v-model="ruleForm.name"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop="pwd">
                <el-input type="password" v-model="ruleForm.pwd"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script>
//Form 定义数据模型 负责定义校验规则//
//FormItem 绑定label prop校验和显示错误信息//
//Input 负责双向数据绑定 通知FormItem做检验//

//provide inject 内部共享数据
import MInput from './Input';
import MFormItem from './FormItem.vue';
import MForm from './Form.vue';
    export default{
        name:'FormElement',
        components: {
            MInput,
            MFormItem,
            MForm
        },
        data(){
            return{
                ruleForm:{   //ref:'ruleForm'绑定了用户名和密码。绑定from组件,获取组件对象。
                    name:'',
                    pwd:''
                },
                rules:{  //绑定了规则
                    name:[
                        {required:true,message:'请输入名称'},  //必须的,值为true。
                        {min:6,max:10,message:'请输入6~10位用户名'}
                    ],
                    pwd:[{require:true,message:'请输入密码'}],
                }
            }
        },
        methods:{
            submitForm(name){
                console.log(naem);  //ruleForm

                this.$refs[name].validate(valid=>{
                    console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            },
            //自己写的校验事件
            submitForm2(name){
                this.$refs[name].validate(valid=>{
                console.log(valid);
                    if(valid){
                        alert('验证成功,可以提交')
                    }else{
                        alert('error 提交');
                        return false;
                    }
                });
            }
        }
    }
</script>

  

posted @ 2022-02-15 21:18  longfei825  阅读(61)  评论(0编辑  收藏  举报