【前端】Vue项目:旅游App-(15)home:网络请求house数据、动态并组件化展示house列表信息

本项目博客总结:【前端】Vue项目:旅游App-博客总结

目标

完成热门精选的内容。

在这里插入图片描述

过程与代码

content组件

我们将houseList的内容抽取到一个组件中,将其命名为home-content.vue。

<template>
    <div class="content">
        <h2>热门精选</h2>
        <div class="list">

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

<script setup>

</script>

<style lang="less" scoped>
.content {
    padding: 0 20px;
    h2 {
        font-size: 20px;
        font-weight: 700;
    }
}
</style>

请求数据:houseList

数据是分页的。

第1页:123.207.32.32:1888/api/home/houselist?page=1
第2页:123.207.32.32:1888/api/home/houselist?page=2
以此类推。

本项目已经多此实现对数据请求的功能,因此这里不再赘述。

数据输出:

在这里插入图片描述

request

get请求的参数为params,post请求的参数为data。

这里先写死:请求第一页数据。

export function getHouseList() {
    return HYRequest.get({
        url: '/home/houselist',
        params: {
            page: 1
        }
    })
}
store
// home.vue页面所有的进行网络请求和数据都封装到这里

import { defineStore } from "pinia";
import { getHotSuggest, getCategories, getHouseList } from '@/service'

const useHomeStore = defineStore('home', {
    state: () => {
        return {
            hotSuggest: [],
            categories: [],
            houseList: [],
        }
    },
    actions: {
        // 网络请求,由于返回一个promise,要异步async await
        async fetchHotSuggest() {
            const res = await getHotSuggest()
            this.hotSuggest = res.data
            // console.log(res)
        },
        async fetchCategories() {
            const res = await getCategories()
            this.categories = res.data
        },
        async fetchHouseList() {
            const res = await getHouseList()
            this.houseList = res.data
        }
    }
})

export default useHomeStore
控制台输出

数据请求成功。
在这里插入图片描述

动态加载更多列表数据

这里的数据一页只有20条,而我们也在代码中写死参数page=1。
实际上,App中此模块的数据是在下拉过程中加载更多的数据。因此我们要动态地加载数据。
这里要使用Array.push...解构语法。

为什么要解构?
答:网络请求得到的是一个数组,不解构会使store中的数据变成二维数组,这不是我们想要的。

如何动态地加载数据?
我们可以先用一个按钮模拟这个功能,每次点击按钮就加载更多的数据,page的参数可以用currentPage来表示,每点击按钮令currentPage++。currentPage存在store中。

request:

export function getHouseList(currentPage) {
    return HYRequest.get({
        url: '/home/houselist',
        params: {
            page: currentPage
        }
    })
}

store:

// home.vue页面所有的进行网络请求和数据都封装到这里

import { defineStore } from "pinia";
import { getHotSuggest, getCategories, getHouseList } from '@/service'

const useHomeStore = defineStore('home', {
    state: () => {
        return {
            hotSuggest: [],
            categories: [],
            houseList: [],
            currentPage: 1,

        }
    },
    actions: {
        // 网络请求,由于返回一个promise,要异步async await
        async fetchHotSuggest() {
            const res = await getHotSuggest()
            this.hotSuggest = res.data
            // console.log(res)
        },
        async fetchCategories() {
            const res = await getCategories()
            this.categories = res.data
        },
        async fetchHouseList() {
            const res = await getHouseList(this.currentPage)
            this.currentPage++
            this.houseList.push(...res.data)
        }
    }
})

export default useHomeStore

按钮:

<button @click="moreList()">page++</button>
function moreList(){
    homeStore.fetchHouseList()
}

初始状态:说明只请求了第一页数据。

在这里插入图片描述
点一次按钮:请求了第二页数据。
在这里插入图片描述
注意store中currentPage的代码。它表示的是下一次要请求的页面。

根据F12中的网络也可以得知请求的数据:

在这里插入图片描述

house-item组件

显然house-item可能会在项目中多个地方使用,因此我们要把它单独抽为对应组件:

在这里插入图片描述
注意,数据中discoveryContentType表示不同的展示列表数据的方式。上图左为9,右为3.

思路:

  • house-content判断discoveryContentType,为3则引入组件house-item-v3,为9则引入组件house-item-v9
  • house-item中定义props:item
  • house-content中传入数据house-item
阶段1:数据传送

数据的传送情况如下:

在这里插入图片描述
当前效果:

在这里插入图片描述
对应代码:

home-content:

<template>
    <div class="content">
        <h2>热门精选</h2>

        <div class="list">
            <template v-for="(item, index) in houseList" :key="item.data.houseId">
                <houseItemV9 v-if="item.discoveryContentType === 9" :item="item.data"></houseItemV9>
                <houseItemV3 v-else-if="item.discoveryContentType === 3" :item="item.data"></houseItemV3>
            </template>
        </div>
    </div>
</template>

<script setup>
import { storeToRefs } from "pinia";
import useHomeStore from "../../../store/modules/home";
import houseItemV9 from "../../../components/house-item/house-item-v9.vue";
import houseItemV3 from "../../../components/house-item/house-item-v3.vue";


const homeStore = useHomeStore()
homeStore.fetchHouseList()
const { houseList } = storeToRefs(homeStore)
console.log(houseList)

</script>

<style lang="less" scoped>
.content {
    padding: 0 20px;

    h2 {
        font-size: 20px;
        font-weight: 700;
    }
}
</style>

house-item-v9:

<template>
    <div class="house-item">
        <h2>v9 {{item.houseName}}</h2>
    </div>
</template>

<script setup>
defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})
</script>

<style lang="less" scoped>

</style>

house-item-v3:

<template>
    <div class="house-item">
        <h2>v3 {{item.houseName}}</h2>
    </div>
</template>

<script setup>
defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})
</script>

<style lang="less" scoped>

</style>
阶段2:对着目标写样式

评分使用vant库。Rate 评分 - Vant 4 (gitee.io)

要显示小数。

在这里插入图片描述

house-item-v9

效果:

在这里插入图片描述
代码:

<template>
    <div class="house-item">
        <div class="house-inner">
            <div class="image">
                <img :src="item.image.url" alt="">
            </div>
            <div class="info">
                <div class="summary">{{ item.summaryText }}</div>
                <div class="name">{{ item.houseName }}</div>
                <div class="tips">
                    <div class="stars" >
                        <van-rate v-model="starValue" readonly allow-half size="12px"/>
                    </div>
                    <div class="price">
                        ¥{{ item.finalPrice }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';

const props = defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})

const starValue = ref(props.item.commentScore);
</script>

<style lang="less" scoped>
.house-item {
    // 每个item宽度占屏幕一半
    width: 50%;
    margin-top: 20px;

    .house-inner {
        // 信息在img上:子绝父相
        position: relative;

        margin: 5px;

        .image {
            img {
                width: 100%;
                border-radius: 6px;
            }
        }

        .info {
            position: absolute;
            bottom: 0;

            padding: 8px 10px;

            color: #fff;

            .summary {
                font-size: 12px;
            }

            .name {
                // 超过2行就省略号省略...
                overflow: hidden;
                text-overflow: ellipsis;
                display: -webkit-box;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;

                margin: 5px 0;
            }

            .tips{
                display: flex;
                justify-content: space-between;
            }
        }

    }
}
</style>
house-item-v9:debug

出现了一个问题:4.8分只有4颗星。

控制台报错:

在这里插入图片描述
大致意思:modelValue要的是Number而不是String。

查看Vue插件的starValue:是字符串。

在这里插入图片描述
因此我们要把它改成Number。

const starValue = ref(Number(props.item.commentScore));
house-item-v3

icon的引入:Icon 图标 - Vant 4 (gitee.io)

在这里插入图片描述
效果:

在这里插入图片描述
代码:

<template>
    <div class="house-item">
        <div class="house-inner">
            <div class="image">
                <img :src="item.image.url" alt="">
            </div>
            <div class="info">
                <div class="location">
                    <van-icon name="location" color="#808080" />
                    {{ item.location }}
                </div>
                <div class="name">{{ item.houseName }}</div>
                <div class="summary">{{ item.summaryText }}</div>
                <div class="price">
                    <div class="cheap">¥{{ item.finalPrice }}</div>
                    <div class="exp">{{ item.productPrice }}</div>
                    <div class="sale">立减¥{{ item.productPrice - item.finalPrice }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})
</script>

<style lang="less" scoped>
.house-item {
    width: 50%;

    .house-inner {
        margin: 5px;

        .image {
            img {
                width: 100%;
                border-radius: 6px;
            }
        }

        .info {
            padding: 15px 8px 0;

            .location {
                color: #000;
            }

            .name {
                color: #333;
                margin: 5px 0;

                overflow: hidden;
                text-overflow: ellipsis;
                display: -webkit-box;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;
            }

            .summary {
                color: #666;
                font-size: 12px;
            }

            .price {
                display: flex;
                justify-content: left;
                align-items: center;

                margin: 10px 0;

                .cheap {
                    color: var(--primary-color);
                }

                .exp {
                    color: #999;
                    text-decoration: line-through;
                    font-size: 12px;
                    margin: 0 5px;
                }

                .sale {
                    font-size: 12px;
                    color: #fff;
                    border-radius: 10px;
                    padding: 0 3px;
                    background-image: linear-gradient(270deg, #f66, #ff9f9f);
                }
            }
        }
    }
}
</style>
阶段3:总体效果

当前:

在这里插入图片描述
css改为:

.list{
        margin-top: 20px;
        display: flex;
        flex-wrap: wrap;
    }

效果:

在这里插入图片描述

效果

在这里插入图片描述

总代码

修改或添加的文件

在这里插入图片描述
在这里插入图片描述

house-item

展示house信息的列表,分为两种,标号分别为9和3.

house-item-v9

标号为9的展示house信息的组件。

<template>
    <div class="house-item">
        <div class="house-inner">
            <div class="image">
                <img :src="item.image.url" alt="">
            </div>
            <div class="info">
                <div class="summary">{{ item.summaryText }}</div>
                <div class="name">{{ item.houseName }}</div>
                <div class="tips">
                    <div class="stars" >
                        <van-rate v-model="starValue" readonly allow-half size="12px"/>
                    </div>
                    <div class="price">
                        ¥{{ item.finalPrice }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';

const props = defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})

const starValue = ref(Number(props.item.commentScore));
</script>

<style lang="less" scoped>
.house-item {
    // 每个item宽度占屏幕一半
    width: 50%;

    .house-inner {
        // 信息在img上:子绝父相
        position: relative;

        margin: 5px;

        .image {
            img {
                width: 100%;
                border-radius: 6px;
            }
        }

        .info {
            position: absolute;
            bottom: 0;

            padding: 8px 10px;

            color: #fff;

            .summary {
                font-size: 12px;
            }

            .name {
                // 超过2行就省略号省略...
                overflow: hidden;
                text-overflow: ellipsis;
                display: -webkit-box;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;

                margin: 5px 0;
            }

            .tips{
                display: flex;
                justify-content: space-between;
            }
        }

    }
}
</style>
house-item-v3

标号为3的展示house信息的组件。

<template>
    <div class="house-item">
        <div class="house-inner">
            <div class="image">
                <img :src="item.image.url" alt="">
            </div>
            <div class="info">
                <div class="location">
                    <van-icon name="location" color="#808080" />
                    {{ item.location }}
                </div>
                <div class="name">{{ item.houseName }}</div>
                <div class="summary">{{ item.summaryText }}</div>
                <div class="price">
                    <div class="cheap">¥{{ item.finalPrice }}</div>
                    <div class="exp">{{ item.productPrice }}</div>
                    <div class="sale">立减¥{{ item.productPrice - item.finalPrice }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
defineProps({
    item: {
        type: Object,
        default: () => ({})
    }
})
</script>

<style lang="less" scoped>
.house-item {
    width: 50%;

    .house-inner {
        margin: 5px;

        .image {
            img {
                width: 100%;
                border-radius: 6px;
            }
        }

        .info {
            padding: 15px 8px 0;

            .location {
                color: #000;
            }

            .name {
                color: #333;
                margin: 5px 0;

                overflow: hidden;
                text-overflow: ellipsis;
                display: -webkit-box;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;
            }

            .summary {
                color: #666;
                font-size: 12px;
            }

            .price {
                display: flex;
                justify-content: left;
                align-items: center;

                margin: 10px 0;

                .cheap {
                    color: var(--primary-color);
                }

                .exp {
                    color: #999;
                    text-decoration: line-through;
                    font-size: 12px;
                    margin: 0 5px;
                }

                .sale {
                    font-size: 12px;
                    color: #fff;
                    border-radius: 10px;
                    padding: 0 3px;
                    background-image: linear-gradient(270deg, #f66, #ff9f9f);
                }
            }
        }
    }
}
</style>

service的home.js

home页面网络请求数据的代码。

// 此文件保存所有home页面的网络请求
import HYRequest from '@/service/request'

export function getHotSuggest() {
    // request的index导出的是一个对象
    return HYRequest.get({
        // 参数也是一个对象
        url: '/home/hotSuggests'
    })
}

export function getCategories() {
    // request的index导出的是一个对象
    return HYRequest.get({
        // 参数也是一个对象
        url: '/home/categories'
    })
}

export function getHouseList(currentPage) {
    return HYRequest.get({
        url: '/home/houselist',
        params: {
            page: currentPage
        }
    })
}

store的home.js

管理home页面网络请求的数据的代码。

// home.vue页面所有的进行网络请求和数据都封装到这里

import { defineStore } from "pinia";
import { getHotSuggest, getCategories, getHouseList } from '@/service'

const useHomeStore = defineStore('home', {
    state: () => {
        return {
            hotSuggest: [],
            categories: [],
            houseList: [],
            currentPage: 1,

        }
    },
    actions: {
        // 网络请求,由于返回一个promise,要异步async await
        async fetchHotSuggest() {
            const res = await getHotSuggest()
            this.hotSuggest = res.data
            // console.log(res)
        },
        async fetchCategories() {
            const res = await getCategories()
            this.categories = res.data
        },
        async fetchHouseList() {
            const res = await getHouseList(this.currentPage)
            this.currentPage++
            this.houseList.push(...res.data)
        }
    }
})

export default useHomeStore

cpns的home-content

抽取了展示house信息的列表。

<template>
    <div class="content">
        <h2>热门精选</h2>

        <div class="list">
            <template v-for="(item, index) in houseList" :key="item.data.houseId">
                <houseItemV9 v-if="item.discoveryContentType === 9" :item="item.data"></houseItemV9>
                <houseItemV3 v-else-if="item.discoveryContentType === 3" :item="item.data"></houseItemV3>
            </template>
        </div>
    </div>
</template>

<script setup>
import { storeToRefs } from "pinia";
import useHomeStore from "../../../store/modules/home";
import houseItemV9 from "../../../components/house-item/house-item-v9.vue";
import houseItemV3 from "../../../components/house-item/house-item-v3.vue";


const homeStore = useHomeStore()
homeStore.fetchHouseList()
const { houseList } = storeToRefs(homeStore)
console.log(houseList)

</script>

<style lang="less" scoped>
.content {
    padding: 0 20px;

    h2 {
        font-size: 20px;
        font-weight: 700;
    }

    .list{
        margin-top: 20px;
        display: flex;
        flex-wrap: wrap;
    }
}
</style>

main.js

引入了vant的Rate和Icon。

参考

HTML文字超过两行以后 就用省略号显示代替 - 简书 (jianshu.com)

/*超过2行就省略*/
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值