1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <template> <div> <!-- Vant索引栏组件:绑定计算后的字母索引列表 --> <van-index-bar :index-list="computedIndexList"> <!-- 循环渲染按字母分组后的城市数据 --> <div v-for="item in cityList" :key="item.type"> <!-- 索引锚点:与右侧字母索引联动,点击索引跳转到对应锚点位置 --> <van-index-anchor :index="item.type" /> <!-- 城市列表项:点击触发城市选择逻辑 --> <van-cell v-for="data in item.list" :key="data.cityId" :title="data.name" @click="handleCityChange(data)" /> </div> </van-index-bar> </div> </template>
<script setup> import axios from "axios" import { onMounted, ref, computed } from "vue" // 导入Vue3核心API:生命周期、响应式、计算属性 import _ from "lodash" // 导入lodash:用于数组分组 import { IndexBar as vanIndexBar, IndexAnchor as vanIndexAnchor, Cell as vanCell } from "vant" // 导入Vant4索引栏相关组件 import useCityStore from "../store/cityStore" // 导入城市状态管理仓库(Pinia) import { useRouter } from "vue-router" // 导入VueRouter:用于页面跳转
const cityStore = useCityStore() // 初始化Pinia仓库:用于存储选中的城市信息 const router = useRouter() // 初始化路由实例:用于返回上一页
const cityList = ref([]) // 响应式数据:存储按字母分组后的城市列表(格式:[{type: 'A', list: [城市1, 城市2]}, ...])
// 生命周期:组件挂载完成后执行(获取城市列表数据) onMounted(async () => { try { // 调用第三方接口获取原始城市列表数据 let res = await axios({ url: "https://m.maizuo.com/gateway", // 城市列表接口地址 headers: { "x-client-info": '{"a":"3000","ch":"1002","v":"5.2.1","e":"1766027790872690109906945"}', "x-host": "mall.film-ticket.city.list", }, }) // 对原始城市数据进行分组处理(按拼音首字母) cityList.value = filterCity(res.data.data.cities) } catch (error) { // 接口请求失败容错:打印错误信息,避免页面崩溃 console.error("城市列表数据请求失败:", error) } })
/** * 城市数据处理核心函数:将原始城市数组按拼音首字母分组 * @param {Array} cities - 原始城市列表(接口返回的未分组数据) * @returns {Array} 按字母分组后的城市列表([{type: 'A', list: [城市项]}, ...]) */ const filterCity = cities => { // 1. 按城市拼音首字母排序(A-Z):通过首字母ASCII码差值排序 cities.sort( (x, y) => x.pinyin.substring(0, 1).toUpperCase().charCodeAt() - y.pinyin.substring(0, 1).toUpperCase().charCodeAt() ) // 2. lodash分组:按拼音首字母(大写)将城市分组为对象({A: [城市1,城市2], B: [城市3,...], ...}) const groupObj = _.groupBy(cities, item => item.pinyin.substring(0, 1).toUpperCase()) // 3. 转换分组对象为数组:适配Vant IndexBar组件的渲染格式 let newCities = [] for (const e in groupObj) { newCities.push({ type: e, // 字母(A/B/C...) list: groupObj[e], // 该字母下的所有城市 }) } console.log("按字母分组后的城市数据:", newCities) return newCities }
// 计算属性:提取城市分组的字母列表(适配IndexBar的index-list属性) // 响应式:当cityList变化时,自动更新索引列表 const computedIndexList = computed(() => cityList.value.map(item => item.type))
/** * 城市选择事件处理函数 * @param {Object} data - 选中的城市对象(包含cityId/name等字段) */ const handleCityChange = ({ cityId, name }) => { console.log("选中的城市ID/名称:", cityId, name) // 1. 将选中的城市信息存入Pinia仓库(全局共享) cityStore.changeCity(cityId, name) // 2. 路由返回上一页(城市选择完成后返回原页面) router.back() } </script>
|