闲云旅游day06-机票列表页跳转到订单页

重要知识点

  • 表单实现逻辑
  • 手机验证码
  • 计算总价格

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9qCf2CS-1605265661547)(%E9%97%B2%E4%BA%91%E6%80%BB%E7%AC%94%E8%AE%B0/%E6%9C%BA%E7%A5%A8%E8%AE%A2%E5%8D%95%E9%A1%B5.png)]

订单首页布局

新建pages/air/order.vue的代码如下

<template>
    <div class="container">
        <el-row type="flex" justify="space-between">
            <!-- 订单表单 -->
            <div class="main">
                
            </div>

            <!-- 侧边栏 -->
            <div class="aside">
                          
            </div>
        </el-row>
    </div>
</template>

<script>
export default {
     
     
    
}
</script>

<style lang="less" scoped>
    .container{
     
     
        width:1000px;
        margin:20px auto;
    }
    
    /*aside*/
    .aside{
     
     
        width: 350px;
        height: fit-content;
        border:1px #ddd solid;
    }
</style>

订单表单组件

思路

  1. 新建表单组件
  2. 添加乘机人
  3. 移除乘机人
  4. 渲染保险数据
  5. 拼接订单数据
  6. 发送手机验证码

实现步骤

新建表单组件

1.新建订单表单组件components/air/orderForm.vue,添加以下内容

<template>
    <div class="main">
        <div class="air-column">
            <h2>剩机人</h2>
            <el-form class="member-info">
                <div class="member-info-item" >

                    <el-form-item label="乘机人类型">
                        <el-input placeholder="姓名" class="input-with-select">
                            <el-select 
                            slot="prepend" 
                            value="1" 
                            placeholder="请选择">
                                <el-option label="成人" value="1"></el-option>
                            </el-select>
                        </el-input>
                    </el-form-item>

                    <el-form-item label="证件类型">
                        <el-input 
                        placeholder="证件号码"  class="input-with-select">
                            <el-select 
                            slot="prepend" 
                            value="1"           
                            placeholder="请选择">
                                <el-option label="身份证" value="1" :checked="true"></el-option>
                            </el-select>
                        </el-input>
                    </el-form-item>

                    <span class="delete-user" @click="handleDeleteUser()">-</span>
                </div>
            </el-form>

            <el-button class="add-member" type='primary' @click="handleAddUsers">添加乘机人</el-button>
        </div>

        <div class="air-column">
            <h2>保险</h2>
            <div>
                <div class="insurance-item">
                    <el-checkbox 
                    label="航空意外险:¥30/份×1  最高赔付260万" 
                    border>
                    </el-checkbox> 
                </div>
            </div>
        </div>

        <div class="air-column">
            <h2>联系人</h2>
            <div class="contact">
                <el-form label-width="60px">
                    <el-form-item label="姓名">
                        <el-input></el-input>
                    </el-form-item>

                    <el-form-item label="手机">
                        <el-input placeholder="请输入内容">
                            <template slot="append">
                            <el-button @click="handleSendCaptcha">发送验证码</el-button>
                            </template>
                        </el-input>
                    </el-form-item>

                    <el-form-item label="验证码">
                        <el-input></el-input>
                    </el-form-item>
                </el-form>   
                <el-button type="warning" class="submit" @click="handleSubmit">提交订单</el-button>
            </div>
        </div>
    </div>
</template>

<script>
export default {
     
     
    methods: {
     
     
        // 添加乘机人
        handleAddUsers(){
     
     
            
        },
        
        // 移除乘机人
        handleDeleteUser(){
     
     

        },
        
        // 发送手机验证码
        handleSendCaptcha(){
     
     
            
        },

        // 提交订单
        handleSubmit(){
     
     
            
        }
    }
}
</script>

<style scoped lang="less">
    .air-column{
     
     
        border-bottom:1px #ddd dashed;
        padding-bottom: 20px;   
        margin-bottom: 20px;
    }

    .air-column h2{
     
     
        margin-bottom: 20px;
        font-size: 22px;
        font-weight: normal;
    }

    /deep/ .el-select .el-input {
     
     
        width: 130px;
    }

    .input-with-select{
     
     
        width:590px;
    }

    .input-with-select /deep/  .el-input-group__prepend {
     
     
        background-color: #fff;
    }
    .member-info /deep/ .el-form-item{
     
     
        margin-bottom:0;
    }

    .member-info-item{
     
     
        border-bottom:1px #eee dashed;
        padding-bottom: 20px;
        position: relative;

        &:first-child{
     
     
            .delete-user{
     
     
                display: none;
            }
        }
    }

    .add-member{
     
     
        margin-top:20px;
    }

    .delete-user{
     
     
        display: block;
        background:#ddd;
        width:16px;
        height:16px;
        font-size:14px;
        text-align: center;
        line-height: 16px;
        color:#fff;
        cursor: pointer;
        border-radius: 50px;
        position:absolute;
        right:-30px;
        top:50%;
    }

    .insurance{
     
     
        > div{
     
     
            margin-top:10px;
        }
    }

    .insurance-item{
     
     
        margin-bottom: 20px;
    }

    .contact{
     
     
        /deep/ .el-input{
     
     
            width:50%;
        }
    }

    .submit{
     
     
        margin: 50px auto;
        display: block;
        width:250px;
        height:50px;
    }
</style>

2.把组件引入到pages/air/order.vue

<template>
    <div class="container">
        <el-row type="flex" justify="space-between">
            <!-- 订单表单 -->
            <OrderForm/>

            <!-- 其他代码... -->
        </el-row>
    </div>
</template>

<script>
import OrderForm from "@/components/air/orderForm.vue";

export default {
     
     
    components: {
     
     
        OrderForm
    }
}
</script>

添加乘机人

点击该按钮添加一个新的乘机人

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aewcihcO-1605265661554)(%E9%97%B2%E4%BA%91%E6%80%BB%E7%AC%94%E8%AE%B0/1560785602730.png)]

说明乘机人是一个列表数据,我们使用数组保存起来,但是一开始需要显示一位乘机人的位置,所以在数组初始化时候添加一位乘机人。

components/air/orderForm.vue

<script>
export default {
     
     
    data(){
     
     
        return {
     
     
            users: [{
     
     
                username: "",
                id: "",
            }]
        }
    },
}
</script>

这样点击添加按钮时候往users数组中添加数据即可,然后再把表单数据双向绑定到users

// 添加乘机人
        handleAddUsers(){
    
    
            console.log(this.users);
            // this.users.push({
    
    
            //     username:'',
            //     id:''
            // })
            this.users=[
                ...this.users,
                {
    
    
                   username:'',
                    id:'' 
                }
            ]
        },

把表单字段双向绑定到users

<template>
<!-- 其他代码 -->

				<div 
                class="member-info-item" 
                v-for="(item, index) in users" 
                :key="index">
                    <el-form-item label="乘机人类型">
                        <el-input 
                        placeholder="姓名" 
                        v-model="item.username" 
                        class="input-with-select">
                            <el-select 
                            slot="prepend" 
                            value="1"  
                            placeholder="请选择">
                                <el-option label="成人" value="1"></el-option>
                            </el-select>
                        </el-input>
                    </el-form-item>

                    <el-form-item label="证件类型">
                        <el-input 
                        placeholder="证件号码" 
                        v-model="item.id"   
                        class="input-with-select">
                            <el-select 
                            slot="prepend" 
                            value="1" 
                            placeholder="请选择">
                                <el-option label="身份证" 
                                           value="1" 
                                           :checked="true">
    							</el-option>
                            </el-select>
                        </el-input>
                    </el-form-item>
					
                	<!-- 移除乘机人按钮 -->
                    <span class="delete-user" 
                          @click="handleDeleteUser(index)">-</span>
                </div>

<!-- 其他代码 -->
</template>

移除乘机人

根据下标移除users列表的元素

// 移除乘机人
handleDeleteUser(index){
    
    
    this.users.splice(index, 1);
},

渲染保险数据

保险的数据来自于后台接口,这也是我们在URL中传递过来的两个参数的作用了,需要他们来请求机票相关的信息。

1.由于数据在表单组件和以后的侧边栏组件都要使用,所以在父组件中调用接口(通常情况下都是父级调用接口比较合理

components/air/order.vue

<template>
    <div class="container">
        <el-row type="flex" justify="space-between">
            <!-- 订单表单 -->
           <orderForm v-if="formData.insurances" :data='formData'  />

            <!-- 其他代码... -->
        </el-row>
    </div>
</template>
<script>
export default {
     
     
    data(){
     
     
        return {
     
     
            //机票信息
            formData:{
     
     },
            },
        }
    },
    
    // 其他代码...
    
    mounted(){
     
     
        const {
     
     query} = this.$route;

        this.$axios({
     
     
            url: `airs/${
       
       query.id}`,
            params: {
     
     
                seat_xid: query.seat_xid
            }
        }).then(res => {
     
     
            this.formData = res.data;
        })
    }
}
</script>    

2.在表单组件中渲染保险数据:利用el-checkbox-group组件

components/air/orderForm.vue

<template>
<!-- 其他代码 -->

		<div class="air-column">
            <h2>保险</h2>
            <div>
                <el-checkbox-group v-model="insurances">
                  <div class="insurance-item" v-for="(item,index) in data.insurances" :key="index">
                    <el-checkbox 
                    :label="item.id" 
                    border>
                    {
   
   {item.type}}:¥{
   
   {item.price}}/份× {
   
   {users.length}}  最高赔付 {
   
   {item.compensation}}
                    </el-checkbox> 
                  </div>
                </el-checkbox-group>
            </div>
        </div>

<!-- 其他代码 -->
</template>
<script>
    export default {
     
     
		props: {
     
     
            // 接收机票信息
            data: {
     
     
                type: Object,
                default: {
     
     }
            }
        },
    }
</script>

拼接订单数据

这是一份提交机票订单的参数文档,我们需要拼接以下的参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iroMnBvh-1605265661556)(%E9%97%B2%E4%BA%91%E6%80%BB%E7%AC%94%E8%AE%B0/1560788151576.png)]

拼接完后在提交事件handleSubmit中先打印出来。

以下代码都在components/air/orderForm.vue中编辑。

新增表单的字段到data

data () {
    
    
        return {
    
    
            users:[
                // 乘机人信息
                // 乘机人应该是一个数组
                // 里面的每个对象都是一个乘机人
                // 添加删除的实话, 只需要 push / splice
                {
    
    
                    username:'xiaobai',
                    id:'441611111111111111'
                }
            ],
            insurances:[],     //保险id
            contactName:'xiaobai',     //联系人名字
            contactPhone:'13722222222',    //电话
            invoice:false,      //是否需要发票
            captcha:'000000'
        }
    },

注意seat_xidair两个字段可以从propsdata中获得

1.用户数据

用户数据已经保存在users中了

2.联系人名字/联系人手机/验证码

<template>
<!-- 其他代码 -->

    <el-form label-width="60px">
        <el-form-item label="姓名">
            <el-input v-model="contactName"></el-input>
        </el-form-item>

        <el-form-item label="手机">
            <el-input placeholder="请输入内容" v-model="contactPhone">
                <template slot="append">
    <el-button @click="handleSendCaptcha">发送验证码</el-button>
                </template>
            </el-input>
        </el-form-item>

        <el-form-item label="验证码">
            <el-input v-model="captcha"></el-input>
        </el-form-item>
    </el-form>  

<!-- 其他代码 -->
</template>

4.测试创建订单的参数

// 提交订单
handleSubmit(){
    
    
    const data = {
    
    
        users: this.users,
        insurances: this.insurances,
        contactName: this.contactName,
        contactPhone: this.contactPhone,
        invoice: this.invoice,
        captcha: this.captcha,
        seat_xid: this.data.seat_infos.seat_xid,
        air: this.data.id
    }
}

手机验证码应该是点击发送验证码后发送到手机的,这里我们模拟手机请求

发送手机验证码

compnents/air/orderForm.vue

// 发送手机验证码
handleSendCaptcha(){
    
    
    if(!this.contactPhone){
    
    
        this.$confirm('手机号码不能为空', '提示', {
    
    
            confirmButtonText: '确定',
            showCancelButton: false,
            type: 'warning'
        })
        return;
    }

    if(this.contactPhone.length !== 11){
    
    
        this.$confirm('手机号码格式错误', '提示', {
    
    
            confirmButtonText: '确定',
            showCancelButton: false,
            type: 'warning'
        })
        return;
    }

    this.$axios({
    
    
        url: `/captchas`,
        method: "POST",
        data: {
    
    
            tel: this.contactPhone
        }
    }).then(res => {
    
    
        const {
    
    code} = res.data;
        this.$confirm(`模拟手机验证码为:${
      
      code}`, '提示', {
    
    
            confirmButtonText: '确定',
            showCancelButton: false,
            type: 'warning'
        })
    })
},

注意目前手机验证码永远都是返回60

提交订单

上面已经准备好创建订单的数据了,接下来就提交订单了,订单提交完后会跳转到微信付款页。

compnents/air/orderForm.vue

// 提交订单
handleSubmit(){
    
    
    const orderData = {
    
    
        users: this.users,
        insurances: this.insurances,
        contactName: this.contactName,
        contactPhone: this.contactPhone,
        invoice: this.invoice,
        captcha: this.captcha,
        seat_xid: this.data.seat_infos.seat_xid,
        air: this.data.id
    }

    const {
    
    user: {
    
    userInfo}} = this.$store.state;

    this.$message({
    
    
        message: "正在生成订单!请稍等",
        type: "success"
    })

    this.$axios({
    
    
        url: `/airorders`,
        method: "POST",
        data: orderData,
        headers: {
    
    
            Authorization: `Bearer ${
      
      userInfo.token || 'NO TOKEN'}`
        }
    }).then(res => {
    
    

        // 跳转到付款页
        this.$router.push({
    
    
            path: "/air/pay"
        });
    }).catch(err => {
    
    
        const {
    
    message} = err.response.data;
        // 警告提示
        this.$confirm(message, '提示', {
    
    
            confirmButtonText: '确定',
            showCancelButton: false,
            type: 'warning'
        })
    })
}

侧边栏组件

侧边栏组件是一个负责展示的组件,其中总价需要在订单表单组件中计算得出,再传递过来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xlcwBdaX-1605265661560)(%E9%97%B2%E4%BA%91%E6%80%BB%E7%AC%94%E8%AE%B0/1560790950171.png)]

思路

  1. 新建侧边栏组件
  2. 计算总额

实现步骤

新建侧边栏组件

1.新建订单表单组件components/air/orderAside.vue,添加以下内容

<template>
    <div class="aside">
        <div class="air-info">
            <el-row type="flex" justify="space-between" class="info-top">
                <div>{
   
   {data.dep_date}}</div>
                <div>{
   
   {data.org_city_name}} - {
   
   {data.dst_city_name}}</div>
            </el-row>    
            <el-row 
            type="flex" 
            justify="space-between" 
            align="middle" 
            class="info-step">
                <el-col :span="5" class="flight-airport">
                    <strong>{
   
   {data.dep_time}}</strong>
                    <span>{
   
   {data.org_airport_name}}{
   
   {data.org_airport_quay}}</span>
                </el-col>
                <el-col :span="14" class="flight-time">
                    <span>--- {
   
   {rankTime}} ---</span>
                    <span>{
   
   {data.airline_name}}{
   
   {data.flight_no}}</span>
                </el-col>
                <el-col :span="5" class="flight-airport">
                    <strong>{
   
   {data.arr_time}}</strong>
                    <span>浦东机场T2</span>
                </el-col>
            </el-row> 
        </div>
        <el-row type="flex" justify="space-between" class="info-bar">
            <span>订单总价</span>
            <span>金额</span>
            <span>数量</span>
        </el-row>
        <el-row type="flex" justify="space-between" class="info-bar">
            <span>成人机票</span>
            <span>¥{
   
   {data.seat_infos.org_settle_price}}</span>
            <span>x1</span>
        </el-row>
        <el-row type="flex" justify="space-between" class="info-bar">
            <span>机建+燃油</span>
            <span>¥{
   
   {data.airport_tax_audlet}}/人/单程</span>
            <span>x1</span>
        </el-row>
        <el-row type="flex" justify="space-between" align="middle" class="info-bar">
            <span>应付总额:</span>
            <span class="price"></span>
        </el-row>           
    </div>
</template>

<script>
export default {
     
     
    props: {
     
     
        data: {
     
     
            type: Object,
            default: {
     
     }
        }
    },

    computed: {
     
     
        rankTime(){
     
     
            // 数据还未请求回来
            if(!this.data.dep_time) return "";

            // 转化为分钟
            const dep = this.data.dep_time.split(":");
            const arr = this.data.arr_time.split(":");
            const depVal = dep[0] * 60 + +dep[1];
            const arrVal = arr[0] * 60 + +arr[1];

            // 到达时间相减得到分钟
            let dis = arrVal - depVal;

            // 如果是第二天凌晨时间段,需要加24小时
            if(dis < 0){
     
     
                dis = arrVal + 24 * 60 - depVal;
            }

            // 得到相差时间
            return `${
       
        Math.floor(dis / 60)}${
       
       dis % 60}分`
        }
    },
}
</script>

<style scoped lang="less">
/*aside*/
.aside{
     
     
    width: 350px;
    height: fit-content;
    border:1px #ddd solid;
}

.air-info{
     
     
    padding:15px;

    .info-top{
     
     
        margin-bottom:10px;
        > div:last-child{
     
     
            font-size:14px;
        }
    }

    .info-step{
     
     
        .flight-airport{
     
     
            strong{
     
     
                display: block;
                font-size: 22px;
                font-weight: normal;
            }

            span{
     
     
                font-size: 12px;
                color:#999;
            }
        }

        .flight-time{
     
     
            text-align: center;
            font-size: 12px;
            color:#999;
            span{
     
     
                display: block;
            }
        }
    }
}

.info-bar{
     
     
    border-top:1px #ddd dashed;
    padding: 10px 15px;
    font-size: 14px;
    color: #666;

    .price{
     
     
        font-size:28px;
        color: orange;
    }
}
</style>

这里的数据展示和功能几乎都是和机票列表是重复的

2.把组件引入到pages/air/order.vue

<template>
    <div class="container">
        <el-row type="flex" justify="space-between">
            <!-- 其他代码... -->

            <!-- 侧边栏 -->
            <OrderAside :data="infoData"/>
        </el-row>
    </div>
</template>

<script>
import OrderForm from "@/components/air/orderForm.vue";
import OrderAside from "@/components/air/OrderAside.vue";

export default {
     
     
    data(){
     
     
        return {
     
     
            // 机票信息
            infoData: {
     
     
                insurances: [], // 初始化保险数据
                seat_infos: {
     
     }
            },
        }
    },
    components: {
     
     
        OrderForm,
        OrderAside
    },
    
    // 其他代码...
}
</script>

计算总额

总金额要使用兄弟组件传值的方式。

在订单表单组件中计算总金额,并传递给父组件

1.父组件

pages/air/order.vue

<template>
    <div class="container">
        <el-row type="flex" justify="space-between">
            <!-- 订单表单 -->
            <OrderForm :data="infoData" @setAllPrice="setAllPrice"/>

            <!-- 侧边栏 -->
            <OrderAside :data="infoData" :allPrice="allPrice"/>
        </el-row>
    </div>
</template>
<script>
// 其他代码...

export default {
     
     
    data(){
     
     
        return {
     
     
            // 机票信息
            infoData: {
     
     
                insurances: [], // 初始化保险数据
                seat_infos: {
     
     }
            },

            allPrice: 0
        }
    },
    
    // 其他代码...

    methods: {
     
     
        setAllPrice(price){
     
     
            this.allPrice = price;
        }
    }
}
</script>

2.订单表单组件

components/air/orderForm.vue

 computed:{
    
    
        totalPrice(){
    
    
            let res=0
            // 机票价格=机票*人数
            res=this.data.seat_infos.org_settle_price*this.users.length
            console.log(res);

            //保险价格=保险*人数
            //几个id就是几份保险,人数是绑定式的,一般要买都买  不买都不买
           this.insurances.forEach(id=>{
    
    
            //    第一层取到选中的保险id
               this.data.insurances.forEach(dataId=>{
    
    
                //    遍历原始数据的id
                   if(dataId.id==id){
    
    

                    res+=dataId.price*this.users.length
                   }
               })
           })
            console.log(res);


            this.$emit('setAllPrice',res)
            return res
        }

    }

computed计算属性的值如果页面中没引用的话函数是不会执行的,所以需要在页面中调用下allPrice.

在页面的template中任意位置加以下代码

<input type="hidden" :value="allPrice">

3.订单侧边栏组件

<template>
 <!-- 其他代码... -->

	<span class="price">¥ {
   
   {allPrice}} </span>

 <!-- 其他代码... -->
</template>
<script>
export default {
     
     
    props: {
     
     
        // 其他代码...
		
        // 总金额
        allPrice: {
     
     
            type: Number,
            default: 0
        }
    },
   
    // 其他代码...
}
</script>

猜你喜欢

转载自blog.csdn.net/weixin_48371382/article/details/109680804