文章目录
- 目标
- 过程与代码
- 添加热门数据
- 热门数据样式
- 索引栏索引
- 监听点击、保存数据、回退
- 首页跳转到city页、显示城市
- 效果
- 总代码
- 修改的文件
- city.js
- currentGroupCity.vue
- home.vue
目标
上一篇以indexBar的形式显示了数据:【前端】Vue项目:旅游App-(10)city:以indexBar的形式显示数据
本篇目标:
点击城市:
跳转的city页面:
把点击的城市显示在首页:
注意,city页面添加了热门城市,并修改其索引。
过程与代码
添加热门数据
效果:
数据详情:是hotCities中的每一个元素。
数据包含Id、name等:
代码:
<!-- 热门 -->
<van-index-anchor index="热门" />
<div class="hot">
<template v-for="(item,index) in currentGroup?.hotCities" :key="index">
<div class="hotItem">
{{ item.cityName }}
</div>
</template>
</div>
效果:以国内数据为例。
热门数据样式
.hot{
display: flex;
// 允许换行
flex-wrap: wrap;
padding: 18px 0;
margin-right: 20px;
.hotItem{
height: 28px;
width: 60px;
line-height: 28px;
text-align: center;
background-color: #fff4ec;
border-radius: 15px;
margin: 8px 5px;
font-size: 13px;
}
}
效果:这样也挺好看的。不求跟之前那个一样(之前那个也是随便写的)
索引栏索引
关于索引栏的索引,有几个问题:
- 热门要添加对应的索引,否则会串,对应到A
- 默认的索引是A-Z,但可能不存在某些字母开头的城市,如V
- 索引的值应当是数据中的Group的值
文档索引indexList:
实际需要的索引数值:
html:
<van-index-bar :index-list="indexList">
js:
import { computed } from 'vue';
// 定义数据currentGroup:一个对象
const props = defineProps({
currentGroup: {
type: Object,
default: () => ({})
}
})
const indexList = computed(() => {
// cities是一个数组
const list = props.currentGroup.cities.map(item => item.group)
list.unshift('#')
return list
})
效果:索引对应正确。
监听点击、保存数据、回退
先试试能不能在组件中监听点击事件。若尝试后发现可以,我们就直接这么写;若不行,则使用组件提供的相关事件。
这里可以。
如何将点击的数据传到首页呢?答案是:store。
在store/modules/city的state中添加对象currentCity,用来表示当前点击的数据:要设置默认值,否则第一次城市显示为空
state: () => {
return {
allCity: {},
currentCity:{
// 默认值
cityName:'广州',
}
}
}
点击事件时修改currentCity:可以直接赋值item,而不是item.cityName。
因为在服务器中一般用id来代表数据,而页面又可能需要显示除了id之外的数据,所以我们赋值整个对象。
还要写回退。
点击事件cityClick
:
const cityStore=useCityStore()
const route=useRouter()
function cityClick(item){
cityStore.currentCity=item
// 回退
route.back()
}
首页跳转到city页、显示城市
跳转:router-link to
代码:
<div class="city">
<router-link to="/city">{{ cityStore.currentCity.cityName }}</router-link>
</div>
效果
总代码
修改的文件
city.js
state添加currentCity。
// city.vue页面所有的进行网络请求和数据都封装到这里
import { getAllCity } from "@/service";
import { defineStore } from "pinia";
const useCityStore = defineStore('city', {
state: () => {
return {
allCity: {},
currentCity:{
// 默认值
cityName:'广州',
}
}
},
actions: {
// 调用网络请求
async fetchAllCity() {
const res = await getAllCity()
this.allCity = res.data
}
}
})
export default useCityStore
currentGroupCity.vue
添加热门,修改索引,新增点击事件:点击城市、保存数据、回退。
<template>
<van-index-bar :index-list="indexList">
<!-- 热门 -->
<van-index-anchor index="热门" />
<div class="hot">
<template v-for="(item, index) in currentGroup?.hotCities" :key="index">
<div class="hotItem" @click="cityClick(item)">
{{ item.cityName }}
</div>
</template>
</div>
<template v-for="(item, index) in currentGroup?.cities" :key="index">
<van-index-anchor :index="item.group" />
<template v-for="(itemm, indexx) in item.cities" :key="indexx">
<van-cell :title="itemm.cityName" @click="cityClick(itemm)" />
</template>
</template>
</van-index-bar>
</template>
<script setup>
import { computed } from 'vue';
import useCityStore from '@/store/modules/city'
import { useRouter } from 'vue-router';
// 定义数据currentGroup:一个对象
const props = defineProps({
currentGroup: {
type: Object,
default: () => ({})
}
})
const indexList = computed(() => {
// cities是一个数组
const list = props.currentGroup.cities.map(item => item.group)
list.unshift('#')
return list
})
const cityStore=useCityStore()
const route=useRouter()
function cityClick(item){
cityStore.currentCity=item
// 回退
route.back()
}
</script>
<style lang="less" scoped>
.hot {
display: flex;
// 允许换行
flex-wrap: wrap;
padding: 18px 0;
margin-right: 20px;
.hotItem {
height: 28px;
width: 60px;
line-height: 28px;
text-align: center;
background-color: #fff4ec;
border-radius: 15px;
margin: 8px 5px;
font-size: 13px;
}
}
</style>
home.vue
点击跳转到city页,显示选中的城市。
<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 class="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>
</div>
</template>
<script setup>
import useCityStore from '../../store/modules/city';
const cityStore = useCityStore()
</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%;
}
}
.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;
}
}
}
}
}
</style>