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

文章目录

    • 目标
    • 过程与代码
      • 搜索部分:
      • 搜索按钮
      • 点击搜索按钮路由跳转并传数据
      • search页面隐藏TabBar
      • 分类部分:
      • 数据请求:request、store
      • 显示数据
      • 分类的样式
    • 总代码
      • 修改或添加的文件
      • common.css
      • router的index.js
      • service的home.js
      • store的home.js
      • home-categories.vue
      • home.vue
      • search.vue
      • App.vue
    • 参考

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

目标

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

  • 搜索按钮,点击后跳转至search页面(只是跳转)
  • 动态显示下面的分类,数据来自123.207.32.32:1888/api/home/categories

过程与代码

搜索部分:

搜索按钮

在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;
}

效果:

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

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

先写一个search.vue:

<template>
    <div class="search">
        <h2>searchh2>
    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>searchh2>
    <h2>{{ $route.query.startDay }}
        {{ $route.query.endDay }}h2>
div>

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

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()

效果:可以隐藏。

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

分类部分:

数据请求: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
        }
    }
})

控制台输出一下数据:

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

显示数据

home-categories组件:

<template>
    <div class="home-categories">
        
        <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>

效果:能显示,但要加些样式
【前端】Vue项目:旅游App-(14)home+search:搜索按钮及其路由跳转、分组数据的网络请求request、数据存储store和动态显示_第7张图片

分类的样式

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

// 滚动条
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;
        }
    }
}

效果:

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

总代码

修改或添加的文件

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

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

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">
        
        <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">旅游Appdiv>
            <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>searchh2>
        <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 渐变

你可能感兴趣的:(前端案例,前端,vue.js,旅游)