【前端】Vue项目:旅游App-(14)home+search:搜索按钮及其路由跳转、分组数据的网络请求request、数据存储store和动态显示

本文详细介绍了在一个Vue项目中如何实现搜索功能,包括点击搜索按钮后路由跳转并传递数据到search页面,以及隐藏TabBar。同时,文章还涵盖了从API获取分类数据并显示的过程,涉及到的数据请求使用了Pinia和自定义样式设计。
摘要由CSDN通过智能技术生成

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

目标

在这里插入图片描述

过程与代码

搜索部分:

搜索按钮

在common.css中设置按钮的渐变色:

/* 渐变色 */
--theme-linear-gradient:linear-gradient(90deg,#fa8c1d,#fcaf3f);

html:

<div class="searchBtn">
    开始搜索
</div>

css:

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

    height: 38px;
    font-size: 18px;
    // 渐变色要用image
    background-image:var(--theme-linear-gradient);
    color: #fff;

    border-radius: 20px;
    margin: 20px 20px;
}

效果:

在这里插入图片描述

点击搜索按钮路由跳转并传数据

先写一个search.vue:

<template>
    <div class="search">
        <h2>search</h2>
    </div>
</template>

<script setup>

</script>

<style lang="less" scoped>

</style>

注册路由:

{
   path:'/search',
   component:()=>import("@/views/search/search.vue")
}

在按钮的地方监听点击:

<div class="searchBtn" @click="searchBtnClick()">
   开始搜索
</div>

对应js:

import { useRouter } from 'vue-router'
const router = useRouter()
// 搜索按钮跳转
function searchBtnClick() {
    router.push({
        path:'/search'
    })
}

到这里,点击了之后就可以跳转至search页面了。

一般来说,点击后的跳转会携带一些数据,我们这里用query属性来携带数据。

在home.vue传送数据:

// 搜索按钮跳转
function searchBtnClick() {
    router.push({
        path:'/search',
        query:{
            // 因为是响应式
            startDay:startDay.value,
            endDay:endDay.value
        }
    })
}

在search页面用$route.query显示数据:

<div class="search">
    <h2>search</h2>
    <h2>{{ $route.query.startDay }}
        {{ $route.query.endDay }}</h2>
</div>

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

search页面隐藏TabBar

在之前的博客中我们写了两种隐藏TabBar的方法。前面用的是第二种,这里我们用第一种。

ps:两种方法都可以,工作中具体看哪种更方便就选哪种,这里只是练习。

在路由处设置meta:

{
    path: '/search',
    component: () => import("@/views/search/search.vue"),
    meta: {
        hideTabBar: true
    }
}

在App.vue处增加v-if的判断:

<tab-bar v-if="!$route.meta.hideTabBar"></tab-bar>

js:引入route

import { useRoute } from 'vue-router'

const route=useRoute()

效果:可以隐藏。

在这里插入图片描述

分类部分:

数据请求:request、store

在本项目中已经进行了两次数据请求,因此具体步骤不再赘述。

接口:123.207.32.32:1888/api/home/categories

在service/home中:

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

在store/home中:

const useHomeStore = defineStore('home', {
    state: () => {
        return {
            hotSuggest: [],
            categories:[]
        }
    },
    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
        }
    }
})

控制台输出一下数据:

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

显示数据

home-categories组件:

<template>
    <div class="home-categories">
        <!-- 一般有id就让key绑定id -->
        <template v-for="(item, index) in categoriesData" :key="item.id">
            <div class="item">
                <img :src=item.pictureUrl alt="">
                <div class="text">{{ item.title }}</div>
            </div>
        </template>
    </div>
</template>

<script setup>
import { storeToRefs } from 'pinia';
import useHomeStore from '../../../store/modules/home';

const homeStore = useHomeStore()
homeStore.fetchCategories()
const { categories } = storeToRefs(homeStore)
const categoriesData = categories
</script>

<style lang="less" scoped>

</style>

效果:能显示,但要加些样式
在这里插入图片描述

分类的样式

注意:让左右可以滚动但滚动条不显示的方法:

// 滚动条
overflow-x: auto;

// 滚动条不显示
&::-webkit-scrollbar {
    display: none;
}

整体css:

.home-categories {
    display: flex;
    align-items: center;
    padding: 0 20px;
    margin-top: 20px;
    margin-left: -10px;
    margin-bottom: 10px;
    height: 65px;

    // 滚动条
    overflow-x: auto;

    // 滚动条不显示
    &::-webkit-scrollbar {
        display: none;
    }

    .item {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        padding: 0 12px;

        img {
            width: 32px;
            height: 32px;
        }

        .text {
            height: 20px;
            width: 56px;
            text-align: center;
        }
    }
}

效果:

在这里插入图片描述

总代码

修改或添加的文件

在这里插入图片描述

在这里插入图片描述

common.css

添加渐变色。

:root {
    /* 渐变色 */
    --theme-linear-gradient:linear-gradient(90deg,#fa8c1d,#fcaf3f);
}

router的index.js

添加search页面的路由。

{
   path: '/search',
   component: () => import("@/views/search/search.vue"),
   meta: {
       hideTabBar: true
   }
}

service的home.js

添加对分类数据categories的网络请求。

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

store的home.js

添加对分类数据categories的store数据存储。

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

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

const useHomeStore = defineStore('home', {
    state: () => {
        return {
            hotSuggest: [],
            categories:[]
        }
    },
    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
        }
    }
})

export default useHomeStore

home-categories.vue

封装了分类数据categories页面的组件。

<template>
    <div class="home-categories">
        <!-- 一般有id就让key绑定id -->
        <template v-for="(item, index) in categoriesData" :key="item.id">
            <div class="item">
                <img :src=item.pictureUrl alt="">
                <div class="text">{{ item.title }}</div>
            </div>
        </template>
    </div>
</template>

<script setup>
import { storeToRefs } from 'pinia';
import useHomeStore from '../../../store/modules/home';

const homeStore = useHomeStore()
homeStore.fetchCategories()
const { categories } = storeToRefs(homeStore)
const categoriesData = categories
</script>

<style lang="less" scoped>
.home-categories {
    display: flex;
    align-items: center;
    padding: 0 20px;
    margin-top: 20px;
    margin-left: -10px;
    margin-bottom: 10px;
    height: 65px;

    // 滚动条
    overflow-x: auto;

    // 滚动条不显示
    &::-webkit-scrollbar {
        display: none;
    }

    .item {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        padding: 0 12px;

        img {
            width: 32px;
            height: 32px;
        }

        .text {
            height: 20px;
            width: 56px;
            text-align: center;
        }
    }
}
</style>

home.vue

增加了search和categories的显示。

<template>
    <div class="home">
        <div class="nav-bar">
            <div class="title">旅游App</div>
            <div class="banner">
                <img src="@/assets/img/home/banner.webp" alt="">
            </div>
        </div>
        <div class="search-box">
            <div class="section location">
                <div class="city">
                    <router-link to="/city">{{ cityStore.currentCity.cityName }}</router-link>
                </div>
                <div class="position">
                    <div class="text">我的位置</div>
                    <img src="@/assets/img/home/icon_location.png" alt="">
                </div>
            </div>

            <div class="section time-range" :value="date" @click="showCalendar = true">
                <div class="start">
                    <span>入住</span>
                    <div class="time">
                        {{ startDay }}
                    </div>
                </div>
                <div class="stay">共{{ date }}晚</div>
                <div class="end">
                    <span>离店</span>
                    <div class="time">
                        {{ endDay }}
                    </div>
                </div>
            </div>

            <!-- 日历 -->
            <van-calendar :round="false" v-model:show="showCalendar" type="range" @confirm="onConfirm"
                :show-confirm="false" />

            <!-- 价格人数 -->
            <div class="price-counter section">
                <div class="left">价格不限</div>
                <div class="right">人数不限</div>
            </div>

            <!-- 关键字 -->
            <div class="keyword section">
                <span>关键字/位置/民宿名</span>
            </div>

            <!-- 热门推荐 -->
            <div class="hotSuggest section">
                <template v-for="(item, index) in hotSuggestData" :key="index">
                    <div class="hotSuggestItem">
                        {{ item.tagText.text }}
                    </div>
                </template>
            </div>

            <div class="searchBtn" @click="searchBtnClick()">
                开始搜索
            </div>

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

<script setup>
import useCityStore from '../../store/modules/city';
import useHomeStore from '../../store/modules/home';
import { formatMonthDay, getDiffDate } from '@/utils/formatDate'
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router'
import homeCategories from './cpns/home-categories.vue'

const cityStore = useCityStore()
const homeStore = useHomeStore()
const router = useRouter()

homeStore.fetchCategories()
const { categories } = storeToRefs(homeStore)
console.log(categories)

// 日期
const today = new Date()
const startDay = ref(formatMonthDay(today))
const endDay = ref(formatMonthDay(new Date().setDate(today.getDate() + 1))) //明天写法

// 日历
const date = ref('1');
const showCalendar = ref(false);

const formatDate = (date) => `${date.getMonth() + 1}/${date.getDate()}`;
const onConfirm = (values) => {
    const [start, end] = values;
    showCalendar.value = false;

    startDay.value = formatMonthDay(start)
    endDay.value = formatMonthDay(end)
    date.value = getDiffDate(start, end)
};

// 热门数据
homeStore.fetchHotSuggest()
const { hotSuggest } = storeToRefs(homeStore)
const hotSuggestData = hotSuggest

// 搜索按钮跳转
function searchBtnClick() {
    router.push({
        path: '/search',
        query: {
            // 因为是响应式
            startDay: startDay.value,
            endDay: endDay.value
        }
    })
}
</script>

<style lang="less" scoped>
.home {
    .nav-bar {
        .title {
            height: 46px;

            // flex居中,以后左右有东西可以直接加
            display: flex;
            align-items: center;
            justify-content: center;

            color: var(--primary-color);
            font-size: 16px;
            font-weight: 700;
        }

        .banner {

            // 图片本身大很多,让它大小刚好
            img {
                width: 100%;
            }
        }
    }

    .search-box {

        --van-calendar-popup-height: 100%;

        // search-box里的每个部分都加上section
        // 都有类似的样式
        .section {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            padding: 0 20px;
            color: #999;
            margin-top: 10px;
        }

        .location {
            height: 44px;

            display: flex;
            align-items: center;
            padding: 0 20px;
            color: #53565c;

            .city {
                // flex:1 === flex:1 1 auto 除了position之外的剩余部分都属于city
                flex: 1;
            }

            .position {
                width: 74px;

                display: flex;
                align-items: center;

                .text {
                    font-size: 12px;
                }

                img {
                    width: 20px;
                    margin-left: 5px;
                }
            }
        }

        .time-range {
            display: flex;
            justify-content: space-between;
            height: 45px;

            span {
                font-size: 16px;
            }

            .time {
                color: #53565c;
            }
        }

        .price-counter {
            justify-content: space-between;
            height: 35px;
        }

        .keyword {
            height: 35px;
        }

        .hotSuggest {

            .hotSuggestItem {

                margin: 3px;
                padding: 4px 8px;
                font-size: 12px;
                background-color: #f1f3f5;
                color: #3f4954;
                border-radius: 20px;
            }
        }

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

            height: 38px;
            font-size: 18px;
            // 渐变色要用image
            background-image: var(--theme-linear-gradient);
            color: #fff;

            border-radius: 20px;
            margin: 20px 20px;
        }
    }
}
</style>

search.vue

新增的页面,还没写内容。

<template>
    <div class="search">
        <h2>search</h2>
        <h2>{{ $route.query.startDay }}
            {{ $route.query.endDay }}</h2>
    </div>
</template>

<script setup>

</script>

<style lang="less" scoped>

</style>

App.vue

增加了search的v-if判断是否显示TabBar。

<template>
    <div class="app">
        <router-view/>
        <tab-bar v-if="!$route.meta.hideTabBar"></tab-bar>
    </div>
</template>

<script setup>

import tabBar from './components/tab-bar/tab-bar.vue';
import { useRoute } from 'vue-router'

const route=useRoute()

</script>

<style lang="less" scoped>

</style>

参考

Pinia-学习之路 03,storeToRefs 及 改变数据状态 - 掘金 (juejin.cn)
overflow - CSS(层叠样式表) | MDN (mozilla.org)
::-webkit-scrollbar - CSS(层叠样式表) | MDN (mozilla.org)
【CSS】渐变背景(background-image)_妙趣前端的博客-CSDN博客_background-image 渐变

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

karshey

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

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

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

打赏作者

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

抵扣说明:

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

余额充值