| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 | 
							- <template>
 
-   <view class="city-picker-container">
 
-     <view class="picker-content">
 
-       <view class="header">
 
-         <text class="title">城市地区</text>
 
-       </view>
 
-       <view class="hot-cities-section">
 
-         <view class="sub-title">热门城市</view>
 
-         <view class="hot-cities-grid">
 
-           <view
 
-             class="hot-city-item"
 
-             v-for="city in hotCities"
 
-             :key="city"
 
-             @click="handleHotCityClick(city)"
 
-           >
 
-             {{ city }}
 
-           </view>
 
-         </view>
 
-       </view>
 
-       <view class="divider"></view>
 
-       <!-- 选择器 -->
 
-       <view class="picker-wrapper">
 
-         <view class="picker-header">
 
-           <text>省份</text>
 
-           <text>城市</text>
 
-           <text>区县</text>
 
-         </view>
 
-         <picker-view :value="pickerValue" @change="handlePickerChange" class="picker-view">
 
-           <!-- 省份列 -->
 
-           <picker-view-column>
 
-             <view
 
-               class="picker-item"
 
-               :class="{ 'selected-item': pickerValue[0] === index }"
 
-               v-for="(province, index) in provinces"
 
-               :key="province.name"
 
-             >
 
-               {{ province.name }}
 
-             </view>
 
-           </picker-view-column>
 
-           <!-- 城市列 -->
 
-           <picker-view-column>
 
-             <view
 
-               class="picker-item"
 
-               :class="{ 'selected-item': pickerValue[1] === index }"
 
-               v-for="(city, index) in cities"
 
-               :key="city.name"
 
-             >
 
-               {{ city.name }}
 
-             </view>
 
-           </picker-view-column>
 
-           <!-- 区县列 -->
 
-           <picker-view-column>
 
-             <view
 
-               class="picker-item"
 
-               :class="{ 'selected-item': pickerValue[2] === index }"
 
-               v-for="(area, index) in areas"
 
-               :key="area.name"
 
-             >
 
-               {{ area.name }}
 
-             </view>
 
-           </picker-view-column>
 
-         </picker-view>
 
-       </view>
 
-       <!-- 确认按钮 -->
 
-       <view class="footer">
 
-         <button class="confirm-btn" @click="handleConfirm">确定</button>
 
-       </view>
 
-     </view>
 
-   </view>
 
- </template>
 
- <script setup>
 
- import { ref, watch, computed } from 'vue';
 
- import { cityData, hotCities } from './city-data.js';
 
- const emit = defineEmits(['update:show', 'confirm']);
 
- // --- Data ---
 
- const provinces = ref(cityData);
 
- const cities = ref([]);
 
- const areas = ref([]);
 
- const pickerValue = ref([0, 0, 0]);
 
- const selectedProvince = computed(() => provinces.value[pickerValue.value[0]] || {});
 
- const selectedCity = computed(() => cities.value[pickerValue.value[1]] || {});
 
- const selectedArea = computed(() => areas.value[pickerValue.value[2]] || {});
 
- // 确认选择
 
- const handleConfirm = () => {
 
-   const result = {
 
-     province: selectedProvince.value.name,
 
-     city: selectedCity.value.name,
 
-     area: selectedArea.value.name,
 
-   };
 
-   emit('confirm', result);
 
- };
 
- // picker-view 滚动时触发
 
- const handlePickerChange = (e) => {
 
-   const newPickerValue = e.detail.value;
 
-   const [provinceIndex, cityIndex, areaIndex] = newPickerValue;
 
-   const oldProvinceIndex = pickerValue.value[0];
 
-   const oldCityIndex = pickerValue.value[1];
 
-   // 如果省份改变了
 
-   if (provinceIndex !== oldProvinceIndex) {
 
-     // 更新城市列表和地区列表,并将它们的索引重置为0
 
-     cities.value = provinces.value[provinceIndex].children;
 
-     areas.value = cities.value[0]?.children || [];
 
-     pickerValue.value = [provinceIndex, 0, 0];
 
-   } 
 
-   // 如果城市改变了
 
-   else if (cityIndex !== oldCityIndex) {
 
-     // 更新地区列表,并将地区索引重置为0
 
-     areas.value = cities.value[cityIndex]?.children || [];
 
-     pickerValue.value = [provinceIndex, cityIndex, 0];
 
-   } 
 
-   // 如果只是地区改变
 
-   else {
 
-     pickerValue.value = newPickerValue;
 
-   }
 
- };
 
- // 点击热门城市
 
- // 新方法:遍历cityData找到对应的省市区并更新pickerValue
 
- const handleHotCityClick = (cityName) => {
 
-   let provinceIndex = -1;
 
-   let cityIndex = -1;
 
-   let found = false;
 
-   // 遍历省份
 
-   for (let i = 0; i < provinces.value.length; i++) {
 
-     const province = provinces.value[i];
 
-     // 遍历城市
 
-     if (province.children && province.children.length > 0) {
 
-       for (let j = 0; j < province.children.length; j++) {
 
-         const city = province.children[j];
 
-         // 检查城市名是否匹配(使用 startsWith 来兼容 "杭州" 和 "杭州市")
 
-         if (city.name.startsWith(cityName)) {
 
-           provinceIndex = i;
 
-           cityIndex = j;
 
-           found = true;
 
-           break; // 找到城市,跳出内层循环
 
-         }
 
-       }
 
-     }
 
-     if (found) {
 
-       break; // 找到城市,跳出外层循环
 
-     }
 
-   }
 
-   // 如果找到了对应的城市
 
-   if (found) {
 
-     // 1. 更新城市列表,使其为选中省份的城市列表
 
-     cities.value = provinces.value[provinceIndex].children;
 
-     
 
-     // 2. 更新区县列表,使其为选中城市的区县列表
 
-     //    使用可选链 ?. 防止选中城市没有区县数据(如海南省直辖县)
 
-     areas.value = cities.value[cityIndex]?.children || [];
 
-     
 
-     // 3. 更新 pickerValue,这会驱动 picker-view 滚动到指定位置
 
-     //    区县默认选择第一个 (索引为0)
 
-     pickerValue.value = [provinceIndex, cityIndex, 0];
 
-   } else {
 
-     // 如果在数据中找不到该热门城市,可以给一个提示
 
-     uni.showToast({
 
-       title: `未在数据源中找到 ${cityName}`,
 
-       icon: 'none'
 
-     });
 
-   }
 
- };
 
- // 初始化数据
 
- const initialize = () => {
 
-   const [pIndex, cIndex] = pickerValue.value;
 
-   cities.value = provinces.value[pIndex]?.children || [];
 
-   areas.value = cities.value[cIndex]?.children || [];
 
- };
 
- // 初始化
 
- initialize();
 
- defineExpose({
 
- 	initialize
 
- })
 
- </script>
 
- <style lang="scss" scoped>
 
- .city-picker-container {
 
-   .picker-content {
 
-     width: 100%;
 
-     background-color: #ffffff;
 
-     border-radius: 0 0 24rpx 24rpx;
 
-     display: flex;
 
-     flex-direction: column;
 
-   }
 
- }
 
- .header {
 
-   padding: 36rpx 0 0 28rpx;
 
-   position: relative;
 
-   .title {
 
-     font-family: PingFang-SC, PingFang-SC;
 
-     font-weight: bold;
 
-     font-size: 36rpx;
 
-     color: #252525;
 
-     line-height: 36rpx;
 
-   }
 
- }
 
- .hot-cities-section {
 
-   padding: 0 28rpx;
 
-   margin-top: 40rpx;
 
-   .sub-title {
 
-     font-family: PingFangSC, PingFang SC;
 
-     font-weight: 400;
 
-     font-size: 24rpx;
 
-     color: #989998;
 
-     line-height: 24rpx;
 
-   }
 
-   .hot-cities-grid {
 
-     display: grid;
 
-     grid-template-columns: repeat(4, 1fr);
 
-     gap: 20rpx;
 
- 	margin: 24rpx 0;
 
-   }
 
-   .hot-city-item {
 
-     border-radius: 6rpx;
 
-     border: 1rpx solid #E5E7EB;
 
-     padding: 13rpx 0;
 
-     font-family: PingFangSC, PingFang SC;
 
-     font-weight: 400;
 
-     font-size: 24rpx;
 
-     color: #252525;
 
-     line-height: 33rpx;
 
-     text-align: center;
 
-   }
 
- }
 
- .divider {
 
-   height: 14rpx;
 
-   background: #F7F7F7;
 
- }
 
- .picker-wrapper {
 
-   .picker-header {
 
-     display: flex;
 
-     justify-content: space-around;
 
-     padding: 20rpx 0;
 
-     font-family: PingFang-SC, PingFang-SC;
 
-     font-weight: bold;
 
-     font-size: 28rpx;
 
-     color: #252525;
 
-     line-height: 36rpx;
 
-   }
 
- }
 
- .picker-view {
 
-   width: 100%;
 
-   height: 380rpx;
 
- }
 
- .picker-item {
 
-   display: flex;
 
-   align-items: center;
 
-   justify-content: center;
 
-   font-size: 30rpx;
 
-   color: #A4A4A4;
 
-   transition: all 0.2s;
 
-   
 
-   &.selected-item {
 
-     font-size: 32rpx;
 
-     color: #252525;
 
-   }
 
- }
 
- .footer {
 
-   padding: 64rpx 147rpx 40rpx;
 
-   .confirm-btn {
 
-     width: 100%;
 
-     height: 80rpx;
 
-     line-height: 88rpx;
 
-     background: #B7F358;
 
-     border-radius: 45rpx;
 
-     font-family: PingFang-SC, PingFang-SC;
 
-     font-weight: bold;
 
-     font-size: 28rpx;
 
-     color: #252525;
 
-     line-height: 80rpx;
 
- 	letter-spacing: 2rpx;
 
-     border: none;
 
-     &:after {
 
-       border: none;
 
-     }
 
-   }
 
- }
 
- </style>
 
 
  |